0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-22 07:37:56 +01:00
nodejs/src/node.js
isaacs 27d8b059fa Pause process.stdin in stdin getter
Otherwise, it'll be ref'ed, and keep the process hanging.
2012-02-21 15:26:35 -08:00

553 lines
16 KiB
JavaScript

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// 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() {
var EventEmitter = NativeModule.require('events').EventEmitter;
process.__proto__ = EventEmitter.prototype;
process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated
startup.globalVariables();
startup.globalTimeouts();
startup.globalConsole();
startup.processAssert();
startup.processNextTick();
startup.processStdio();
startup.processKillAndExit();
startup.processSignalHandlers();
startup.processChannel();
startup.resolveArgv0();
// There are various modes that Node can run in. The most common two
// are running from a script and running the REPL - but there are a few
// others like the debugger or running --eval arguments. Here we decide
// which mode we run in.
if (NativeModule.exists('_third_party_main')) {
// 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.
process.nextTick(function() {
NativeModule.require('_third_party_main');
});
} else if (process.argv[1] == 'debug') {
// Start the debugger agent
var d = NativeModule.require('_debugger');
d.start();
} else if (process._eval != null) {
// User passed '-e' or '--eval' arguments to Node.
var Module = NativeModule.require('module');
var path = NativeModule.require('path');
var cwd = process.cwd();
var module = new Module('eval');
module.filename = path.join(cwd, 'eval');
module.paths = Module._nodeModulePaths(cwd);
var result = module._compile('return eval(process._eval)', 'eval');
if (process._print_eval) console.log(result);
} else if (process.argv[1]) {
// make process.argv[1] into a full path
var path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);
// If this is a worker in cluster mode, start up the communiction
// channel.
if (process.env.NODE_UNIQUE_ID) {
var cluster = NativeModule.require('cluster');
cluster._setupWorker();
}
var Module = NativeModule.require('module');
// REMOVEME: nextTick should not be necessary. This hack to get
// test/simple/test-exception-handler2.js working.
// Main entry point into most programs:
process.nextTick(Module.runMain);
} else {
var Module = NativeModule.require('module');
// If stdin is a TTY.
if (NativeModule.require('tty').isatty(0)) {
// REPL
var repl = Module.requireRepl().start('> ', null, null, true);
} else {
// Read all of stdin - execute it.
process.stdin.resume();
process.stdin.setEncoding('utf8');
var code = '';
process.stdin.on('data', function(d) {
code += d;
});
process.stdin.on('end', function() {
new Module()._compile(code, '[stdin]');
});
}
}
}
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;
var q = nextTickQueue;
nextTickQueue = [];
try {
for (var i = 0; i < l; i++) q[i]();
}
catch (e) {
if (i + 1 < l) {
nextTickQueue = q.slice(i + 1).concat(nextTickQueue);
}
if (nextTickQueue.length) {
process._needTickCallback();
}
throw e; // process.nextTick error, or 'error' event on first tick
}
};
process.nextTick = function(callback) {
nextTickQueue.push(callback);
process._needTickCallback();
};
};
function errnoException(errorno, syscall) {
// TODO make this more compatible with ErrnoException from src/node.cc
// Once all of Node is using this function the ErrnoException from
// src/node.cc should be removed.
var e = new Error(syscall + ' ' + errorno);
e.errno = e.code = errorno;
e.syscall = syscall;
return e;
}
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 = NativeModule.require('tty');
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = NativeModule.require('fs');
stream = new fs.SyncWriteStream(fd);
stream._type = 'fs';
break;
case 'PIPE':
var net = NativeModule.require('net');
stream = 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
stream.readable = false;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
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;
}
startup.processStdio = function() {
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);
};
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);
};
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 = NativeModule.require('tty');
stdin = new tty.ReadStream(fd);
break;
case 'FILE':
var fs = NativeModule.require('fs');
stdin = new fs.ReadStream(null, {fd: fd});
break;
case 'PIPE':
var net = NativeModule.require('net');
stdin = new net.Stream(fd);
stdin.readable = 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. Call pause() explicitly to unref() it.
stdin.pause();
return stdin;
});
process.openStdin = function() {
process.stdin.resume();
return process.stdin;
};
};
startup.processKillAndExit = function() {
var exiting = false;
process.exit = function(code) {
if (!exiting) {
exiting = true;
process.emit('exit', code || 0);
}
process.reallyExit(code || 0);
};
process.kill = function(pid, sig) {
var r;
// preserve null signal
if (0 === sig) {
r = process._kill(pid, 0);
} else {
sig = sig || 'SIGTERM';
if (startup.lazyConstants()[sig]) {
r = process._kill(pid, startup.lazyConstants()[sig]);
} else {
throw new Error('Unknown signal: ' + sig);
}
}
if (r) {
throw errnoException(errno, 'kill');
}
};
};
startup.processSignalHandlers = function() {
// Load events module in order to access prototype elements on process like
// process.addListener.
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[type].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.processChannel = function() {
// 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) {
assert(parseInt(process.env.NODE_CHANNEL_FD) >= 0);
var cp = NativeModule.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();
assert(process.send);
}
}
startup.resolveArgv0 = function() {
var cwd = process.cwd();
// 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 path = NativeModule.require('path');
process.argv[0] = path.resolve(process.argv[0]);
};
// 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').NodeScript;
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);
}
process.moduleLoadList.push('NativeModule ' + 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 NativeModule._source.hasOwnProperty(id);
}
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;
};
// Wrap a core module's method in a wrapper that will warn on first use
// and then return the result of invoking the original function. After
// first being called the original method is restored.
NativeModule.prototype.deprecate = function(method, message) {
var original = this.exports[method];
var self = this;
var warned = false;
message = message || '';
Object.defineProperty(this.exports, method, {
enumerable: false,
value: function() {
if (!warned) {
warned = true;
message = self.id + '.' + method + ' is deprecated. ' + message;
var moduleIdCheck = new RegExp('\\b' + self.id + '\\b');
if (moduleIdCheck.test(process.env.NODE_DEBUG))
console.trace(message);
else
console.error(message);
self.exports[method] = original;
}
return original.apply(this, arguments);
}
});
};
startup();
});