f2b4854815629c337737b220fa83b0fdeacbbbc5
[platform/upstream/nodejs.git] / src / node.js
1 (function (process) {
2
3 process.global.process = process;
4 process.global.global = process.global;
5 global.GLOBAL = global;
6
7 /** deprecation errors ************************************************/
8
9 function removed (reason) {
10   return function () {
11     throw new Error(reason)
12   }
13 }
14
15 GLOBAL.__module = removed("'__module' has been renamed to 'module'");
16 GLOBAL.include = removed("include(module) has been removed. Use require(module)");
17 GLOBAL.puts = removed("puts() has moved. Use require('sys') to bring it back.");
18 GLOBAL.print = removed("print() has moved. Use require('sys') to bring it back.");
19 GLOBAL.p = removed("p() has moved. Use require('sys') to bring it back.");
20 process.debug = removed("process.debug() has moved. Use require('sys') to bring it back.");
21 process.error = removed("process.error() has moved. Use require('sys') to bring it back.");
22 process.watchFile = removed("process.watchFile() has moved to fs.watchFile()");
23 process.unwatchFile = removed("process.unwatchFile() has moved to fs.unwatchFile()");
24 process.mixin = removed('process.mixin() has been removed.');
25
26 GLOBAL.node = {};
27
28 node.createProcess = removed("node.createProcess() has been changed to process.createChildProcess() update your code");
29 process.createChildProcess = removed("childProcess API has changed. See doc/api.txt.");
30 node.exec = removed("process.exec() has moved. Use require('sys') to bring it back.");
31 node.inherits = removed("node.inherits() has moved. Use require('sys') to access it.");
32 process.inherits = removed("process.inherits() has moved to sys.inherits.");
33
34 node.http = {};
35 node.http.createServer = removed("node.http.createServer() has moved. Use require('http') to access it.");
36 node.http.createClient = removed("node.http.createClient() has moved. Use require('http') to access it.");
37
38 node.tcp = {};
39 node.tcp.createServer = removed("node.tcp.createServer() has moved. Use require('tcp') to access it.");
40 node.tcp.createConnection = removed("node.tcp.createConnection() has moved. Use require('tcp') to access it.");
41
42 node.dns = {};
43 node.dns.createConnection = removed("node.dns.createConnection() has moved. Use require('dns') to access it.");
44
45 /**********************************************************************/
46
47 // Module
48
49 var internalModuleCache = {};
50 var extensionCache = {};
51
52 function Module (id, parent) {
53   this.id = id;
54   this.exports = {};
55   this.parent = parent;
56
57   if (parent) {
58     this.moduleCache = parent.moduleCache;
59   } else {
60     this.moduleCache = {};
61   }
62   this.moduleCache[this.id] = this;
63
64   this.filename = null;
65   this.loaded = false;
66   this.exited = false;
67   this.children = [];
68 };
69
70 function createInternalModule (id, constructor) {
71   var m = new Module(id);
72   constructor(m.exports);
73   m.loaded = true;
74   internalModuleCache[id] = m;
75   return m;
76 };
77
78
79 // This contains the source code for the files in lib/
80 // Like, natives.fs is the contents of lib/fs.js
81 var natives = process.binding('natives');
82
83 function loadNative (id) {
84   var m = new Module(id);
85   internalModuleCache[id] = m;
86   var e = m._compile(natives[id], id);
87   if (e) throw e;
88   m.loaded = true;
89   return m;
90 }
91
92 function requireNative (id) {
93   if (internalModuleCache[id]) return internalModuleCache[id].exports;
94   if (!natives[id]) throw new Error('No such native module ' + id);
95   return loadNative(id).exports;
96 }
97
98
99 process.assert = function (x, msg) {
100   if (!(x)) throw new Error(msg || "assertion error");
101 };
102
103 process.evalcx = process.binding('evals').Script.runInNewContext;
104
105 // Event
106 var eventsModule = createInternalModule
107   ( 'events'
108   , process.compile
109     ( "(function (exports) {" + natives.events + "})"
110     , "events"
111     )
112   );
113 var events = eventsModule.exports;
114
115 // nextTick()
116
117 var nextTickQueue = [];
118
119 process._tickCallback = function () {
120   var l = nextTickQueue.length;
121   while (l--) {
122     var cb = nextTickQueue.shift();
123     cb();
124   }
125 };
126
127 process.nextTick = function (callback) {
128   nextTickQueue.push(callback);
129   process._needTickCallback();
130 };
131
132
133
134
135
136 // Signal Handlers
137
138 function isSignal (event) {
139   return event.slice(0, 3) === 'SIG' && process.hasOwnProperty(event);
140 };
141
142 process.addListener("newListener", function (event) {
143   if (isSignal(event) && process.listeners(event).length === 0) {
144     var b = process.binding('signal_watcher');
145     var w = new b.SignalWatcher(process[event]);
146     w.addListener("signal", function () {
147       process.emit(event);
148     });
149   }
150 });
151
152
153 // Timers
154 function addTimerListener (callback) {
155   var timer = this;
156   // Special case the no param case to avoid the extra object creation.
157   if (arguments.length > 2) {
158     var args = Array.prototype.slice.call(arguments, 2);
159     timer.callback = function () { callback.apply(timer, args); };
160   } else {
161     timer.callback = callback;
162   }
163 }
164
165 global.setTimeout = function (callback, after) {
166   var timer = new process.Timer();
167   addTimerListener.apply(timer, arguments);
168   timer.start(after, 0);
169   return timer;
170 };
171
172 global.setInterval = function (callback, repeat) {
173   var timer = new process.Timer();
174   addTimerListener.apply(timer, arguments);
175   timer.start(repeat, repeat);
176   return timer;
177 };
178
179 global.clearTimeout = function (timer) {
180   if (timer instanceof process.Timer) {
181     timer.stop();
182   }
183 };
184
185 global.clearInterval = global.clearTimeout;
186
187
188
189
190 // Modules
191
192 var debugLevel = parseInt(process.env["NODE_DEBUG"]);
193 function debug (x) {
194   if (debugLevel > 0) {
195     process.binding('stdio').writeError(x + "\n");
196   }
197 }
198
199 var pathModule = createInternalModule
200   ( 'path'
201   , process.compile
202     ( "(function (exports) {" + natives.path + "})"
203     , "path"
204     )
205   );
206
207 var path = pathModule.exports;
208
209 function existsSync (path) {
210   try {
211     process.binding('fs').stat(path);
212     return true;
213   } catch (e) {
214     return false;
215   }
216 }
217
218
219
220 var modulePaths = [];
221
222 if (process.env["HOME"]) {
223   modulePaths.unshift(path.join(process.env["HOME"], ".node_libraries"));
224 }
225
226 if (process.env["NODE_PATH"]) {
227   modulePaths = process.env["NODE_PATH"].split(":").concat(modulePaths);
228 }
229
230
231 /* Sync unless callback given */
232 function findModulePath (id, dirs, callback) {
233   process.assert(dirs.constructor == Array);
234
235   if (/^https?:\/\//.exec(id)) {
236     if (callback) {
237       callback(id);
238     } else {
239       throw new Error("Sync http require not allowed.");
240     }
241     return;
242   }
243
244   if (/\.(js|node)$/.exec(id)) {
245     throw new Error("No longer accepting filename extension in module names");
246   }
247
248   if (dirs.length == 0) {
249     if (callback) {
250       callback();
251     } else {
252       return; // sync returns null
253     }
254   }
255
256   var dir = dirs[0];
257   var rest = dirs.slice(1, dirs.length);
258
259   if (id.charAt(0) == '/') {
260     dir = '';
261     rest = [];
262   }
263
264   var locations = [
265     path.join(dir, id + ".js"),
266     path.join(dir, id + ".node"),
267     path.join(dir, id, "index.js"),
268     path.join(dir, id, "index.node")
269   ];
270
271   var ext;
272   var extensions = Object.keys(extensionCache);
273   for (var i = 0, l = extensions.length; i < l; i++) {
274     var ext = extensions[i];
275     locations.push(path.join(dir, id + ext));
276     locations.push(path.join(dir, id, 'index' + ext));
277   }
278
279   function searchLocations () {
280     var location = locations.shift();
281     if (!location) {
282       return findModulePath(id, rest, callback);
283     }
284
285     // if async
286     if (callback) {
287       path.exists(location, function (found) {
288         if (found) {
289           callback(location);
290         } else {
291           return searchLocations();
292         }
293       });
294
295     // if sync
296     } else {
297       if (existsSync(location)) {
298         return location;
299       } else {
300         return searchLocations();
301       }
302     }
303   }
304   return searchLocations();
305 }
306
307
308 // sync - no i/o performed
309 function resolveModulePath(request, parent) {
310   var id, paths;
311   if (request.charAt(0) == "." && (request.charAt(1) == "/" || request.charAt(1) == ".")) {
312     // Relative request
313     debug("RELATIVE: requested:" + request + " set ID to: "+id+" from "+parent.id);
314
315     var exts = ['js', 'node'], ext;
316     var extensions = Object.keys(extensionCache);
317     for (var i = 0, l = extensions.length; i < l; i++) {
318       var ext = extensions[i];
319       exts.push(ext.slice(1));
320     }
321
322     var parentIdPath = path.dirname(parent.id +
323       (path.basename(parent.filename).match(new RegExp('^index\\.(' + exts.join('|') + ')$')) ? "/" : ""));
324     id = path.join(parentIdPath, request);
325     paths = [path.dirname(parent.filename)];
326   } else {
327     id = request;
328     // debug("ABSOLUTE: id="+id);
329     paths = modulePaths;
330   }
331
332   return [id, paths];
333 }
334
335
336 function loadModule (request, parent, callback) {
337   var resolvedModule = resolveModulePath(request, parent),
338       id = resolvedModule[0],
339       paths = resolvedModule[1];
340
341   debug("loadModule REQUEST  " + (request) + " parent: " + parent.id);
342
343   var cachedModule = internalModuleCache[id] || parent.moduleCache[id];
344
345   if (!cachedModule) {
346     // Try to compile from native modules
347     if (natives[id]) {
348       debug('load native module ' + id);
349       cachedModule = loadNative(id);
350     }
351   }
352
353   if (cachedModule) {
354     debug("found  " + JSON.stringify(id) + " in cache");
355     if (callback) {
356       callback(null, cachedModule.exports);
357     } else {
358       return cachedModule.exports;
359     }
360
361   } else {
362     // Not in cache
363     debug("looking for " + JSON.stringify(id) + " in " + JSON.stringify(paths));
364
365     if (!callback) {
366       // sync
367       var filename = findModulePath(request, paths);
368       if (!filename) {
369         throw new Error("Cannot find module '" + request + "'");
370       } else {
371         var module = new Module(id, parent);
372         module.loadSync(filename);
373         return module.exports;
374       }
375
376     } else {
377       // async
378       findModulePath(request, paths, function (filename) {
379         if (!filename) {
380           var err = new Error("Cannot find module '" + request + "'");
381           callback(err);
382         } else {
383           var module = new Module(id, parent);
384           module.load(filename, callback);
385         }
386       });
387     }
388   }
389 };
390
391
392 // This function allows the user to register file extensions to custom
393 // Javascript 'compilers'.  It accepts 2 arguments, where ext is a file
394 // extension as a string. E.g. '.coffee' for coffee-script files.  compiler
395 // is the second argument, which is a function that gets called when the
396 // specified file extension is found. The compiler is passed a single
397 // argument, which is, the file contents, which need to be compiled.
398 //
399 // The function needs to return the compiled source, or an non-string
400 // variable that will get attached directly to the module exports. Example:
401 //
402 //    require.registerExtension('.coffee', function(content) {
403 //      return doCompileMagic(content);
404 //    });
405 function registerExtension(ext, compiler) {
406   if ('string' !== typeof ext && false === /\.\w+$/.test(ext)) {
407     throw new Error('require.registerExtension: First argument not a valid extension string.');
408   }
409
410   if ('function' !== typeof compiler) {
411     throw new Error('require.registerExtension: Second argument not a valid compiler function.');
412   }
413
414   extensionCache[ext] = compiler;
415 }
416
417
418 Module.prototype.loadSync = function (filename) {
419   debug("loadSync " + JSON.stringify(filename) + " for module " + JSON.stringify(this.id));
420
421   process.assert(!this.loaded);
422   this.filename = filename;
423
424   if (filename.match(/\.node$/)) {
425     this._loadObjectSync(filename);
426   } else {
427     this._loadScriptSync(filename);
428   }
429 };
430
431
432 Module.prototype.load = function (filename, callback) {
433   debug("load " + JSON.stringify(filename) + " for module " + JSON.stringify(this.id));
434
435   process.assert(!this.loaded);
436
437   this.filename = filename;
438
439   if (filename.match(/\.node$/)) {
440     this._loadObject(filename, callback);
441   } else {
442     this._loadScript(filename, callback);
443   }
444 };
445
446
447 Module.prototype._loadObjectSync = function (filename) {
448   this.loaded = true;
449   process.dlopen(filename, this.exports);
450 };
451
452
453 Module.prototype._loadObject = function (filename, callback) {
454   var self = this;
455   // XXX Not yet supporting loading from HTTP. would need to download the
456   // file, store it to tmp then run dlopen on it.
457   self.loaded = true;
458   process.dlopen(filename, self.exports); // FIXME synchronus
459   if (callback) callback(null, self.exports);
460 };
461
462
463 function cat (id, callback) {
464   if (id.match(/^http:\/\//)) {
465     loadModule('http', process.mainModule, function (err, http) {
466       if (err) {
467         if (callback) callback(err);
468       } else {
469         http.cat(id, callback);
470       }
471     });
472   } else {
473     requireNative('fs').readFile(id, callback);
474   }
475 }
476
477
478 // Returns exception if any
479 Module.prototype._compile = function (content, filename) {
480   var self = this;
481   // remove shebang
482   content = content.replace(/^\#\!.*/, '');
483
484   // Compile content if needed
485   var ext = path.extname(filename);
486   if (extensionCache[ext]) {
487     content = extensionCache[ext](content);
488   }
489
490   function requireAsync (url, cb) {
491     loadModule(url, self, cb);
492   }
493
494   function require (path) {
495     return loadModule(path, self);
496   }
497
498   require.paths = modulePaths;
499   require.async = requireAsync;
500   require.main = process.mainModule;
501   require.registerExtension = registerExtension;
502
503
504   if ('string' === typeof content) {
505     // create wrapper function
506     var wrapper = "(function (exports, require, module, __filename, __dirname) { "
507                 + content
508                 + "\n});";
509
510     try {
511       var compiledWrapper = process.compile(wrapper, filename);
512       var dirName = path.dirname(filename);
513       if (filename === process.argv[1]) {
514         process.checkBreak();
515       }
516       compiledWrapper.apply(self.exports, [self.exports, require, self, filename, dirName]);
517     } catch (e) {
518       return e;
519     }
520   } else {
521     self.exports = content;
522   }
523 };
524
525
526 Module.prototype._loadScriptSync = function (filename) {
527   var content = requireNative('fs').readFileSync(filename);
528   var e = this._compile(content, filename);
529   if (e) {
530     throw e;
531   } else {
532     this.loaded = true;
533   }
534 };
535
536
537 Module.prototype._loadScript = function (filename, callback) {
538   var self = this;
539   cat(filename, function (err, content) {
540     debug('cat done');
541     if (err) {
542       if (callback) callback(err);
543     } else {
544       var e = self._compile(content, filename);
545       if (e) {
546         if (callback) callback(e);
547       } else {
548         self._waitChildrenLoad(function () {
549           self.loaded = true;
550           if (self.onload) self.onload();
551           if (callback) callback(null, self.exports);
552         });
553       }
554     }
555   });
556 };
557
558
559 Module.prototype._waitChildrenLoad = function (callback) {
560   var nloaded = 0;
561   var children = this.children;
562   for (var i = 0; i < children.length; i++) {
563     var child = children[i];
564     if (child.loaded) {
565       nloaded++;
566     } else {
567       child.onload = function () {
568         child.onload = null;
569         nloaded++;
570         if (children.length == nloaded && callback) callback();
571       };
572     }
573   }
574   if (children.length == nloaded && callback) callback();
575 };
576
577
578 var stdout;
579 process.__defineGetter__('stdout', function () {
580   if (stdout) return stdout;
581   var net = requireNative('net');
582   stdout = new net.Stream(process.binding('stdio').stdoutFD);
583   return stdout;
584 });
585
586 var stdin;
587 process.openStdin = function () {
588   if (stdin) return stdin;
589   var net = requireNative('net');
590   var fd = process.binding('stdio').openStdin();
591   stdin = new net.Stream(fd);
592   stdin.resume();
593   stdin.readable = true;
594   return stdin;
595 };
596
597
598 process.exit = function (code) {
599   process.emit("exit");
600   process.reallyExit(code);
601 };
602
603 var cwd = process.cwd();
604
605 // Make process.argv[0] and process.argv[1] into full paths.
606 if (process.argv[0].indexOf('/') > 0) {
607   process.argv[0] = path.join(cwd, process.argv[0]);
608 }
609
610 if (process.argv[1].charAt(0) != "/" && !(/^http:\/\//).exec(process.argv[1])) {
611   process.argv[1] = path.join(cwd, process.argv[1]);
612 }
613
614 // Load the main module--the command line argument.
615 process.mainModule = new Module(".");
616 process.mainModule.load(process.argv[1], function (err) {
617   if (err) throw err;
618 });
619
620 // All our arguments are loaded. We've evaluated all of the scripts. We
621 // might even have created TCP servers. Now we enter the main eventloop. If
622 // there are no watchers on the loop (except for the ones that were
623 // ev_unref'd) then this function exits. As long as there are active
624 // watchers, it blocks.
625 process.loop();
626
627 process.emit("exit");
628
629 });