1 jls.loader.provide('jls.lang.ModuleLoader');
  2 
  3 /*
  4  * for /f %d in ('dir /b ..\res\cjsunit') do jls -Djls.enableCommonJSModule -sp ..\res\cjsunit\%d program.js
  5  */
  6 
  7 /**
  8  * @class A module loader is an object that is responsible for loading Common JS modules.
  9  * The ModuleLoader class implements the CommonJS Modules 1.0 specification.
 10  */
 11 jls.lang.ModuleLoader = jls.lang.Class.create(/** @lends jls.lang.ModuleLoader.prototype */
 12 {
 13     // private
 14     initialize : function() {
 15         this._cache = {};
 16         this._path = '.';
 17         this._commonJSEnabled = ('jls.enableCommonJSModule' in _native.core.properties);
 18     },
 19     /**
 20      * Returns the exported API of the foreign module.
 21      * 
 22      * @param {String} path The path of the module to load.
 23      */
 24     require : function(path) {
 25         // Find out the absolute path that is used as unique id
 26         var pathItems = path.split('/');
 27         if ((this._path != '.') && ((pathItems[0] == '.') || (pathItems[0] == '..'))) {
 28             pathItems = this._path.split('/').concat(pathItems);
 29         }
 30         for (var i = 0; i < pathItems.length; i++) {
 31             if (pathItems[i] == '.') {
 32                 pathItems.splice(i, 1);
 33                 i--;
 34                 continue;
 35             }
 36             if (pathItems[i] == '..') {
 37                 if (i == 0) {
 38                     throw new jls.lang.Exception('Invalid path');
 39                 }
 40                 pathItems.splice(i - 1, 1);
 41                 i -= 2;
 42                 continue;
 43             }
 44         }
 45         var absPath = pathItems.join('/');
 46         if (absPath in this._cache) {
 47             return this._cache[absPath];
 48         }
 49         // We have to create the object before evaluating the module in case of cyclic dependency.
 50         var exports = {};
 51         // We have to keep our path for relative requires
 52         var previousPath = this._path;
 53         try {
 54             if (pathItems.length > 1) {
 55                 pathItems.splice(pathItems.length - 1, 1);
 56                 this._path = pathItems.join('/');
 57             } else {
 58                 this._path = '.';
 59             }
 60             // TODO Remove
 61             //jls.logger.warn('require("' + path + '") previousPath: ' + previousPath + ', path: ' + this._path + ', absPath: ' + absPath);
 62             this._cache[absPath] = exports;
 63             var rsrcStr = _native.core.getResourceAsString(absPath + '.js');
 64             var evalSrc = 'function (exports) { ';
 65             if (! this._commonJSEnabled) {
 66                 // Add require in the scope
 67                 evalSrc += 'var require = jls.lang.ModuleLoader.getInstance().require.bind(jls.lang.ModuleLoader.getInstance()); ';
 68             }
 69             evalSrc += rsrcStr;
 70             evalSrc += ' }';
 71             var fn = eval(evalSrc);
 72             fn(exports);
 73         }
 74         catch (e) {
 75             // remove entry as something goes wrong
 76             delete this._cache[absPath];
 77             throw e; // rethrow
 78         }
 79         finally {
 80             // Restore previous path
 81             this._path = previousPath;
 82         }
 83         return exports;
 84     }
 85 });
 86 
 87 Object.extend(jls.lang.ModuleLoader, /** @lends jls.lang.ModuleLoader */
 88 {
 89     _instance : null,
 90     /**
 91      * Gets the current module loader instance.
 92      * 
 93      * @returns {jls.lang.ModuleLoader} The module loader instance.
 94      */
 95     getInstance : function() {
 96         if (jls.lang.ModuleLoader._instance == null) {
 97             jls.lang.ModuleLoader._instance = new jls.lang.ModuleLoader();
 98         }
 99         return jls.lang.ModuleLoader._instance;
100     }
101 });
102 
103