1 // Copyright Joyent, Inc. and other Node contributors.
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // Hello, and welcome to hacking node.js!
24 // This file is invoked by node::Load in src/node.cc, and responsible for
25 // bootstrapping the node.js core. Special caution is given to the performance
26 // of the startup process, so many dependencies are invoked lazily.
32 var EventEmitter = NativeModule.require('events').EventEmitter;
34 process.__proto__ = Object.create(EventEmitter.prototype, {
36 value: process.constructor
39 EventEmitter.call(process);
41 process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated
43 // do this good and early, since it handles errors.
44 startup.processFatal();
46 startup.globalVariables();
47 startup.globalTimeouts();
48 startup.globalConsole();
50 startup.processAsyncListener();
51 startup.processAssert();
52 startup.processConfig();
53 startup.processNextTick();
54 startup.processStdio();
55 startup.processKillAndExit();
56 startup.processSignalHandlers();
58 startup.processChannel();
60 startup.processRawDebug();
62 startup.resolveArgv0();
64 // There are various modes that Node can run in. The most common two
65 // are running from a script and running the REPL - but there are a few
66 // others like the debugger or running --eval arguments. Here we decide
67 // which mode we run in.
69 if (NativeModule.exists('_third_party_main')) {
70 // To allow people to extend Node in different ways, this hook allows
71 // one to drop a file lib/_third_party_main.js into the build
72 // directory which will be executed instead of Node's normal loading.
73 process.nextTick(function() {
74 NativeModule.require('_third_party_main');
77 } else if (process.argv[1] == 'debug') {
78 // Start the debugger agent
79 var d = NativeModule.require('_debugger');
82 } else if (process._eval != null) {
83 // User passed '-e' or '--eval' arguments to Node.
85 } else if (process.argv[1]) {
86 // make process.argv[1] into a full path
87 var path = NativeModule.require('path');
88 process.argv[1] = path.resolve(process.argv[1]);
90 // If this is a worker in cluster mode, start up the communication
92 if (process.env.NODE_UNIQUE_ID) {
93 var cluster = NativeModule.require('cluster');
94 cluster._setupWorker();
96 // Make sure it's not accidentally inherited by child processes.
97 delete process.env.NODE_UNIQUE_ID;
100 var Module = NativeModule.require('module');
102 if (global.v8debug &&
103 process.execArgv.some(function(arg) {
104 return arg.match(/^--debug-brk(=[0-9]*)?$/);
107 // XXX Fix this terrible hack!
109 // Give the client program a few ticks to connect.
110 // Otherwise, there's a race condition where `node debug foo.js`
111 // will not be able to connect in time to catch the first
112 // breakpoint message on line 1.
114 // A better fix would be to somehow get a message from the
115 // global.v8debug object about a connection, and runMain when
116 // that occurs. --isaacs
118 var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50;
119 setTimeout(Module.runMain, debugTimeout);
122 // Main entry point into most programs:
127 var Module = NativeModule.require('module');
129 // If -i or --interactive were passed, or stdin is a TTY.
130 if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
134 ignoreUndefined: false
136 if (parseInt(process.env['NODE_NO_READLINE'], 10)) {
137 opts.terminal = false;
139 if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) {
140 opts.useColors = false;
142 var repl = Module.requireRepl().start(opts);
143 repl.on('exit', function() {
148 // Read all of stdin - execute it.
149 process.stdin.setEncoding('utf8');
152 process.stdin.on('data', function(d) {
156 process.stdin.on('end', function() {
157 process._eval = code;
158 evalScript('[stdin]');
164 startup.globalVariables = function() {
165 global.process = process;
166 global.global = global;
167 global.GLOBAL = global;
168 global.root = global;
169 global.Buffer = NativeModule.require('buffer').Buffer;
170 process.domain = null;
171 process._exiting = false;
174 startup.globalTimeouts = function() {
175 global.setTimeout = function() {
176 var t = NativeModule.require('timers');
177 return t.setTimeout.apply(this, arguments);
180 global.setInterval = function() {
181 var t = NativeModule.require('timers');
182 return t.setInterval.apply(this, arguments);
185 global.clearTimeout = function() {
186 var t = NativeModule.require('timers');
187 return t.clearTimeout.apply(this, arguments);
190 global.clearInterval = function() {
191 var t = NativeModule.require('timers');
192 return t.clearInterval.apply(this, arguments);
195 global.setImmediate = function() {
196 var t = NativeModule.require('timers');
197 return t.setImmediate.apply(this, arguments);
200 global.clearImmediate = function() {
201 var t = NativeModule.require('timers');
202 return t.clearImmediate.apply(this, arguments);
206 startup.globalConsole = function() {
207 global.__defineGetter__('console', function() {
208 return NativeModule.require('console');
213 startup._lazyConstants = null;
215 startup.lazyConstants = function() {
216 if (!startup._lazyConstants) {
217 startup._lazyConstants = process.binding('constants');
219 return startup._lazyConstants;
222 startup.processFatal = function() {
223 process._fatalException = function(er) {
224 // First run through error handlers from asyncListener.
225 var caught = _errorHandler(er);
228 caught = process.emit('uncaughtException', er);
230 // If someone handled it, then great. Otherwise die in C++ since
231 // that means we'll exit the process, emit the 'exit' event.
234 if (!process._exiting) {
235 process._exiting = true;
236 process.emit('exit', 1);
239 // nothing to be done about it at this point.
242 // if we handled an error, then make sure any ticks get processed
244 var t = setImmediate(process._tickCallback);
245 // Complete hack to make sure any errors thrown from async
246 // listeners don't cause an infinite loop.
255 startup.processAsyncListener = function() {
260 // Stateful flags shared with Environment for quick JS/C++
264 // Prevent accidentally suppressed thrown errors from before/after.
265 var inAsyncTick = false;
267 // To prevent infinite recursion when an error handler also throws
268 // flag when an error is currenly being handled.
269 var inErrorTick = false;
271 // Needs to be the same as src/env.h
274 // _errorHandler is scoped so it's also accessible by _fatalException.
275 _errorHandler = errorHandler;
277 // Needs to be accessible from lib/timers.js so they know when async
278 // listeners are currently in queue. They'll be cleaned up once
279 // references there are made.
280 process._asyncFlags = asyncFlags;
281 process._runAsyncQueue = runAsyncQueue;
282 process._loadAsyncQueue = loadAsyncQueue;
283 process._unloadAsyncQueue = unloadAsyncQueue;
286 process.createAsyncListener = createAsyncListener;
287 process.addAsyncListener = addAsyncListener;
288 process.removeAsyncListener = removeAsyncListener;
290 // Setup shared objects/callbacks with native layer.
291 process._setupAsyncListener(asyncFlags,
298 function popQueue() {
299 if (asyncStack.length > 0)
300 asyncQueue = asyncStack.pop();
305 // Run all the async listeners attached when an asynchronous event is
307 function runAsyncQueue(context) {
309 var queueItem, item, i, value;
312 for (i = 0; i < asyncQueue.length; i++) {
313 queueItem = asyncQueue[i];
314 if (!queueItem.callbacks.create) {
315 queue[i] = queueItem;
318 // Not passing "this" context because it hasn't actually been
319 // instantiated yet, so accessing some of the object properties
320 // can cause a segfault.
321 // Passing the original value will allow users to manipulate the
322 // original value object, while also allowing them to return a
323 // new value for current async call tracking.
324 value = queueItem.callbacks.create(queueItem.value);
325 if (typeof value !== 'undefined') {
327 callbacks: queueItem.callbacks,
338 context._asyncQueue = queue;
341 // Uses the _asyncQueue object attached by runAsyncQueue.
342 function loadAsyncQueue(context) {
343 var queue = context._asyncQueue;
346 asyncStack.push(asyncQueue);
347 asyncQueue = queue.slice();
348 // Since the async listener callback is required, the number of
349 // objects in the asyncQueue implies the number of async listeners
350 // there are to be processed.
351 asyncFlags[kCount] = queue.length;
353 // Run "before" callbacks.
355 for (i = 0; i < queue.length; i++) {
359 before = item.callbacks.before;
360 if (typeof before === 'function')
361 before(context, item.value);
366 // Unload one level of the async stack. Returns true if there are
367 // still listeners somewhere in the stack.
368 function unloadAsyncQueue(context) {
369 var queue = context._asyncQueue;
372 // Run "after" callbacks.
374 for (i = 0; i < queue.length; i++) {
378 after = item.callbacks.after;
379 if (typeof after === 'function')
380 after(context, item.value);
384 // Unload the current queue from the stack.
387 asyncFlags[kCount] = asyncQueue.length;
389 return asyncQueue.length > 0 || asyncStack.length > 0;
392 // Create new async listener object. Useful when instantiating a new
393 // object and want the listener instance, but not add it to the stack.
394 function createAsyncListener(callbacks, value) {
396 callbacks: callbacks,
402 // Add a listener to the current queue.
403 function addAsyncListener(callbacks, value) {
404 // Accept new listeners or previous created listeners.
405 if (typeof callbacks.uid !== 'number')
406 callbacks = createAsyncListener(callbacks, value);
409 // The asyncQueue will be small. Probably always <= 3 items.
410 for (var i = 0; i < asyncQueue.length; i++) {
411 if (callbacks.uid === asyncQueue[i].uid) {
417 // Make sure the callback doesn't already exist in the queue.
419 asyncQueue.push(callbacks);
421 asyncFlags[kCount] = asyncQueue.length;
425 // Remove listener from the current queue and the entire stack.
426 function removeAsyncListener(obj) {
429 for (i = 0; i < asyncQueue.length; i++) {
430 if (obj.uid === asyncQueue[i].uid) {
431 asyncQueue.splice(i, 1);
436 for (i = 0; i < asyncStack.length; i++) {
437 for (j = 0; j < asyncStack[i].length; j++) {
438 if (obj.uid === asyncStack[i][j].uid) {
439 asyncStack[i].splice(j, 1);
445 asyncFlags[kCount] = asyncQueue.length;
448 // Error handler used by _fatalException to run through all error
449 // callbacks in the current asyncQueue.
450 function errorHandler(er) {
458 for (i = 0; i < asyncQueue.length; i++) {
459 item = asyncQueue[i];
462 error = item.callbacks.error;
463 if (typeof error === 'function') {
466 handled = error(item.value, er) || handled;
469 // If the error callback throws then we're going to die
470 // quickly with no chance of recovery. Only thing we're going
471 // to allow is execution of process exit event callbacks.
473 process._exiting = true;
474 process.emit('exit', 1);
481 // Unload the current queue from the stack.
484 return handled && !inAsyncTick;
487 // Used by AsyncWrap::AddAsyncListener() to add an individual listener
488 // to the async queue. It will check the uid of the listener and only
489 // allow it to be added once.
490 function pushListener(obj) {
491 if (!this._asyncQueue)
492 this._asyncQueue = [];
494 var queue = this._asyncQueue;
496 // The asyncQueue will be small. Probably always <= 3 items.
497 for (var i = 0; i < queue.length; i++) {
498 if (obj.uid === queue[i].uid) {
508 // Used by AsyncWrap::RemoveAsyncListener() to remove an individual
509 // listener from the async queue, and return whether there are still
510 // listeners in the queue.
511 function stripListener(obj) {
512 if (!this._asyncQueue || this._asyncQueue.length === 0)
515 // The asyncQueue will be small. Probably always <= 3 items.
516 for (var i = 0; i < this._asyncQueue.length; i++) {
517 if (obj.uid === this._asyncQueue[i].uid) {
518 this._asyncQueue.splice(i, 1);
523 return this._asyncQueue.length > 0;
528 startup.processAssert = function() {
529 assert = process.assert = function(x, msg) {
530 if (!x) throw new Error(msg || 'assertion error');
534 startup.processConfig = function() {
535 // used for `process.config`, but not a real module
536 var config = NativeModule._source.config;
537 delete NativeModule._source.config;
539 // strip the gyp comment line at the beginning
540 config = config.split('\n').slice(1).join('\n').replace(/'/g, '"');
542 process.config = JSON.parse(config, function(key, value) {
543 if (value === 'true') return true;
544 if (value === 'false') return false;
549 startup.processNextTick = function() {
550 var nextTickQueue = [];
551 var asyncFlags = process._asyncFlags;
552 var _runAsyncQueue = process._runAsyncQueue;
553 var _loadAsyncQueue = process._loadAsyncQueue;
554 var _unloadAsyncQueue = process._unloadAsyncQueue;
556 // This tickInfo thing is used so that the C++ code in src/node.cc
557 // can have easy accesss to our nextTick state, and avoid unnecessary
560 // *Must* match Environment::TickInfo::Fields in src/env.h.
565 // *Must* match Environment::AsyncListeners::Fields in src/env.h
568 process.nextTick = nextTick;
569 // Needs to be accessible from beyond this scope.
570 process._tickCallback = _tickCallback;
572 process._setupNextTick(tickInfo, _tickCallback);
574 function tickDone() {
575 if (tickInfo[kLength] !== 0) {
576 if (tickInfo[kLength] <= tickInfo[kIndex]) {
578 tickInfo[kLength] = 0;
580 nextTickQueue.splice(0, tickInfo[kIndex]);
581 tickInfo[kLength] = nextTickQueue.length;
584 tickInfo[kIndex] = 0;
587 // Run callbacks that have no domain.
588 function _tickCallback() {
589 var callback, hasQueue, threw, tock;
591 while (tickInfo[kIndex] < tickInfo[kLength]) {
592 tock = nextTickQueue[tickInfo[kIndex]++];
593 callback = tock.callback;
595 hasQueue = !!tock._asyncQueue;
597 _loadAsyncQueue(tock);
606 _unloadAsyncQueue(tock);
607 if (1e4 < tickInfo[kIndex])
614 function nextTick(callback) {
615 // on the way out, don't bother. it won't get fired anyway.
616 if (process._exiting)
621 _asyncQueue: undefined
624 if (asyncFlags[kCount] > 0)
627 nextTickQueue.push(obj);
632 function evalScript(name) {
633 var Module = NativeModule.require('module');
634 var path = NativeModule.require('path');
635 var cwd = process.cwd();
637 var module = new Module(name);
638 module.filename = path.join(cwd, name);
639 module.paths = Module._nodeModulePaths(cwd);
640 var script = process._eval;
641 if (!Module._contextLoad) {
643 script = 'global.__filename = ' + JSON.stringify(name) + ';\n' +
644 'global.exports = exports;\n' +
645 'global.module = module;\n' +
646 'global.__dirname = __dirname;\n' +
647 'global.require = require;\n' +
648 'return require("vm").runInThisContext(' +
649 JSON.stringify(body) + ', { filename: ' +
650 JSON.stringify(name) + ' });\n';
652 var result = module._compile(script, name + '-wrapper');
653 if (process._print_eval) console.log(result);
656 function createWritableStdioStream(fd) {
658 var tty_wrap = process.binding('tty_wrap');
660 // Note stream._type is used for test-module-load-list.js
662 switch (tty_wrap.guessHandleType(fd)) {
664 var tty = NativeModule.require('tty');
665 stream = new tty.WriteStream(fd);
666 stream._type = 'tty';
668 // Hack to have stream not keep the event loop alive.
669 // See https://github.com/joyent/node/issues/1726
670 if (stream._handle && stream._handle.unref) {
671 stream._handle.unref();
676 var fs = NativeModule.require('fs');
677 stream = new fs.SyncWriteStream(fd);
683 var net = NativeModule.require('net');
684 stream = new net.Socket({
690 // FIXME Should probably have an option in net.Socket to create a
691 // stream from an existing fd which is writable only. But for now
692 // we'll just add this hack and set the `readable` member to false.
693 // Test: ./node test/fixtures/echo.js < /etc/passwd
694 stream.readable = false;
696 stream._type = 'pipe';
698 // FIXME Hack to have stream not keep the event loop alive.
699 // See https://github.com/joyent/node/issues/1726
700 if (stream._handle && stream._handle.unref) {
701 stream._handle.unref();
706 // Probably an error on in uv_guess_handle()
707 throw new Error('Implement me. Unknown stream file type!');
710 // For supporting legacy API we put the FD here.
713 stream._isStdio = true;
718 startup.processStdio = function() {
719 var stdin, stdout, stderr;
721 process.__defineGetter__('stdout', function() {
722 if (stdout) return stdout;
723 stdout = createWritableStdioStream(1);
724 stdout.destroy = stdout.destroySoon = function(er) {
725 er = er || new Error('process.stdout cannot be closed.');
726 stdout.emit('error', er);
729 process.on('SIGWINCH', function() {
730 stdout._refreshSize();
736 process.__defineGetter__('stderr', function() {
737 if (stderr) return stderr;
738 stderr = createWritableStdioStream(2);
739 stderr.destroy = stderr.destroySoon = function(er) {
740 er = er || new Error('process.stderr cannot be closed.');
741 stderr.emit('error', er);
746 process.__defineGetter__('stdin', function() {
747 if (stdin) return stdin;
749 var tty_wrap = process.binding('tty_wrap');
752 switch (tty_wrap.guessHandleType(fd)) {
754 var tty = NativeModule.require('tty');
755 stdin = new tty.ReadStream(fd, {
763 var fs = NativeModule.require('fs');
764 stdin = new fs.ReadStream(null, { fd: fd });
769 var net = NativeModule.require('net');
770 stdin = new net.Socket({
778 // Probably an error on in uv_guess_handle()
779 throw new Error('Implement me. Unknown stdin file type!');
782 // For supporting legacy API we put the FD here.
785 // stdin starts out life in a paused state, but node doesn't
786 // know yet. Explicitly to readStop() it to put it in the
787 // not-reading state.
788 if (stdin._handle && stdin._handle.readStop) {
789 stdin._handle.reading = false;
790 stdin._readableState.reading = false;
791 stdin._handle.readStop();
794 // if the user calls stdin.pause(), then we need to stop reading
795 // immediately, so that the process can close down.
796 stdin.on('pause', function() {
799 stdin._readableState.reading = false;
800 stdin._handle.reading = false;
801 stdin._handle.readStop();
807 process.openStdin = function() {
808 process.stdin.resume();
809 return process.stdin;
813 startup.processKillAndExit = function() {
814 process.exitCode = 0;
815 process.exit = function(code) {
816 if (code || code === 0)
817 process.exitCode = code;
819 if (!process._exiting) {
820 process._exiting = true;
821 process.emit('exit', process.exitCode || 0);
823 process.reallyExit(process.exitCode || 0);
826 process.kill = function(pid, sig) {
829 // preserve null signal
831 err = process._kill(pid, 0);
833 sig = sig || 'SIGTERM';
834 if (startup.lazyConstants()[sig] &&
835 sig.slice(0, 3) === 'SIG') {
836 err = process._kill(pid, startup.lazyConstants()[sig]);
838 throw new Error('Unknown signal: ' + sig);
843 var errnoException = NativeModule.require('util')._errnoException;
844 throw errnoException(err, 'kill');
851 startup.processSignalHandlers = function() {
852 // Load events module in order to access prototype elements on process like
853 // process.addListener.
854 var signalWraps = {};
855 var addListener = process.addListener;
856 var removeListener = process.removeListener;
858 function isSignal(event) {
859 return event.slice(0, 3) === 'SIG' &&
860 startup.lazyConstants().hasOwnProperty(event);
863 // Wrap addListener for the special signal types
864 process.on = process.addListener = function(type, listener) {
865 if (isSignal(type) &&
866 !signalWraps.hasOwnProperty(type)) {
867 var Signal = process.binding('signal_wrap').Signal;
868 var wrap = new Signal();
872 wrap.onsignal = function() { process.emit(type); };
874 var signum = startup.lazyConstants()[type];
875 var err = wrap.start(signum);
878 var errnoException = NativeModule.require('util')._errnoException;
879 throw errnoException(err, 'uv_signal_start');
882 signalWraps[type] = wrap;
885 return addListener.apply(this, arguments);
888 process.removeListener = function(type, listener) {
889 var ret = removeListener.apply(this, arguments);
890 if (isSignal(type)) {
891 assert(signalWraps.hasOwnProperty(type));
893 if (this.listeners(type).length === 0) {
894 signalWraps[type].close();
895 delete signalWraps[type];
904 startup.processChannel = function() {
905 // If we were spawned with env NODE_CHANNEL_FD then load that up and
906 // start parsing data from that stream.
907 if (process.env.NODE_CHANNEL_FD) {
908 var fd = parseInt(process.env.NODE_CHANNEL_FD, 10);
911 // Make sure it's not accidentally inherited by child processes.
912 delete process.env.NODE_CHANNEL_FD;
914 var cp = NativeModule.require('child_process');
916 // Load tcp_wrap to avoid situation where we might immediately receive
918 // FIXME is this really necessary?
919 process.binding('tcp_wrap');
922 assert(process.send);
927 startup.processRawDebug = function() {
928 var format = NativeModule.require('util').format;
929 var rawDebug = process._rawDebug;
930 process._rawDebug = function() {
931 rawDebug(format.apply(null, arguments));
936 startup.resolveArgv0 = function() {
937 var cwd = process.cwd();
938 var isWindows = process.platform === 'win32';
940 // Make process.argv[0] into a full path, but only touch argv[0] if it's
941 // not a system $PATH lookup.
942 // TODO: Make this work on Windows as well. Note that "node" might
943 // execute cwd\node.exe, or some %PATH%\node.exe on Windows,
944 // and that every directory has its own cwd, so d:node.exe is valid.
945 var argv0 = process.argv[0];
946 if (!isWindows && argv0.indexOf('/') !== -1 && argv0.charAt(0) !== '/') {
947 var path = NativeModule.require('path');
948 process.argv[0] = path.join(cwd, process.argv[0]);
952 // Below you find a minimal module system, which is used to load the node
953 // core modules found in lib/*.js. All core modules are compiled into the
954 // node binary, so they can be loaded faster.
956 var ContextifyScript = process.binding('contextify').ContextifyScript;
957 function runInThisContext(code, options) {
958 var script = new ContextifyScript(code, options);
959 return script.runInThisContext();
962 function NativeModule(id) {
963 this.filename = id + '.js';
969 NativeModule._source = process.binding('natives');
970 NativeModule._cache = {};
972 NativeModule.require = function(id) {
973 if (id == 'native_module') {
977 var cached = NativeModule.getCached(id);
979 return cached.exports;
982 if (!NativeModule.exists(id)) {
983 throw new Error('No such native module ' + id);
986 process.moduleLoadList.push('NativeModule ' + id);
988 var nativeModule = new NativeModule(id);
990 nativeModule.cache();
991 nativeModule.compile();
993 return nativeModule.exports;
996 NativeModule.getCached = function(id) {
997 return NativeModule._cache[id];
1000 NativeModule.exists = function(id) {
1001 return NativeModule._source.hasOwnProperty(id);
1004 NativeModule.getSource = function(id) {
1005 return NativeModule._source[id];
1008 NativeModule.wrap = function(script) {
1009 return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
1012 NativeModule.wrapper = [
1013 '(function (exports, require, module, __filename, __dirname) { ',
1017 NativeModule.prototype.compile = function() {
1018 var source = NativeModule.getSource(this.id);
1019 source = NativeModule.wrap(source);
1021 var fn = runInThisContext(source, { filename: this.filename });
1022 fn(this.exports, NativeModule.require, this, this.filename);
1027 NativeModule.prototype.cache = function() {
1028 NativeModule._cache[this.id] = this;