1 /*!
  2  * 
  3  * JavaLikeScript
  4  * 
  5  */
  6 /**
  7  * jls Base framework.
  8  * All the framework objects are created under the jls namespace.
  9  * The default objects are extended using prototype like behaviors.
 10  * 
 11  * @namespace
 12  * @see jls.lang
 13  */
 14 var jls = {};
 15 
 16 /**
 17  * @namespace Provides base classes for the jls language.
 18  * @see jls.lang.Exception
 19  * @see jls.lang.Logger
 20  * @see jls.lang.ClassLoader
 21  */
 22 jls.lang = {};
 23 
 24 /*
 25  * Begin prototype.js
 26  */
 27 Array.prototype.clone = function() {
 28   return [].concat(this);
 29 };
 30 Array.prototype.toArray = Array.prototype.clone;
 31 // The primary use of Array.from() is to obtain an actual Array object based on anything that could pass as an array.
 32 // the predefined arguments reference within your functions
 33 Array.from = function(iterable) {
 34   if (!iterable) return [];
 35   if (iterable.toArray) return iterable.toArray();
 36   var length = iterable.length || 0, results = new Array(length);
 37   while (length--) results[length] = iterable[length];
 38   return results;
 39 }
 40 // extend Object
 41 Object.extend = function(destination, source) {
 42   for (var property in source)
 43     destination[property] = source[property];
 44   return destination;
 45 };
 46 Object.extend(Object, {
 47   toJSON: function(object) {
 48     var type = typeof object;
 49     switch (type) {
 50       case 'undefined':
 51       case 'function':
 52       case 'unknown': return;
 53       case 'boolean': return object.toString();
 54     }
 55     if (object === null) return 'null';
 56     if (object.toJSON) return object.toJSON();
 57     var results = [];
 58     for (var property in object) {
 59       var value = Object.toJSON(object[property]);
 60       if (!Object.isUndefined(value))
 61         results.push(property.toJSON() + ': ' + value);
 62     }
 63     return '{' + results.join(', ') + '}';
 64   },
 65   keys: function(object) {
 66     var keys = [];
 67     for (var property in object)
 68       keys.push(property);
 69     return keys;
 70   },
 71   values: function(object) {
 72     var values = [];
 73     for (var property in object)
 74       values.push(object[property]);
 75     return values;
 76   },
 77   isArray: function(object) {
 78     return object != null && typeof object == "object" &&
 79       'splice' in object && 'join' in object;
 80   },
 81   isHash: function(object) {
 82     return object instanceof Hash;
 83   },
 84   isFunction: function(object) {
 85     return typeof object == "function";
 86   },
 87   isString: function(object) {
 88     return typeof object == "string";
 89   },
 90   isNumber: function(object) {
 91     return typeof object == "number";
 92   },
 93   isUndefined: function(object) {
 94     return typeof object == "undefined";
 95   },
 96   inheritConstants: function(destination, source) {
 97     for (var property in source) {
 98       if ((typeof source[property] == "string") || (typeof source[property] == "number")) {
 99           destination[property] = source[property];
100       }
101     }
102     return destination;
103   }
104 });
105 // extend Function
106 Object.extend(Function.prototype, {
107   argumentNames: function() {
108     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
109       .replace(/\s+/g, '').split(',');
110     return names.length == 1 && !names[0] ? [] : names;
111   },
112   bind: function() {
113     if (arguments.length < 2 && (typeof arguments[0] == "undefined")) return this;
114     var __method = this, args = Array.from(arguments), object = args.shift();
115     return function() {
116       return __method.apply(object, args.concat(Array.from(arguments)));
117     }
118   },
119   curry: function() {
120     if (!arguments.length) return this;
121     var __method = this, args = Array.from(arguments);
122     return function() {
123       return __method.apply(this, args.concat(Array.from(arguments)));
124     }
125   },
126   wrap: function(wrapper) {
127     var __method = this;
128     return function() {
129       return wrapper.apply(this, [__method.bind(this)].concat(Array.from(arguments)));
130     }
131   },
132   methodize: function() {
133     if (this._methodized) return this._methodized;
134     var __method = this;
135     return this._methodized = function() {
136       return __method.apply(null, [this].concat(Array.from(arguments)));
137     };
138   }
139 });
140 Object.extend(String.prototype, {
141   camelize: function(prefix, suffix) {
142     var parts = this.split('-');
143     if (prefix) parts.unshift(prefix);
144     if (suffix) parts.push(suffix);
145     var len = parts.length;
146     if (len == 1) return parts[0];
147 
148     var camelized = this.charAt(0) == '-'
149       ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
150       : parts[0];
151 
152     for (var i = 1; i < len; i++)
153       camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
154 
155     return camelized;
156   },
157   capitalize: function() {
158     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
159   },
160   startsWith: function(pattern) {
161     return this.indexOf(pattern) === 0;
162   },
163   endsWith: function(pattern) {
164     var d = this.length - pattern.length;
165     return d >= 0 && this.lastIndexOf(pattern) === d;
166   },
167   empty: function() {
168     return this == '';
169   },
170   strip: function() {
171     return this.replace(/^\s+/, '').replace(/\s+$/, '');
172   },
173   // TODO Remove this
174   times: function(count) {
175     return count < 1 ? '' : new Array(count + 1).join(this);
176   },
177   toJSON: function() {
178     // TODO Fix quotes...
179     return '\'' + this.toString().replace(/([\\'])/g, '\\$1') + '\'';
180   }
181 });
182 Object.extend(Number.prototype, {
183   toPaddedString: function(length, radix) {
184     var string = this.toString(radix || 10);
185     return '0'.times(length - string.length) + string;
186   },
187   toJSON: function() {
188     return isFinite(this) ? this.toString() : 'null';
189   }
190 });
191 Object.extend(Array.prototype, {
192   indexOf: function(object, from) {
193     var len = this.length;
194     from = from || 0;
195     for (var i = from; i < len; i++) {
196       if (this[i] === object) {
197         return i;
198       }
199     }
200     return -1;
201   },
202   remove: function(object) {
203     var index = 0;
204     if (typeof object == 'number') {
205       index = object;
206     } else {
207       index = this.indexOf(object);
208       if (index < 0) {
209         return null;
210       }
211     }
212     return this.splice(index, 1)[0];
213   },
214   toJSON: function() {
215     var results = [];
216     this.each(function(object) {
217       var value = Object.toJSON(object);
218       if (!Object.isUndefined(value)) results.push(value);
219     });
220     return '[' + results.join(', ') + ']';
221   }
222 });
223 Object.extend(Date.prototype, {
224   // TODO Fix UTC
225   toJSON: function() {
226     return '"' + this.getUTCFullYear() + '-' +
227       (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
228       this.getUTCDate().toPaddedString(2) + 'T' +
229       this.getUTCHours().toPaddedString(2) + ':' +
230       this.getUTCMinutes().toPaddedString(2) + ':' +
231       this.getUTCSeconds().toPaddedString(2) + 'Z"';
232   }
233 });
234 /**
235  * @class Class like creation with inheritance. Based on Alex Arnell's inheritance implementation.
236  */
237 jls.lang.Class = {/** @lends jls.lang.Class */
238     /**
239      * Creates a new class.
240      * 
241      * @param {Object} declaration An object containing the class declaration.
242      * @returns {Object} The new class.
243      */
244   create: function() {
245     var parent = null, properties = Array.from(arguments);
246     if (typeof properties[0] == "function")
247       parent = properties.shift();
248 
249     function klass() {
250       this.initialize.apply(this, arguments);
251     }
252 
253     Object.extend(klass, jls.lang.Class.Methods);
254     klass.superclass = parent;
255     klass.subclasses = [];
256 
257     if (parent) {
258       var subclass = function() { };
259       subclass.prototype = parent.prototype;
260       klass.prototype = new subclass;
261       parent.subclasses.push(klass);
262     }
263 
264     for (var i = 0; i < properties.length; i++)
265       klass.addMethods(properties[i]);
266 
267     if (!klass.prototype.initialize)
268       klass.prototype.initialize = jls.lang.Class.emptyFunction;
269 
270     klass.prototype.constructor = klass;
271 
272     return klass;
273   },
274   emptyFunction: function() { },
275   notAvailableFunction: function() {
276       throw new jls.lang.Exception('Function not available');
277   },
278   abstractMethod: function() {
279       throw new jls.lang.Exception('Abstract method');
280   },
281   // instantiate
282   newInstance: function(object) {
283     if (!(object && object.classname)) {
284       throw new jls.lang.Exception('Invalid object, the classname property is required');
285     }
286     var classname = object.classname;
287     jls.loader.require(classname);
288     /*var properties = Array.from(arguments);
289     var args = '';
290     for (var i = 0; i < properties.length; i++) {
291       if (i > 0) args += ', ';
292       args += 'properties[' + i + ']';
293     }*/
294     var args = 'object';
295     for (var i = 1; i < arguments.length; i++) {
296       args += ', arguments[' + i + ']';
297     }
298     //jls.logger.info('newInstance() : eval(\'new ' + classname + '(' + args + ')\')');
299     return eval('new ' + classname + '(' + args + ')');
300   }
301 };
302 /**
303  * @class Class helper.
304  */
305 jls.lang.Class.Methods = /** @lends jls.lang.Class.Methods */
306 {
307   addMethods: function(source) {
308     var ancestor   = this.superclass && this.superclass.prototype;
309     var properties = Object.keys(source);
310 
311     if (!Object.keys({ toString: true }).length)
312       properties.push("toString", "valueOf");
313 
314     for (var i = 0, length = properties.length; i < length; i++) {
315       var property = properties[i], value = source[property];
316       if (ancestor && (typeof value == 'function') &&
317           value.argumentNames()[0] == "$super") {
318         var method = value;
319         value = (function(m) {
320           return function() { return ancestor[m].apply(this, arguments) };
321         })(property).wrap(method);
322 
323         value.valueOf = method.valueOf.bind(method);
324         value.toString = method.toString.bind(method);
325       }
326       this.prototype[property] = value;
327     }
328 
329     return this;
330   }
331 };
332 /**
333  * @class Class bean helper.
334  */
335 jls.lang.Class.Bean = /** @lends jls.lang.Class.Bean */
336 {
337   set : function(key, value) {
338     var setter = key.camelize('set');
339     if (Object.isFunction(this[setter])) {
340       this[setter](value);
341     }
342     return this;
343   },
344   get : function(key) {
345     var getter = key.camelize('get');
346     if (Object.isFunction(this[getter])) {
347       return this[getter]();
348     }
349     return null;
350   },
351   setAll : function(values) {
352     for (var key in values) {
353       this.set(key, values[key]);
354     }
355     return this;
356   }
357 };
358 /* End prototype.js */
359 
360 // Load base classes
361 _native.core.evalScript('jls/lang/Exception.js');
362 _native.core.evalScript('jls/lang/Logger.js');
363 
364 /**
365  * Logger.
366  * 
367  * @type jls.lang.Logger
368  * @memberOf jls
369  */
370 jls.logger = new jls.lang.Logger(jls.lang.Logger.INFO);
371 
372 if ('jls.logger.logLevel' in _native.core.properties) {
373     jls.logger.setLogLevel(_native.core.properties['jls.logger.logLevel']);
374 }
375 
376 _native.core.evalScript('jls/lang/ClassLoader.js');
377 
378 /**
379  * ClassLoader.
380  * 
381  * @type jls.lang.ClassLoader
382  * @memberOf jls
383  */
384 jls.loader = new jls.lang.ClassLoader();
385 
386 // Register base classes
387 jls.loader.provide('jls.lang.Exception', true);
388 jls.loader.provide('jls.lang.Logger', true);
389 jls.loader.provide('jls.lang.ClassLoader', true);
390 
391 /*
392  * Provide all lang classes
393  */
394 jls.loader.require('jls.lang.Buffer');
395 jls.loader.require('jls.lang.Process');
396 jls.loader.require('jls.lang.Runtime');
397 jls.loader.require('jls.lang.System');
398 
399 jls.lang.System._initialize();
400 
401 jls.loader.require('jls.lang.Thread');
402 jls.loader.require('jls.lang.Signal');
403 jls.loader.require('jls.lang.Lock');
404 jls.loader.require('jls.lang.Monitor');
405 //jls.loader.require('jls.lang.ProcessBuilder');
406 jls.loader.require('jls.lang.Struct');
407 //jls.loader.require('jls.lang.AssertionError');
408 
409 if ('jls.disableDefaultExceptionHandler' in _native.core.properties) {
410     jls.logger.info('Default exception handler disabled');
411 } else {
412     /*
413      * Override exception handler
414      */
415     _native.core.exceptionHandler = function(e) {
416         jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err);
417     }
418 }
419 
420 if ('jls.disableDefaultSignalHandler' in _native.core.properties) {
421     jls.logger.info('Default signal handlers disabled');
422 } else {
423     /*
424      * Register default signal handlers.
425      */
426     jls.lang.Signal.handle(jls.lang.Signal.SIGINT, function() {
427         jls.lang.Runtime.exit(0);
428     });
429     /*
430      * Don't know how to dump javascript thread stacktraces
431      */
432     /*jls.lang.Signal.handle(jls.lang.Signal.SIGBREAK, function() {
433         //jls.lang.System.out.println('... Dump stack trace ...');
434         //new jls.lang.Exception().printStackTrace(jls.lang.System.out);
435         var name = 'jlsdump.' + new Date().getTime() + '.log';
436         jls.lang.System.out.println('... Dump head to ' + name + '...');
437         _native.core.dumpHead(name);
438     });*/
439 }
440 
441 if ('jls.enableCommonJSModule' in _native.core.properties) {
442     jls.loader.require('jls.lang.ModuleLoader');
443     require = jls.lang.ModuleLoader.getInstance().require.bind(jls.lang.ModuleLoader.getInstance());
444 }
445 
446 if (_native.core.arguments.length > 0) {
447     var args = Array.from(_native.core.arguments);
448     var arg = args.shift();
449     
450     if ((arg == null) || (arg == '-help')) {
451         _native.core.boot = function() {
452             var lines = [
453                 'Usage: jls [options] classname [args...]',
454                 'where options include:',
455                 '\t-bs <bootstrap script file name>',
456                 '\t\tThe file name of the script to use for bootstrap.',
457                 '\t-ep <extension path>',
458                 '\t\tA directory to search for native library files,',
459                 '\t\tscript ZIP files and directories.',
460                 '\t-lp <library search path of directories>',
461                 '\t\tA ; separated list of directories to search for native library files.',
462                 '\t-sp <script search path of directories and ZIP files>',
463                 '\t\tA ; separated list of directories and ZIP archives',
464                 '\t\tto search for script files.',
465                 '\t-D<name>=<value>',
466                 '\t\tSet a system property.',
467                 '\t-logLevel <level>',
468                 '\t\tSet the native log level.',
469                 '\t--',
470                 '\t\tStop the jls options parsing.',
471                 'where system properties include:',
472                 '\t-Djls.logger.logLevel=<level>',
473                 '\t\tSet the log level.',
474                 '\t-Djls.disableDefaultExceptionHandler',
475                 '\t\tDisable uncaught exception default handler.',
476                 '\t-Djls.disableDefaultSignalHandler',
477                 '\t\tDisable default signal handlers.',
478                 '\t-Djls.keep',
479                 '\t\tWaits Ctrl-C if an exception occurs at boot phase.',
480                 '\t-Djls.interactive',
481                 '\t\tEnables interactive boot mode.',
482                 'See http://javalikescript.free.fr/ for more details.'
483             ];
484             for (var i = 0; i < lines.length; i++) {
485                 jls.lang.System.out.println(lines[i]);
486             }
487         }
488     } else if (! arg.endsWith('.js')) {
489         /*
490          * Override boot function
491          */
492         _native.core.boot = function() {
493             jls.loader.require(arg);
494             var mainclass = eval(arg);
495             if (! ('main' in mainclass)) {
496                 throw new jls.lang.Exception('The class ' + arg + ' does not have a main method.');
497             }
498             mainclass['main'](args);
499         };
500     }
501 } else {
502     if ('jls.interactive' in _native.core.properties) {
503         // TODO
504     }
505 }
506 
507 if ('jls.keep' in _native.core.properties) {
508     var previousBoot = _native.core.boot;
509     _native.core.boot = function() {
510         try {
511             previousBoot.apply(this, arguments);
512         } catch (e) {
513             jls.lang.System.err.println('Error during boot, hit ctrl-c to end.');
514             jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err);
515             // Start an infinite non daemon thread
516             var thread = new jls.lang.Thread();
517             thread.run = function() {
518                 while (true) {
519                     jls.lang.Thread.sleep(500);
520                 }
521             };
522             thread.start();
523         }
524     };
525 }
526