diff options
author | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2016-03-15 16:13:52 -0400 |
---|---|---|
committer | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2016-03-22 19:20:01 -0400 |
commit | 015cef25eb30b8ac447fa855dd434f4da327d884 (patch) | |
tree | d8ad529429cf89027a3198ff24f961cb562dfc1c /lib | |
parent | 2c672891e11618b0ef401012d97524f43564cbb3 (diff) | |
download | android-node-v8-015cef25eb30b8ac447fa855dd434f4da327d884.tar.gz android-node-v8-015cef25eb30b8ac447fa855dd434f4da327d884.tar.bz2 android-node-v8-015cef25eb30b8ac447fa855dd434f4da327d884.zip |
lib,src: refactor src/node.js into internal files
PR-URL: https://github.com/nodejs/node/pull/5103
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/process.js | 186 | ||||
-rw-r--r-- | lib/internal/process/next_tick.js | 157 | ||||
-rw-r--r-- | lib/internal/process/promises.js | 61 | ||||
-rw-r--r-- | lib/internal/process/stdio.js | 161 |
4 files changed, 565 insertions, 0 deletions
diff --git a/lib/internal/process.js b/lib/internal/process.js new file mode 100644 index 0000000000..17ca5bc326 --- /dev/null +++ b/lib/internal/process.js @@ -0,0 +1,186 @@ +'use strict'; + +var _lazyConstants = null; + +function lazyConstants() { + if (!_lazyConstants) { + _lazyConstants = process.binding('constants'); + } + return _lazyConstants; +} + +exports.setup_hrtime = setup_hrtime; +exports.setupConfig = setupConfig; +exports.setupKillAndExit = setupKillAndExit; +exports.setupSignalHandlers = setupSignalHandlers; +exports.setupChannel = setupChannel; +exports.setupRawDebug = setupRawDebug; + + +const assert = process.assert = function(x, msg) { + if (!x) throw new Error(msg || 'assertion error'); +}; + + +function setup_hrtime() { + const _hrtime = process.hrtime; + const hrValues = new Uint32Array(3); + + process.hrtime = function hrtime(ar) { + _hrtime(hrValues); + + if (typeof ar !== 'undefined') { + if (Array.isArray(ar)) { + const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - ar[0]; + const nsec = hrValues[2] - ar[1]; + return [nsec < 0 ? sec - 1 : sec, nsec < 0 ? nsec + 1e9 : nsec]; + } + + throw new TypeError('process.hrtime() only accepts an Array tuple'); + } + + return [ + hrValues[0] * 0x100000000 + hrValues[1], + hrValues[2] + ]; + }; +} + + +function setupConfig(_source) { + // NativeModule._source + // used for `process.config`, but not a real module + var config = _source.config; + delete _source.config; + + // strip the gyp comment line at the beginning + config = config.split('\n') + .slice(1) + .join('\n') + .replace(/"/g, '\\"') + .replace(/'/g, '"'); + + process.config = JSON.parse(config, function(key, value) { + if (value === 'true') return true; + if (value === 'false') return false; + return value; + }); +} + + +function setupKillAndExit() { + + process.exit = function(code) { + if (code || code === 0) + process.exitCode = code; + + if (!process._exiting) { + process._exiting = true; + process.emit('exit', process.exitCode || 0); + } + process.reallyExit(process.exitCode || 0); + }; + + process.kill = function(pid, sig) { + var err; + + if (pid != (pid | 0)) { + throw new TypeError('invalid pid'); + } + + // preserve null signal + if (0 === sig) { + err = process._kill(pid, 0); + } else { + sig = sig || 'SIGTERM'; + if (lazyConstants()[sig] && + sig.slice(0, 3) === 'SIG') { + err = process._kill(pid, lazyConstants()[sig]); + } else { + throw new Error(`Unknown signal: ${sig}`); + } + } + + if (err) { + var errnoException = require('util')._errnoException; + throw errnoException(err, 'kill'); + } + + return true; + }; +} + + +function setupSignalHandlers() { + // Load events module in order to access prototype elements on process like + // process.addListener. + var signalWraps = {}; + + function isSignal(event) { + return typeof event === 'string' && + event.slice(0, 3) === 'SIG' && + lazyConstants().hasOwnProperty(event); + } + + // Detect presence of a listener for the special signal types + process.on('newListener', function(type, listener) { + if (isSignal(type) && + !signalWraps.hasOwnProperty(type)) { + var Signal = process.binding('signal_wrap').Signal; + var wrap = new Signal(); + + wrap.unref(); + + wrap.onsignal = function() { process.emit(type); }; + + var signum = lazyConstants()[type]; + var err = wrap.start(signum); + if (err) { + wrap.close(); + var errnoException = require('util')._errnoException; + throw errnoException(err, 'uv_signal_start'); + } + + signalWraps[type] = wrap; + } + }); + + process.on('removeListener', function(type, listener) { + if (signalWraps.hasOwnProperty(type) && this.listenerCount(type) === 0) { + signalWraps[type].close(); + delete signalWraps[type]; + } + }); +} + + +function setupChannel() { + // If we were spawned with env NODE_CHANNEL_FD then load that up and + // start parsing data from that stream. + if (process.env.NODE_CHANNEL_FD) { + var fd = parseInt(process.env.NODE_CHANNEL_FD, 10); + assert(fd >= 0); + + // Make sure it's not accidentally inherited by child processes. + delete process.env.NODE_CHANNEL_FD; + + var cp = require('child_process'); + + // Load tcp_wrap to avoid situation where we might immediately receive + // a message. + // FIXME is this really necessary? + process.binding('tcp_wrap'); + + cp._forkChild(fd); + assert(process.send); + } +} + + +function setupRawDebug() { + var format = require('util').format; + var rawDebug = process._rawDebug; + process._rawDebug = function() { + rawDebug(format.apply(null, arguments)); + }; +} diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js new file mode 100644 index 0000000000..529645aa8d --- /dev/null +++ b/lib/internal/process/next_tick.js @@ -0,0 +1,157 @@ +'use strict'; + +exports.setup = setupNextTick; + +function setupNextTick() { + const promises = require('internal/process/promises'); + const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks); + var nextTickQueue = []; + var microtasksScheduled = false; + + // Used to run V8's micro task queue. + var _runMicrotasks = {}; + + // *Must* match Environment::TickInfo::Fields in src/env.h. + var kIndex = 0; + var kLength = 1; + + process.nextTick = nextTick; + // Needs to be accessible from beyond this scope. + process._tickCallback = _tickCallback; + process._tickDomainCallback = _tickDomainCallback; + + // This tickInfo thing is used so that the C++ code in src/node.cc + // can have easy access to our nextTick state, and avoid unnecessary + // calls into JS land. + const tickInfo = process._setupNextTick(_tickCallback, _runMicrotasks); + + _runMicrotasks = _runMicrotasks.runMicrotasks; + + function tickDone() { + if (tickInfo[kLength] !== 0) { + if (tickInfo[kLength] <= tickInfo[kIndex]) { + nextTickQueue = []; + tickInfo[kLength] = 0; + } else { + nextTickQueue.splice(0, tickInfo[kIndex]); + tickInfo[kLength] = nextTickQueue.length; + } + } + tickInfo[kIndex] = 0; + } + + function scheduleMicrotasks() { + if (microtasksScheduled) + return; + + nextTickQueue.push({ + callback: runMicrotasksCallback, + domain: null + }); + + tickInfo[kLength]++; + microtasksScheduled = true; + } + + function runMicrotasksCallback() { + microtasksScheduled = false; + _runMicrotasks(); + + if (tickInfo[kIndex] < tickInfo[kLength] || + emitPendingUnhandledRejections()) + scheduleMicrotasks(); + } + + function _combinedTickCallback(args, callback) { + if (args === undefined) { + callback(); + } else { + switch (args.length) { + case 1: + callback(args[0]); + break; + case 2: + callback(args[0], args[1]); + break; + case 3: + callback(args[0], args[1], args[2]); + break; + default: + callback.apply(null, args); + } + } + } + + // Run callbacks that have no domain. + // Using domains will cause this to be overridden. + function _tickCallback() { + var callback, args, tock; + + do { + while (tickInfo[kIndex] < tickInfo[kLength]) { + tock = nextTickQueue[tickInfo[kIndex]++]; + callback = tock.callback; + args = tock.args; + // Using separate callback execution functions allows direct + // callback invocation with small numbers of arguments to avoid the + // performance hit associated with using `fn.apply()` + _combinedTickCallback(args, callback); + if (1e4 < tickInfo[kIndex]) + tickDone(); + } + tickDone(); + _runMicrotasks(); + emitPendingUnhandledRejections(); + } while (tickInfo[kLength] !== 0); + } + + function _tickDomainCallback() { + var callback, domain, args, tock; + + do { + while (tickInfo[kIndex] < tickInfo[kLength]) { + tock = nextTickQueue[tickInfo[kIndex]++]; + callback = tock.callback; + domain = tock.domain; + args = tock.args; + if (domain) + domain.enter(); + // Using separate callback execution functions allows direct + // callback invocation with small numbers of arguments to avoid the + // performance hit associated with using `fn.apply()` + _combinedTickCallback(args, callback); + if (1e4 < tickInfo[kIndex]) + tickDone(); + if (domain) + domain.exit(); + } + tickDone(); + _runMicrotasks(); + emitPendingUnhandledRejections(); + } while (tickInfo[kLength] !== 0); + } + + function TickObject(c, args) { + this.callback = c; + this.domain = process.domain || null; + this.args = args; + } + + function nextTick(callback) { + if (typeof callback !== 'function') + throw new TypeError('callback is not a function'); + // on the way out, don't bother. it won't get fired anyway. + if (process._exiting) + return; + + var args; + if (arguments.length > 1) { + args = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i++) + args[i - 1] = arguments[i]; + } + + nextTickQueue.push(new TickObject(callback, args)); + tickInfo[kLength]++; + } +} diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js new file mode 100644 index 0000000000..165bf3319e --- /dev/null +++ b/lib/internal/process/promises.js @@ -0,0 +1,61 @@ +'use strict'; + +const promiseRejectEvent = process._promiseRejectEvent; +const hasBeenNotifiedProperty = new WeakMap(); +const pendingUnhandledRejections = []; + +exports.setup = setupPromises; + +function setupPromises(scheduleMicrotasks) { + process._setupPromises(function(event, promise, reason) { + if (event === promiseRejectEvent.unhandled) + unhandledRejection(promise, reason); + else if (event === promiseRejectEvent.handled) + rejectionHandled(promise); + else + require('assert').fail('unexpected PromiseRejectEvent'); + }); + + function unhandledRejection(promise, reason) { + hasBeenNotifiedProperty.set(promise, false); + addPendingUnhandledRejection(promise, reason); + } + + function rejectionHandled(promise) { + var hasBeenNotified = hasBeenNotifiedProperty.get(promise); + if (hasBeenNotified !== undefined) { + hasBeenNotifiedProperty.delete(promise); + if (hasBeenNotified === true) { + process.nextTick(function() { + process.emit('rejectionHandled', promise); + }); + } + + } + } + + function emitPendingUnhandledRejections() { + var hadListeners = false; + while (pendingUnhandledRejections.length > 0) { + var promise = pendingUnhandledRejections.shift(); + var reason = pendingUnhandledRejections.shift(); + if (hasBeenNotifiedProperty.get(promise) === false) { + hasBeenNotifiedProperty.set(promise, true); + if (!process.emit('unhandledRejection', reason, promise)) { + // Nobody is listening. + // TODO(petkaantonov) Take some default action, see #830 + } else { + hadListeners = true; + } + } + } + return hadListeners; + } + + function addPendingUnhandledRejection(promise, reason) { + pendingUnhandledRejections.push(promise, reason); + scheduleMicrotasks(); + } + + return emitPendingUnhandledRejections; +} diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js new file mode 100644 index 0000000000..55689069ff --- /dev/null +++ b/lib/internal/process/stdio.js @@ -0,0 +1,161 @@ +'use strict'; + +exports.setup = setupStdio; + +function setupStdio() { + var stdin, stdout, stderr; + + process.__defineGetter__('stdout', function() { + if (stdout) return stdout; + stdout = createWritableStdioStream(1); + stdout.destroy = stdout.destroySoon = function(er) { + er = er || new Error('process.stdout cannot be closed.'); + stdout.emit('error', er); + }; + if (stdout.isTTY) { + process.on('SIGWINCH', function() { + stdout._refreshSize(); + }); + } + return stdout; + }); + + process.__defineGetter__('stderr', function() { + if (stderr) return stderr; + stderr = createWritableStdioStream(2); + stderr.destroy = stderr.destroySoon = function(er) { + er = er || new Error('process.stderr cannot be closed.'); + stderr.emit('error', er); + }; + if (stderr.isTTY) { + process.on('SIGWINCH', function() { + stderr._refreshSize(); + }); + } + return stderr; + }); + + process.__defineGetter__('stdin', function() { + if (stdin) return stdin; + + var tty_wrap = process.binding('tty_wrap'); + var fd = 0; + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + var tty = require('tty'); + stdin = new tty.ReadStream(fd, { + highWaterMark: 0, + readable: true, + writable: false + }); + break; + + case 'FILE': + var fs = require('fs'); + stdin = new fs.ReadStream(null, { fd: fd, autoClose: false }); + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + + // It could be that process has been started with an IPC channel + // sitting on fd=0, in such case the pipe for this fd is already + // present and creating a new one will lead to the assertion failure + // in libuv. + if (process._channel && process._channel.fd === fd) { + stdin = new net.Socket({ + handle: process._channel, + readable: true, + writable: false + }); + } else { + stdin = new net.Socket({ + fd: fd, + readable: true, + writable: false + }); + } + // Make sure the stdin can't be `.end()`-ed + stdin._writableState.ended = true; + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stdin file type!'); + } + + // For supporting legacy API we put the FD here. + stdin.fd = fd; + + // stdin starts out life in a paused state, but node doesn't + // know yet. Explicitly to readStop() it to put it in the + // not-reading state. + if (stdin._handle && stdin._handle.readStop) { + stdin._handle.reading = false; + stdin._readableState.reading = false; + stdin._handle.readStop(); + } + + // if the user calls stdin.pause(), then we need to stop reading + // immediately, so that the process can close down. + stdin.on('pause', function() { + if (!stdin._handle) + return; + stdin._readableState.reading = false; + stdin._handle.reading = false; + stdin._handle.readStop(); + }); + + return stdin; + }); + + process.openStdin = function() { + process.stdin.resume(); + return process.stdin; + }; +} + +function createWritableStdioStream(fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + var tty = require('tty'); + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + break; + + case 'FILE': + var fs = require('fs'); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + stream._type = 'pipe'; + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} |