// 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; function startup() { startup.globalVariables(); startup.globalTimeouts(); startup.globalConsole(); startup.processAssert(); startup.processNextTick(); startup.processStdio(); startup.processKillAndExit(); startup.processSignalHandlers(); startup.removedMethods(); startup.resolveArgv0(); if (startup.runThirdPartyMain()) { return; } if (startup.runDebugger()) { return; } if (startup.runScript()) { return; } if (startup.runEval()) { return; } startup.runRepl(); } startup.globalVariables = function() { global.process = process; global.global = global; global.GLOBAL = global; global.root = global; global.Buffer = NativeModule.require('buffer').Buffer; }; startup.globalTimeouts = function() { 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); }; global.clearTimeout = function() { var t = NativeModule.require('timers'); return t.clearTimeout.apply(this, arguments); }; global.clearInterval = function() { var t = NativeModule.require('timers'); return t.clearInterval.apply(this, arguments); }; }; startup.globalConsole = function() { global.__defineGetter__('console', function() { return NativeModule.require('console'); }); }; startup._lazyConstants = null; startup.lazyConstants = function() { if (!startup._lazyConstants) { startup._lazyConstants = process.binding('constants'); } return startup._lazyConstants; }; var assert; startup.processAssert = function() { // Note that calls to assert() are pre-processed out by JS2C for the // normal build of node. They persist only in the node_g build. // Similarly for debug(). assert = process.assert = function(x, msg) { if (!x) throw new Error(msg || 'assertion error'); }; }; startup.processNextTick = function() { var nextTickQueue = []; process._tickCallback = function() { var l = nextTickQueue.length; if (l === 0) return; 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 } nextTickQueue.splice(0, l); }; process.nextTick = function(callback) { nextTickQueue.push(callback); process._needTickCallback(); }; }; 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; }); var stderr = process.stderr = { writable: true, readable: false, write: process.binding('stdio').writeError }; stderr.end = stderr.destroy = stderr.destroySoon = function() { }; 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; } return stdin; }); process.openStdin = function() { process.stdin.resume(); return process.stdin; }; }; startup.processKillAndExit = function() { process.exit = function(code) { process.emit('exit', code || 0); process.reallyExit(code || 0); }; process.kill = function(pid, sig) { sig = sig || 'SIGTERM'; if (!startup.lazyConstants()[sig]) { throw new Error('Unknown signal: ' + sig); } process._kill(pid, startup.lazyConstants()[sig]); }; }; 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' && startup.lazyConstants()[event]; } // Wrap addListener for the special signal types process.on = process.addListener = function(type, listener) { var ret = addListener.apply(this, arguments); if (isSignal(type)) { if (!signalWatchers.hasOwnProperty(type)) { var b = process.binding('signal_watcher'); var w = new b.SignalWatcher(startup.lazyConstants()[type]); w.callback = function() { process.emit(type); }; signalWatchers[type] = w; w.start(); } else if (this.listeners(type).length === 1) { signalWatchers[event].start(); } } return ret; }; process.removeListener = function(type, listener) { var ret = removeListener.apply(this, arguments); if (isSignal(type)) { assert(signalWatchers.hasOwnProperty(type)); if (this.listeners(type).length === 0) { signalWatchers[type].stop(); } } return ret; }; }; startup._removedProcessMethods = { 'assert': 'process.assert() use require("assert").ok() instead', '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', }; startup.removedMethods = function() { for (var method in startup._removedProcessMethods) { var reason = startup._removedProcessMethods[method]; process[method] = startup._removedMethod(reason); } }; 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]); } }; 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; }; startup.runDebugger = function() { if (!(process.argv[1] == 'debug')) { return; } // Start the debugger agent var d = NativeModule.require('_debugger'); d.start(); return true; }; startup.runScript = function() { if (!process.argv[1]) { return; } // 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'); // REMOVEME: nextTick should not be necessary. This hack to get // test/simple/test-exception-handler2.js working. process.nextTick(Module.runMain); return true; }; startup.runEval = function() { // -e, --eval if (!process._eval) { return; } var Module = NativeModule.require('module'); var rv = new Module()._compile('return eval(process._eval)', 'eval'); console.log(rv); return true; }; startup.runRepl = function() { var Module = NativeModule.require('module'); // REPL Module.requireRepl().start(); }; // 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; function NativeModule(id) { this.filename = id + '.js'; this.id = id; this.exports = {}; this.loaded = false; } NativeModule._source = process.binding('natives'); NativeModule._cache = {}; NativeModule.require = function(id) { if (id == 'native_module') { return NativeModule; } var cached = NativeModule.getCached(id); if (cached) { return cached.exports; } if (!NativeModule.exists(id)) { throw new Error('No such native module ' + id); } var nativeModule = new NativeModule(id); nativeModule.compile(); nativeModule.cache(); return nativeModule.exports; }; NativeModule.getCached = function(id) { return NativeModule._cache[id]; } NativeModule.exists = function(id) { return (id in NativeModule._source); } 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(); });