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