From 91cc2d8c4bff5c5fbacd757f38b9a8c690dce131 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20Geisendo=CC=88rfer?= Date: Mon, 24 Jan 2011 23:15:10 +0100 Subject: [PATCH] Restructure src/node.js startup code This patch introduces a logical structure and sequence for the bootstrap code found src/node.js. --- src/node.js | 540 ++++++++++++--------- .../message/undefined_reference_in_new_context.out | 4 +- 2 files changed, 308 insertions(+), 236 deletions(-) diff --git a/src/node.js b/src/node.js index 5e0f12b..a2a5f3f 100644 --- a/src/node.js +++ b/src/node.js @@ -1,167 +1,210 @@ +// Hello, and welcome to hacking node.js! +// +// This file is invoked by node::Load in src/node.cc, and responsible for +// bootstrapping the node.js core. Special caution is given to the performance +// of the startup process, so many dependencies are invoked lazily. (function(process) { - global = this; - global.process = process; - global.global = global; - global.GLOBAL = global; - global.root = global; - - /** deprecation errors ************************************************/ - - function removed(reason) { - return function() { - throw new Error(reason); - }; - } - - process.debug = - removed('process.debug() use console.error() instead'); - process.error = - removed('process.error() use console.error() instead'); - process.watchFile = - removed('process.watchFile() has moved to fs.watchFile()'); - process.unwatchFile = - removed('process.unwatchFile() has moved to fs.unwatchFile()'); - process.mixin = - removed('process.mixin() has been removed.'); - process.createChildProcess = - removed('childProcess API has changed. See doc/api.txt.'); - process.inherits = - removed('process.inherits() has moved to sys.inherits.'); - process._byteLength = - removed('process._byteLength() has moved to Buffer.byteLength'); - - process.assert = function(x, msg) { - if (!x) throw new Error(msg || 'assertion error'); - }; - var Script = process.binding('evals').Script; - var runInThisContext = Script.runInThisContext; + function startup() { + startup.globalVariables(); + startup.globalTimeouts(); + startup.globalConsole(); - // lazy loaded. - var constants; - function lazyConstants() { - if (!constants) constants = process.binding('constants'); - return constants; - } + startup.processAssert(); + startup.processNextTick(); + startup.processStdio(); + startup.processKillAndExit(); + startup.processSignalHandlers(); + startup.removedMethods(); - // nextTick() + startup.resolveArgv0(); - var nextTickQueue = []; + if (startup.runThirdPartyMain()) { + return; + } - process._tickCallback = function() { - var l = nextTickQueue.length; - if (l === 0) return; + if (startup.runDebugger()) { + return; + } - try { - for (var i = 0; i < l; i++) { - nextTickQueue[i](); - } + if (startup.runScript()) { + return; } - catch (e) { - nextTickQueue.splice(0, i + 1); - if (i + 1 < l) { - process._needTickCallback(); - } - throw e; // process.nextTick error, or 'error' event on first tick + + if (startup.runEval()) { + return; } - nextTickQueue.splice(0, l); - }; + startup.runRepl(); + } - process.nextTick = function(callback) { - nextTickQueue.push(callback); - process._needTickCallback(); + startup.globalVariables = function() { + global.process = process; + global.global = global; + global.GLOBAL = global; + global.root = global; + global.Buffer = NativeModule.require('buffer').Buffer; }; - // Native modules don't need a full require function. So we can bootstrap - // most of the system with this mini module system. - var NativeModule = (function() { - function NativeModule(id) { - this.filename = id + '.js'; - this.id = id; - this.exports = {}; - this.loaded = false; - } + startup.globalTimeouts = function() { + global.setTimeout = function() { + var t = NativeModule.require('timers'); + return t.setTimeout.apply(this, arguments); + }; - NativeModule._source = process.binding('natives'); - NativeModule._cache = {}; + global.setInterval = function() { + var t = NativeModule.require('timers'); + return t.setInterval.apply(this, arguments); + }; - NativeModule.require = function(id) { - if (id == 'native_module') { - return NativeModule; - } + global.clearTimeout = function() { + var t = NativeModule.require('timers'); + return t.clearTimeout.apply(this, arguments); + }; - var cached = NativeModule.getCached(id); - if (cached) { - return cached.exports; - } + global.clearInterval = function() { + var t = NativeModule.require('timers'); + return t.clearInterval.apply(this, arguments); + }; + }; - if (!NativeModule.exists(id)) { - throw new Error('No such native module ' + id); - } + startup.globalConsole = function() { + global.__defineGetter__('console', function() { + return NativeModule.require('console'); + }); + }; - var nativeModule = new NativeModule(id); - nativeModule.compile(); - nativeModule.cache(); + startup._lazyConstants = null; + + startup.lazyConstants = function() { + if (!startup._lazyConstants) { + startup._lazyConstants = process.binding('constants'); + } + return startup._lazyConstants; + }; - return nativeModule.exports; + startup.processAssert = function() { + process.assert = function(x, msg) { + if (!x) { + throw new Error(msg || 'assertion error'); + } }; + }; - NativeModule.getCached = function(id) { - return NativeModule._cache[id]; - } + startup.processNextTick = function() { + var nextTickQueue = []; - NativeModule.exists = function(id) { - return (id in NativeModule._source); - } + process._tickCallback = function() { + var l = nextTickQueue.length; + if (l === 0) return; - NativeModule.getSource = function(id) { - return NativeModule._source[id]; - } + try { + for (var i = 0; i < l; i++) { + nextTickQueue[i](); + } + } + catch (e) { + nextTickQueue.splice(0, i + 1); + if (i + 1 < l) { + process._needTickCallback(); + } + throw e; // process.nextTick error, or 'error' event on first tick + } - NativeModule.wrap = function(script) { - return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; + nextTickQueue.splice(0, l); }; - NativeModule.wrapper = [ - '(function (exports, require, module, __filename, __dirname) { ', - '\n});' - ]; + process.nextTick = function(callback) { + nextTickQueue.push(callback); + process._needTickCallback(); + }; + }; - NativeModule.prototype.compile = function() { - var source = NativeModule.getSource(this.id); - source = NativeModule.wrap(source); + startup.processStdio = function() { + var stdout, stdin; + + process.__defineGetter__('stdout', function() { + if (stdout) return stdout; + + var binding = process.binding('stdio'), + net = NativeModule.require('net'), + fs = NativeModule.require('fs'), + tty = NativeModule.require('tty'), + fd = binding.stdoutFD; + + if (binding.isatty(fd)) { + stdout = new tty.WriteStream(fd); + } else if (binding.isStdoutBlocking()) { + stdout = new fs.WriteStream(null, {fd: fd}); + } else { + stdout = new net.Stream(fd); + // FIXME Should probably have an option in net.Stream to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stdout.readable = false; + } + + return stdout; + }); + + process.__defineGetter__('stdin', function() { + if (stdin) return stdin; + + var binding = process.binding('stdio'), + net = NativeModule.require('net'), + fs = NativeModule.require('fs'), + tty = NativeModule.require('tty'), + fd = binding.openStdin(); + + if (binding.isatty(fd)) { + stdin = new tty.ReadStream(fd); + } else if (binding.isStdinBlocking()) { + stdin = new fs.ReadStream(null, {fd: fd}); + } else { + stdin = new net.Stream(fd); + stdin.readable = true; + } - var fn = runInThisContext(source, this.filename, true); - fn(this.exports, NativeModule.require, this, this.filename); + return stdin; + }); - this.loaded = true; + process.openStdin = function() { + process.stdin.resume(); + return process.stdin; }; + }; - NativeModule.prototype.cache = function() { - NativeModule._cache[this.id] = this; + startup.processKillAndExit = function() { + process.exit = function(code) { + process.emit('exit', code || 0); + process.reallyExit(code || 0); }; - return NativeModule; - })(); + process.kill = function(pid, sig) { + sig = sig || 'SIGTERM'; - var Module = NativeModule.require('module').Module; + if (!startup.lazyConstants()[sig]) { + throw new Error('Unknown signal: ' + sig); + } - // Load events module in order to access prototype elements on process like - // process.addListener. - var events = NativeModule.require('events'); + process._kill(pid, startup.lazyConstants()[sig]); + }; + }; - // Signal Handlers - (function() { + startup.processSignalHandlers = function() { + // Load events module in order to access prototype elements on process like + // process.addListener. + var events = NativeModule.require('events'); var signalWatchers = {}; var addListener = process.addListener; var removeListener = process.removeListener; function isSignal(event) { - return event.slice(0, 3) === 'SIG' && lazyConstants()[event]; + return event.slice(0, 3) === 'SIG' && startup.lazyConstants()[event]; } // Wrap addListener for the special signal types @@ -170,7 +213,7 @@ if (isSignal(type)) { if (!signalWatchers.hasOwnProperty(type)) { var b = process.binding('signal_watcher'); - var w = new b.SignalWatcher(lazyConstants()[type]); + var w = new b.SignalWatcher(startup.lazyConstants()[type]); w.callback = function() { process.emit(type); }; signalWatchers[type] = w; w.start(); @@ -195,157 +238,186 @@ return ret; }; - })(); - - - global.setTimeout = function() { - var t = NativeModule.require('timers'); - return t.setTimeout.apply(this, arguments); }; - global.setInterval = function() { - var t = NativeModule.require('timers'); - return t.setInterval.apply(this, arguments); + startup._removedProcessMethods = { + 'debug': 'process.debug() use console.error() instead', + 'error': 'process.error() use console.error() instead', + 'watchFile': 'process.watchFile() has moved to fs.watchFile()', + 'unwatchFile': 'process.unwatchFile() has moved to fs.unwatchFile()', + 'mixin': 'process.mixin() has been removed.', + 'createChildProcess': 'childProcess API has changed. See doc/api.txt.', + 'inherits': 'process.inherits() has moved to sys.inherits.', + '_byteLength': 'process._byteLength() has moved to Buffer.byteLength', }; - global.clearTimeout = function() { - var t = NativeModule.require('timers'); - return t.clearTimeout.apply(this, arguments); + startup.removedMethods = function() { + for (var method in startup._removedProcessMethods) { + var reason = startup._removedProcessMethods[method]; + process[method] = startup._removedMethod(reason); + } }; - global.clearInterval = function() { - var t = NativeModule.require('timers'); - return t.clearInterval.apply(this, arguments); + startup._removedMethod = function(reason) { + return function() { + throw new Error(reason); + }; }; + startup.resolveArgv0 = function() { + var cwd = process.cwd(); + var isWindows = process.platform === 'win32'; + + // Make process.argv[0] into a full path, but only touch argv[0] if it's + // not a system $PATH lookup. + // TODO: Make this work on Windows as well. Note that "node" might + // execute cwd\node.exe, or some %PATH%\node.exe on Windows, + // and that every directory has its own cwd, so d:node.exe is valid. + var argv0 = process.argv[0]; + if (!isWindows && argv0.indexOf('/') !== -1 && argv0.charAt(0) !== '/') { + var path = NativeModule.require('path'); + process.argv[0] = path.join(cwd, process.argv[0]); + } + }; - var stdout, stdin; + startup.runThirdPartyMain = function() { + // To allow people to extend Node in different ways, this hook allows + // one to drop a file lib/_third_party_main.js into the build directory + // which will be executed instead of Node's normal loading. + if (!NativeModule.exists('_third_party_main')) { + return; + } + process.nextTick(function() { + NativeModule.require('_third_party_main'); + }); + return true; + }; - process.__defineGetter__('stdout', function() { - if (stdout) return stdout; + startup.runDebugger = function() { + if (!(process.argv[1] == 'debug')) { + return; + } - var binding = process.binding('stdio'), - net = NativeModule.require('net'), - fs = NativeModule.require('fs'), - tty = NativeModule.require('tty'), - fd = binding.stdoutFD; + // Start the debugger agent + var d = NativeModule.require('_debugger'); + d.start(); + return true; + }; - if (binding.isatty(fd)) { - stdout = new tty.WriteStream(fd); - } else if (binding.isStdoutBlocking()) { - stdout = new fs.WriteStream(null, {fd: fd}); - } else { - stdout = new net.Stream(fd); - // FIXME Should probably have an option in net.Stream to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stdout.readable = false; + startup.runScript = function() { + if (!process.argv[1]) { + return; } - return stdout; - }); + // make process.argv[1] into a full path + if (!(/^http:\/\//).exec(process.argv[1])) { + var path = NativeModule.require('path'); + process.argv[1] = path.resolve(process.argv[1]); + } + var Module = NativeModule.require('module'); - process.__defineGetter__('stdin', function() { - if (stdin) return stdin; + // REMOVEME: nextTick should not be necessary. This hack to get + // test/simple/test-exception-handler2.js working. + process.nextTick(Module.runMain); - var binding = process.binding('stdio'), - net = NativeModule.require('net'), - fs = NativeModule.require('fs'), - tty = NativeModule.require('tty'), - fd = binding.openStdin(); + return true; + }; - if (binding.isatty(fd)) { - stdin = new tty.ReadStream(fd); - } else if (binding.isStdinBlocking()) { - stdin = new fs.ReadStream(null, {fd: fd}); - } else { - stdin = new net.Stream(fd); - stdin.readable = true; + startup.runEval = function() { + // -e, --eval + if (!process._eval) { + return; } - return stdin; - }); + var Module = NativeModule.require('module'); + var rv = new Module()._compile('return eval(process._eval)', 'eval'); + console.log(rv); + return true; + }; - process.openStdin = function() { - process.stdin.resume(); - return process.stdin; + startup.runRepl = function() { + var Module = NativeModule.require('module'); + // REPL + Module.requireRepl().start(); }; - // Lazy load console object - global.__defineGetter__('console', function() { - return NativeModule.require('console'); - }); + // Below you find a minimal module system, which is used to load the node + // core modules found in lib/*.js. All core modules are compiled into the + // node binary, so they can be loaded faster. + var Script = process.binding('evals').Script; + var runInThisContext = Script.runInThisContext; - global.Buffer = NativeModule.require('buffer').Buffer; + function NativeModule(id) { + this.filename = id + '.js'; + this.id = id; + this.exports = {}; + this.loaded = false; + } - process.exit = function(code) { - process.emit('exit', code || 0); - process.reallyExit(code || 0); - }; + NativeModule._source = process.binding('natives'); + NativeModule._cache = {}; - process.kill = function(pid, sig) { - sig = sig || 'SIGTERM'; - if (!lazyConstants()[sig]) throw new Error('Unknown signal: ' + sig); - process._kill(pid, lazyConstants()[sig]); - }; + NativeModule.require = function(id) { + if (id == 'native_module') { + return NativeModule; + } + var cached = NativeModule.getCached(id); + if (cached) { + return cached.exports; + } - var cwd = process.cwd(); - var path = NativeModule.require('path'); - var isWindows = process.platform === 'win32'; + if (!NativeModule.exists(id)) { + throw new Error('No such native module ' + id); + } - // Make process.argv[0] and process.argv[1] into full paths, but only - // touch argv[0] if it's not a system $PATH lookup. - // TODO: Make this work on Windows as well. Note that "node" might - // execute cwd\node.exe, or some %PATH%\node.exe on Windows, - // and that every directory has its own cwd, so d:node.exe is valid. - var argv0 = process.argv[0]; - if (!isWindows && argv0.indexOf('/') !== -1 && argv0.charAt(0) !== '/') { - process.argv[0] = path.join(cwd, process.argv[0]); - } + var nativeModule = new NativeModule(id); - // To allow people to extend Node in different ways, this hook allows - // one to drop a file lib/_third_party_main.js into the build directory - // which will be executed instead of Node's normal loading. - if (NativeModule.exists('_third_party_main')) { - process.nextTick(function() { - NativeModule.require('_third_party_main'); - }); - return; - } + nativeModule.compile(); + nativeModule.cache(); - if (process.argv[1]) { - if (process.argv[1] == 'debug') { - // Start the debugger agent - var d = NativeModule.require('_debugger'); - d.start(); - return; - } + return nativeModule.exports; + }; - // Load Module - // make process.argv[1] into a full path - if (!(/^http:\/\//).exec(process.argv[1])) { - process.argv[1] = path.resolve(process.argv[1]); - } - // REMOVEME: nextTick should not be necessary. This hack to get - // test/simple/test-exception-handler2.js working. - process.nextTick(Module.runMain); - return; + NativeModule.getCached = function(id) { + return NativeModule._cache[id]; } - if (process._eval) { - // -e, --eval - var rv = new Module()._compile('return eval(process._eval)', 'eval'); - console.log(rv); - return; + NativeModule.exists = function(id) { + return (id in NativeModule._source); } - // REPL - Module.requireRepl().start(); + NativeModule.getSource = function(id) { + return NativeModule._source[id]; + } + + NativeModule.wrap = function(script) { + return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; + }; + + NativeModule.wrapper = [ + '(function (exports, require, module, __filename, __dirname) { ', + '\n});' + ]; + + NativeModule.prototype.compile = function() { + var source = NativeModule.getSource(this.id); + source = NativeModule.wrap(source); + + var fn = runInThisContext(source, this.filename, true); + fn(this.exports, NativeModule.require, this, this.filename); + + this.loaded = true; + }; + + NativeModule.prototype.cache = function() { + NativeModule._cache[this.id] = this; + }; + + startup(); }); diff --git a/test/message/undefined_reference_in_new_context.out b/test/message/undefined_reference_in_new_context.out index 0c6cdf5..7237047 100644 --- a/test/message/undefined_reference_in_new_context.out +++ b/test/message/undefined_reference_in_new_context.out @@ -1,8 +1,8 @@ before node.js:* - throw e; // process.nextTick error, or 'error' event on first tick - ^ + throw e; // process.nextTick error, or 'error' event on first tick + ^ ReferenceError: foo is not defined at evalmachine.:* at Object. (*test*message*undefined_reference_in_new_context.js:*) -- 2.7.4