var CoreModule = {}; CoreModule['com/io']='com/io.browser'; CoreModule['crypto']='os/crypto'; CoreModule['util']='os/util'; CoreModule['http']='os/http.browser'; CoreModule['url']='os/url'; CoreModule['path']='os/path'; CoreModule['string_decoder']='os/string_decoder'; CoreModule['fs']=''; CoreModule['stream']=''; CoreModule['zlib']=''; CoreModule['dgram']=''; CoreModule['net']=''; CoreModule['child_process']=''; var BundleModuleCode=[]; var BundleObjectCode=[]; var BundleModules = []; PATH=[".","/home/sbosse/proj/jam/js"]; if (typeof global == "undefined") global=(typeof window != "undefined"?window:{}) if (typeof process == "undefined") var process={}; Require=function(modupath) { if (CoreModule[modupath]!=undefined) modupath=CoreModule[modupath]; if (modupath=='') return undefined; if (BundleModules[modupath]) return BundleModules[modupath]; var exports={}, module={exports:exports}; if (BundleModuleCode[modupath]) BundleModuleCode[modupath](module,exports,window,process); else if (BundleObjectCode[modupath]) BundleObjectCode[modupath](module,exports,window,process); else return undefined; BundleModules[modupath]=module.exports||module; return module.exports||module;}; var FilesEmbedded = {}; var FileEmbedd = function (path,format) {}; var FileEmbedded = function (path,format) {return FilesEmbedded[path](format);}; global.TARGET='browser'; BundleModuleCode['com/io.browser']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: sbosse on 28-3-15. ** $VERSION: 1.10.1 ** ** $INFO: * * This module encapsulates all IO operations (except networking) supporting * browser applications. * ** $ENDOFINFO */ /* ************ ** Browser ************ */ var tracing = true; var stderr_fun = function (str) { console.log(str); }; var stdout_fun = function (str) { console.log(str); }; var args=[]; var inspect = Require('os/inspect'); Require('os/polyfill') global.checkOptions = function(options,defaultOptions) { return Object.assign({}, defaultOptions||{}, options) }; global.checkOption = function (option,defaultOption) { return option==undefined? defaultOption:option }; var io = { /* ************ ** Browser ************ */ /* ** FILE IO * TODO WebStorage */ close: function (fd) { return; }, exists: function (path) { return false; }, open: function (path, mode) { var fd = Fs.openSync(path, mode); return fd; }, read: function (fd, len, foff) { // TODO }, read_file: function (path) { return ''; }, read_line: function (fd) { // TODO }, /** * * @param fd * @param buf * @param boff * @param len * @param [foff] * @returns {*} */ read_buf: function (fd, buf, boff, len, foff) { return -1; }, sync: function (fd) { return; }, /** * * @param fd * @param data * @param [foff] * @returns {*} */ write: function (fd, data, foff) { return -1; }, /** * * @param fd * @param buf * @param bpos * @param blen * @param [foff] * @returns {*} */ write_buf: function (fd, buf, bpos, blen, foff) { return -1; }, /* ** CONSOLE IO */ debug: function (msg) { stderr_fun('Debug: ' + msg); }, err: function (msg) { stderr_fun('Error: ' + msg); throw Error(msg); }, fail: function (msg) { stderr_fun('Fatal Error: ' + msg); }, inspect: function (obj) { return inspect(obj); }, stacktrace: function () { var e = new Error('dummy'); var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '') .replace(/^\s+at\s+/gm, '') .replace(/^Object.\s*\(/gm, '{anonymous}()@') .split('\n'); stderr_fun('Stack Trace'); stderr_fun('--------------------------------'); for(var i in stack) { if (i>0) { var line = stack[i]; if(line.indexOf('Module.',0)>=0) break; stderr_fun(line); } } stderr_fun('--------------------------------'); }, /** * * @param e * @param where */ printstack: function (e,where) { if (where==undefined) stderr_fun(e); else stderr_fun(where+': '+e); }, sprintstack: function (e) { return e?e.toString():'' }, /** * * @param {boolean|string} condmsg conditional message var log=X; log((log lt. N)||(msg)) */ log: function (condmsg) { if (condmsg != true) console.warn(condmsg); }, out: function (msg) { stdout_fun(msg) }, warn: function (msg) { stderr_fun('Warning: ' + msg); }, set_stderr: function(fun) { stderr_fun=fun; }, set_stdout: function(fun) { stdout_fun=fun; }, stderr: function (msg) { stderr_fun(msg); }, stdout: function (msg) { stdout_fun(msg); }, /** Write a message with a time stamp written to the trace file. * * @param {boolean|string} condmsg conditional message var trace=Io.tracing; trace(trace||(msg)) */ trace: function (condmsg) { if (condmsg != true && tracefile != undefined) { var date = new Date(); var time = date.getTime(); this.log('[' + time + '] ' + condmsg + '\n'); } }, tracing: tracing, /** * * @param {string} path */ trace_open: function (path) { return undefined; }, exit: function (n) { return; }, getenv: function (name, def) { return def; }, workdir: function () { return ''; }, /** * @return {string []} */ getargs: function () { return args; }, set_args: function (argv) { args=argv; }, sleep: function(delay) { var start = new Date().getTime(); while (new Date().getTime() < start + delay); }, /** Return system time in milliseconds */ time: function () { var date = new Date(); return date.getTime(); }, /** ** Return current time in hour:minute:second format */ Time: function () { var now = new Date(); var hour = "0" + now.getHours(); hour = hour.substring(hour.length-2); var minute = "0" + now.getMinutes(); minute = minute.substring(minute.length-2); var second = "0" + now.getSeconds(); second = second.substring(second.length-2); return hour + ":" + minute + ":" + second; }, /** ** Return current date in year-month-day format */ Date: function () { var now = new Date(); var year = "" + now.getFullYear(); var month = "0" + (now.getMonth()+1); month = month.substring(month.length-2); var date = "0" + now.getDate(); date = date.substring(date.length-2); return year + "-" + month + "-" + date; }, }; module.exports = io; }; BundleModuleCode['os/inspect']=function (module,exports,global,process){ /** * Module dependencies. */ var map = function(array, callback) { var length = array.length, i = -1, il = length - 1, results = new Array(length); while (i++ < il) { results[i] = callback(array[i], i, array); } return results; } var indexOf = function(arr, obj){ if (arr.indexOf) return arr.indexOf(obj); for (var i = 0; i < arr.length; ++i) { if (arr[i] === obj) return i; } return -1; }; var str = Object.prototype.toString; var isArray = isArray || function (val) { return !! val && '[object Array]' == str.call(val); }; var forEach = function (ary, callback, thisArg) { if (ary.forEach) { ary.forEach(callback, thisArg); return; } for (var i = 0; i < ary.length; i+=1) { callback.call(thisArg, ary[i], i, ary); } }; var _hasOwn = Object.prototype.hasOwnProperty; var reduce = function (xs, f, acc) { var hasAcc = arguments.length >= 3; if (hasAcc && xs.reduce) return xs.reduce(f, acc); if (xs.reduce) return xs.reduce(f); for (var i = 0; i < xs.length; i++) { if (!_hasOwn.call(xs, i)) continue; if (!hasAcc) { acc = xs[i]; hasAcc = true; continue; } acc = f(acc, xs[i], i); } return acc; }; var getObjectKeys = Require('os/object-keys'); var JSON = Require('os/json3'); /** * Make sure `Object.keys` work for `undefined` * values that are still there, like `document.all`. * http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html * * @api private */ function objectKeys(val){ if (Object.keys) return Object.keys(val); return getObjectKeys(val); } /** * Module exports. */ module.exports = inspect; /** * Echos the value of a value. Trys to print the value out * in the best way possible given the different types. * * @param {Object} obj The object to print out. * @param {Object} opts Optional options object that alters the output. * @license MIT (© Joyent) */ /* legacy: obj, showHidden, depth, colors*/ function inspect(obj, opts) { // default options var ctx = { seen: [], stylize: stylizeNoColor }; // legacy... if (arguments.length >= 3) ctx.depth = arguments[2]; if (arguments.length >= 4) ctx.colors = arguments[3]; if (isBoolean(opts)) { // legacy... ctx.showHidden = opts; } else if (opts) { // got an "options" object _extend(ctx, opts); } // set default options if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; if (isUndefined(ctx.colors)) ctx.colors = false; if (isUndefined(ctx.customInspect)) ctx.customInspect = true; if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics inspect.colors = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; // Don't use 'blue' not visible on cmd.exe inspect.styles = { 'special': 'cyan', 'number': 'yellow', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }; function stylizeNoColor(str, styleType) { return str; } function isBoolean(arg) { return typeof arg === 'boolean'; } function isUndefined(arg) { return arg === void 0; } function stylizeWithColor(str, styleType) { var style = inspect.styles[styleType]; if (style) { return '\u001b[' + inspect.colors[style][0] + 'm' + str + '\u001b[' + inspect.colors[style][1] + 'm'; } else { return str; } } function isFunction(arg) { return typeof arg === 'function'; } function isString(arg) { return typeof arg === 'string'; } function isNumber(arg) { return typeof arg === 'number'; } function isNull(arg) { return arg === null; } function hasOwn(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } function isRegExp(re) { return isObject(re) && objectToString(re) === '[object RegExp]'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isError(e) { return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error); } function isDate(d) { return isObject(d) && objectToString(d) === '[object Date]'; } function objectToString(o) { return Object.prototype.toString.call(o); } function arrayToHash(array) { var hash = {}; forEach(array, function(val, idx) { hash[val] = true; }); return hash; } function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; for (var i = 0, l = value.length; i < l; ++i) { if (hasOwn(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true)); } else { output.push(''); } } forEach(keys, function(key) { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } }); return output; } function formatError(value) { return '[' + Error.prototype.toString.call(value) + ']'; } function formatValue(ctx, value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special value.inspect !== inspect && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { var ret = value.inspect(recurseTimes, ctx); if (!isString(ret)) { ret = formatValue(ctx, ret, recurseTimes); } return ret; } // Primitive types cannot have properties var primitive = formatPrimitive(ctx, value); if (primitive) { return primitive; } // Look up the keys of the object. var keys = objectKeys(value); var visibleKeys = arrayToHash(keys); if (ctx.showHidden && Object.getOwnPropertyNames) { keys = Object.getOwnPropertyNames(value); } // IE doesn't make error fields non-enumerable // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx if (isError(value) && (indexOf(keys, 'message') >= 0 || indexOf(keys, 'description') >= 0)) { return formatError(value); } // Some type of object without properties can be shortcutted. if (keys.length === 0) { if (isFunction(value)) { var name = value.name ? ': ' + value.name : ''; return ctx.stylize('[Function' + name + ']', 'special'); } if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } if (isDate(value)) { return ctx.stylize(Date.prototype.toString.call(value), 'date'); } if (isError(value)) { return formatError(value); } } var base = '', array = false, braces = ['{', '}']; // Make Array say that they are Array if (isArray(value)) { array = true; braces = ['[', ']']; } // Make functions say that they are functions if (isFunction(value)) { var n = value.name ? ': ' + value.name : ''; base = ' [Function' + n + ']'; } // Make RegExps say that they are RegExps if (isRegExp(value)) { base = ' ' + RegExp.prototype.toString.call(value); } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + Date.prototype.toUTCString.call(value); } // Make error with message first say the error if (isError(value)) { base = ' ' + formatError(value); } if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } else { return ctx.stylize('[Object]', 'special'); } } ctx.seen.push(value); var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = map(keys, function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); }); } ctx.seen.pop(); return reduceToSingleString(output, base, braces); } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { var name, str, desc; desc = { value: value[key] }; if (Object.getOwnPropertyDescriptor) { desc = Object.getOwnPropertyDescriptor(value, key) || desc; } if (desc.get) { if (desc.set) { str = ctx.stylize('[Getter/Setter]', 'special'); } else { str = ctx.stylize('[Getter]', 'special'); } } else { if (desc.set) { str = ctx.stylize('[Setter]', 'special'); } } if (!hasOwn(visibleKeys, key)) { name = '[' + key + ']'; } if (!str) { if (indexOf(ctx.seen, desc.value) < 0) { if (isNull(recurseTimes)) { str = formatValue(ctx, desc.value, null); } else { str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (array) { str = map(str.split('\n'), function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + map(str.split('\n'), function(line) { return ' ' + line; }).join('\n'); } } } else { str = ctx.stylize('[Circular]', 'special'); } } if (isUndefined(name)) { if (array && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = ctx.stylize(name, 'string'); } } return name + ': ' + str; } function formatPrimitive(ctx, value) { if (isUndefined(value)) return ctx.stylize('undefined', 'undefined'); if (isString(value)) { var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return ctx.stylize(simple, 'string'); } if (isNumber(value)) return ctx.stylize('' + value, 'number'); if (isBoolean(value)) return ctx.stylize('' + value, 'boolean'); // For some reason typeof null is "object", so special case here. if (isNull(value)) return ctx.stylize('null', 'null'); } function reduceToSingleString(output, base, braces) { var numLinesEst = 0; var length = reduce(output, function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; }, 0); if (length > 60) { return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } function _extend(origin, add) { // Don't do anything if add isn't an object if (!add || !isObject(add)) return origin; var keys = objectKeys(add); var i = keys.length; while (i--) { origin[keys[i]] = add[keys[i]]; } return origin; } }; BundleModuleCode['os/object-keys']=function (module,exports,global,process){ 'use strict'; // modified from https://github.com/es-shims/es5-shim var has = Object.prototype.hasOwnProperty; var toStr = Object.prototype.toString; var slice = Array.prototype.slice; var toStr = Object.prototype.toString; var isArgs = function (value) { var str = toStr.call(value); var isArgs = str === '[object Arguments]'; if (!isArgs) { isArgs = str !== '[object Array]' && value !== null && typeof value === 'object' && typeof value.length === 'number' && value.length >= 0 && toStr.call(value.callee) === '[object Function]'; } return isArgs; }; var isEnumerable = Object.prototype.propertyIsEnumerable; var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); var dontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor' ]; var equalsConstructorPrototype = function (o) { var ctor = o.constructor; return ctor && ctor.prototype === o; }; var excludedKeys = { $console: true, $external: true, $frame: true, $frameElement: true, $frames: true, $innerHeight: true, $innerWidth: true, $outerHeight: true, $outerWidth: true, $pageXOffset: true, $pageYOffset: true, $parent: true, $scrollLeft: true, $scrollTop: true, $scrollX: true, $scrollY: true, $self: true, $webkitIndexedDB: true, $webkitStorageInfo: true, $window: true }; var hasAutomationEqualityBug = (function () { /* global window */ if (typeof window === 'undefined') { return false; } for (var k in window) { try { if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { try { equalsConstructorPrototype(window[k]); } catch (e) { return true; } } } catch (e) { return true; } } return false; }()); var equalsConstructorPrototypeIfNotBuggy = function (o) { /* global window */ if (typeof window === 'undefined' || !hasAutomationEqualityBug) { return equalsConstructorPrototype(o); } try { return equalsConstructorPrototype(o); } catch (e) { return false; } }; var keysShim = function keys(object) { var isObject = object !== null && typeof object === 'object'; var isFunction = toStr.call(object) === '[object Function]'; var isArguments = isArgs(object); var isString = isObject && toStr.call(object) === '[object String]'; var theKeys = []; if (!isObject && !isFunction && !isArguments) { throw new TypeError('Object.keys called on a non-object'); } var skipProto = hasProtoEnumBug && isFunction; if (isString && object.length > 0 && !has.call(object, 0)) { for (var i = 0; i < object.length; ++i) { theKeys.push(String(i)); } } if (isArguments && object.length > 0) { for (var j = 0; j < object.length; ++j) { theKeys.push(String(j)); } } else { for (var name in object) { if (!(skipProto && name === 'prototype') && has.call(object, name)) { theKeys.push(String(name)); } } } if (hasDontEnumBug) { var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); for (var k = 0; k < dontEnums.length; ++k) { if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { theKeys.push(dontEnums[k]); } } } return theKeys; }; keysShim.shim = function shimObjectKeys() { if (Object.keys) { var keysWorksWithArguments = (function () { // Safari 5.0 bug return (Object.keys(arguments) || '').length === 2; }(1, 2)); if (!keysWorksWithArguments) { var originalKeys = Object.keys; Object.keys = function keys(object) { // eslint-disable-line func-name-matching if (isArgs(object)) { return originalKeys(slice.call(object)); } else { return originalKeys(object); } }; } } else { Object.keys = keysShim; } return Object.keys || keysShim; }; module.exports = keysShim; }; BundleModuleCode['os/json3']=function (module,exports,global,process){ /*! JSON v3.3.2 | https://bestiejs.github.io/json3 | Copyright 2012-2015, Kit Cambridge, Benjamin Tan | http://kit.mit-license.org */ ;(function () { // Detect the `define` function exposed by asynchronous module loaders. The // strict `define` check is necessary for compatibility with `r.js`. var isLoader = typeof define === "function" && define.amd; // A set of types used to distinguish objects from primitives. var objectTypes = { "function": true, "object": true }; // Detect the `exports` object exposed by CommonJS implementations. var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; // Use the `global` object exposed by Node (including Browserify via // `insert-module-globals`), Narwhal, and Ringo as the default context, // and the `window` object in browsers. Rhino exports a `global` function // instead. var root = objectTypes[typeof window] && window || this, freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global; if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { root = freeGlobal; } // Public: Initializes JSON 3 using the given `context` object, attaching the // `stringify` and `parse` functions to the specified `exports` object. function runInContext(context, exports) { context || (context = root.Object()); exports || (exports = root.Object()); // Native constructor aliases. var Number = context.Number || root.Number, String = context.String || root.String, Object = context.Object || root.Object, Date = context.Date || root.Date, SyntaxError = context.SyntaxError || root.SyntaxError, TypeError = context.TypeError || root.TypeError, Math = context.Math || root.Math, nativeJSON = context.JSON || root.JSON; // Delegate to the native `stringify` and `parse` implementations. if (typeof nativeJSON == "object" && nativeJSON) { exports.stringify = nativeJSON.stringify; exports.parse = nativeJSON.parse; } // Convenience aliases. var objectProto = Object.prototype, getClass = objectProto.toString, isProperty = objectProto.hasOwnProperty, undefined; // Internal: Contains `try...catch` logic used by other functions. // This prevents other functions from being deoptimized. function attempt(func, errorFunc) { try { func(); } catch (exception) { if (errorFunc) { errorFunc(); } } } // Test the `Date#getUTC*` methods. Based on work by @Yaffle. var isExtended = new Date(-3509827334573292); attempt(function () { // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical // results for certain dates in Opera >= 10.53. isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; }); // Internal: Determines whether the native `JSON.stringify` and `parse` // implementations are spec-compliant. Based on work by Ken Snyder. function has(name) { if (has[name] != null) { // Return cached feature test result. return has[name]; } var isSupported; if (name == "bug-string-char-index") { // IE <= 7 doesn't support accessing string characters using square // bracket notation. IE 8 only supports this for primitives. isSupported = "a"[0] != "a"; } else if (name == "json") { // Indicates whether both `JSON.stringify` and `JSON.parse` are // supported. isSupported = has("json-stringify") && has("date-serialization") && has("json-parse"); } else if (name == "date-serialization") { // Indicates whether `Date`s can be serialized accurately by `JSON.stringify`. isSupported = has("json-stringify") && isExtended; if (isSupported) { var stringify = exports.stringify; attempt(function () { isSupported = // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly // serialize extended years. stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && // The milliseconds are optional in ES 5, but required in 5.1. stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative // four-digit years instead of six-digit years. Credits: @Yaffle. stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond // values less than 1000. Credits: @Yaffle. stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; }); } } else { var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; // Test `JSON.stringify`. if (name == "json-stringify") { var stringify = exports.stringify, stringifySupported = typeof stringify == "function"; if (stringifySupported) { // A test function object with a custom `toJSON` method. (value = function () { return 1; }).toJSON = value; attempt(function () { stringifySupported = // Firefox 3.1b1 and b2 serialize string, number, and boolean // primitives as object literals. stringify(0) === "0" && // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object // literals. stringify(new Number()) === "0" && stringify(new String()) == '""' && // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or // does not define a canonical JSON representation (this applies to // objects with `toJSON` properties as well, *unless* they are nested // within an object or array). stringify(getClass) === undefined && // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and // FF 3.1b3 pass this test. stringify(undefined) === undefined && // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, // respectively, if the value is omitted entirely. stringify() === undefined && // FF 3.1b1, 2 throw an error if the given value is not a number, // string, array, object, Boolean, or `null` literal. This applies to // objects with custom `toJSON` methods as well, unless they are nested // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` // methods entirely. stringify(value) === "1" && stringify([value]) == "[1]" && // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of // `"[null]"`. stringify([undefined]) == "[null]" && // YUI 3.0.0b1 fails to serialize `null` literals. stringify(null) == "null" && // FF 3.1b1, 2 halts serialization if an array contains a function: // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 // elides non-JSON values from objects and arrays, unless they // define custom `toJSON` methods. stringify([undefined, getClass, null]) == "[null,null,null]" && // Simple serialization test. FF 3.1b1 uses Unicode escape sequences // where character escape codes are expected (e.g., `\b` => `\u0008`). stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. stringify(null, value) === "1" && stringify([1, 2], null, 1) == "[\n 1,\n 2\n]"; }, function () { stringifySupported = false; }); } isSupported = stringifySupported; } // Test `JSON.parse`. if (name == "json-parse") { var parse = exports.parse, parseSupported; if (typeof parse == "function") { attempt(function () { // FF 3.1b1, b2 will throw an exception if a bare literal is provided. // Conforming implementations should also coerce the initial argument to // a string prior to parsing. if (parse("0") === 0 && !parse(false)) { // Simple parsing test. value = parse(serialized); parseSupported = value["a"].length == 5 && value["a"][0] === 1; if (parseSupported) { attempt(function () { // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. parseSupported = !parse('"\t"'); }); if (parseSupported) { attempt(function () { // FF 4.0 and 4.0.1 allow leading `+` signs and leading // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow // certain octal literals. parseSupported = parse("01") !== 1; }); } if (parseSupported) { attempt(function () { // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal // points. These environments, along with FF 3.1b1 and 2, // also allow trailing commas in JSON objects and arrays. parseSupported = parse("1.") !== 1; }); } } } }, function () { parseSupported = false; }); } isSupported = parseSupported; } } return has[name] = !!isSupported; } has["bug-string-char-index"] = has["date-serialization"] = has["json"] = has["json-stringify"] = has["json-parse"] = null; if (!has("json")) { // Common `[[Class]]` name aliases. var functionClass = "[object Function]", dateClass = "[object Date]", numberClass = "[object Number]", stringClass = "[object String]", arrayClass = "[object Array]", booleanClass = "[object Boolean]"; // Detect incomplete support for accessing string characters by index. var charIndexBuggy = has("bug-string-char-index"); // Internal: Normalizes the `for...in` iteration algorithm across // environments. Each enumerated key is yielded to a `callback` function. var forOwn = function (object, callback) { var size = 0, Properties, members, property; // Tests for bugs in the current environment's `for...in` algorithm. The // `valueOf` property inherits the non-enumerable flag from // `Object.prototype` in older versions of IE, Netscape, and Mozilla. (Properties = function () { this.valueOf = 0; }).prototype.valueOf = 0; // Iterate over a new instance of the `Properties` class. members = new Properties(); for (property in members) { // Ignore all properties inherited from `Object.prototype`. if (isProperty.call(members, property)) { size++; } } Properties = members = null; // Normalize the iteration algorithm. if (!size) { // A list of non-enumerable properties inherited from `Object.prototype`. members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable // properties. forOwn = function (object, callback) { var isFunction = getClass.call(object) == functionClass, property, length; var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty; for (property in object) { // Gecko <= 1.0 enumerates the `prototype` property of functions under // certain conditions; IE does not. if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { callback(property); } } // Manually invoke the callback for each non-enumerable property. for (length = dontEnums.length; property = dontEnums[--length];) { if (hasProperty.call(object, property)) { callback(property); } } }; } else { // No bugs detected; use the standard `for...in` algorithm. forOwn = function (object, callback) { var isFunction = getClass.call(object) == functionClass, property, isConstructor; for (property in object) { if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { callback(property); } } // Manually invoke the callback for the `constructor` property due to // cross-environment inconsistencies. if (isConstructor || isProperty.call(object, (property = "constructor"))) { callback(property); } }; } return forOwn(object, callback); }; // Public: Serializes a JavaScript `value` as a JSON string. The optional // `filter` argument may specify either a function that alters how object and // array members are serialized, or an array of strings and numbers that // indicates which properties should be serialized. The optional `width` // argument may be either a string or number that specifies the indentation // level of the output. if (!has("json-stringify") && !has("date-serialization")) { // Internal: A map of control characters and their escaped equivalents. var Escapes = { 92: "\\\\", 34: '\\"', 8: "\\b", 12: "\\f", 10: "\\n", 13: "\\r", 9: "\\t" }; // Internal: Converts `value` into a zero-padded string such that its // length is at least equal to `width`. The `width` must be <= 6. var leadingZeroes = "000000"; var toPaddedString = function (width, value) { // The `|| 0` expression is necessary to work around a bug in // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. return (leadingZeroes + (value || 0)).slice(-width); }; // Internal: Serializes a date object. var serializeDate = function (value) { var getData, year, month, date, time, hours, minutes, seconds, milliseconds; // Define additional utility methods if the `Date` methods are buggy. if (!isExtended) { var floor = Math.floor; // A mapping between the months of the year and the number of days between // January 1st and the first of the respective month. var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; // Internal: Calculates the number of days between the Unix epoch and the // first day of the given month. var getDay = function (year, month) { return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); }; getData = function (value) { // Manually compute the year, month, date, hours, minutes, // seconds, and milliseconds if the `getUTC*` methods are // buggy. Adapted from @Yaffle's `date-shim` project. date = floor(value / 864e5); for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); date = 1 + date - getDay(year, month); // The `time` value specifies the time within the day (see ES // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used // to compute `A modulo B`, as the `%` operator does not // correspond to the `modulo` operation for negative numbers. time = (value % 864e5 + 864e5) % 864e5; // The hours, minutes, seconds, and milliseconds are obtained by // decomposing the time within the day. See section 15.9.1.10. hours = floor(time / 36e5) % 24; minutes = floor(time / 6e4) % 60; seconds = floor(time / 1e3) % 60; milliseconds = time % 1e3; }; } else { getData = function (value) { year = value.getUTCFullYear(); month = value.getUTCMonth(); date = value.getUTCDate(); hours = value.getUTCHours(); minutes = value.getUTCMinutes(); seconds = value.getUTCSeconds(); milliseconds = value.getUTCMilliseconds(); }; } serializeDate = function (value) { if (value > -1 / 0 && value < 1 / 0) { // Dates are serialized according to the `Date#toJSON` method // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 // for the ISO 8601 date time string format. getData(value); // Serialize extended years correctly. value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + // Months, dates, hours, minutes, and seconds should have two // digits; milliseconds should have three. "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + // Milliseconds are optional in ES 5.0, but required in 5.1. "." + toPaddedString(3, milliseconds) + "Z"; year = month = date = hours = minutes = seconds = milliseconds = null; } else { value = null; } return value; }; return serializeDate(value); }; // For environments with `JSON.stringify` but buggy date serialization, // we override the native `Date#toJSON` implementation with a // spec-compliant one. if (has("json-stringify") && !has("date-serialization")) { // Internal: the `Date#toJSON` implementation used to override the native one. function dateToJSON (key) { return serializeDate(this); } // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. var nativeStringify = exports.stringify; exports.stringify = function (source, filter, width) { var nativeToJSON = Date.prototype.toJSON; Date.prototype.toJSON = dateToJSON; var result = nativeStringify(source, filter, width); Date.prototype.toJSON = nativeToJSON; return result; } } else { // Internal: Double-quotes a string `value`, replacing all ASCII control // characters (characters with code unit values between 0 and 31) with // their escaped equivalents. This is an implementation of the // `Quote(value)` operation defined in ES 5.1 section 15.12.3. var unicodePrefix = "\\u00"; var escapeChar = function (character) { var charCode = character.charCodeAt(0), escaped = Escapes[charCode]; if (escaped) { return escaped; } return unicodePrefix + toPaddedString(2, charCode.toString(16)); }; var reEscape = /[\x00-\x1f\x22\x5c]/g; var quote = function (value) { reEscape.lastIndex = 0; return '"' + ( reEscape.test(value) ? value.replace(reEscape, escapeChar) : value ) + '"'; }; // Internal: Recursively serializes an object. Implements the // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { var value, type, className, results, element, index, length, prefix, result; attempt(function () { // Necessary for host object support. value = object[property]; }); if (typeof value == "object" && value) { if (value.getUTCFullYear && getClass.call(value) == dateClass && value.toJSON === Date.prototype.toJSON) { value = serializeDate(value); } else if (typeof value.toJSON == "function") { value = value.toJSON(property); } } if (callback) { // If a replacement function was provided, call it to obtain the value // for serialization. value = callback.call(object, property, value); } // Exit early if value is `undefined` or `null`. if (value == undefined) { return value === undefined ? value : "null"; } type = typeof value; // Only call `getClass` if the value is an object. if (type == "object") { className = getClass.call(value); } switch (className || type) { case "boolean": case booleanClass: // Booleans are represented literally. return "" + value; case "number": case numberClass: // JSON numbers must be finite. `Infinity` and `NaN` are serialized as // `"null"`. return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; case "string": case stringClass: // Strings are double-quoted and escaped. return quote("" + value); } // Recursively serialize objects and arrays. if (typeof value == "object") { // Check for cyclic structures. This is a linear search; performance // is inversely proportional to the number of unique nested objects. for (length = stack.length; length--;) { if (stack[length] === value) { // Cyclic structures cannot be serialized by `JSON.stringify`. throw TypeError(); } } // Add the object to the stack of traversed objects. stack.push(value); results = []; // Save the current indentation level and indent one additional level. prefix = indentation; indentation += whitespace; if (className == arrayClass) { // Recursively serialize array elements. for (index = 0, length = value.length; index < length; index++) { element = serialize(index, value, callback, properties, whitespace, indentation, stack); results.push(element === undefined ? "null" : element); } result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; } else { // Recursively serialize object members. Members are selected from // either a user-specified list of property names, or the object // itself. forOwn(properties || value, function (property) { var element = serialize(property, value, callback, properties, whitespace, indentation, stack); if (element !== undefined) { // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} // is not the empty string, let `member` {quote(property) + ":"} // be the concatenation of `member` and the `space` character." // The "`space` character" refers to the literal space // character, not the `space` {width} argument provided to // `JSON.stringify`. results.push(quote(property) + ":" + (whitespace ? " " : "") + element); } }); result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; } // Remove the object from the traversed object stack. stack.pop(); return result; } }; // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. exports.stringify = function (source, filter, width) { var whitespace, callback, properties, className; if (objectTypes[typeof filter] && filter) { className = getClass.call(filter); if (className == functionClass) { callback = filter; } else if (className == arrayClass) { // Convert the property names array into a makeshift set. properties = {}; for (var index = 0, length = filter.length, value; index < length;) { value = filter[index++]; className = getClass.call(value); if (className == "[object String]" || className == "[object Number]") { properties[value] = 1; } } } } if (width) { className = getClass.call(width); if (className == numberClass) { // Convert the `width` to an integer and create a string containing // `width` number of space characters. if ((width -= width % 1) > 0) { if (width > 10) { width = 10; } for (whitespace = ""; whitespace.length < width;) { whitespace += " "; } } } else if (className == stringClass) { whitespace = width.length <= 10 ? width : width.slice(0, 10); } } // Opera <= 7.54u2 discards the values associated with empty string keys // (`""`) only if they are used directly within an object member list // (e.g., `!("" in { "": 1})`). return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); }; } } // Public: Parses a JSON source string. if (!has("json-parse")) { var fromCharCode = String.fromCharCode; // Internal: A map of escaped control characters and their unescaped // equivalents. var Unescapes = { 92: "\\", 34: '"', 47: "/", 98: "\b", 116: "\t", 110: "\n", 102: "\f", 114: "\r" }; // Internal: Stores the parser state. var Index, Source; // Internal: Resets the parser state and throws a `SyntaxError`. var abort = function () { Index = Source = null; throw SyntaxError(); }; // Internal: Returns the next token, or `"$"` if the parser has reached // the end of the source string. A token may be a string, number, `null` // literal, or Boolean literal. var lex = function () { var source = Source, length = source.length, value, begin, position, isSigned, charCode; while (Index < length) { charCode = source.charCodeAt(Index); switch (charCode) { case 9: case 10: case 13: case 32: // Skip whitespace tokens, including tabs, carriage returns, line // feeds, and space characters. Index++; break; case 123: case 125: case 91: case 93: case 58: case 44: // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at // the current position. value = charIndexBuggy ? source.charAt(Index) : source[Index]; Index++; return value; case 34: // `"` delimits a JSON string; advance to the next character and // begin parsing the string. String tokens are prefixed with the // sentinel `@` character to distinguish them from punctuators and // end-of-string tokens. for (value = "@", Index++; Index < length;) { charCode = source.charCodeAt(Index); if (charCode < 32) { // Unescaped ASCII control characters (those with a code unit // less than the space character) are not permitted. abort(); } else if (charCode == 92) { // A reverse solidus (`\`) marks the beginning of an escaped // control character (including `"`, `\`, and `/`) or Unicode // escape sequence. charCode = source.charCodeAt(++Index); switch (charCode) { case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: // Revive escaped control characters. value += Unescapes[charCode]; Index++; break; case 117: // `\u` marks the beginning of a Unicode escape sequence. // Advance to the first character and validate the // four-digit code point. begin = ++Index; for (position = Index + 4; Index < position; Index++) { charCode = source.charCodeAt(Index); // A valid sequence comprises four hexdigits (case- // insensitive) that form a single hexadecimal value. if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { // Invalid Unicode escape sequence. abort(); } } // Revive the escaped character. value += fromCharCode("0x" + source.slice(begin, Index)); break; default: // Invalid escape sequence. abort(); } } else { if (charCode == 34) { // An unescaped double-quote character marks the end of the // string. break; } charCode = source.charCodeAt(Index); begin = Index; // Optimize for the common case where a string is valid. while (charCode >= 32 && charCode != 92 && charCode != 34) { charCode = source.charCodeAt(++Index); } // Append the string as-is. value += source.slice(begin, Index); } } if (source.charCodeAt(Index) == 34) { // Advance to the next character and return the revived string. Index++; return value; } // Unterminated string. abort(); default: // Parse numbers and literals. begin = Index; // Advance past the negative sign, if one is specified. if (charCode == 45) { isSigned = true; charCode = source.charCodeAt(++Index); } // Parse an integer or floating-point value. if (charCode >= 48 && charCode <= 57) { // Leading zeroes are interpreted as octal literals. if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { // Illegal octal literal. abort(); } isSigned = false; // Parse the integer component. for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); // Floats cannot contain a leading decimal point; however, this // case is already accounted for by the parser. if (source.charCodeAt(Index) == 46) { position = ++Index; // Parse the decimal component. for (; position < length; position++) { charCode = source.charCodeAt(position); if (charCode < 48 || charCode > 57) { break; } } if (position == Index) { // Illegal trailing decimal. abort(); } Index = position; } // Parse exponents. The `e` denoting the exponent is // case-insensitive. charCode = source.charCodeAt(Index); if (charCode == 101 || charCode == 69) { charCode = source.charCodeAt(++Index); // Skip past the sign following the exponent, if one is // specified. if (charCode == 43 || charCode == 45) { Index++; } // Parse the exponential component. for (position = Index; position < length; position++) { charCode = source.charCodeAt(position); if (charCode < 48 || charCode > 57) { break; } } if (position == Index) { // Illegal empty exponent. abort(); } Index = position; } // Coerce the parsed value to a JavaScript number. return +source.slice(begin, Index); } // A negative sign may only precede numbers. if (isSigned) { abort(); } // `true`, `false`, and `null` literals. var temp = source.slice(Index, Index + 4); if (temp == "true") { Index += 4; return true; } else if (temp == "fals" && source.charCodeAt(Index + 4 ) == 101) { Index += 5; return false; } else if (temp == "null") { Index += 4; return null; } // Unrecognized token. abort(); } } // Return the sentinel `$` character if the parser has reached the end // of the source string. return "$"; }; // Internal: Parses a JSON `value` token. var get = function (value) { var results, hasMembers; if (value == "$") { // Unexpected end of input. abort(); } if (typeof value == "string") { if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { // Remove the sentinel `@` character. return value.slice(1); } // Parse object and array literals. if (value == "[") { // Parses a JSON array, returning a new JavaScript array. results = []; for (;;) { value = lex(); // A closing square bracket marks the end of the array literal. if (value == "]") { break; } // If the array literal contains elements, the current token // should be a comma separating the previous element from the // next. if (hasMembers) { if (value == ",") { value = lex(); if (value == "]") { // Unexpected trailing `,` in array literal. abort(); } } else { // A `,` must separate each array element. abort(); } } else { hasMembers = true; } // Elisions and leading commas are not permitted. if (value == ",") { abort(); } results.push(get(value)); } return results; } else if (value == "{") { // Parses a JSON object, returning a new JavaScript object. results = {}; for (;;) { value = lex(); // A closing curly brace marks the end of the object literal. if (value == "}") { break; } // If the object literal contains members, the current token // should be a comma separator. if (hasMembers) { if (value == ",") { value = lex(); if (value == "}") { // Unexpected trailing `,` in object literal. abort(); } } else { // A `,` must separate each object member. abort(); } } else { hasMembers = true; } // Leading commas are not permitted, object property names must be // double-quoted strings, and a `:` must separate each property // name and value. if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { abort(); } results[value.slice(1)] = get(lex()); } return results; } // Unexpected token encountered. abort(); } return value; }; // Internal: Updates a traversed object member. var update = function (source, property, callback) { var element = walk(source, property, callback); if (element === undefined) { delete source[property]; } else { source[property] = element; } }; // Internal: Recursively traverses a parsed JSON object, invoking the // `callback` function for each value. This is an implementation of the // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. var walk = function (source, property, callback) { var value = source[property], length; if (typeof value == "object" && value) { // `forOwn` can't be used to traverse an array in Opera <= 8.54 // because its `Object#hasOwnProperty` implementation returns `false` // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). if (getClass.call(value) == arrayClass) { for (length = value.length; length--;) { update(getClass, forOwn, value, length, callback); } } else { forOwn(value, function (property) { update(value, property, callback); }); } } return callback.call(source, property, value); }; // Public: `JSON.parse`. See ES 5.1 section 15.12.2. exports.parse = function (source, callback) { var result, value; Index = 0; Source = "" + source; result = get(lex()); // If a JSON string contains multiple tokens, it is invalid. if (lex() != "$") { abort(); } // Reset the parser state. Index = Source = null; return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; }; } } exports.runInContext = runInContext; return exports; } if (freeExports && !isLoader) { // Export for CommonJS environments. runInContext(root, freeExports); } else { // Export for web browsers and JavaScript engines. var nativeJSON = root.JSON, previousJSON = root.JSON3, isRestored = false; var JSON3 = runInContext(root, (root.JSON3 = { // Public: Restores the original value of the global `JSON` object and // returns a reference to the `JSON3` object. "noConflict": function () { if (!isRestored) { isRestored = true; root.JSON = nativeJSON; root.JSON3 = previousJSON; nativeJSON = previousJSON = null; } return JSON3; } })); root.JSON = { "parse": JSON3.parse, "stringify": JSON3.stringify }; } // Export for asynchronous module loaders. if (isLoader) { define(function () { return JSON3; }); } }).call(this); }; BundleModuleCode['os/polyfill']=function (module,exports,global,process){ /************** ARRAY ********************/ if (!Array.prototype.find) { Object.defineProperty(Array.prototype, 'find', { value: function(predicate) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, ( kValue, k, O ))). // d. If testResult is true, return kValue. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return kValue; } // e. Increase k by 1. k++; } // 7. Return undefined. return undefined; } }); } if (typeof Object.assign != 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { // .length of function is 2 'use strict'; if (target == null) { // TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource != null) { // Skip over if undefined or null for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } }; BundleModuleCode['com/path']=function (module,exports,global,process){ var Fs = Require('fs'); var _process = process || {}; (function () { "use strict"; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var isWindows = _process.platform === 'win32'; var util = Require('util'); if (!util.deprecate) util.deprecate=function(f,w) {return f;}; // resolves . and .. elements in a path array with directory names there // must be no slashes, empty elements, or device names (c:\) in the array // (so also no leading and trailing slashes - it does not distinguish // relative and absolute paths) function normalizeArray(parts, allowAboveRoot) { // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === '.') { parts.splice(i, 1); } else if (last === '..') { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (allowAboveRoot) { for (; up--; up) { parts.unshift('..'); } } return parts; } if (isWindows) { // Regex to split a windows path into three parts: [*, device, slash, // tail] windows-only var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; // Regex to split the tail part of the above into [*, dir, basename, ext] var splitTailRe = /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; // Function to split a filename into [root, dir, basename, ext] // windows version var splitPath = function(filename) { // Separate device+slash from tail var result = splitDeviceRe.exec(filename), device = (result[1] || '') + (result[2] || ''), tail = result[3] || ''; // Split the tail into dir, basename and extension var result2 = splitTailRe.exec(tail), dir = result2[1], basename = result2[2], ext = result2[3]; return [device, dir, basename, ext]; }; var normalizeUNCRoot = function(device) { return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\'); }; // path.resolve([from ...], to) // windows version exports.resolve = function() { var resolvedDevice = '', resolvedTail = '', resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1; i--) { var path; if (i >= 0) { path = arguments[i]; } else if (!resolvedDevice) { path = _process.cwd(); } else { // Windows has the concept of drive-specific current working // directories. If we've resolved a drive letter but not yet an // absolute path, get cwd for that drive. We're sure the device is not // an unc path at this points, because unc paths are always absolute. path = _process.env['=' + resolvedDevice]; // Verify that a drive-local cwd was found and that it actually points // to our drive. If not, default to the drive's root. if (!path || path.substr(0, 3).toLowerCase() !== resolvedDevice.toLowerCase() + '\\') { path = resolvedDevice + '\\'; } } // Skip empty and invalid entries if (!util.isString(path)) { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { continue; } var result = splitDeviceRe.exec(path), device = result[1] || '', isUnc = device && device.charAt(1) !== ':', isAbsolute = exports.isAbsolute(path), tail = result[3]; if (device && resolvedDevice && device.toLowerCase() !== resolvedDevice.toLowerCase()) { // This path points to another device so it is not applicable continue; } if (!resolvedDevice) { resolvedDevice = device; } if (!resolvedAbsolute) { resolvedTail = tail + '\\' + resolvedTail; resolvedAbsolute = isAbsolute; } if (resolvedDevice && resolvedAbsolute) { break; } } // Convert slashes to backslashes when `resolvedDevice` points to an UNC // root. Also squash multiple slashes into a single one where appropriate. if (isUnc) { resolvedDevice = normalizeUNCRoot(resolvedDevice); } // At this point the path should be resolved to a full absolute path, // but handle relative paths to be safe (might happen when process.cwd() // fails) // Normalize the tail path function f(p) { return !!p; } resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), !resolvedAbsolute).join('\\'); return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || '.'; }; // windows version exports.normalize = function(path) { var result = splitDeviceRe.exec(path), device = result[1] || '', isUnc = device && device.charAt(1) !== ':', isAbsolute = exports.isAbsolute(path), tail = result[3], trailingSlash = /[\\\/]$/.test(tail); // If device is a drive letter, we'll normalize to lower case. if (device && device.charAt(1) === ':') { device = device[0].toLowerCase() + device.substr(1); } // Normalize the tail path tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { return !!p; }), !isAbsolute).join('\\'); if (!tail && !isAbsolute) { tail = '.'; } if (tail && trailingSlash) { tail += '\\'; } // Convert slashes to backslashes when `device` points to an UNC root. // Also squash multiple slashes into a single one where appropriate. if (isUnc) { device = normalizeUNCRoot(device); } return device + (isAbsolute ? '\\' : '') + tail; }; // windows version exports.isAbsolute = function(path) { var result = splitDeviceRe.exec(path), device = result[1] || '', isUnc = !!device && device.charAt(1) !== ':'; // UNC paths are always absolute return !!result[2] || isUnc; }; // windows version exports.join = function() { function f(p) { if (!util.isString(p)) { throw new TypeError('Arguments to path.join must be strings'); } return p; } var paths = Array.prototype.filter.call(arguments, f); var joined = paths.join('\\'); // Make sure that the joined path doesn't start with two slashes, because // normalize() will mistake it for an UNC path then. // // This step is skipped when it is very clear that the user actually // intended to point at an UNC path. This is assumed when the first // non-empty string arguments starts with exactly two slashes followed by // at least one more non-slash character. // // Note that for normalize() to treat a path as an UNC path it needs to // have at least 2 components, so we don't filter for that here. // This means that the user can use join to construct UNC paths from // a server name and a share name; for example: // path.join('//server', 'share') -> '\\\\server\\share\') if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { joined = joined.replace(/^[\\\/]{2,}/, '\\'); } return exports.normalize(joined); }; // path.relative(from, to) // it will solve the relative path from 'from' to 'to', for instance: // from = 'C:\\orandea\\test\\aaa' // to = 'C:\\orandea\\impl\\bbb' // The output of the function should be: '..\\..\\impl\\bbb' // windows version exports.relative = function(from, to) { from = exports.resolve(from); to = exports.resolve(to); // windows is not case sensitive var lowerFrom = from.toLowerCase(); var lowerTo = to.toLowerCase(); function trim(arr) { var start = 0; for (; start < arr.length; start++) { if (arr[start] !== '') break; } var end = arr.length - 1; for (; end >= 0; end--) { if (arr[end] !== '') break; } if (start > end) return []; return arr.slice(start, end + 1); } var toParts = trim(to.split('\\')); var lowerFromParts = trim(lowerFrom.split('\\')); var lowerToParts = trim(lowerTo.split('\\')); var length = Math.min(lowerFromParts.length, lowerToParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (lowerFromParts[i] !== lowerToParts[i]) { samePartsLength = i; break; } } if (samePartsLength == 0) { return to; } var outputParts = []; for (var i = samePartsLength; i < lowerFromParts.length; i++) { outputParts.push('..'); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join('\\'); }; exports.sep = '\\'; exports.delimiter = ';'; } else /* posix */ { // Split a filename into [root, dir, basename, ext], unix version // 'root' is just a slash, or nothing. var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; var splitPath = function(filename) { return splitPathRe.exec(filename).slice(1); }; // path.resolve([from ...], to) // posix version exports.resolve = function() { var resolvedPath = '', resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? arguments[i] : _process.cwd(); // Skip empty and invalid entries if (!util.isString(path)) { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { continue; } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charAt(0) === '/'; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { return !!p; }), !resolvedAbsolute).join('/'); return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; }; // path.normalize(path) // posix version exports.normalize = function(path) { var isAbsolute = exports.isAbsolute(path), trailingSlash = path[path.length - 1] === '/', segments = path.split('/'), nonEmptySegments = []; // Normalize the path for (var i = 0; i < segments.length; i++) { if (segments[i]) { nonEmptySegments.push(segments[i]); } } path = normalizeArray(nonEmptySegments, !isAbsolute).join('/'); if (!path && !isAbsolute) { path = '.'; } if (path && trailingSlash) { path += '/'; } return (isAbsolute ? '/' : '') + path; }; // posix version exports.isAbsolute = function(path) { return path.charAt(0) === '/'; }; // posix version exports.join = function() { var path = ''; for (var i = 0; i < arguments.length; i++) { var segment = arguments[i]; if (!util.isString(segment)) { throw new TypeError('Arguments to path.join must be strings'); } if (segment) { if (!path) { path += segment; } else { path += '/' + segment; } } } return exports.normalize(path); }; // path.relative(from, to) // posix version exports.relative = function(from, to) { from = exports.resolve(from).substr(1); to = exports.resolve(to).substr(1); function trim(arr) { var start = 0; for (; start < arr.length; start++) { if (arr[start] !== '') break; } var end = arr.length - 1; for (; end >= 0; end--) { if (arr[end] !== '') break; } if (start > end) return []; return arr.slice(start, end + 1); } var fromParts = trim(from.split('/')); var toParts = trim(to.split('/')); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..'); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join('/'); }; exports.sep = '/'; exports.delimiter = ':'; } exports.dirname = function(path) { var result = splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) { // No dirname whatsoever return '.'; } if (dir) { // It has a dirname, strip trailing slash dir = dir.substr(0, dir.length - 1); } return root + dir; }; exports.basename = function(path, ext) { var f = splitPath(path)[2]; // TODO: make this comparison case-insensitive on windows? if (ext && f.substr(-1 * ext.length) === ext) { f = f.substr(0, f.length - ext.length); } return f; }; exports.extname = function(path) { return splitPath(path)[3]; }; exports.exists = util.deprecate(function(path, callback) { if (Fs) Fs.exists(path, callback); else callback(false); }, 'path.exists is now called `fs.exists`.'); exports.existsSync = util.deprecate(function(path) { if (Fs) return Fs.existsSync(path); else return false; }, 'path.existsSync is now called `fs.existsSync`.'); if (isWindows) { exports._makeLong = function(path) { // Note: this will *probably* throw somewhere. if (!util.isString(path)) return path; if (!path) { return ''; } var resolvedPath = exports.resolve(path); if (/^[a-zA-Z]\:\\/.test(resolvedPath)) { // path is local filesystem path, which needs to be converted // to long UNC path. return '\\\\?\\' + resolvedPath; } else if (/^\\\\[^?.]/.test(resolvedPath)) { // path is network UNC path, which needs to be converted // to long UNC path. return '\\\\?\\UNC\\' + resolvedPath.substring(2); } return path; }; } else { exports._makeLong = function(path) { return path; }; } }()); }; BundleModuleCode['os/util']=function (module,exports,global,process){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (!isString(f)) { var objects = []; for (var i = 0; i < arguments.length; i++) { objects.push(inspect(arguments[i])); } return objects.join(' '); } var i = 1; var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': try { return JSON.stringify(args[i++]); } catch (_) { return '[Circular]'; } default: return x; } }); for (var x = args[i]; i < len; x = args[++i]) { if (isNull(x) || !isObject(x)) { str += ' ' + x; } else { str += ' ' + inspect(x); } } return str; }; // Mark that a method should not be used. // Returns a modified function which warns once by default. // If --no-deprecation is set, then it is a no-op. exports.deprecate = function(fn, msg) { // Allow for deprecating things in the process of starting up. if (isUndefined(global.process)) { return function() { return exports.deprecate(fn, msg).apply(this, arguments); }; } if (process.noDeprecation === true) { return fn; } var warned = false; function deprecated() { if (!warned) { if (process.throwDeprecation) { throw new Error(msg); } else if (process.traceDeprecation) { console.trace(msg); } else { console.error(msg); } warned = true; } return fn.apply(this, arguments); } return deprecated; }; var debugs = {}; var debugEnviron; exports.debuglog = function(set) { if (isUndefined(debugEnviron)) debugEnviron = process.env.NODE_DEBUG || ''; set = set.toUpperCase(); if (!debugs[set]) { if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { var pid = process.pid; debugs[set] = function() { var msg = exports.format.apply(exports, arguments); console.error('%s %d: %s', set, pid, msg); }; } else { debugs[set] = function() {}; } } return debugs[set]; }; /** * Echos the value of a value. Trys to print the value out * in the best way possible given the different types. * * @param {Object} obj The object to print out. * @param {Object} opts Optional options object that alters the output. */ /* legacy: obj, showHidden, depth, colors*/ function inspect(obj, opts) { // default options var ctx = { seen: [], stylize: stylizeNoColor }; // legacy... if (arguments.length >= 3) ctx.depth = arguments[2]; if (arguments.length >= 4) ctx.colors = arguments[3]; if (isBoolean(opts)) { // legacy... ctx.showHidden = opts; } else if (opts) { // got an "options" object exports._extend(ctx, opts); } // set default options if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; if (isUndefined(ctx.colors)) ctx.colors = false; if (isUndefined(ctx.customInspect)) ctx.customInspect = true; if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } exports.inspect = inspect; // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics inspect.colors = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; // Don't use 'blue' not visible on cmd.exe inspect.styles = { 'special': 'cyan', 'number': 'yellow', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }; function stylizeWithColor(str, styleType) { var style = inspect.styles[styleType]; if (style) { return '\u001b[' + inspect.colors[style][0] + 'm' + str + '\u001b[' + inspect.colors[style][1] + 'm'; } else { return str; } } function stylizeNoColor(str, styleType) { return str; } function arrayToHash(array) { var hash = {}; array.forEach(function(val, idx) { hash[val] = true; }); return hash; } function formatValue(ctx, value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { var ret = value.inspect(recurseTimes, ctx); if (!isString(ret)) { ret = formatValue(ctx, ret, recurseTimes); } return ret; } // Primitive types cannot have properties var primitive = formatPrimitive(ctx, value); if (primitive) { return primitive; } // Look up the keys of the object. var keys = Object.keys(value); var visibleKeys = arrayToHash(keys); if (ctx.showHidden) { keys = Object.getOwnPropertyNames(value); } // IE doesn't make error fields non-enumerable // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { return formatError(value); } // Some type of object without properties can be shortcutted. if (keys.length === 0) { if (isFunction(value)) { var name = value.name ? ': ' + value.name : ''; return ctx.stylize('[Function' + name + ']', 'special'); } if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } if (isDate(value)) { return ctx.stylize(Date.prototype.toString.call(value), 'date'); } if (isError(value)) { return formatError(value); } } var base = '', array = false, braces = ['{', '}']; // Make Array say that they are Array if (isArray(value)) { array = true; braces = ['[', ']']; } // Make functions say that they are functions if (isFunction(value)) { var n = value.name ? ': ' + value.name : ''; base = ' [Function' + n + ']'; } // Make RegExps say that they are RegExps if (isRegExp(value)) { base = ' ' + RegExp.prototype.toString.call(value); } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + Date.prototype.toUTCString.call(value); } // Make error with message first say the error if (isError(value)) { base = ' ' + formatError(value); } if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } else { return ctx.stylize('[Object]', 'special'); } } ctx.seen.push(value); var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); }); } ctx.seen.pop(); return reduceToSingleString(output, base, braces); } function formatPrimitive(ctx, value) { if (isUndefined(value)) return ctx.stylize('undefined', 'undefined'); if (isString(value)) { var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return ctx.stylize(simple, 'string'); } if (isNumber(value)) return ctx.stylize('' + value, 'number'); if (isBoolean(value)) return ctx.stylize('' + value, 'boolean'); // For some reason typeof null is "object", so special case here. if (isNull(value)) return ctx.stylize('null', 'null'); } function formatError(value) { return '[' + Error.prototype.toString.call(value) + ']'; } function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; for (var i = 0, l = value.length; i < l; ++i) { if (hasOwnProperty(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true)); } else { output.push(''); } } keys.forEach(function(key) { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } }); return output; } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { var name, str, desc; desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; if (desc.get) { if (desc.set) { str = ctx.stylize('[Getter/Setter]', 'special'); } else { str = ctx.stylize('[Getter]', 'special'); } } else { if (desc.set) { str = ctx.stylize('[Setter]', 'special'); } } if (!hasOwnProperty(visibleKeys, key)) { name = '[' + key + ']'; } if (!str) { if (ctx.seen.indexOf(desc.value) < 0) { if (isNull(recurseTimes)) { str = formatValue(ctx, desc.value, null); } else { str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (array) { str = str.split('\n').map(function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + str.split('\n').map(function(line) { return ' ' + line; }).join('\n'); } } } else { str = ctx.stylize('[Circular]', 'special'); } } if (isUndefined(name)) { if (array && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = ctx.stylize(name, 'string'); } } return name + ': ' + str; } function reduceToSingleString(output, base, braces) { var numLinesEst = 0; var length = output.reduce(function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; }, 0); if (length > 60) { return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. function isArray(ar) { return Array.isArray(ar); } exports.isArray = isArray; function isBoolean(arg) { return typeof arg === 'boolean'; } exports.isBoolean = isBoolean; function isNull(arg) { return arg === null; } exports.isNull = isNull; function isNullOrUndefined(arg) { return arg == null; } exports.isNullOrUndefined = isNullOrUndefined; function isNumber(arg) { return typeof arg === 'number'; } exports.isNumber = isNumber; function isString(arg) { return typeof arg === 'string'; } exports.isString = isString; function isSymbol(arg) { return typeof arg === 'symbol'; } exports.isSymbol = isSymbol; function isUndefined(arg) { return arg === void 0; } exports.isUndefined = isUndefined; function isRegExp(re) { return isObject(re) && objectToString(re) === '[object RegExp]'; } exports.isRegExp = isRegExp; function isObject(arg) { return typeof arg === 'object' && arg !== null; } exports.isObject = isObject; function isDate(d) { return isObject(d) && objectToString(d) === '[object Date]'; } exports.isDate = isDate; function isError(e) { return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error); } exports.isError = isError; function isFunction(arg) { return typeof arg === 'function'; } exports.isFunction = isFunction; function isPrimitive(arg) { return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol typeof arg === 'undefined'; } exports.isPrimitive = isPrimitive; exports.isBuffer = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; }; function objectToString(o) { return Object.prototype.toString.call(o); } function pad(n) { return n < 10 ? '0' + n.toString(10) : n.toString(10); } var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // 26 Feb 16:19:34 function timestamp() { var d = new Date(); var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':'); return [d.getDate(), months[d.getMonth()], time].join(' '); } // log is just a thin wrapper to console.log that prepends a timestamp exports.log = function() { console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); }; /** * Inherit the prototype methods from one constructor into another. * * The Function.prototype.inherits from lang.js rewritten as a standalone * function (not on Function.prototype). NOTE: If this file is to be loaded * during bootstrapping this function needs to be rewritten using some native * functions as prototype setup using normal JavaScript does not work as * expected during bootstrapping (see mirror.js in r114903). * * @param {function} ctor Constructor function which needs to inherit the * prototype. * @param {function} superCtor Constructor function to inherit prototype from. */ exports.inherits = Require('os/inherits'); exports._extend = function(origin, add) { // Don't do anything if add isn't an object if (!add || !isObject(add)) return origin; var keys = Object.keys(add); var i = keys.length; while (i--) { origin[keys[i]] = add[keys[i]]; } return origin; }; function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } }; BundleModuleCode['os/inherits']=function (module,exports,global,process){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; } else { // old school shim for old browsers module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor var TempCtor = function () {} TempCtor.prototype = superCtor.prototype ctor.prototype = new TempCtor() ctor.prototype.constructor = ctor } } }; BundleModuleCode['com/sprintf']=function (module,exports,global,process){ (function(window) { var re = { not_string: /[^s]/, number: /[diefg]/, json: /[j]/, not_json: /[^j]/, text: /^[^\x25]+/, modulo: /^\x25{2}/, placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/, key: /^([a-z_][a-z_\d]*)/i, key_access: /^\.([a-z_][a-z_\d]*)/i, index_access: /^\[(\d+)\]/, sign: /^[\+\-]/ } function sprintf() { var key = arguments[0], cache = sprintf.cache if (!(cache[key] && cache.hasOwnProperty(key))) { cache[key] = sprintf.parse(key) } return sprintf.format.call(null, cache[key], arguments) } sprintf.format = function(parse_tree, argv) { var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = "" for (i = 0; i < tree_length; i++) { node_type = get_type(parse_tree[i]) if (node_type === "string") { output[output.length] = parse_tree[i] } else if (node_type === "array") { match = parse_tree[i] // convenience purposes only if (match[2]) { // keyword argument arg = argv[cursor] for (k = 0; k < match[2].length; k++) { if (!arg.hasOwnProperty(match[2][k])) { throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k])) } arg = arg[match[2][k]] } } else if (match[1]) { // positional argument (explicit) arg = argv[match[1]] } else { // positional argument (implicit) arg = argv[cursor++] } if (get_type(arg) == "function") { arg = arg() } if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) { throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg))) } if (re.number.test(match[8])) { is_positive = arg >= 0 } switch (match[8]) { case "b": arg = arg.toString(2) break case "c": arg = String.fromCharCode(arg) break case "d": case "i": arg = parseInt(arg, 10) break case "j": arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0) break case "e": arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential() break case "f": arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg) break case "g": arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg) break case "o": arg = arg.toString(8) break case "s": arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg) break case "u": arg = arg >>> 0 break case "x": arg = arg.toString(16) break case "X": arg = arg.toString(16).toUpperCase() break } if (re.json.test(match[8])) { output[output.length] = arg } else { if (re.number.test(match[8]) && (!is_positive || match[3])) { sign = is_positive ? "+" : "-" arg = arg.toString().replace(re.sign, "") } else { sign = "" } pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " " pad_length = match[6] - (sign + arg).length pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : "" output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg) } } } return output.join("") } sprintf.cache = {} sprintf.parse = function(fmt) { var _fmt = fmt, match = [], parse_tree = [], arg_names = 0 while (_fmt) { if ((match = re.text.exec(_fmt)) !== null) { parse_tree[parse_tree.length] = match[0] } else if ((match = re.modulo.exec(_fmt)) !== null) { parse_tree[parse_tree.length] = "%" } else if ((match = re.placeholder.exec(_fmt)) !== null) { if (match[2]) { arg_names |= 1 var field_list = [], replacement_field = match[2], field_match = [] if ((field_match = re.key.exec(replacement_field)) !== null) { field_list[field_list.length] = field_match[1] while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") { if ((field_match = re.key_access.exec(replacement_field)) !== null) { field_list[field_list.length] = field_match[1] } else if ((field_match = re.index_access.exec(replacement_field)) !== null) { field_list[field_list.length] = field_match[1] } else { throw new SyntaxError("[sprintf] failed to parse named argument key") } } } else { throw new SyntaxError("[sprintf] failed to parse named argument key") } match[2] = field_list } else { arg_names |= 2 } if (arg_names === 3) { throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported") } parse_tree[parse_tree.length] = match } else { throw new SyntaxError("[sprintf] unexpected placeholder") } try {_fmt = _fmt.substring(match[0].length)} catch (e) {throw new SyntaxError("[sprintf] unexpected fromat")} } return parse_tree } var vsprintf = function(fmt, argv, _argv) { _argv = (argv || []).slice(0) _argv.splice(0, 0, fmt) return sprintf.apply(null, _argv) } /** * helpers */ function get_type(variable) { return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase() } function str_repeat(input, multiplier) { return Array(multiplier + 1).join(input) } /** * export to either browser or node.js */ if (typeof exports !== "undefined") { exports.sprintf = sprintf exports.vsprintf = vsprintf } else { window.sprintf = sprintf window.vsprintf = vsprintf if (typeof define === "function" && define.amd) { define(function() { return { sprintf: sprintf, vsprintf: vsprintf } }) } } })(typeof window === "undefined" ? this : window); }; BundleModuleCode['os/base64']=function (module,exports,global,process){ var keyStr = "ABCDEFGHIJKLMNOP" + "QRSTUVWXYZabcdef" + "ghijklmnopqrstuv" + "wxyz0123456789+/" + "="; var Base64 = { encode: function (input) { input = escape(input); var output = ""; var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; do { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < input.length); return output; }, encodeBuf: function (input) { var output = ""; var NaN = output.charCodeAt(2); var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; var len = input.length; do { chr1 = input.readUInt8(i++); chr2 = (i> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < len); return output; }, decode: function (input) { var output = ""; var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < input.length); return unescape(output); }, decodeBuf: function (input) { var len = input.length; var buf = new Buffer(len); var chr1, chr2, chr3 = ""; var enc1, enc2, enc3, enc4 = ""; var i = 0; var buflen = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); buf.fill(0); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; buf.writeUInt8(chr1,buflen); buflen++; if (enc3 != 64) { buf.writeUInt8(chr2,buflen); buflen++; } if (enc4 != 64) { buf.writeUInt8(chr3,buflen); buflen++; } chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } while (i < input.length); return buf.slice(0,buflen); } }; module.exports = Base64; }; BundleModuleCode['os/buffer']=function (module,exports,global,process){ var Ieee754 = Require('os/buffer_ieee754'); /* ------- base64-js -------- */ var lookup = [] var revLookup = [] var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array function init () { var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' for (var i = 0, len = code.length; i < len; ++i) { lookup[i] = code[i] revLookup[code.charCodeAt(i)] = i } revLookup['-'.charCodeAt(0)] = 62 revLookup['_'.charCodeAt(0)] = 63 } init() function toByteArray (b64) { var i, j, l, tmp, placeHolders, arr var len = b64.length if (len % 4 > 0) { throw new Error('Invalid string. Length must be a multiple of 4') } // the number of equal signs (place holders) // if there are two placeholders, than the two characters before it // represent one byte // if there is only one, then the three characters before it represent 2 bytes // this is just a cheap hack to not do indexOf twice placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 // base64 is 4/3 + up to two characters of the original data arr = new Arr(len * 3 / 4 - placeHolders) // if there are placeholders, only get up to the last complete 4 chars l = placeHolders > 0 ? len - 4 : len var L = 0 for (i = 0, j = 0; i < l; i += 4, j += 3) { tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] arr[L++] = (tmp >> 16) & 0xFF arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } if (placeHolders === 2) { tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) arr[L++] = tmp & 0xFF } else if (placeHolders === 1) { tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } return arr } function tripletToBase64 (num) { return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] } function encodeChunk (uint8, start, end) { var tmp var output = [] for (var i = start; i < end; i += 3) { tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) output.push(tripletToBase64(tmp)) } return output.join('') } function fromByteArray (uint8) { var tmp var len = uint8.length var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes var output = '' var parts = [] var maxChunkLength = 16383 // must be multiple of 3 // go through the array every three bytes, we'll deal with trailing stuff later for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) } // pad the end with zeros, but make sure to not forget the extra bytes if (extraBytes === 1) { tmp = uint8[len - 1] output += lookup[tmp >> 2] output += lookup[(tmp << 4) & 0x3F] output += '==' } else if (extraBytes === 2) { tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) output += lookup[tmp >> 10] output += lookup[(tmp >> 4) & 0x3F] output += lookup[(tmp << 2) & 0x3F] output += '=' } parts.push(output) return parts.join('') } /* ------- base64-js -------- */ var assert; exports.Buffer = Buffer; exports.SlowBuffer = Buffer; Buffer.poolSize = 8192; exports.INSPECT_MAX_BYTES = 50; function stringtrim(str) { if (str.trim) return str.trim(); return str.replace(/^\s+|\s+$/g, ''); } function Buffer(subject, encoding, offset) { if(!assert) assert= { ok : function(cond,msg) { if (cond != true) { console.log('** Assertion failed: '+msg+' **'); throw Error(msg); } } }; if (!(this instanceof Buffer)) { return new Buffer(subject, encoding, offset); } this.parent = this; this.offset = 0; // Work-around: node's base64 implementation // allows for non-padded strings while base64-js // does not.. if (encoding == "base64" && typeof subject == "string") { subject = stringtrim(subject); while (subject.length % 4 != 0) { subject = subject + "="; } } var type; // Are we slicing? if (typeof offset === 'number') { this.length = coerce(encoding); // slicing works, with limitations (no parent tracking/update) // check https://github.com/toots/buffer-browserify/issues/19 for (var i = 0; i < this.length; i++) { this[i] = subject.get(i+offset); } } else { // Find the length switch (type = typeof subject) { case 'number': this.length = coerce(subject); break; case 'string': this.length = Buffer.byteLength(subject, encoding); break; case 'object': // Assume object is an array this.length = coerce(subject.length); break; default: throw new TypeError('First argument needs to be a number, ' + 'array or string.'); } // Treat array-ish objects as a byte array. if (isArrayIsh(subject)) { for (var i = 0; i < this.length; i++) { if (subject instanceof Buffer) { this[i] = subject.readUInt8(i); } else { // Round-up subject[i] to a UInt8. // e.g.: ((-432 % 256) + 256) % 256 = (-176 + 256) % 256 // = 80 this[i] = ((subject[i] % 256) + 256) % 256; } } } else if (type == 'string') { // We are a string this.length = this.write(subject, 0, encoding); } else if (type === 'number') { for (var i = 0; i < this.length; i++) { this[i] = 0; } } } } Buffer.prototype.get = function get(i) { if (i < 0 || i >= this.length) throw new Error('oob'); return this[i]; }; Buffer.prototype.set = function set(i, v) { if (i < 0 || i >= this.length) throw new Error('oob'); return this[i] = v; }; Buffer.byteLength = function (str, encoding) { switch (encoding || "utf8") { case 'hex': return str.length / 2; case 'utf8': case 'utf-8': return utf8ToBytes(str).length; case 'ascii': case 'binary': return str.length; case 'base64': return base64ToBytes(str).length; default: throw new Error('Unknown encoding'); } }; Buffer.prototype.utf8Write = function (string, offset, length) { var bytes, pos; return Buffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); }; Buffer.prototype.asciiWrite = function (string, offset, length) { var bytes, pos; return Buffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); }; Buffer.prototype.binaryWrite = Buffer.prototype.asciiWrite; Buffer.prototype.base64Write = function (string, offset, length) { var bytes, pos; return Buffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); }; Buffer.prototype.base64Slice = function (start, end) { var bytes = Array.prototype.slice.apply(this, arguments) return fromByteArray(bytes); }; Buffer.prototype.utf8Slice = function () { var bytes = Array.prototype.slice.apply(this, arguments); var res = ""; var tmp = ""; var i = 0; while (i < bytes.length) { if (bytes[i] <= 0x7F) { res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); tmp = ""; } else tmp += "%" + bytes[i].toString(16); i++; } return res + decodeUtf8Char(tmp); } Buffer.prototype.asciiSlice = function () { var bytes = Array.prototype.slice.apply(this, arguments); var ret = ""; for (var i = 0; i < bytes.length; i++) ret += String.fromCharCode(bytes[i]); return ret; } Buffer.prototype.binarySlice = Buffer.prototype.asciiSlice; Buffer.prototype.inspect = function() { var out = [], len = this.length; for (var i = 0; i < len; i++) { out[i] = toHex(this[i]); if (i == exports.INSPECT_MAX_BYTES) { out[i + 1] = '...'; break; } } return ''; }; Buffer.prototype.hexSlice = function(start, end) { var len = this.length; if (!start || start < 0) start = 0; if (!end || end < 0 || end > len) end = len; var out = ''; for (var i = start; i < end; i++) { out += toHex(this[i]); } return out; }; Buffer.prototype.toString = function(encoding, start, end) { encoding = String(encoding || 'utf8').toLowerCase(); start = +start || 0; if (typeof end == 'undefined') end = this.length; // Fastpath empty strings if (+end == start) { return ''; } switch (encoding) { case 'hex': return this.hexSlice(start, end); case 'utf8': case 'utf-8': return this.utf8Slice(start, end); case 'ascii': return this.asciiSlice(start, end); case 'binary': return this.binarySlice(start, end); case 'base64': return this.base64Slice(start, end); case 'ucs2': case 'ucs-2': return this.ucs2Slice(start, end); default: throw new Error('Unknown encoding'); } }; Buffer.prototype.hexWrite = function(string, offset, length) { offset = +offset || 0; var remaining = this.length - offset; if (!length) { length = remaining; } else { length = +length; if (length > remaining) { length = remaining; } } // must be an even number of digits var strLen = string.length; if (strLen % 2) { throw new Error('Invalid hex string'); } if (length > strLen / 2) { length = strLen / 2; } for (var i = 0; i < length; i++) { var b = parseInt(string.substr(i * 2, 2), 16); if (isNaN(b)) throw new Error('Invalid hex string'); this[offset + i] = b; } Buffer._charsWritten = i * 2; return i; }; Buffer.prototype.write = function(string, offset, length, encoding) { // Support both (string, offset, length, encoding) // and the legacy (string, encoding, offset, length) if (isFinite(offset)) { if (!isFinite(length)) { encoding = length; length = undefined; } } else { // legacy var swap = encoding; encoding = offset; offset = length; length = swap; } offset = +offset || 0; var remaining = this.length - offset; if (!length) { length = remaining; } else { length = +length; if (length > remaining) { length = remaining; } } encoding = String(encoding || 'utf8').toLowerCase(); switch (encoding) { case 'hex': return this.hexWrite(string, offset, length); case 'utf8': case 'utf-8': return this.utf8Write(string, offset, length); case 'ascii': return this.asciiWrite(string, offset, length); case 'binary': return this.binaryWrite(string, offset, length); case 'base64': return this.base64Write(string, offset, length); case 'ucs2': case 'ucs-2': return this.ucs2Write(string, offset, length); default: throw new Error('Unknown encoding'); } }; // slice(start, end) function clamp(index, len, defaultValue) { if (typeof index !== 'number') return defaultValue; index = ~~index; // Coerce to integer. if (index >= len) return len; if (index >= 0) return index; index += len; if (index >= 0) return index; return 0; } Buffer.prototype.slice = function(start, end) { var len = this.length; start = clamp(start, len, 0); end = clamp(end, len, len); return new Buffer(this, end - start, +start); }; // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) Buffer.prototype.copy = function(target, target_start, start, end) { var source = this; start || (start = 0); if (end === undefined || isNaN(end)) { end = this.length; } target_start || (target_start = 0); if (end < start) throw new Error('sourceEnd < sourceStart'); // Copy 0 bytes; we're done if (end === start) return 0; if (target.length == 0 || source.length == 0) return 0; if (target_start < 0 || target_start >= target.length) { throw new Error('targetStart out of bounds'); } if (start < 0 || start >= source.length) { throw new Error('sourceStart out of bounds'); } if (end < 0 || end > source.length) { throw new Error('sourceEnd out of bounds'); } // Are we oob? if (end > this.length) { end = this.length; } if (target.length - target_start < end - start) { end = target.length - target_start + start; } var temp = []; for (var i=start; i= this.length) { throw new Error('start out of bounds'); } if (end < 0 || end > this.length) { throw new Error('end out of bounds'); } for (var i = start; i < end; i++) { this[i] = value; } } // Static methods Buffer.isBuffer = function isBuffer(b) { return b instanceof Buffer; }; Buffer.concat = function (list, totalLength) { if (!isArray(list)) { throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ list should be an Array."); } if (list.length === 0) { return new Buffer(0); } else if (list.length === 1) { return list[0]; } if (typeof totalLength !== 'number') { totalLength = 0; for (var i = 0; i < list.length; i++) { var buf = list[i]; totalLength += buf.length; } } var buffer = new Buffer(totalLength); var pos = 0; for (var i = 0; i < list.length; i++) { var buf = list[i]; buf.copy(buffer, pos); pos += buf.length; } return buffer; }; Buffer.isEncoding = function(encoding) { switch ((encoding + '').toLowerCase()) { case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; default: return false; } }; // helpers function coerce(length) { // Coerce length to a number (possibly NaN), round up // in case it's fractional (e.g. 123.456) then do a // double negate to coerce a NaN to 0. Easy, right? length = ~~Math.ceil(+length); return length < 0 ? 0 : length; } function isArray(subject) { return (Array.isArray || function(subject){ return {}.toString.apply(subject) == '[object Array]' }) (subject) } function isArrayIsh(subject) { return isArray(subject) || Buffer.isBuffer(subject) || subject && typeof subject === 'object' && typeof subject.length === 'number'; } function toHex(n) { if (n < 16) return '0' + n.toString(16); return n.toString(16); } function utf8ToBytes(str) { var byteArray = []; for (var i = 0; i < str.length; i++) if (str.charCodeAt(i) <= 0x7F) byteArray.push(str.charCodeAt(i)); else { var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); for (var j = 0; j < h.length; j++) byteArray.push(parseInt(h[j], 16)); } return byteArray; } function asciiToBytes(str) { var byteArray = [] for (var i = 0; i < str.length; i++ ) // Node's code seems to be doing this and not & 0x7F.. byteArray.push( str.charCodeAt(i) & 0xFF ); return byteArray; } function base64ToBytes(str) { return toByteArray(str); } function blitBuffer(src, dst, offset, length) { var pos, i = 0; while (i < length) { if ((i+offset >= dst.length) || (i >= src.length)) break; dst[i + offset] = src[i]; i++; } return i; } function decodeUtf8Char(str) { try { return decodeURIComponent(str); } catch (err) { return String.fromCharCode(0xFFFD); // UTF 8 invalid char } } // read/write bit-twiddling Buffer.prototype.readUInt8 = function(offset, noAssert) { var buffer = this; if (!noAssert) { assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset < buffer.length, 'Trying to read beyond buffer length'); } if (offset >= buffer.length) return; return buffer[offset]; }; function readUInt16(buffer, offset, isBigEndian, noAssert) { var val = 0; if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 1 < buffer.length, 'Trying to read beyond buffer length'); } if (offset >= buffer.length) return 0; if (isBigEndian) { val = buffer[offset] << 8; if (offset + 1 < buffer.length) { val |= buffer[offset + 1]; } } else { val = buffer[offset]; if (offset + 1 < buffer.length) { val |= buffer[offset + 1] << 8; } } return val; } Buffer.prototype.readUInt16LE = function(offset, noAssert) { return readUInt16(this, offset, false, noAssert); }; Buffer.prototype.readUInt16BE = function(offset, noAssert) { return readUInt16(this, offset, true, noAssert); }; function readUInt32(buffer, offset, isBigEndian, noAssert) { var val = 0; if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 3 < buffer.length, 'Trying to read beyond buffer length'); } if (offset >= buffer.length) return 0; if (isBigEndian) { if (offset + 1 < buffer.length) val = buffer[offset + 1] << 16; if (offset + 2 < buffer.length) val |= buffer[offset + 2] << 8; if (offset + 3 < buffer.length) val |= buffer[offset + 3]; val = val + (buffer[offset] << 24 >>> 0); } else { if (offset + 2 < buffer.length) val = buffer[offset + 2] << 16; if (offset + 1 < buffer.length) val |= buffer[offset + 1] << 8; val |= buffer[offset]; if (offset + 3 < buffer.length) val = val + (buffer[offset + 3] << 24 >>> 0); } return val; } Buffer.prototype.readUInt32LE = function(offset, noAssert) { return readUInt32(this, offset, false, noAssert); }; Buffer.prototype.readUInt32BE = function(offset, noAssert) { return readUInt32(this, offset, true, noAssert); }; /* * Signed integer types, yay team! A reminder on how two's complement actually * works. The first bit is the signed bit, i.e. tells us whether or not the * number should be positive or negative. If the two's complement value is * positive, then we're done, as it's equivalent to the unsigned representation. * * Now if the number is positive, you're pretty much done, you can just leverage * the unsigned translations and return those. Unfortunately, negative numbers * aren't quite that straightforward. * * At first glance, one might be inclined to use the traditional formula to * translate binary numbers between the positive and negative values in two's * complement. (Though it doesn't quite work for the most negative value) * Mainly: * - invert all the bits * - add one to the result * * Of course, this doesn't quite work in Javascript. Take for example the value * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of * course, Javascript will do the following: * * > ~0xff80 * -65409 * * Whoh there, Javascript, that's not quite right. But wait, according to * Javascript that's perfectly correct. When Javascript ends up seeing the * constant 0xff80, it has no notion that it is actually a signed number. It * assumes that we've input the unsigned value 0xff80. Thus, when it does the * binary negation, it casts it into a signed value, (positive 0xff80). Then * when you perform binary negation on that, it turns it into a negative number. * * Instead, we're going to have to use the following general formula, that works * in a rather Javascript friendly way. I'm glad we don't support this kind of * weird numbering scheme in the kernel. * * (BIT-MAX - (unsigned)val + 1) * -1 * * The astute observer, may think that this doesn't make sense for 8-bit numbers * (really it isn't necessary for them). However, when you get 16-bit numbers, * you do. Let's go back to our prior example and see how this will look: * * (0xffff - 0xff80 + 1) * -1 * (0x007f + 1) * -1 * (0x0080) * -1 */ Buffer.prototype.readInt8 = function(offset, noAssert) { var buffer = this; var neg; if (!noAssert) { assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset < buffer.length, 'Trying to read beyond buffer length'); } if (offset >= buffer.length) return; neg = buffer[offset] & 0x80; if (!neg) { return (buffer[offset]); } return ((0xff - buffer[offset] + 1) * -1); }; function readInt16(buffer, offset, isBigEndian, noAssert) { var neg, val; if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 1 < buffer.length, 'Trying to read beyond buffer length'); } val = readUInt16(buffer, offset, isBigEndian, noAssert); neg = val & 0x8000; if (!neg) { return val; } return (0xffff - val + 1) * -1; } Buffer.prototype.readInt16LE = function(offset, noAssert) { return readInt16(this, offset, false, noAssert); }; Buffer.prototype.readInt16BE = function(offset, noAssert) { return readInt16(this, offset, true, noAssert); }; function readInt32(buffer, offset, isBigEndian, noAssert) { var neg, val; if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 3 < buffer.length, 'Trying to read beyond buffer length'); } val = readUInt32(buffer, offset, isBigEndian, noAssert); neg = val & 0x80000000; if (!neg) { return (val); } return (0xffffffff - val + 1) * -1; } Buffer.prototype.readInt32LE = function(offset, noAssert) { return readInt32(this, offset, false, noAssert); }; Buffer.prototype.readInt32BE = function(offset, noAssert) { return readInt32(this, offset, true, noAssert); }; function readFloat(buffer, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset + 3 < buffer.length, 'Trying to read beyond buffer length'); } // TODO return Ieee754.readIEEE754(buffer, offset, isBigEndian, 23, 4); } Buffer.prototype.readFloatLE = function(offset, noAssert) { return readFloat(this, offset, false, noAssert); }; Buffer.prototype.readFloatBE = function(offset, noAssert) { return readFloat(this, offset, true, noAssert); }; function readDouble(buffer, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset + 7 < buffer.length, 'Trying to read beyond buffer length'); } return Ieee754.readIEEE754(buffer, offset, isBigEndian, 52, 8); } Buffer.prototype.readDoubleLE = function(offset, noAssert) { return readDouble(this, offset, false, noAssert); }; Buffer.prototype.readDoubleBE = function(offset, noAssert) { return readDouble(this, offset, true, noAssert); }; /* * We have to make sure that the value is a valid integer. This means that it is * non-negative. It has no fractional component and that it does not exceed the * maximum allowed value. * * value The number to check for validity * * max The maximum value */ function verifuint(value, max) { assert.ok(typeof (value) == 'number', 'cannot write a non-number as a number'); assert.ok(value >= 0, 'specified a negative value for writing an unsigned value'); assert.ok(value <= max, 'value is larger than maximum value for type'); assert.ok(Math.floor(value) === value, 'value has a fractional component'); } Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { var buffer = this; if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset < buffer.length, 'trying to write beyond buffer length'); verifuint(value, 0xff); } if (offset < buffer.length) { buffer[offset] = value; } }; function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 1 < buffer.length, 'trying to write beyond buffer length'); verifuint(value, 0xffff); } for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { buffer[offset + i] = (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> (isBigEndian ? 1 - i : i) * 8; } } Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { writeUInt16(this, value, offset, false, noAssert); }; Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { writeUInt16(this, value, offset, true, noAssert); }; function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 3 < buffer.length, 'trying to write beyond buffer length'); verifuint(value, 0xffffffff); } for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { buffer[offset + i] = (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; } } Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { writeUInt32(this, value, offset, false, noAssert); }; Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { writeUInt32(this, value, offset, true, noAssert); }; /* * We now move onto our friends in the signed number category. Unlike unsigned * numbers, we're going to have to worry a bit more about how we put values into * arrays. Since we are only worrying about signed 32-bit values, we're in * slightly better shape. Unfortunately, we really can't do our favorite binary * & in this system. It really seems to do the wrong thing. For example: * * > -32 & 0xff * 224 * * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of * this aren't treated as a signed number. Ultimately a bad thing. * * What we're going to want to do is basically create the unsigned equivalent of * our representation and pass that off to the wuint* functions. To do that * we're going to do the following: * * - if the value is positive * we can pass it directly off to the equivalent wuint * - if the value is negative * we do the following computation: * mb + val + 1, where * mb is the maximum unsigned value in that byte size * val is the Javascript negative integer * * * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If * you do out the computations: * * 0xffff - 128 + 1 * 0xffff - 127 * 0xff80 * * You can then encode this value as the signed version. This is really rather * hacky, but it should work and get the job done which is our goal here. */ /* * A series of checks to make sure we actually have a signed 32-bit number */ function verifsint(value, max, min) { assert.ok(typeof (value) == 'number', 'cannot write a non-number as a number'); assert.ok(value <= max, 'value larger than maximum allowed value'); assert.ok(value >= min, 'value smaller than minimum allowed value'); assert.ok(Math.floor(value) === value, 'value has a fractional component'); } function verifIEEE754(value, max, min) { assert.ok(typeof (value) == 'number', 'cannot write a non-number as a number'); assert.ok(value <= max, 'value larger than maximum allowed value'); assert.ok(value >= min, 'value smaller than minimum allowed value'); } Buffer.prototype.writeInt8 = function(value, offset, noAssert) { var buffer = this; if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset < buffer.length, 'Trying to write beyond buffer length'); verifsint(value, 0x7f, -0x80); } if (value >= 0) { buffer.writeUInt8(value, offset, noAssert); } else { buffer.writeUInt8(0xff + value + 1, offset, noAssert); } }; function writeInt16(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 1 < buffer.length, 'Trying to write beyond buffer length'); verifsint(value, 0x7fff, -0x8000); } if (value >= 0) { writeUInt16(buffer, value, offset, isBigEndian, noAssert); } else { writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); } } Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { writeInt16(this, value, offset, false, noAssert); }; Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { writeInt16(this, value, offset, true, noAssert); }; function writeInt32(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 3 < buffer.length, 'Trying to write beyond buffer length'); verifsint(value, 0x7fffffff, -0x80000000); } if (value >= 0) { writeUInt32(buffer, value, offset, isBigEndian, noAssert); } else { writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); } } Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { writeInt32(this, value, offset, false, noAssert); }; Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { writeInt32(this, value, offset, true, noAssert); }; function writeFloat(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 3 < buffer.length, 'Trying to write beyond buffer length'); verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); } Ieee754.writeIEEE754(buffer, value, offset, isBigEndian, 23, 4); } Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { writeFloat(this, value, offset, false, noAssert); }; Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { writeFloat(this, value, offset, true, noAssert); }; function writeDouble(buffer, value, offset, isBigEndian, noAssert) { if (!noAssert) { assert.ok(value !== undefined && value !== null, 'missing value'); assert.ok(typeof (isBigEndian) === 'boolean', 'missing or invalid endian'); assert.ok(offset !== undefined && offset !== null, 'missing offset'); assert.ok(offset + 7 < buffer.length, 'Trying to write beyond buffer length'); verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); } Ieee754.writeIEEE754(buffer, value, offset, isBigEndian, 52, 8); } Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { writeDouble(this, value, offset, false, noAssert); }; Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { writeDouble(this, value, offset, true, noAssert); }; }; BundleModuleCode['os/buffer_ieee754']=function (module,exports,global,process){ exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { var e, m, eLen = nBytes * 8 - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, nBits = -7, i = isBE ? 0 : (nBytes - 1), d = isBE ? 1 : -1, s = buffer[offset + i]; i += d; e = s & ((1 << (-nBits)) - 1); s >>= (-nBits); nBits += eLen; for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); m = e & ((1 << (-nBits)) - 1); e >>= (-nBits); nBits += mLen; for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); if (e === 0) { e = 1 - eBias; } else if (e === eMax) { return m ? NaN : ((s ? -1 : 1) * Infinity); } else { m = m + Math.pow(2, mLen); e = e - eBias; } return (s ? -1 : 1) * m * Math.pow(2, e - mLen); }; exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { var e, m, c, eLen = nBytes * 8 - mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1, rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), i = isBE ? (nBytes - 1) : 0, d = isBE ? -1 : 1, s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; value = Math.abs(value); if (isNaN(value) || value === Infinity) { m = isNaN(value) ? 1 : 0; e = eMax; } else { e = Math.floor(Math.log(value) / Math.LN2); if (value * (c = Math.pow(2, -e)) < 1) { e--; c *= 2; } if (e + eBias >= 1) { value += rt / c; } else { value += rt * Math.pow(2, 1 - eBias); } if (value * c >= 2) { e++; c /= 2; } if (e + eBias >= eMax) { m = 0; e = eMax; } else if (e + eBias >= 1) { m = (value * c - 1) * Math.pow(2, mLen); e = e + eBias; } else { m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); e = 0; } } for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); e = (e << mLen) | m; eLen += mLen; for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); buffer[offset + i - d] |= s * 128; }; }; BundleModuleCode['os/process']=function (module,exports,global,process){ // shim for using process in browser if (typeof process != 'undefined' && process.env) { module.exports = process; return; } var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; (function () { try { cachedSetTimeout = setTimeout; } catch (e) { cachedSetTimeout = function () { throw new Error('setTimeout is not defined'); } } try { cachedClearTimeout = clearTimeout; } catch (e) { cachedClearTimeout = function () { throw new Error('clearTimeout is not defined'); } } } ()) var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = cachedSetTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; cachedClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { cachedSetTimeout(drainQueue, 0); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; process.pid=0; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; }; BundleModuleCode['/home/sbosse/proj/jam/js/top/jamlib.js']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 25-12-16 by sbosse. ** $RCS: $Id: jamlib.js,v 1.4 2017/06/19 17:18:39 sbosse Exp sbosse $ ** $VERSION: 1.28.2 ** ** $INFO: ** ** JAM library API that can be embedded in any host application. ** ** ** New: Embedded auto setup (e.g., for clusters) using command line arguments ** ** jamlib autosetup:"{options}" ** ** ** $ENDOFINFO */ var onexit=false; var start=false; var options = { geo:undefined, verbose:0, version:'1.28.2' // public version }; global.config={simulation:false,nonetwork:false}; var Io = Require('com/io'); var Comp = Require('com/compat'); var Aios = Require('jam/aios'); var Esprima = Require('parser/esprima'); var Json = Require('jam/jsonfn'); var fs = Require('fs'); var Sat = Require('dos/ext/satelize'); var CBL = Require('com/cbl'); var DIR = Aios.DIR; // Parse command line arguments; extract a:v attributes var environment = process.env; process.argv.slice(2).forEach(function (arg) { var tokens=arg.match(/([a-zA-Z]+):(['"0-9a-zA-Z_:\->\.\{\},;]+)/); if (tokens && tokens.length==3) environment[tokens[1]]=tokens[2]; }); if (typeof setImmediate == 'undefined') { function setImmediate(callback) {return setTimeout(callback,0)}; } // Extend DIR with IP capabilities of NORTH, .. DIR.North= function (ip) { return {tag:DIR.NORTH,ip:ip}} DIR.South= function (ip) { return {tag:DIR.SOUTH,ip:ip}} DIR.West = function (ip) { return {tag:DIR.WEST ,ip:ip}} DIR.East = function (ip) { return {tag:DIR.EAST ,ip:ip}} DIR.Up = function (ip) { return {tag:DIR.UP ,ip:ip}} DIR.Down = function (ip) { return {tag:DIR.DOWN ,ip:ip}} /** * typeof options = { * connections?, * print? is agent and control message output function, * printAgent? is agent message only output function, * fork?, * provider?, consumer?, * classes?, * id?:string is JAM and JAM root node id, * world?:string is JAM world id, * position?:{x,y}, * cluster?:boolean|[] is an attached cluster node, * nowatch:boolean is a disable flag for agent watchdog checking, * checkpoint:boolean is a flag forcing code checkpointing (even if watchdog is available), * nolimits:boolean is a disable flag for agent resource monitoring, * log?:{class?:boolean,node?,agent?,parent?,host?,time?,Time?,pid?}, * logJam?:{host?,time?,pid?,node?,world?}, * scheduler?:scheduler is an external scheduler, singlestep?, * network?:{cluster?,rows,columns,connect?:function}, * verbose?, TMO? } * with typeof connections = { * @kind : {from:string,to?:string,proto:string='udp'|'tcp'|'http'|'stream',num?:number,on?,range?:number[]}, * @kind : {send:function, status:function, register?:function(@link)} , * @kind : .. } * with @kind = {north,south,west,east,ip, ..} * * Connecting JAM nodes (IP) * ------------------------- * * .. Jam({ * connections: { * // Generic, P2PN * ip?: {from:string,to?:string,proto:string='udp'|'tcp'|'http',num?:number} // AMP link (UDP) or set of AMP links (num>1) * // Assigned to a logical direction, P2P * north?: { * from:string,to?:string,proto?='udp'|'tcp'|'http'|'device',device?:string // device is a hardware P2P stream device * }, .. * * Integration of host program streams * ------------------------------------ * * var chan = Some Stream Channel Object; * * .. Jam({ * connections: { * north?: { * register: function (link) { * // register channel data handler with link handler * chan.on('data',function (data) { * // process raw data, extract msg={agent:string,to?,from?,..} or {signal:string,to?,from?,..} * if (msg.agent) link.emit('agent',msg.agent); * if (msg.signal) link.emit('signal',msg.signal); * }); * } * send: function (msg) { * chan.send(msg); * }, * status: function (to) { * return true; * } * } * }, .. * } * * Cluster * -------- * * A forked cluster consists of a master node (0) and up to 8 child ndoes connected around the root node * by streams in directions {E,S,SE,W,SW,N,NW,NE}. Each node is executed physically in a different JAM process. * Ex. network: {cluster:true, rows:2, columns:2}, * */ var jam = function (options) { var self=this, p,conn,node; this.options = options||{}; this.environment=environment; if (this.setup) this.setup(); // overwrite options if (this.options.world && !this.options.id) this.options.id=this.options.world; if (!this.options.id) this.options.id=Aios.aidgen(); if (!this.options.log) this.options.log={}; if (!this.options.logJam) this.options.logJam={pid:false,host:false,time:false}; this.verbose = this.options.verbose || 0; this.Aios = Aios; this.DIR = Aios.aios.DIR; Aios.options.verbose=this.verbose; if (options.scheduler) Aios.current.scheduler=scheduler; if (options.nolimits||options.nowatch||options.checkpoint) Aios.config({nolimits:options.nolimits,nowatch:options.nowatch,checkpoint:options.checkpoint}); // out=function (msg) { Io.print('[JAM '+self.options.id+'] '+msg)}; if (this.options.print) Aios.print=Aios.printAgent=this.options.print; if (this.options.print2) Aios.printAgent=this.options.print2; if (this.options.printAgent) Aios.printAgent=this.options.printAgent; // JAM messages this.log=function (msg) { var s='[JAM',sep=' '; if (self.options.logJam.pid && process) s += (' '+process.pid),sep=':'; if (self.options.logJam.world && Aios.current.world) s += (sep+Aios.current.world.id),sep=':'; if (self.options.logJam.node && Aios.current.node) s += (sep+Aios.current.node.id),sep=':'; if (self.options.logJam.time) s += (sep+Aios.time()); Aios.print(s+'] '+msg); }; this.err=function (msg,err) { self.log('Error: '+msg); throw (err||'JAMLIB'); } this.warn=function (msg) { self.log('Warning: '+msg); } this.error=undefined; // Create a world this.world = Aios.World.World([],{ id:this.options.world||this.options.id.toUpperCase(), classes:options.classes||[], scheduler:options.scheduler, verbose:options.verbose }); if (this.verbose) this.log('Created world '+this.world.id+'.'); this.node=none; this.run=false; // Service loop executing the AIOS scheduler // NOT USED if there is an external scheduler supplied (world will create JAM scheduling loop) this.ticks=0; // schedule loop execution counter! this.steps=0; // Number of schedule loop execution steps this.loop=none; // Schedule loop function this.looping=none; // Current schedule loop run (or none); can be waiting for a timeout Aios.config({fastcopy:this.options.fastcopy, verbose:this.options.verbose}); if (this.options.log) for(p in this.options.log) Aios.config(this.options.log[p]?{"log+":p}:{"log-":p}); this.process = Aios.Proc.Proc(); this.process.agent={id:'jamlib'}; this.events={}; } // Import analyzer class... var JamAnal = Require('jam/analyzer'); JamAnal.current(Aios); jam.prototype.analyzeSyntax=JamAnal.jamc.prototype.analyze; jam.prototype.syntax=JamAnal.jamc.prototype.syntax; /** Add agent class to the JAM world and create sandboxed constructors. * type constructor = function|string */ jam.prototype.addClass = function (name,constructor,env) { this.world.addClass(name,constructor,env); if (this.verbose) this.log('Agent class '+name+' added to world library.'); }; /** Add a new node to the world. * Assumption: 2d meshgrid network with (x,y) coordinates. * The root node has position {x=0,y=0}. * type of nodeDesc = {x:number,y:number,id?} * */ jam.prototype.addNode = function (nodeDesc) { var node,x,y; x=nodeDesc.x; y=nodeDesc.y; if (Comp.array.find(this.world.nodes,function (node) { return node.position.x==x && node.position.y==y; })) { this.err('addNodes: Node at positition ('+x+','+y+') exists already.'); return; } node=Aios.Node.Node({id:nodeDesc.id||Aios.aidgen(),position:{x:x,y:y}},true); if (this.verbose) this.log('Created node '+node.id+' ('+x+','+y+').'); // Add node to world this.world.addNode(node); return node.id; } /** Add logical nodes. * The root node has position {x=0,y=0}. * type of nodes = [{x:number,y:number,id?},..] */ jam.prototype.addNodes = function (nodes) { var n,node,x,y,nodeids=[]; for(n in nodes) { nodeids.push(this.addNode(nodes[n])); } return nodeids; } /** Analyze agent class template in text or object form ** typeof @options = {..,classname?:string} * Returns {report:string,interface} */ jam.prototype.analyze = function (ac,options) { var source,name,syntax,content,report,interface; if (Comp.obj.isString(ac)) { // TODO } else if (Comp.obj.isObject(ac)) { // TODO } else if (Comp.obj.isFunction(ac)) { source = ac.toString(); if (!options.classname) { name=source.match(/^ *function *([^\s\(]*)\(/); if (name && name[1]!='') options.classname=name[1]; } content = 'var ac ='+source; syntax = Esprima.parse(content, { tolerant: true, loc:true }); try { interface=this.analyzeSyntax(syntax,{ classname:options.classname||'anonymous', level:options.level==undefined?2:options.level, verbose:options.verbose, err:function (msg){throw msg}, out:function (msg){if (!report) report=msg; else report=report+'\n'+msg;}, warn:function (msg){if (!report) report=msg; else report=report+'\n'+msg;} }); return {report:report||'OK',interface:interface}; } catch (e) { return {report:e,interface:interface}; } } } jam.prototype.clock = Aios.clock; /** Compile (analyze) an agent class constructor function and add it to the world class library. ** Can be used after an open statement. ** Usage: compileClass(name,constructor,options?) ** compileClass(constructor,options?) ** ** typeof @name=string|undefined ** typeof @constructor=function|string ** typeof @options={verbose:number|boolean)|number|undefined */ jam.prototype.compileClass = function (name,constructor,options) { var ac,p,verbose,content,syntax,report,text,env={ac:undefined},self=this,ac; if (typeof name == 'function') constructor=name,name=undefined,options=constructor; if (typeof options == 'object') verbose=options.verbose; else if (options!=undefined) verbose=options; else verbose=this.verbose; // if (typeof constructor != 'function') throw 'compileClass: second constructor argument not a function'; if (typeof constructor == 'function') text = constructor.toString(); else text = constructor; if (!name) { // try to find name in function definition name=text.match(/[\s]*function[\s]*([A-Za-z0-9_]+)[\s]*\(/); if (!name) throw ('compileClass: No class name provided and no name found in constructor '+ text.substring(0,80)); name=name[1]; } content = 'var ac = '+text; try { syntax = Esprima.parse(content, { tolerant: true, loc:true }) } catch (e) { throw 'compileClass('+name+'): Parsing failed with '+e } report = this.analyzeSyntax(syntax, { classname:name, level:2, verbose:verbose||0, err: function (msg){self.log(msg)}, out: function (msg){self.log(msg)}, warn: function (msg){self.log(msg)} }); if (report.errors.length) { throw 'compileClass('+name+'): failed with '+report.errors.join('; ')}; for (p in report.activities) env[p]=p; with (env) { eval(content) }; ac=env.ac; env.ac=undefined; this.addClass(name,ac,env); return name; } /** Connect logical nodes (virtual link). * The root node has position {x=0,y=0}. * type of links = [{x1:number,y1:number,x2:number,x2:number},..]|[{x,y},{x,y}] */ jam.prototype.connectNodes = function (connections) { var c,node1,node2,x1,y1,x2,y2,dir; if (connections[0].x != undefined && connections[0].y != undefined) { if (connections.length!=2) throw 'INVALID'; // invalid // simple style connections=[{x1:connections[0].x,x2:connections[1].x, y1:connections[0].y,y2:connections[0].y}]; } for(c in connections) { x1=connections[c].x1; y1=connections[c].y1; x2=connections[c].x2; y2=connections[c].y2; if (this.verbose) this.log('Connecting ('+x1+','+y1+') -> ('+x2+','+y2+')'); node1=Comp.array.find(this.world.nodes,function (node) { return node.position.x==x1 && node.position.y==y1; }); node2=Comp.array.find(this.world.nodes,function (node) { return node.position.x==x2 && node.position.y==y2; }); if (!node1) this.err('connectNodes: Node at positition ('+x1+','+y1+') does not exist.'); if (!node2) this.err('connectNodes: Node at positition ('+x2+','+y2+') does not exist.'); if ((x2-x1)==0) { if ((y2-y1) > 0) dir=Aios.DIR.SOUTH; else dir=Aios.DIR.NORTH; } else if ((x2-x1)>0) dir=Aios.DIR.EAST; else dir=Aios.DIR.WEST; this.world.connect(dir,node1,node2); this.world.connect(Aios.DIR.opposite(dir),node2,node1); } } /** Dynamically connect remote endpoint at run-time * typeof @to = string url>| */ jam.prototype.connectTo = function (to,nodeid) { var node=this.getNode(nodeid), tokens=(typeof to=='string')?to.split('->'):null, dir; // console.log(tokens) if (!node) return; if (to.tag) dir=to; else if (tokens.length==2) { dir=Aios.DIR.from(tokens[0]); if (dir) dir.ip=tokens[1]; } else dir={tag:'DIR.IP',ip:to}; if (dir) this.world.connectTo(dir,node); } /** Check connection status of a link * */ jam.prototype.connected = function (dir,nodeid) { var node=this.getNode(nodeid); if (!node) return; return this.world.connected(dir,node); } /** Create and start an agent from class ac with arguments. * Ac is either already loaded (i.e., ac specifies the class name) or * AC is supplied as a constructor function (ac), a class name, or a sandboxed constructor * {fun:function,mask:{}} object for a specific level. * * type of ac = string|object|function * type of args = * [] * level = {0,1,2,3} * */ jam.prototype.createAgent = function (ac,args,level,className,parent) { var node=this.world.nodes[this.node], process=none,sac; if (level==undefined) level=1; if (!className && typeof ac == 'string') className=ac; if (!className && typeof ac == 'function') className=Aios.Code.className(ac); if (Comp.obj.isFunction(ac) || Comp.obj.isObject(ac)) { // Create an agent process from a constructor function or sandboxed constructor object process = Aios.Code.createOn(node,ac,args,level,className); if (process && !process.agent.parent) process.agent.parent=parent; if (process) return process.agent.id; } else { // It is a class name. Find an already sandboxed constructor from world classes pool if (this.world.classes[ac]) process = Aios.Code.createOn(node,this.world.classes[ac][level],args,level,className); else { this.error='createAgent: Cannot find agent class '+ac; this.log(this.error); return; } if (process) { if (!process.agent.parent) process.agent.parent=parent; process.agent.ac=ac; return process.agent.id; } else return none; } } /** Create agent on specified (logical or physical) node. * typeof node = number|string|{x,y} */ jam.prototype.createAgentOn = function (node,ac,args,level,className,parent) { var res,_currentNode=this.node,found=this.getNode(node); if (found) { this.setCurrentNode(); res=this.createAgent(ac,args,level,className,parent); this.setCurrentNode(_currentNode); } return res; } /** Create a physical communication port * */ jam.prototype.createPort = function (dir,options,nodeid) { if (!options) options={}; var multicast=options.multicast; switch (dir.tag) { case Aios.DIR.NORTH: case Aios.DIR.SOUTH: case Aios.DIR.WEST: case Aios.DIR.EAST: case Aios.DIR.UP: case Aios.DIR.DOWN: multicast=false; } if (options.from==undefined && dir.ip) options.from=dir.ip.toString(); var chan=this.world.connectPhy( dir, this.getNode(nodeid), { broker:options.broker, multicast:multicast, name:options.name, on:options.on, oneway:options.oneway, proto:options.proto||'udp', rcv:options.from, snd:options.to, verbose:(options.verbose!=undefined?options.verbose:this.verbose) }); chan.init(); chan.start(); return chan; } /** Dynamically disconnect remote endpoint at run-time * */ jam.prototype.disconnect = function (to,nodeid) { var node=this.getNode(nodeid); if (node) { this.world.disconnect(to,node); } } /** Emit an event ** function emit(@event,@arg1,..) */ jam.prototype.emit = function () { Aios.emit.apply(this,arguments); } /** Execute an agent snapshot on current node delivered in JSON+ text format or read from a file. */ jam.prototype.execute = function (data,file) { if (!data && file && fs) try { data=fs.readFileSync(file,'utf8'); } catch (e) { this.log('Error: Reading file '+file+' failed: '+e); return undefined; } if (data) return this.world.nodes[this.node].receive(data,true); } /** Execute an agent snapshot on node @node delivered in JSON+ text format or read from a file. */ jam.prototype.executeOn = function (data,node,file) { node=this.getNode(node); if (!node) return; if (!data && file && fs) try { data=fs.readFileSync(file,'utf8'); } catch (e) { this.log('Error: Reading file '+file+' failed: '+e); return undefined; } if (data) return node.receive(data,true); } /** Extend AIOS of specific privilege level. The added functions can be accessed by agents. * * function extend(level:number [],name:string,func:function,argn?:number|number []); */ jam.prototype.extend = function (level,name,func,argn) { var self=this; if (Comp.obj.isArray(level)) { Comp.array.iter(level,function (l) {self.extend(l,name,func,argn)}); return; } switch (level) { case 0: if (Aios.aios0[name]) throw Error('JAM: Cannot extend AIOS(0) with '+name+', existst already!'); Aios.aios0[name]=func; break; case 1: if (Aios.aios1[name]) throw Error('JAM: Cannot extend AIOS(1) with '+name+', existst already!'); Aios.aios1[name]=func; break; case 2: if (Aios.aios2[name]) throw Error('JAM: Cannot extend AIOS(2) with '+name+', existst already!'); Aios.aios2[name]=func; break; case 3: if (Aios.aios3[name]) throw Error('JAM: Cannot extend AIOS(3) with '+name+', existst already!'); Aios.aios3[name]=func; break; default: throw Error('JAM: Extend: Invalid privilige level argument ([0,1,2,3])'); } if (!JamAnal.corefuncs[name]) JamAnal.corefuncs[name]={argn:argn||func.length}; } /** Return node object referenced by logical node number, position, or name * If @id is undefined return current node object. */ jam.prototype.getNode = function (id) { var node; if (id==undefined) return this.world.nodes[this.node]; if (typeof id == 'number') node=this.world.nodes[id]; else if (typeof id == 'string') { // Search node identifier or position; loop: for(var i in this.world.nodes) { if (this.world.nodes[i] && this.world.nodes[i].id==id) { node = this.world.nodes[i]; break loop; } } } else if (id.x != undefined && id.y != undefined) { // Search node position; loop: for(var i in this.world.nodes) { if (this.world.nodes[i] && Comp.obj.equal(this.world.nodes[i].position,id)) { node = this.world.nodes[i]; break loop; } } } return node; } /** Return node name from logical node number or position * */ jam.prototype.getNodeName = function (nodeNumberorPosition) { var node=this.getNode(nodeNumberorPosition); if (node) return node.id; } /** Get current agent process or search for agent process * */ jam.prototype.getProcess = function (agent) { if (!agent) return Aios.current.process; else { // TODO } } /** Return node name from logical node number or position * */ jam.prototype.getWorldName = function () { return this.world.id; } jam.prototype.info = function (kind,id) { switch (kind) { case 'node': var node=this.getNode(id); if (!node) return; return { id:node.id, position: node.position, location:node.location, type:node.type } break; case 'version': return Aios.options.version; case 'host': return { type:global.TARGET, watchdog:Aios.watchdog?true:false, protect: Aios.watchdog&&Aios.watchdog.protect?true:false, jsonify:Aios.options.json, minify:!Aios.Code.options.compactit, }; } } /** INITIALIZE * 1. Create and initialize node(s)/world * 2. Add optional TS provider/consumer * 3. Create physical network conenctions */ jam.prototype.init = function (callback) { var i=0,j=0, n, p, id, node, connect=[], chan, dir, dirs, pos, self=this; // Current node == root node this.node=0; ///////////// CREATE NODES ///////// if (!this.options.network) { if (this.options.position) i=this.options.position.x,j=this.options.position.y; // Create one (root) node if not already existing if (!this.getNode({x:i,y:j})) { node = Aios.Node.Node({ id:this.options.id, position:{x:i,y:j}, TMO:this.options.TMO, type:this.options.type },true); // Add node to world if (this.verbose) this.log('Created '+(i==0&&j==0?'root ':'')+'node '+node.id+' ('+i+','+j+').'); this.world.addNode(node); } // Register jamlib event handler for the root node this.register(node); } else if (!this.options.network.cluster) { // Create a virtual network of logical nodes. Default: grid if (this.options.network.rows && this.options.network.columns) { for(j=0;j world node# function makeconn (p,conn) { var link = { _handler:[], emit: function (event,msg) { if (link._handler[event]) link._handler[event](msg); }, on: function (event,callback) { link._handler[event]=callback; }, send: function (data,dest,context) { var res; self.world.nodes[self.node].connections[p]._count += data.length; res=conn.send(data,dest); if (!res) { context.error='Migration to destination '+dest+' failed'; // We're still in the agent process context! Throw an error for this agent .. throw 'MOVE'; }; // kill ghost agent context.process.finalize(); }, status : conn.status?conn.status:(function () {return true}), count: conn.count?conn.count:function () {return link._count}, _count:0 }; if (conn.register) conn.register(link); return link; } node.connections[p] = makeconn(p,conn); // register agent receiver and signal handler node.connections[p].on('agent',node.receive.bind(node)); node.connections[p].on('signal',node.handle.bind(node)); } else if (p=='stream') { // 3. Physical process stream interface (cluster); child->parent proecss connection chan=this.world.connectPhy( conn.dir, this.getNode(), { proto:'stream', sock:process, mode:'object', verbose:this.verbose }); chan.init(); } } } if (callback) callback(); } /** Tuple space input operation - non blocking, i.e., equiv. to inp(pat,_,0) */ jam.prototype.inp = function (pat,all) { return this.world.nodes[this.node].ts.extern.inp(pat,all); } /** Kill agent with specified id ('*': kill all agents on node or current node) */ jam.prototype.kill = function (id,node) { if (id=='*') { this.world.nodes[this.node].processes.table.forEach(function (p) { if (p) Aios.kill(p.agent.id); }); } else return Aios.kill(id); } /** Try to locate this node (based on network connectivity) * Any geospatial information is attached to current (node=undefined) or specific node */ jam.prototype.locate = function (nodeid,cb) { var node=this.getNode(nodeid); if (!node) return; Sat.satelize({},function (err,info) { if (err) { return cb?cb(undefined,err):console.log(err.toString()); } else { var location = { ip:info.query, gps:{lat:info.lat,lon:info.lon}, geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip} } node.location=location; if (cb) cb(location); } }) } /** Lookup nodes and get connection info (more general as connected and broker support) * */ jam.prototype.lookup = function (dir,callback,nodeid) { var node=this.getNode(nodeid); if (!node) return; return this.world.lookup(dir,callback,node); } /** Execute an agent snapshot in JSON+ text form after migration provided from host application */ jam.prototype.migrate = function (data) { return this.world.nodes[this.node].receive(data,false); } /** Install event handler * * typeof @event = {'agent','agent+','agent-','signal+','signal','link+','link-',..} * agent+/agent-: Agent creation and destruction event * agent: Agent receive event * signal+: Signal raise event * signal: Signal receive (handle) event * route+: A new link was established * route-: A link is broken */ jam.prototype.on = function (event,handler) { Aios.on(event,handler); } /** Remove event handler */ jam.prototype.off = function (ev) { Aios.off(event); } /** Read and parse one agent class from file. Can contain nested open statements. * Browser (no fs module): @file parameter contains source text. * File/source text format: function [ac] (p1,p2,..) { this.x; .. ; this.act = {..}; ..} * open(file:string,options?:{verbose?:number|boolean,classname?:string}) -> function | object * * Output can be processed by method compileClass */ jam.prototype.open = function (file,options) { var self=this, res, text, name, ast=null; if (!options) options={}; name=options.classname||''; if (options.verbose>0) this.log('Reading agent class template '+name+' from '+file); function parseModel (text) { var modu={},more,module={exports:{}},name=text.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); if (name) name=name[1]; function open(filename) { var text; try { text=fs?fs.readFileSync(filename,'utf8'):null; return parseModel(text); } catch (e) { self.log('Error: Opening of '+(fs?file:'text')+' failed: '+e); } } try { with (module) {eval('res = '+text)}; if (name) { modu[name]=res; return modu} else if (module.exports) return module.exports; else return res; } catch (e) { try { ast = Esprima.parse(text, { tolerant: true, loc:true }); if (ast.errors && ast.errors.length>0) more = ', '+ast.errors[0]; } catch (e) { if (e.lineNumber) more = ', in line '+e.lineNumber; } self.log(e.name+(e.message?': '+e.message:'')+(more?more:'')); } } try { text=fs?fs.readFileSync(file,'utf8'):file; // Browser: file parameter contains already source text return parseModel(text); } catch (e) { this.log('Error: Opening of '+(fs?file:'text')+' failed: '+e); } }; /** Tuple space output operation */ jam.prototype.out = function (tuple) { return this.world.nodes[this.node].ts.extern.out(tuple); } /** Tuple space read operation - non blocking, i.e., equiv. to rd(pat,_,0) */ jam.prototype.rd = function (pat,all) { return this.world.nodes[this.node].ts.extern.rd(pat,all); } /** 1. Read agent template classes from file and compile (analyze) agent constructor functions. * Expected file format: module.exports = { ac1: function (p1,p2,..) {}, ac2:.. } * 2. Read single agent constructor function from file * * typeof @options={verbose,error:function} */ // TODO: clean up, split fs interface, no require caching .. if (fs) jam.prototype.readClass = function (file,options) { var self=this, ac, name, env, interface, text, modu, path, p,m, regex1, ast=null, fileText=null, off=null; this.error=_; function errLoc(ast) { var err; if (ast && ast.errors && ast.errors.length) { err=ast.errors[0]; if (err.lineNumber != undefined) return 'line '+err.lineNumber; } return 'unknown' } try { if (!options) options={}; if (options.verbose>0) this.log('Looking up agent class template(s) from '+file); //modu=Require(file); if (Comp.obj.isEmpty(modu)) { if (options.verbose>0) this.log('Reading agent class template(s) from file '+file); if (Comp.string.get(file,0)!='/') path = (process.cwd?process.cwd()+'/':'./')+file; else path = file; fileText=fs.readFileSync(path,'utf8'); ast=Esprima.parse(fileText, { tolerant: true, loc:true }); if (require.cache) delete require.cache[file]; // force reload of file by require modu=require(path); if(Comp.obj.isEmpty(modu)) { modu={}; // Try evaluation of fileText containing one single function definition if (!fileText) throw 'No such file!'; name=fileText.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); if (!name) throw ('Export interface of module is empty and file contains no valid function definition!'); name=name[1]; eval('(function () {'+fileText+' modu["'+name+'"]='+name+'})()'); } } if (!modu || Comp.obj.isEmpty(modu)) throw 'Empty module.'; for (m in modu) { ac=modu[m]; env={}; if (fileText) off=this.syntax.find(fileText,'VariableDeclarator',m); if (off && off.loc) this.syntax.offset=off.loc.start.line-1; content = 'var ac = '+ac; syntax = Esprima.parse(content, { tolerant: true, loc:true }); interface = this.analyzeSyntax(syntax, { classname:m, level:2, verbose: options.verbose||0, err: options.error||function (msg){throw(msg)}, out: function (msg){self.log(msg)}, warn: function (msg){self.log(msg)} }); // text=Json.stringify(ac); for (var p in interface.activities) env[p]=p; with (env) { eval(content) }; if (options.verbose>0) this.log('Adding agent class constructor '+m+' ('+(typeof ac)+').'); this.addClass(m,ac,env); this.syntax.offset=0; } this.error=undefined; return true; } catch (e) { this.error='Compiling agent class file "'+file+'" failed: '+e+ (ast && ast.errors.length?', in '+errLoc(ast):''); if (options.error) options.error(e+(ast && ast.errors.length?', in '+errLoc(ast):'')); else { this.log(this.error); } return false; } }; /** Register jamlib event handler for the (root) node */ jam.prototype.register = function (node) { this.on('agent', function (msg) { node.receive(msg) }); this.on('signal', function (msg) { node.handle(msg) }); } /** Disconnect and remove a virtual node from the world * */ jam.prototype.removeNode = function (nodeid) { this.world.removeNode(nodeid); } /** Tuple space remove operation */ jam.prototype.rm = function (pat,all) { return this.world.nodes[this.node].ts.extern.rm(pat,all); } /** Take an agent process snapshot executed currently on given node @node:number|string|undefined. * If @file:string is not specified, a string containing the snapshot is * returned, otehrwise it is saved to the file (text format. JSON+). * If @node is undefined, the current node is used. * If @kill is set, the agent is killed after taken the snapshot. */ jam.prototype.saveSnapshotOn = function (aid,node,file,kill) { var snapshot,pro; node=this.getNode(node); if (!node) return; // Look-up agent process .. pro=node.getAgentProcess(aid); if (!pro) return; // Take snapshot od the process .. snapshot=Aios.Code.ofCode(pro,false); if (kill) Aios.killOn(aid,node); // Save it .. if (!file) return snapshot; else if (fs) return fs.writeFileSync(file, snapshot, 'utf8'); } jam.prototype.saveSnapshot = function (aid,file,kill) { return this.saveSnapshotOn(aid,_,file,kill); } /** Force a scheduler run immediately normally executed by the * jam service loop. Required if there were externeal agent * management, e.g., by sending signals. */ jam.prototype.schedule = function () { if (this.loop) clearTimeout(this.loop); this.loop=setTimeout(this.looping,1); } /** Access to JAM security module * */ jam.prototype.security = Aios.Sec; /** Set current node * */ jam.prototype.setCurrentNode=function (n) { if (n>=0 && n < this.world.nodes.length) this.node=n; } /** Send a signal to a specific agent 'to'. * */ jam.prototype.signal=function (to,sig,arg,broadcast) { var node=this.getNode(), _process=Aios.current.process; Aios.current.process=this.process; if (!broadcast) Aios.aios.send(to,sig,arg); else Aios.aios.broadcast(to,sig,arg); Aios.current.process=_process; this.schedule(); } /** Start the JAM scheduler * */ jam.prototype.start=function (callback) { var self=this,cbl=CBL(callback); // Start all connections if not already done this.world.nodes.forEach(function (node) { node.connections.forEach(function (chan,kind) { if (!chan) return; if (chan.start) cbl.push(function (next) {chan.start(next)}); }); }); cbl.start(); Aios.on('schedule',function () { self.schedule(); }); function loop() { var loop = function () { var nexttime,curtime; if (self.verbose>2) self.log('loop: Entering scheduler #'+self.ticks); self.ticks++; nexttime=Aios.scheduler(); curtime=Aios.time(); if (self.verbose>2) self.log('loop: Scheduler returned nexttime='+nexttime+ ' ('+(nexttime>0?nexttime-curtime:0)+')'); if (!self.run) return; if (nexttime>0) self.loop=setTimeout(loop,nexttime-curtime); else if (nexttime==0) self.loop=setTimeout(loop,1000); else setImmediate(loop); }; self.loop = setTimeout(loop,1); }; this.looping=loop; Aios.config({iterations:100}); this.run=true; this.world.start(); this.log('Starting JAM loop .. '); if (!this.options.scheduler) this.loop = setTimeout(loop,1); // Start internal scheduling loop } /** Get agent process table info and other statistics * * type kind = 'process'|'agent'|'node'|'vm'|'conn' */ jam.prototype.stats = function (kind,id) { var p,n,sys,conn,pro,agent,state,stats,allstats={},signals,node; switch (kind) { case 'process': case 'agent': for(n in this.world.nodes) { stats={}; node=this.world.nodes[n]; for (p in node.processes.table) { if (node.processes.table[p]) { pro=node.processes.table[p]; if (pro.signals.length == 0) signals=[]; else signals = pro.signals.map(function (sig) {return sig[0] }); agent=pro.agent; if (pro.suspended) state='SUSPENDED'; else if (pro.blocked) state='BLOCKED'; else if (pro.dead) state='DEAD'; else if (pro.kill) state='KILL'; else if (pro.move) state='MOVE'; else state='READY'; stats[agent.id]={ pid:pro.pid, gid:pro.gid, state:state, parent:pro.agent.parent, class:pro.agent.ac, next:agent.next, resources:Comp.obj.copy(pro.resources) }; if (signals.length) stats[agent.id].signals=signals; } } allstats[node.id]=stats; } break; case 'node': return Comp.obj.copy(this.getNode(id).stats); break; case 'conn': for(n in this.world.nodes) { stats={}; node=this.world.nodes[n]; for (p in node.connections) { conn=node.connections[p]; if (conn) { stats[p]={count:conn.count(),conn:conn.status('%')}; } } allstats[node.id]=stats; } break; case 'vm': // Return VM memory usage in kB units and VM system information if (process && process.memoryUsage) { sys=process.memoryUsage(); for ( p in sys) sys[p] = (sys[p]/1024)|0; sys.v8 = process.versions && process.versions.v8; sys.node = process.versions && process.versions.node; sys.arch = process.arch; sys.platform = process.platform; sys.watchdog = Aios.watchdog?(Aios.watchdog.checkPoint?'semi':'full'):'none'; return sys; } break; } if (this.world.nodes.length==1) return stats; else return allstats; } /** Stepping the scheduler loop */ jam.prototype.step = function (steps,callback) { // TODO: accurate timing var self=this, milliTime=function () {return Math.ceil(Date.now())}, curtime=Aios.time(),// Aios.time(); lasttime=curtime; function loop () { var loop = function () { var nexttime,curtime; if (self.verbose>1) self.log('loop: Entering scheduler #'+self.ticks); self.ticks++,self.steps--; self.time=curtime=Aios.time(); // Execute scheduler loop nexttime=Aios.scheduler(); curtime=Aios.time(); if (self.verbose>1) self.log('loop: Scheduler returned nexttime='+nexttime+ ' ('+(nexttime>0?nexttime-curtime:0)+')'); if (self.steps==0 || !self.run) { self.loop=none; self.run=false; self.time=curtime; if (callback) callback(); return; } if (nexttime>0) self.loop=setTimeout(loop,nexttime-curtime); else if (nexttime < 0) self.loop=setImmediate(loop); else { self.loop=none; self.run=false; self.time=curtime; if (callback) callback(); } }; self.loop = setTimeout(loop,1); }; this.looping=loop; Aios.config({iterations:1}); this.steps=steps; this.run=true; if (this.time>0) current.world.lag=current.world.lag+(curtime-this.time); this.time=curtime; if (!this.options.scheduler) this.loop = setTimeout(loop,0); // Start internal scheduling loop } /** Stop the JAM scheduler and all network connections * */ jam.prototype.stop=function (callback) { this.run=false,cbl=CBL(callback); this.log('Stopping JAM ..'); Aios.off('schedule'); if (this.loop) clearTimeout(this.loop); this.world.nodes.forEach(function (node) { node.connections.forEach(function (chan,kind) { if (!chan) return; if (chan.stop) cbl.push(function (next) {chan.stop(next)}); }); }); cbl.start(); } /** Tuple space test operation - non blocking */ jam.prototype.test = function (pat) { return this.world.nodes[this.node].ts.extern.exists(pat); } /** Tuple space testandset operation */ jam.prototype.ts = function (pat,callback) { return this.world.nodes[this.node].ts.extern.ts(pat,callback); } /** Get JAM time */ jam.prototype.time=function () { return Aios.time(); } /** Get JAMLIB version */ jam.prototype.version=function () { return options.version; } var Jam = function(options) { var obj = new jam(options); return obj; }; /** Embedded cluster setup and start; * Provided by process arguments */ if (environment.autosetup) { try { var _options=JSON.parse(environment.autosetup); // console.log('['+process.pid+'] JAM cluster setup with options:',process.argv[_index+1]); jam.prototype.setup=function () { for(var p in _options) this.options[p]=_options[p]; } } catch (e) { console.log('['+process.pid+'] JAM auto setup failed: '+e); } } module.exports = { Aios:Aios, Comp:Comp, Esprima:Esprima, Io:Io, Jam:Jam, Json:Json, environment:environment, options:options } }; BundleModuleCode['com/compat']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 30-3-15 by sbosse. ** $VERSION: 1.23.1 ** ** $INFO: ** ** JavaScript-OCaML Compatibility Module ** ** $ENDOFINFO */ var Io = Require('com/io'); var Path = Require('com/path'); var Sprintf = Require('com/sprintf'); /******************************* ** Some global special "values" ********************************/ /** A matching template pattern matching any value * * @type {undefined} */ global.any = undefined; /** A matching template pattern matching any value * * @type {undefined} */ global._ = undefined; /** * * @type {null} */ global.none = null; /** * * @type {null} */ global.empty = null; global.NL = '\n'; global.int = function (v) {return v|0}; global.div = function (a,b) {return a/b|0}; if (!Object.prototype.forEach) { Object.defineProperties(Object.prototype, { 'forEach': { value: function (callback) { if (this == null) { throw new TypeError('Not an object'); } var obj = this; for (var key in obj) { if (obj.hasOwnProperty(key)) { callback.call(obj, obj[key], key, obj); } } }, writable: true } }); } /** Just transfer parent prototypes to child * */ function inherit(child,parent) { for(var p in parent.prototype) { if (p == '__proto__') continue; child.prototype[p]=parent.prototype[p]; } } /** Portable class inheritance and instanceOf polyfill * */ // SomeObject.prototype.__proto__=SomeObject2.prototype; // Child class inherits prototype from parent using __proto__ function inheritPrototype(child,parent) { var __proto__=child.__proto__; child.prototype.__proto__=parent.prototype; if (!__proto__) for(var p in parent.prototype) { if (p == '__proto__') continue; child.prototype[p]=parent.prototype[p]; } } // Polyfill fir o instanceof c with inheritance check (checking __proto__) function instanceOf(obj,cla) { var p=obj.__proto__; if (obj instanceof cla) return true; while (p) { if (p === cla.prototype) return true; p=p.__proto__ } return false; } // Polyfill for __defineGetter__ / __defineSetter__ function defineGetter(cla,prop,fun) { Object.defineProperty(cla.prototype,prop,{ configurable:true, get:fun }); } function defineSetter(cla,prop,fun) { Object.defineProperty(cla.prototype,prop,{ configurable:true, set:fun }); } global.inherit = inherit; global.inheritPrototype = inheritPrototype; global.instanceOf = instanceOf; global.defineGetter = defineGetter; global.defineSetter = defineSetter; /** * */ var assert = function(condmsg) { if (condmsg != true) { Io.out('** Assertion failed: '+condmsg+' **'); Io.stacktrace(); throw Error(condmsg); } }; global.assert=assert; function forof(obj,f) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = obj[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { element = _step.value; f(element); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } global.forof=forof; /** OBJ * */ var obj = { /** Compact an object: * [{a:b},[c:d},..] -> {a:b,c:d,..} * {a:[b]} -> {a:b} * */ compact: function (o) { var a; if (obj.isArray(o)) { if (o.length==1 && obj.isObject(o[0])) return obj.compact(o[0]); else return o; } else if (obj.isObject(o)) for (a in o) { var elem=o[a]; o[a]=obj.compact(elem); } return o; }, copy: function (o) { if (o === null || typeof o !== 'object') { return o; } var temp = (o instanceof Array) ? [] : {}; for (var key in o) { temp[key] = obj.copy(o[key]); } return temp; }, equal: function (o1,o2) { if (!o1 || !o2) return false; for(var i in o1) if (o1[i]!=o2[i]) return false; for(var i in o2) if (o1[i]!=o2[i]) return false; return true; }, extend: function (o1,o2) { for(var i in o2) o1[i]=o2[i]; return o1; }, find: function(obj,fun) { var p; for(p in obj) { if (fun(obj[p],p)) return obj[p]; } }, hasProperty: function (o,p) { return o[p]!=undefined || (p in o); }, head:function(o) { for (var p in o) return p; return undefined; }, // transfer src attributes to dst recusively (no object overwrite) inherit: function (dst,src) { for(var i in src) { if (typeof dst[i] == 'object' && typeof src[i] == 'object') inherit(dst[i],src[i]); else if (typeof dst[i] == 'undefined') dst[i]=src[i]; } return dst; }, isArray:function (o) { if (o==_ || o ==null) return false; else return typeof o == "array" || (typeof o == "object" && o.constructor === Array); }, isMatrix:function (o) { if (o==_ || o ==null) return false; else return obj.isArray(o) && obj.isArray(o[0]); }, isEmpty: function (o) { for(var prop in o) { if (o[prop]!=undefined) return false; } return true; }, isFunction: function (o) { return typeof o == "function"; }, isObj:function (o) { return typeof o == "object"; }, isObject:function (o) { return typeof o == "object"; }, isRegex: function (o) { return o instanceof RegExp; }, isString: function (o) { return typeof o == "string" || (typeof o == "object" && o.constructor === String); }, isNumber: function (o) { return typeof o == "number" || (typeof o == "object" && o.constructor === Number); }, iter: function(obj,fun) { var p; for(p in obj) { fun(obj[p],p) } } }; /** ARRAY * */ var array = { /** Evaluate a function returning a boolean value for each member of the array and * compute the boolean conjunction. * * @param {* []} array * @param {function(*,number)} fun */ and: function(array,fun) { var res=true; var i=0; var len=array.length; for(i=0;i|string|Blob|ArrayBuffer} */ copy: function(src,dst) { var i; if (dst) { for(i in src) dst[i]=src[i]; } else return src.slice(); }, /** Create a new array with initial element values. * * @param length * @param init * @returns {Array} */ create : function(length,init) { var arr = [], i = length; while (i--) { arr[i] = init; } return arr; }, /** Create a matrix (array of array) with initial element values. * */ create_matrix : function(rows,cols,init) { var m = []; var r = []; var i,j; for (i = 0; i < rows; i++) { r=[]; for(j=0;j=0;i--) { fun(array[i],i) } }, /** Return last element of array. * */ last : function(array) { var len=array.length; if (len==0) return none; else return array[len-1]; }, length : function(array) { return array.length; }, /** * * @param {* []} array1 * @param {* []} array2 * @param {function(*,*,number)} fun * @returns {* []} */ map2: function(array1,array2,fun) { var i=0; assert((array1.length == array2.length)||('Array.map2: arrays of different lengths')); var len=array1.length; var res=[]; for(i=0;i1) { var hd = this.head(array); var tl = this.tail(array); fun_hdtl(hd,tl); } else fun_hdtl(this.head(array),[]); }, /** * * @param {* []} array * @param {Function} fun_hd1hd2 - function(hd1,hd2) * @param {Function} [fun_hdtl] - function(hd,tl) * @param {Function} [fun_empty] - function() */ match2: function(array,fun_hd1hd2,fun_hdtl,fun_empty) { if (array.length == 0 && fun_empty) fun_empty(); else if (array.length == 2) { var hd1 = this.head(array); var hd2 = this.second(array); fun_hd1hd2(hd1,hd2); } else if (array.length>1 && fun_hdtl) { var hd = this.head(array); var tl = this.tail(array); fun_hdtl(hd,tl); } else if (fun_hdtl) fun_hdtl(this.head(array),[]); }, /** Return the maximum number of an array applying * an optional mapping function. * * @param {* []} array * @param [fun] * @returns {number|undefined} */ max : function (array,fun) { var res=undefined; for(var i in array) { var num; if (fun) num=fun(array[i]); else num=array[i]; if (!obj.isNumber(num)) return undefined; if (res==undefined) res=num; else res=pervasives.max(res,num); } return res; }, /** Return the minimum number of an array applying * an optional mapping function. * * @param {* []} array * @param [fun] * @returns {number|undefined} */ min : function (array,fun) { var res=undefined; for(var i in array) { var num; if (fun) num=fun(array[i]); else num=array[i]; if (!obj.isNumber(num)) return undefined; if (res==undefined) res=num; else res=pervasives.min(res,num); } return res; }, /** Check for an element in the array. * * @param {(number|string|boolean) []} array * @param {number|string|boolean} element * @returns {boolean} */ member: function(array,element) { var i,exist; var len=array.length; exist=false; loop: for(i=0;i ['barney', 'fred'] */ pluck: function(collection, key) { return collection.map(function(object) { return object == null ? undefined : object[key]; }); }, /* ** Push/pop head elements (Stack behaviour) */ /** Remove and return top element of array. * * @param array * @returns {*} */ pop : function(array) { var element=array[0]; array.shift(); return element; }, print: function(array) { var i; var len=array.length; var str='['; for(i=0;i [1,2,6] * @param {* []} array * @returns {* []} */ remove: function(array,begin,end) { var i,a; if (end==undefined) end=begin+1; if (begin<0 || end >= array.length) return []; a=array.slice(0,begin); for(i=end;i use remove!!! split should return two arrays!! * * @param array * @param pos * @param [len] * @param element */ split: function(array,pos,len) { if (pos==0) return array.slice((len||1)); else { var a1=array.slice(0,pos); var a2=array.slice(pos+(len||1)); return a1.concat(a2); } }, /** Return the sum number of an array applying * an optional mapping function. * * @param {* []} array * @param [fun] * @returns {number|undefined} */ sum : function (array,fun) { var res=0; for(var i in array) { var num=0; if (fun) num=fun(array[i]); else num=array[i]; if (!obj.isNumber(num)) return undefined; res += num; } return res; }, /** Return a new array w/o the head element (or optional * w/o the first top elements). * */ tail : function(array,top) { var array2=array.slice(); array2.shift(); if (top) for(;top>1;top--) array2.shift(); return array2; }, /** Return union of two sets (== conjunction set) * * @param {* []} set1 * @param {* []} set2 * @param {function} [fun] Equality test * @returns {* []} */ union : function(set1,set2,fun) { var i,j,res = []; for (i in set1) { for (j in set2) { if (fun != undefined && fun(set1[i],set2[j])) res.push(set1[i]); else if (fun == undefined && set1[i]==set2[j]) res.push(set1[i]); } } return res; }, /** * Creates a duplicate-free version of an array */ unique: function(array) { var length = array ? array.length : 0; function baseUniq(array) { var index = -1, length = array.length, seen, result = []; seen = result; outer: while (++index < length) { var value = array[index]; var seenIndex = seen.length; while (seenIndex--) { if (seen[seenIndex] === value) { continue outer; } } result.push(value); } return result; } if (!length) { return []; } return baseUniq(array); }, /** * Creates an array excluding all provided values * without([1, 2, 1, 3], 1, 2); * // => [3] */ without: function () { var array, values=[]; for(var i in arguments) { if (i==0) array=arguments[0]; else values.push(arguments[i]); } return array.filter(function (e) { return values.indexOf(e) == -1; }); }, /** Test for zero elements {0, '', false, undefined, ..} */ zero: function (array) { for(var i in array) if (!!array[i]) return false; return true; }, }; /** STRING * */ var string = { /** Is pattern conatined in template? * */ contains: function (template,pattern) { return template.indexOf(pattern)>-1; }, copy: function(src) { var i; var dst=''; for(i=0;i>4) & 0xf).toString(16))+ ((n&0xf).toString(16)); case 4: return (((n>>12) & 0xf).toString(16)+ ((n>>8) & 0xf).toString(16)+ ((n>>4) & 0xf).toString(16)+ (n&0xf).toString(16)); case 6: return (((n>>20) & 0xf).toString(16)+ ((n>>16) & 0xf).toString(16)+ ((n>>12) & 0xf).toString(16)+ ((n>>8) & 0xf).toString(16)+ ((n>>4) & 0xf).toString(16)+ (n&0xf).toString(16)); case 8: return (((n>>28) & 0xf).toString(16)+ ((n>>24) & 0xf).toString(16)+ ((n>>20) & 0xf).toString(16)+ ((n>>16) & 0xf).toString(16)+ ((n>>12) & 0xf).toString(16)+ ((n>>8) & 0xf).toString(16)+ ((n>>4) & 0xf).toString(16)+ (n&0xf).toString(16)); default: return 'format_hex??'; } }, /** * * @param {string} str * @param {number} index * @returns {string} */ get: function (str,index) { assert((str != undefined && index < str.length && index >= 0)||('string.get ('+str.length+')')); return str.charAt(index); }, isBoolean: function (str) { return (str=='true' || str=='false') }, isNumeric: function (str) { return !isNaN(parseFloat(str)) && isFinite(str); }, isText: function (s) { var is_text=true; string.iter(s,function (ch,i) { string.match(ch,[ ['a','z',function () {}], ['A','Z',function () {}], ['0','9',function () {if (i==0) is_text=false;}], function () {is_text=false;} ]); }); return is_text; }, /** * * @param {string} str * @param {function(string,number)} fun */ iter: function(str,fun) { var i; var len=str.length; for (i = 0; i < len; i++) { var c = str.charAt(i); fun(c,i); } }, /** * * @param str * @returns {*} */ length: function(str) { if (str!=undefined) return str.length; else return 0; }, /** * * @param str * @returns {string} */ lowercase : function (str) { return str.toLowerCase(); }, /** * * @param {number} size * @param {string} init * @returns {string} */ make: function(size,init) { var i; var s=''; for(i=0;i,,..],fun] | [:string,:string,fun] | fun) [] */ match: function(str,cases) { var i,j; var cas,cex,cv; for(i in cases) { cas=cases[i]; if (obj.isArray(cas)) { switch (cas.length) { case 2: // Multi-value-case cex=cas[0]; if (!obj.isArray(cex)) { if (this.equal(str,cex)) { cas[1](); return; } } else { for(j in cex) { cv=cex[j]; if (this.equal(str,cv)) { cas[1](); return; } } } break; case 3: // Character range check try { j=pervasives.int_of_char(str); if (j>= pervasives.int_of_char(cas[0]) && j<=pervasives.int_of_char(cas[1])) { cas[2](str); return; } } catch(e) { return }; break; case 1: cas[0](str); // Default case - obsolete return; default: throw 'String.match #args'; } } else if (obj.isFunction(cas)) { // Default case cas(str); return; } } }, /** Pad a string on the left (pre-str.length) if pre>0, * right (post-str.length) if post>0, or centered (pre>0&post>0). * */ pad: function (str,pre,post,char) { var len = str.length; if (pre>0 && post==0) return string.make(len-pre,char||' ')+str; else if (post>0 && pre==0) return str+string.make(post-len,char||' '); else return string.make(len-pre/2,char||' ')+str+string.make(len-post/2,char||' '); }, /** * * @param str * @param pos * @param len * @returns {Number} */ parse_hex: function (str,pos,len) { // parse a hexadecimal number in string 'str' starting at position 'pos' with 'len' figures. return parseInt(this.sub(str,pos,len),16); }, /** Return the sub-string after a point in the source string ('.' or optional point string). * If there is no splitting point, the original string is returned. * * @param str * @param [point] * @returns {string} */ postfix: function (str,point) { var n = str.indexOf(point||'.'); if (n <= 0) return str; else return str.substr(n+1); }, /** Return the sub-string before a point in the source string ('.' or optional point string) * If there is no splitting point, the original string is returned. * * @param str * @param [point] * @returns {string} */ prefix: function (str,point) { var n = str.indexOf(point||'.'); if (n <= 0) return str; else return str.substr(0,n); }, replace_first: function (pat,repl,str) { return str.replace(pat,repl); }, replace_all: function (pat,repl,str) { return str.replace('/'+pat+'/g',repl); }, /** * * @param str * @param index * @param char * @returns {string} */ set: function (str,index,char) { assert((str != undefined && index < str.length && index >= 0)||'string.get'); return str.substr(0, index) + char + str.substr(index+1) }, /** * * @param delim * @param str * @returns {*|Array} */ split: function (delim,str) { return str.split(delim); }, startsWith : function (str,head) { return !str.indexOf(head); }, /** Return a sub-string. * * @param str * @param off * @param [len] If not give, return a sub-string from off to end * @returns {string} */ sub: function (str,off,len) { if (len) return str.substr(off,len); else return str.substr(off); }, /** Remove leading and trailing characters from string * * @param str * @param {number} pref number of head characters to remove * @param {number} post number of tail characters to remove * @returns {*} */ trim: function (str,pref,post) { if (str.length==0 || pref>str.length || post>str.length || pref < 0 || post < 0 || (pref==0 && post==0) ) return str; return str.substr(pref,str.length-pref-post); }, /** Return a string with all characters converted to uppercase letters. * * @param str * @returns {string} */ uppercase : function (str) { return str.toUpperCase(); }, /** Return a string with first character converted to uppercase letter. * * @param str * @returns {string} */ Uppercase : function (str) { var len = str.length; if (len > 1) { var head = str.substr(0,1); var tail = str.substr(1,len-1); return head.toUpperCase()+tail.toLowerCase() } if (len==1) return str.toUpperCase(); else return ''; } }; /** RANDOM * */ var rnd = Math.random; /* Antti Syk�ri's algorithm adapted from Wikipedia MWC ** Returns a random generator function [0.0,1.0| with seed initialization */ var seeder = function(s) { var m_w = s; var m_z = 987654321; var mask = 0xffffffff; return function() { m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; var result = ((m_z << 16) + m_w) & mask; result /= 4294967296; return result + 0.5; } } var random = { float: function(max) { return rnd()*max }, int: function(max) { return Math.floor(rnd()*max+0) }, // integer interval: function(min,max) { return Math.round(min+rnd()*(max-min)) }, // float range: function(min,max) { return min+rnd()*(max-min) }, seed: function (s) { // Create a new initialized random generator rnd=seeder(s); } }; /** PRINTF * */ var printf = { /** Trim string(s). * * @param str * @param indent * @param [width] * @param {string} [tab] * @returns {string} */ align: function (str,indent,width,tab) { var lines = string.split('\n',str); var form = ''; var sp = printf.spaces(indent); var spbreak = sp; array.iter(lines,function(line){ var rest; function breakit(spbreak,str) { if (width < (str.length + spbreak.length)) { return spbreak+string.sub(str,0,width-spbreak.length)+'\n'+ breakit(spbreak,string.sub(str,width-spbreak.length,str.length-width+spbreak.length)); } else return spbreak+str+'\n'; } if (width && width < (line.length + indent)) { if (tab) { var pos = string.find(tab,line); if (pos > 0 && pos < width) spbreak=printf.spaces(pos+indent+1); else spbreak=sp; } form=form+sp+string.sub(line,0,width-indent)+'\n'; rest=string.sub(line,width-indent,line.length-width+indent); form=form+breakit(spbreak,rest); } else form=form+sp+line+'\n'; }); return form; }, /** Format a list of array elements using the (optional) mapping * function and the separator (optional, too, default is ','). * */ list: function (array,fun,sep) { var i, str=''; if (sep==undefined) sep=','; if (fun==undefined) fun=function (s) {return s;}; if (!obj.isArray(array)) array=[array]; for (i in array) { if (str==='') str=fun(array[i]); else str=str+sep+fun(array[i]); } return str; }, /** * * @param n * @returns {string} */ spaces: function (n){ return string.make(n,' '); }, /** Formatted printer (simplified) * * @param {* []} args (['%format',arg]|string) [] format=%s,%d,%f,%c,%x,%#d,%#s,.. * @returns {string} */ sprintf2: function(args) { var str=''; array.iter(args,function(fmtarg) { var len, n,fs; if (obj.isArray(fmtarg)) { if (fmtarg.length==2) { var fmt=fmtarg[0]; var arg=fmtarg[1]; var fc=''; var fn=0; string.iter(fmt,function(c) { if (c=='s' || c=='d' || c=='f' || c=='x') { fc=c; } else if (c!='%') { fn=fn*10; n=parseInt(c); if (!isNaN(n)) fn=fn+n; } }); if (fc=='s' && obj.isString(arg)) { str=str+arg; if (fn!=0) { len=arg.length; if (len 0 && path[0] == '/'); }, /** * * @param pathl * @param absolute * @returns {string} */ join: function (pathl,absolute) { var path=(absolute?'/':''); array.iter(pathl,function (name,index) { if (index>0) { path=path+'/'+name; } else { path=path+name; } }); return path; }, /** * * @param path * @returns {string} */ normalize : function (path) { return Path.normalize(path) }, /** * * @param path * @returns {*} */ path_absolute: function (path) { if (this.is_relative(path)) { var workdir = Io.workdir(); return this.path_normalize(workdir + '/' + path); } else return this.path_normalize(path); }, /** Duplicate of Path.normalize!? * * @param path * @returns {string} */ path_normalize: function (path) { var i; if (string.equal(path, '')) path = '/'; var relpath = !(string.get(path, 0) == '/'); var pathlist = path.split('/'); var pathlist2 = pathlist.filter(function (s) { return (!string.equal(s, '') && !string.equal(s, '.')) }); var pathlist3 = []; array.iter(pathlist2, function (pe) { if (!string.equal(pe, '..')) { array.push(pathlist3, pe) } else { if (pathlist3.length == 0) return ''; else pathlist3 = array.tail(pathlist3); } }); var path2 = ''; i = 0; array.iter(pathlist3, function (pe) { var sep; if (i == 0) sep = ''; else sep = '/'; path2 = pe + sep + path2; i++; }); if (relpath) return path2; else return '/' + path2; }, removeext: function (path) { return path.substr(0, path.lastIndexOf('.')); } }; /** PERVASIVES * * */ var pervasives = { assert:assert, char_of_int: function (i) {return String.fromCharCode(i)}, div: function(a,b) {return a/b|0;}, failwith: function(msg) {Io.err(msg);}, float_of_string: function(s) {var num=parseFloat(s); if (isNaN(num)) throw 'NaN'; else return num;}, int_of_char: function(c) {return c.charCodeAt()}, int_of_float: function(f) {return f|0;}, int_of_string: function(s) { var num=parseInt(s); if (isNaN(num)) throw 'NaN'; else return num; }, /** Try to find a value in a search list and return a mapping value. * * @param {*} value * @param {* []} mapping [testval,mapval] [] * @returns {*} */ map: function(value,mapping) { function eq(v1,v2) { if (v1==v2) return true; if (obj.isString(v1) && obj.isString(v2)) return string.equal(v1,v2); return false; } if (!array.empty(mapping)) { var hd=array.head(mapping); var tl=array.tail(mapping); if (eq(hd[0],value)) return hd[1]; else return pervasives.map(value,tl); } else return undefined; }, /** Apply a matcher function to a list of cases with case handler functions. * A case is matched if the matcher function returns a value/object. * * The result of the matcher function is passed as an argument ot the case handler function. * The return value of the case handler fucntion is finally returned by this match function * or undefined if there was no matching case. * * @param {function(*,*):*} matcher function(expr,pat) * @param {*} expr * @param {*[]} cases (pattern,handler function | handler function) [] * @returns {*|undefined} */ match: function (matcher,expr,cases) { var ret = undefined; array.iter_break(cases, function (match) { var quit, succ, pat, fun; if (match.length == 2) { /* ** Pattern, Function */ pat = match[0]; fun = match[1]; succ = matcher(expr, pat); if (succ) ret = fun(succ); quit = succ!=undefined; } else if (match.length == 1) { /* ** Default case, Function */ fun = match[0]; ret = fun(); quit= true; } return quit; }); return ret; }, mtime: function () {var time = new Date(); return time.getTime();}, min: function(a,b) { return (ab)?a:b}, string_of_float: function(f) {return f.toString()}, string_of_int: function(i) {return i.toString()}, string_of_int64: function(i) {return i.toString()}, time: function () {var time = new Date(); return (time.getTime()/1000)|0;} }; /** BIT * */ var bit = { get: function (v,b) {return (v >> b) && 1;}, isSet: function (v,b) {return ((v >> b) && 1)==1;}, set: function (v,b) {return v & (1 << b);} }; /** ARGS * */ var args = { /** Parse process or command line arguments (array argv). The first offset [1] arguments are ** ignored. The numarg pattern '*' consumes all remaining arguments. * * @param {string []} argv * @param {*[]} map [,,]|[] [] * @param {number} [offset] */ parse: function(argv,map,offset) { var shift=undefined, in_shift=0, shift_args=[], names, mapfun, numarg, len=argv.length; if (offset==undefined) offset=1; argv.forEach(function (val, index) { var last=index==(len-1); if(index>=offset) { if (in_shift==0) { array.check(map,function (onemap) { assert(onemap!=undefined||'map'); if (onemap.length==3) { names = onemap[0]; numarg = onemap[1]; mapfun = onemap[2]; if (!obj.isArray(names)) names=[names]; var found = array.find(names,function (name) { if (string.equal(val, name)) return name; else _; }); if (found) { if (numarg==0) mapfun(found); else { in_shift=numarg; shift_args=[]; shift=mapfun; } return true; } } else if (obj.isFunction(onemap)) { onemap(val); return true; } else if (onemap.length==1) { mapfun = onemap[0]; mapfun(val); return true; } return false; }); } else { shift_args.push(val); if (in_shift!='*') in_shift--; if (in_shift==0 && shift!=undefined) { numarg=shift_args.length; switch (numarg) { case 0: shift(val);break; case 1: shift(shift_args[0],val); break; case 2: shift(shift_args[0],shift_args[1],val); break; case 3: shift(shift_args[0],shift_args[1],shift_args[2],val); break; default: break; } shift=undefined; } else if (in_shift=='*' && last) shift(shift_args); } } }); } }; /** HASHTBL * */ var hashtbl = { add: function(hash,key,data) { hash[key]=data; }, create: function(initial) { return []; }, empty: function(hash) { for (var key in hash) return false; return true; }, find: function(hash,key) { return hash[key]; }, invalidate: function(hash,key) { hash[key]=undefined; }, iter: function(hash,fun) { for (var key in hash) { if (hash[key]!=undefined) fun(key,hash[key]); } }, mem: function(hash,key) { return hash[key] != undefined; }, remove: function(hash,key) { // TODO: check, its wrong! if (!hash.hasOwnProperty(key)) return; if (isNaN(parseInt(key)) || !(hash instanceof Array)) delete hash[key]; else hash.splice(key, 1) } }; var types = []; /** * * @param name * @returns {number} */ function register_type(name) { var typoff = 1000+types.length*1000; if (array.member(types,name)) throw('[COMP] register_type: type '+name+' exists already.'); types.push(name); return typoff; } /** * * @typedef {{v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tuple */ /** * * @typedef {{t:number, v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tagged_tuple */ module.exports = { args:args, assert: assert, array:array, bit:bit, div:pervasives.div, filename:filename, hashtbl:hashtbl, isNodeJS: function () { return (typeof global !== "undefined" && {}.toString.call(global) == '[object global]'); }, obj:obj, pervasives:pervasives, printf:printf, random:random, string:string, isArray: obj.isArray, isString: obj.isString, isNumber: obj.isNumber, register_type:register_type, /** * * @param tag * @param [val1] * @param [val2] * @param [val3] * @returns {(tagged_tuple)} */ Tuple: function (tag,val1,val2,val3) { if(val3) return {t:tag,v1:val1,v2:val2,v3:val3}; else if (val2) return {t:tag,v1:val1,v2:val2}; else if (val1) return {t:tag,v1:val1}; else return {t:tag}; } }; }; BundleModuleCode['jam/aios']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 15-1-16 by sbosse. ** $VERSION: 1.37.4 ** $RCS: $Id: aios.js,v 1.5 2017/06/19 17:18:39 sbosse Exp sbosse $ ** $INFO: ** ** JavaScript AIOS: Agent Execution & IO System with Sandbox environment. ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var Name = Require('com/pwgen'); var Conf = Require('jam/conf'); var Code = Require('jam/code'); var Sig = Require('jam/sig'); var Node = Require('jam/node'); var Proc = Require('jam/proc'); var Sec = Require('jam/security'); var Ts = Require('jam/ts'); var World = Require('jam/world'); var Chan = Require('jam/chan'); var Mobi = Require('jam/mobi'); var Ml = Require('ml/ml'); var Nn = Require('nn/nn'); var Simu = global.config.simulation?Require(global.config.simulation):none; var Json = Require('jam/jsonfn'); var Net = Require('dos/network'); var watchdog = Require('jam/watchdog'); var util = Require('util'); var aiosExceptions = ['CREATE','MOVE','SIGNAL','SCHEDULE','WATCHDOG','EOL','KILL']; var aiosEvents = ['agent','agent+','agent-','signal','signal+','node+','node-']; // AIOS OPTIONS // var options = { version: "1.37.4", // Fast dirty process forking and migration between logical nodes (virtual) // w/o using of/toCode? fastcopy:false, // Using JSON+ (json compliant) or JSOB (raw object) in to/ofCode? json:false, // logging parameters log : { node:false, agent:true, parent:false, pid:false, // agent process id! host:false, // host id (os pid) time:false, // time in milliseconds Time:true, // time in hour:minute:sec format class:false }, // agent ID generator name options nameopts : {length:8, memorable:true, lowercase:true}, // Disable agent checkpointing and resource control nolimits:false, // No statistics nostats:false, // Use process memory for resource control? (slows down JAM execution) useproc: false, // Verbosity level verbose:0, // Default maximal run-time of an agent process activity TIMESCHED:200, // Default maximal run-time of an agent process TIMEPOOL:5000, // Default maximal memory of an agent (code+data) MEMPOOL:50000, // Maximal number of tuple generations on current node TSPOOL:1000, // Maximal number of tuple generations on current node AGENTPOOL:20, // Default minimal run-time costs below 1ms resolution (very short activity executions) MINCOST:0.1, // Default maximal scheduler run-time RUNTIME:1000 }; var timer, ticks=0, // scheduler execution counter! iterations=0, events={}; // Current execution environment (scheduler: global scheduler) var current = {process:none,world:none,node:none,network:none,error:none,scheduler:none}; // System clock in ms or hh:mm:ss format function clock (ms) { if (!ms) return Io.Time(); else return Io.time(); } // AIOS smart logging function for Agents var logAgent = function(){ var msg=''; arguments.forEach(function (arg,i) { if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg)); }); (Aios.printAgent||Aios.print)('['+(options.log.host?(process.pid+'.'):'')+ (options.log.world&¤t.world?(current.world.id+'.'):'')+ (options.log.node&¤t.node?(current.node.id+'.'):'')+ (options.log.class&¤t.process?(current.process.agent.ac+'.'):'')+ (options.log.agent&¤t.process?(current.process.agent.id):'')+ (options.log.parent&¤t.process?('<'+current.process.agent.parent):'')+ (options.log.pid&¤t.process?('('+current.process.pid+')'):'')+ (options.log.time?(':'+time()):'')+ (options.log.Time?(':'+Io.Time()):'')+ '] '+msg) } // AIOS smart logging function for AIOS internals (w/o agent messages) var logAIOS = function(){ var msg=''; arguments.forEach(function (arg,i) { if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg)); }); Aios.print('['+(options.log.host?(process.pid+'.'):'')+ (options.log.world&¤t.world?(current.world.id+'.'):'')+ (options.log.node&¤t.node?(current.node.id+'.'):'')+ (options.log.pid&¤t.process?('('+current.process.pid+')'):'')+ (options.log.time?(':'+time()):'')+ (options.log.Time?(':'+Io.Time()):'')+ '] '+msg) } // Generic AIOS messages var log = function () { var msg=''; arguments.forEach(function (arg,i) { if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); else msg += (i>0?', '+Io.inspect(arg):Io.inspect(arg)); }); Aios.print('[AIOS] '+msg); } /** Sandbox module environment for agents (level 0): Untrusted, * minimal set of operations (no move, fork, kill(others),..) */ var aios0 = { abs:Math.abs, add: function (a,b) { var res,i; if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a+b; if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) { if (a.length!=b.length) return none; res=Comp.array.copy(a); for (i in a) { res[i]=aios0.add(a[i],b[i]); } return res; } if (Comp.obj.isArray(a) && Comp.obj.isFunction(b)) { res=Comp.array.copy(a); for (i in a) { res[i]=aios0.add(a[i],b.call(current.process.agent,a[i])); } return res; } if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { res={}; for (i in a) { res[i]=aios0.add(a[i],b[i]); } return res; } return none; }, Capability: Sec.Capability, clock: clock, concat: function (a,b) { var res,i; if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) return Comp.array.concat(a,b); else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { res={}; for (i in a) { res[i]=a[i]; } for (i in b) { res[i]=b[i]; } return res; } else if (Comp.obj.isString(a) && Comp.obj.isString(b)) { return a+b; } else return undefined; }, contains : function (o,e) { // e can be a scalar or array of values if (Comp.obj.isArray(o)) return Comp.array.contains(o,e); else if (Comp.obj.isObj(o) && (Comp.obj.isString(e) || Comp.obj.isNumber(e))) return o[e] != undefined; else if (Comp.obj.isString(o) && Comp.obj.isString(e)) return o.indexOf(e)!=-1 }, copy : function (o) { var _o,p; if (Comp.obj.isArray(o)) return o.slice(); else if (Comp.obj.isObject(o)) { _o={}; for(p in o) _o[p]=aios0.copy(o[p]); return _o; } else if (Comp.obj.isString(o)) return o.slice(); else return o; }, div: div, dump: function (x) { if (x=='res') x=Comp.obj.copy(current.process.resources); if (x=='?') x=this; logAgent(util.inspect(x)); }, empty: function (o) { if (Comp.obj.isArray(o) || Comp.obj.isString(o)) return o.length==0; else if (Comp.obj.isObj(o)) return Comp.obj.isEmpty(o); else return false; }, equal: function (a,b) { var i; if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a==b; else if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) { if (a.length!=b.length) return false; for (i in a) { if (!aios0.equal(a[i],b[i])) return false; } return true; } else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { for (i in a) { if (!aios0.equal(a[i],b[i])) return false; } return true; } else if (Comp.obj.isString(a) && Comp.obj.isString(b)) return (a.length==b.length && a==b) return false; }, filter:function (a,f) { var element,res=[],len,len2,i,j,found; if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) { res=[]; len=a.length; for(i=0;iv) {v=a0; vi=i}; }); if (vi!=undefined) return a[vi]; } else return Math.max(a,b); }, me: function () { return current.process.agent.id; }, min: function (a,b) { if (Comp.obj.isArray(a)) { var f=function (x) {return x},v,vi; if (Comp.obj.isFunction(b)) f=b; Comp.array.iter(a,function (a0,i) { a0=f(a0); if (v==undefined || a00) return a[Comp.random.int(n)]; else return none; } else if (Comp.obj.isObj(a)) { n=0; for(p in a) if (a[p]!=undefined) n++; i=Math.min(Comp.random.interval(0,n),n-1); n=0; for(p in a) if (a[p]!=undefined) {if (n==i) return a[p]; n++}; } else if (b==undefined) {b=a;a=0}; if (!frac ||frac==1) return Comp.random.interval(a,b); else { r=Comp.random.range(a,b); return ((r/frac)|0)*frac; } }, Port: Sec.Port, Private: Sec.Private, reverse: function (a) { if (Comp.obj.isArray(a)) return a.slice().reverse(); else if (Comp.obj.isString(a)) return a.split("").reverse().join("") }, sleep:Sig.agent.sleep, sort: function (a,f) { if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) { return Comp.array.sort(a,function (x,y) { return f.call(current.process.agent,x,y); }); } else return undefined; }, sum: function (o,f) { if (Comp.obj.isArray(o)) return Comp.array.sum(o,f); else if (Comp.obj.isObject(o)) { var s=0,p; if (!f) f=function(x){return x}; for(p in o) s+=f(o[p]); return s; } }, string:function (o) {if (Comp.obj.isString(o)) return o; else return o.toString()}, tail:function (a) { if (Comp.obj.isArray(a)) return Comp.array.tail(a); else return undefined; }, time:function () { return time()-current.world.lag}, zero: function (a) { var i; if (Comp.obj.isNumber(a)) return a==0; if (Comp.obj.isArray(a)) { for (i in a) { if (!aios0.zero(a[i])) return false; } return true; } if (Comp.obj.isObj(a)) { for (i in a) { if (!aios0.zero(a[i])) return false; } return true; } return false; }, Vector: function (x,y,z) {var o={}; if (x!=_) o['x']=x; if (y!=_) o['y']=y; if (z!=_) o['z']=z; return o}, // Scheduling and checkpointing B:B, CP:CP, I:I, L:L, RT:RT, Math:Math } // Sandbox module environment for agents (level 1): Trusted, standard operational set var aios1 = { abs:aios0.abs, add:aios0.add, act:Conf.agent.act, alt:Ts.agent.alt, broadcast:Sig.agent.broadcast, Capability: Sec.Capability, clock: clock, collect:Ts.agent.collect, concat:aios0.concat, contains:aios0.contains, copy:aios0.copy, copyto:Ts.agent.copyto, // type create = function(ac:string,args:object|[]) -> agentid:string create: function(ac,args,level) { if (level==undefined || level>1) level=1; var process=none; if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) { current.error='Invalid argument: Agent argument is neither array nor object'; throw 'CREATE'; }; if (current.world.classes[ac] && current.world.classes[ac][level]) process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac); else if (current.process.agent.subclass && current.process.agent.subclass[ac]) { process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac); } else { current.error='Invalid argument: Unknown agent class '+ac; throw 'CREATE'; } if (process) { if (current.process!=none && process.gid==none) { process.gid=current.process.pid; if (!process.agent.parent) process.agent.parent=current.process.agent.id; } return process.agent.id; } else return none; }, div: aios0.div, dump: aios0.dump, empty:aios0.empty, evaluate:Ts.agent.evaluate, equal:aios0.equal, exists:Ts.agent.exists, Export:function (name,code) { current.node.export(name,code) }, filter:aios0.filter, fork:function (parameter) {var process = current.process.fork(parameter,undefined,options.fastcopy); return process.agent.id}, head:aios0.head, id:aidgen, Import:function (name) { return current.node.import(name) }, info:aios0.info, inp:Ts.agent.inp, int: aios0.int, isin: aios0.isin, iter:aios0.iter, kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)}, length: aios0.length, link:function (dir) {return current.world.connected(dir,current.node)}, listen:Ts.agent.listen, log:aios0.log, me:aios0.me, ml:Ml.agent, nn:Nn.agent, mark:Ts.agent.mark, map:aios0.map, max:aios0.max, matrix:aios0.matrix, moveto:Mobi.agent.move, min:aios0.min, myClass:aios0.myClass, myNode:aios0.myNode, myParent:aios0.myParent, neg:aios0.neg, negotiate:function (res,val,cap) { return negotiate(1,res,val,cap) }, opposite:Mobi.agent.opposite, out:Ts.agent.out, random: aios0.random, rd:Ts.agent.rd, rm:Ts.agent.rm, Port: Sec.Port, position: function () {return current.node.position}, Private: Sec.Private, privilege: function () {return 1}, reverse:aios0.reverse, security: Sec, send:Sig.agent.send, sendto:Sig.agent.sendto, sleep:Sig.agent.sleep, sort:aios0.sort, store:Ts.agent.store, string:aios0.string, sum:aios0.sum, tail:aios0.tail, test:Ts.agent.exists, time:aios0.time, timer:Sig.agent.timer, trans:Conf.agent.trans, try_alt:Ts.agent.try.alt, try_inp:Ts.agent.try.inp, try_rd:Ts.agent.try.rd, wakeup:Sig.agent.wakeup, zero:aios0.zero, B:B, CP:CP, I:I, L:L, RT:RT, Vector:aios0.Vector, DIR:Mobi.agent.DIR, Math:Math }; // Sandbox module environment for agents (level 2): Trusted with extended privileges var aios2 = { abs:aios0.abs, add:aios0.add, act:Conf.agent.act, alt:Ts.agent.alt, broadcast:Sig.agent.broadcast, Capability: Sec.Capability, clock: clock, collect:Ts.agent.collect, concat:aios0.concat, contains:aios0.contains, copy:aios0.copy, copyto:Ts.agent.copyto, create: function(ac,args,level) { var process=none; if (level==undefined) level=2; if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) { current.error='Invalid argument: Agent arguments is neither array nor object'; throw 'CREATE'; }; if (current.world.classes[ac] && current.world.classes[ac][level]) process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac); else if (current.process.agent.subclass && current.process.agent.subclass[ac]) { process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac); } else { current.error='Invalid argument: Unknown agent class '+ac; throw 'CREATE'; } if (process) { process.agent.ac=ac; if (current.process!=none && process.gid==none) { process.gid=current.process.pid; if (process.agent.parent==_ || process.agent.parent==none) process.agent.parent=current.process.agent.id; } return process.agent.id; } else return none; }, div: aios0.div, dump: aios0.dump, empty:aios0.empty, evaluate:Ts.agent.evaluate, equal:aios0.equal, exists:Ts.agent.exists, Export:function (name,code) { current.node.export(name,code) }, filter:aios0.filter, fork:function (parameter) {var process = current.process.fork(parameter); return process.agent.id}, head:aios0.head, id:aidgen, Import:function (name) { return current.node.import(name) }, info:aios0.info, inp:Ts.agent.inp, int: aios0.int, isin: aios0.isin, iter:aios0.iter, kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)}, length: aios0.length, link:function (dir) {return current.world.connected(dir,current.node)}, listen:Ts.agent.listen, log:aios0.log, max:aios0.max, me:aios0.me, ml:Ml.agent, min:aios0.min, myClass:aios0.myClass, myNode:aios0.myNode, myParent:aios0.myParent, mark:Ts.agent.mark, map:aios0.map, matrix:aios0.matrix, moveto:Mobi.agent.move, neg:aios0.neg, negotiate:function (res,val,cap) { return negotiate(2,res,val,cap) }, nn:Nn.agent, opposite:Mobi.agent.opposite, out:Ts.agent.out, random: aios0.random, rd:Ts.agent.rd, reverse:aios0.reverse, rm:Ts.agent.rm, Port: Sec.Port, position: function () {return current.node.position}, Private: Sec.Private, privilege: function () {return 2}, security: Sec, send:Sig.agent.send, sendto:Sig.agent.sendto, sleep:Sig.agent.sleep, sort:aios0.sort, store:Ts.agent.store, string:aios0.string, sum:aios0.sum, tail:aios0.tail, test:Ts.agent.exists, time:aios0.time, timer:Sig.agent.timer, trans:Conf.agent.trans, try_alt:Ts.agent.try.alt, try_inp:Ts.agent.try.inp, try_rd:Ts.agent.try.rd, wakeup:Sig.agent.wakeup, zero:aios0.zero, B:B, CP:CP, I:I, L:L, RT:RT, Vector:aios0.Vector, DIR:Mobi.agent.DIR, Math:Math, }; // Sandbox module environment for agents (level 3): Trusted with extended privileges, system level // May not migrate!! var aios3 = { abs:aios0.abs, add:aios0.add, act:Conf.agent.act, alt:Ts.agent.alt, broadcast:Sig.agent.broadcast, Capability: Sec.Capability, clock: clock, collect:Ts.agent.collect, connectTo:function (dir,options) { // Connect this node with another node using a virtual or physical channel link var node=current.node, world=current.world; if (!dir || !dir.tag) throw('CONNECT'); world.connectTo(dir,node,options); }, concat:aios0.concat, contains:aios0.contains, copy:aios0.copy, copyto:Ts.agent.copyto, create: aios2.create, div: aios0.div, dump: aios0.dump, empty:aios0.empty, equal:aios0.equal, evaluate:Ts.agent.evaluate, exists:Ts.agent.exists, Export:aios2.Export, filter:aios0.filter, fork:aios2.fork, head:aios0.head, id:aidgen, Import:aios2.Import, info:aios0.info, inp:Ts.agent.inp, int: aios0.int, isin: aios0.isin, iter:aios0.iter, kill:aios2.kill, length:aios0.length, link:aios2.link, listen:Ts.agent.listen, log:aios0.log, max:aios0.max, me:aios0.me, ml:Ml.agent, min:aios0.min, myClass:aios0.myClass, myNode:aios0.myNode, myParent:aios0.myParent, mark:Ts.agent.mark, map:aios0.map, matrix:aios0.matrix, moveto:function () {/* System level agents may not migrate ! */ current.error='ENOTSUPPORTED';throw 'MOVE';}, neg:aios0.neg, negotiate:function (res,val,cap) { return negotiate(3,res,val,cap) }, nn:Nn.agent, opposite:Mobi.agent.opposite, out:Ts.agent.out, random: aios0.random, rd:Ts.agent.rd, rm:Ts.agent.rm, Port: Sec.Port, position: function () {return current.node.position}, Private: Sec.Private, privilege: function () {return 3}, reverse:aios0.reverse, send:Sig.agent.send, sendto:Sig.agent.sendto, sleep:aios0.sleep, sort:aios0.sort, store:Ts.agent.store, string:aios0.string, sum:aios0.sum, tail:aios0.tail, test:Ts.agent.exists, time:aios0.time, timer:Sig.agent.timer, trans:Conf.agent.trans, try_alt:Ts.agent.try.alt, try_inp:Ts.agent.try.inp, try_rd:Ts.agent.try.rd, wakeup:Sig.agent.wakeup, zero:aios0.zero, B:B, CP:CP, I:I, L:L, RT:RT, Vector:aios0.Vector, DIR:Mobi.agent.DIR, Math:Math, // Exucute an IO block sequence in an agent process context IOB: function (block) { var proc=current.process; setImmediate(function () { var index=0; function next (to) { var _proc=current.process,_node=current.node; if (to==none) { // done or failiure proc.mask.next=undefined; proc.wakeup(); return; } index=index+to; try { current.process=proc; current.node=proc.node; block[index].call(proc.agent); } catch (e) { logAgent('Caught IOB error: '+e); } current.process=_proc; current.node=_node; } proc.mask.next=next; next(0); }); proc.suspend(); } }; var aios = aios1; /* ** Agent code scheduling blocks can migrate ** - must be handled different from internal scheduling blocks! */ // Schedule linear sequence of functions that may block (suspending execution of current agent process). function B(block) { if (current.process.schedule.length==0) current.process.schedule = block; else current.process.schedule = Comp.array.concat(block,current.process.schedule); } /** Add pending callback call to process scheduling block * */ function CB(process,cb,args) { if (args) Comp.array.push(process.schedule,function () { cb.apply(this,args) }); else Comp.array.push(process.schedule,cb); } /** Agent process activity check pointing (injected in loops/functions) * */ function CP() { if (current.process.runtime && (current.process.runtime+current.world.lag-Date.now())<0) throw "SCHEDULE"; return true; } /** Agent exception checker; agents may not consume scheduler/watchdog exceptions!! */ function RT(e) { if (['WATCHDOG','SCHEDULE'].indexOf(e.toString())!=-1) throw(e); } /** Schedule an object iteration sequence that may block (suspending execution of current agent process). * */ function I(obj,next,block,finalize) { /* ** Iterate and schedule a block * obj: [] * next: function(next) {} */ var index=0; var length=obj.length; var iterator = [ function() { next(obj[index]); if (index\s*\(/gm, '{anonymous}()@') .split('\n'); log(e); log('Stack Trace'); log('--------------------------------'); for(var i in stack) { if (i>0) { var line = stack[i]; if(line.indexOf('Module.',0)>=0) break; log(line); } } log('--------------------------------'); }; /** Emit event * function emit(@event,@arg1,..) */ function emit() { if (events[arguments[0]]) events[arguments[0]](arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]); } /** Try to get the source position of an error raised in an agent activity * */ function errorLocation(process,err) { try { var stack = err.stack.split('\n'); for (var i in stack) { var line=stack[i]; if (line.indexOf('at act.')>=0||line.indexOf('at F.act.')>=0) { return line.replace(/\([^\)]+\)/,'').replace(/\)/,''); } else if (line.indexOf('at trans.')>=0 || line.indexOf('at F.trans.')>=0) { return line.replace(/\([^\)]+\)/,'').replace(/\)/,''); } } return ''; } catch (e) { return ''; } } // Execute a block scheduling function function exec_block_fun(next) { var fun = next[0]||next, argn = next.length-1; switch (argn) { case 0: case -1: fun(); break; case 1: fun(next[1]); break; case 2: fun(next[1],next[2]); break; case 3: fun(next[1],next[2],next[3]); break; case 4: fun(next[1],next[2],next[3],next[4]); break; case 5: fun(next[1],next[2],next[3],next[4],next[5]); break; case 6: fun(next[1],next[2],next[3],next[4],next[5],next[6]); break; case 7: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7]); break; case 8: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8]); break; case 9: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8],next[9]); break; default: // TODO: fun.apply(undefined,next.slice(1)) Io.err('Aios.exec_block_fun: more than 9 function arguments'); } } /** Fork the current agent with an optional new set of parameters. * */ function fork(parameters) { return current.process.fork(parameters); } /** Kill an agent (if agent identifier is undefined the current agent will be killed). * */ function kill(agent) { var process; if (!agent) { process=current.process; } else { process=current.node.processes.process(agent); } if (process) { process.kill=true; current.node.unregister(process); return true; } else return false; } function killOn(agent,node) { var process; process=node.processes.process(agent); if (process) { process.kill=true; node.unregister(process); }; } /** Lock the global namespace. Disable inter-agent communication * by using the global namespace => Sandbox (level 2) * */ function lock() { Object.preventExtensions(global); } /** Execute agent processes until there are no more schedulable agents. * Loop returns if there are no more runnable agents. If there are waiting * agent processes, the loop will be rescheduled on the earliest time event. * */ function loop(services) { var nexttime = scheduler(services); if (nexttime>0) { // Nothing to do. // Sleep until next event and re-enter the scheduling loop. if (options.verbose>2) log('[LOOP '+current.node.id+'] next schedule on '+ nexttime); timer=setTimeout(function () {loop (services)},nexttime-time()); } } function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; /** Call agent exception handler. If exception was handled by agent return true, otherwise false. * */ function handleException(process,exc,arg1,arg2,arg3,arg4) { var agent=process.agent; if (Aios.watchdog && Aios.watchdog.protect) { try { Aios.watchdog.protect(function () {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)})} catch(e) { // If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated! if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')'); process.kill=true return false; }; } else try {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)} catch(e) { // If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated! if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')'); process.kill=true return false; } return true; } /** Agent resource constraint negotiation * */ function negotiate (level,resource,value,cap) { var obj; // Check capability rights function checkRights(r) { return (level > 1) || (cap && Net.prv_rights_check(cap.cap_priv,Aios.current.node.random[cap.cap_port],r)) } switch (resource) { case 'CPU': if (!checkRights(Net.Rights.NEG_CPU)) return false; if (value>options.TIMEPOOL) current.process.resources.CPU=value; break; case 'SCHED': case 'SCHEDULE': if (!checkRights(Net.Rights.NEG_SCHED)) return false; if (value>options.TIMESCHED) current.process.resources.SCHED=value; break; case 'MEM': case 'MEMORY': if (!checkRights(Net.Rights.NEG_RES)) return false; if (value>options.MEMPOOL) current.process.resources.MEM=value; break; case 'TS': if (!checkRights(Net.Rights.NEG_RES)) return false; if (value>options.TSPOOL) current.process.resources.TS=value; break; case 'AGENT': if (!checkRights(Net.Rights.NEG_RES)) return false; if (value>options.AGENTPOOL) current.process.resources.AGENT=value; break; case 'LEVEL': if (!checkRights(Net.Rights.NEG_LEVEL)) return false; // Extend process mask switch (value) { case 1: break; case 2: break; } break; case '?': obj=Comp.obj.copy(current.process.resources); Comp.obj.extend(obj,{ SCHED: current.process.resources.SCHED||options.TIMESCHED, CPU: current.process.resources.CPU||options.TIMEPOOL, MEM: current.process.resources.MEM||options.MEMPOOL, TS: current.process.resources.TS||options.TSPOOL, AGENT: current.process.resources.AGENT||options.AGENTPOOL, }); return obj; break; default: return false; } return true; } /** Event callback management * */ function off(event) { // TODO: care of function chains?? events[event]=undefined; } function on(event,fun) { if (events[event]) { // Implement callback function chain var funorig=events[event]; events[event]=function () { funorig.apply(this,arguments); fun.apply(this,arguments); }; } else events[event]=fun; } function out(str) {log(str)}; /** Get current resource allocation of process memory * */ function resource(r0) { var r; if (!options.useproc) return 0; // Time expensive operation: requires system call and a lot of internal computation r=process.memoryUsage(); // console.log(r) if (r0==undefined) return {r:r.rss-r.heapTotal,h:r.heapUsed}; else return int((Math.max(0,r.rss-r.heapTotal-r0.r)+Math.max(0,r.heapUsed-r0.h))/1024); } /** Scheduling function for one agent process. * * Scheduling order: * 1. Process Blocks (process.block, passed to global DOS scheduler) * 2. Signals (process.signals, handled by AIOS scheduler) * 3. Transition (process.transition==true, handled by AIOS scheduler) * 4. Agent Blocks (process.schedule, handled by AIOS scheduler) * 5. Activity (handled by AIOS scheduler) * */ var SA = { NOOP:0, BLOCK:1, NORES:2, SIG:3, TRANS:4, SCHED:5, ACT:6, print: function (op) { switch (op) { case SA.NOOP: return 'NOOP'; case SA.BLOCK: return 'BLOCK'; case SA.NORES: return 'NORES'; case SA.SIG: return 'SIG'; case SA.TRANS: return 'TRANS'; case SA.SCHED: return 'SCHED'; case SA.ACT: return 'ACT'; } } } // One scheduler run function schedule(process) { var exec,sig,start,delta,next, _current, node=current.node, agent=process.agent, action='', op=SA.NOOP, handled, exception, r0; ticks++; // move to scheduler ??? // console.log(process); assert((process.agent!=undefined && process.id=='agent')||('Aios.schedule: not an agent process: '+process.id)); /* Order of operation selection: ** 0. Process (internal) block scheduling [block] ** 1. Resource exception handling ** 2. Signal handling [signals] ** - Signals only handled if process priority < HIGH ** - Signal handling increase proecss priority to enable act scheduling! ** 3. Transition execution ** 4. Agent schedule block execution [schedule] ** 5. Next activity execution */ if (process.blocked || (process.suspended==true && process.block.length==0 && process.signals.length==0) || process.dead==true || (agent.next==none && process.signals.length==0 && process.schedule.length == 0)) op=SA.NOOP; // if (process.suspended==true && process.schedule.length==0 && process.signals.length==0) op=SA.NOOP; else if (!process.blocked && process.block.length > 0) op=SA.BLOCK; else if (!options.nolimits && (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL) || process.resources.memory>(process.resources.MEM||options.MEMPOOL))) op=SA.NORES; else if (process.priority0) op=SA.SIG; else if (!process.suspended && process.transition) op=SA.TRANS; else if (!process.suspended && process.schedule.length > 0) op=SA.SCHED; else if (!process.suspended) op=SA.ACT; if (options.verbose>2) print('[SCH] '+time()+' '+process.agent.id+' : '+ SA.print(op)+' [susp='+process.suspended+ ',trans='+process.transition+',tmo='+process.timeout+']'); if (op==SA.NOOP) return 0; start=time(); if (Aios.watchdog) Aios.watchdog.start(process.resources.SCHED||options.TIMESCHED); else if (!options.nolimits) process.runtime=start-current.world.lag+(process.resources.SCHED||options.TIMESCHED); if (!options.nolimits) r0=resource(); // Start resource monitor current.process=process; current.error=none; if (current.scheduler) _current=current.scheduler.SetCurrent(process); try { switch (op) { case SA.BLOCK: // An internal schedule block [Linear/Loop] // Pass to global scheduler // console.log(process.block) schedule_block(process); break; case SA.NORES: throw 'EOL'; break; case SA.SIG: /* Execute a signal handler ** 1. A signal handler can wakeup a suspended agent process by calling wakeup() ** 2. A signal handler can wakeup a suspended agent process by modifying variables and satisfying the current ** transition condition resulting in an activity transition! */ if (!process.suspended && !process.transition) process.priority++; // Pending activity execution -> block signal handling temporarily action='signal'; sig=Comp.array.pop(process.signals); try { // sig=[signal,argument?,from?] agent.on[sig[0]].call(agent,sig[1],sig[2]); if (process.suspended && process.transition) process.suspended=false; // ==> 2.) } catch(e) { if (!agent.on[sig[0]]) logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] not defined, ignoring signal.'); else logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] failed: '+e+ (current.error?' / '+current.error:'')+', in: \n'+Code.print(agent.on[sig[0]])+ +errorLocation(process,e)) current.error=none; process.kill=true; // Always? }; Aios.emit('signal+',process,node,sig[0],sig[1],sig[2]); break; case SA.TRANS: // Pending next computation: Compute next transition after wakeup or after a signal was handled. // If still not successfull, suspend agent process. try { action='transition'; next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; // TODO: check blocking state - transitions may not block! if (next) { agent.next=next; process.suspended=false; process.transition=false; } else { process.suspended=true; } } catch (e) { if (agent.trans[agent.next]==undefined) logAIOS ('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].'); else logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+ ' failed:\n'+e+(current.error?' / '+current.error:'')+ +errorLocation(process,e)); process.kill=true; current.error=none; } break; case SA.SCHED: // An agent schedule block function [Linear/Loop] executed in agent context action='block'; exec = Comp.array.pop(process.schedule); Aios.watchdog&&Aios.watchdog.protect?Aios.watchdog.protect(exec.bind(agent)):exec.call(agent); if (!process.kill && !process.suspended && process.schedule.length == 0) { // next=agent.trans[agent.next].call(agent); next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; if (!next) process.suspend(0,true); // no current transition enabled; suspend process else agent.next=next; } break; case SA.ACT: // Normal activity execution // console.log('[SCH] next:'+agent.next) if (process.priority==Proc.PRIO.HIGH) process.priority--; action='activity'; if (agent.next==none) throw 'KILL'; Aios.watchdog&&Aios.watchdog.protect?Aios.watchdog.protect(agent.act[agent.next].bind(agent)):agent.act[agent.next].call(agent); if (!process.kill && !process.suspended && process.schedule.length == 0) { action='transition'; // next=agent.trans[agent.next].call(agent); next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; // TODO: check blocking state - transitions may not block! if (!next) process.suspend(0,true); // no current transition enabled; suspend process else agent.next=next; } break; } } catch (e) { if (Aios.watchdog) Aios.watchdog.stop(); exception=true; switch (e) { case 'SCHEDULE': case 'WATCHDOG': e='SCHEDULE'; if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); else process.runtime=time()-current.world.lag+options.TIMESCHED/10; handleException(process,'error',e,options.TIMESCHED,agent.next); break; case 'EOL': if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); else process.runtime=time()-current.world.lag+options.TIMESCHED/10; // New time or memory contingent must be negotiated based on policy! if (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL)) { handleException(process,'error',e,process.resources.consumed,agent.next); if (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL)) process.kill=true; } else if (process.resources.memory>(process.resources.MEM||options.MEMPOOL)) { handleException(process,'error','EOM',process.resources.memory,agent.next); if (process.resources.memory>(process.resources.MEM||options.MEMPOOL)) process.kill=true; } else { // TODO generic resource overflow? handleException(process,'error','EOR',0,agent.next); process.kill=true; } break; case 'KILL': if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); else process.runtime=time()-current.world.lag+options.TIMESCHED/10; handleException(process,'exit'); process.kill=true; break; default: if (agent.act[agent.next]==undefined) logAIOS('Activity '+agent.next+' not defined in agent '+ agent.id+' ['+agent.ac+'].'); else if (agent.trans[agent.next]==undefined) logAIOS('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].'); else { handled=handleException(process,aiosExceptions.indexOf(e.toString())!=-1?e:'error',e,current.error,agent.next); if (!handled && options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in '+(action=='block'?'block in':action)+' '+ (action=='signal'?sig[0]:agent.next)+ ' failed: Error '+e+(current.error?('; '+current.error):'')+ ', in code: \n'+( action=='activity'?Code.print(agent.act[agent.next]): (action=='transition'?Code.print(agent.trans[agent.next]): (agent.on && sig && agent.on[sig[0]])?Code.print(agent.on[sig[0]]):'none') )+ errorLocation(process,e)); if (!handled && options.verbose>1 && ['CREATE','MOVE','SIGNAL'].indexOf(e) == -1) Io.printstack(e); } if (!handled) process.kill=true; current.error=none; } } if (Aios.watchdog) Aios.watchdog.stop(); else process.runtime=0; if (!options.nostats) { delta=(time()-start)||options.MINCOST; process.resources.consumed += delta; process.resources.memory += resource(r0); current.node.stats.cpu += delta; } if (options.verbose && exception && process.kill) logAIOS('Killed agent '+agent.id); if (current.scheduler) current.scheduler.SetCurrent(_current); current.process=none; if (options.verbose>2) print(time()+' <- '+process.print()); return 1; } /** * Internal block scheduling */ function schedule_block(process) { var next; /* ** Process current function block sequence first! ** Format: [[fun,arg1,arg2,...],[block2], [block3], ..] ** Simplified: [fun,fun,...] */ if (!process.blocked) { next = process.block[0]; process.block.splice(0,1); /* ** Do no execute handler blocks maybe at the end of a subsection ** of the block list. */ while (!Comp.array.empty(process.block) && next.handler!=undefined) { next = process.block[0]; process.block.splice(0,1); } if (next.handler==undefined) { try {exec_block_fun(next)} catch(e) { /* ** Iterate through the block list and try to find a handler entry. */ while (next.handler==undefined && !Comp.array.empty(process.block)) { next = process.block[0]; process.block.splice(0,1); } if (next.handler!=undefined) { /* ** Call handler ... */ // console.log(next.handler.toString()) try {exec_block_fun([next.handler,e])} catch (e) { Io.out('Aios.schedule_block [Internal B], in agent context '+ process.agent.id+', got exception in exception handler: '+e); // Io.printstack(e); Io.out(Json.stringify(next).replace(/\\n/g,'\n')); }; } else { logAIOS ('Agent '+process.agent.id+' ['+process.agent.ac+'] in activity '+ process.agent.next+ ' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+ Code.print(process.agent.act[process.agent.next]))+ '');// '\nat:\n'+Io.sprintstack(e))); process.kill=true; current.error=none; } } } } } /** Main scheduler entry. * Returns the next event time, negative number of scheduled agent processes, or zero. * If result is negative, the scheduler should be executed immediately again because there * can be pending agent signals created in the current run. */ function scheduler(services) { var scheduled=0,run=1,nexttime=0,n=0,curtime,process,env,node,pro, timeout=time()+options.RUNTIME; while (run && (iterations==0 || n0) { remove=false; // 1.1. Check timers and execute runnable signaled agents Comp.array.iter(node.timers, function(timer,i) { if (timer && timer[1]<=curtime) { var process=timer[0], agent=process.agent, // Save original process state suspended=process.suspended, timeout=process.timeout; // process.suspeneded=false; ?? Signal handler can be executed even with blocked process process.signals.push([timer[2],timer[3],agent.id]); // TODO: A wakeup call in the signal handler re-enters schedule() !!! run += schedule(process); curtime=time()-current.world.lag; remove=true; node.timers[i]=undefined; // Restore original process state //process.suspended=suspended; ?? process.timeout=timeout; } else if (timer) nexttime=min0(nexttime,timer[1]); }); // 1.2. Timer destruction if (remove) node.timers= Comp.array.filter(node.timers,function (timer) { return timer!=undefined; }); } curtime=time()-current.world.lag; // Node service management (caches, TS) node.service(curtime); // 3. Agent process management for (pro in node.processes.table) { if (node.processes.table[pro]) { // 2.1 Agent execution curtime=time()-current.world.lag; process=node.processes.table[pro]; // Io.out('scheduler: checking '+process.agent.id+': '+process.suspended+' '+process.timeout); if (process.suspended && process.timeout && process.timeout<=curtime) { // Io.out('scheduler: waking up '+process.agent.id); process.wakeup(); } run += schedule(process); // 2.2 Agent destruction if (node.processes.table[pro] && node.processes.table[pro].kill) node.unregister(node.processes.table[pro]); if (node.processes.table[pro] && process.suspended && process.timeout>0) nexttime=min0(nexttime,process.timeout); } } } scheduled += run; } if (scheduled>0) return -scheduled; else if (nexttime>0) return nexttime; else return 0; } /* ** The time function can be changed, e.g., by simulators handling simulation ** steps instead of real time. Can be changed with Aios.config({time:fun}), ** updating all Aios/aiosX references and CP as well. */ var time = function () {return Math.ceil(Date.now())}; var Aios = { aidgen:aidgen, aios:aios1, aios0:aios0, aios1:aios1, aios2:aios2, aios3:aios3, aiosEvents:aiosEvents, callback:undefined, clock:clock, collect:Ts.agent.collect, // External API: Change AIOS settings only using config! config:config, current:current, emit:emit, // Emit event err: function (msg) {if (options.verbose) log('Error: '+msg)}, fork:fork, kill:kill, killOn:killOn, lock:lock, loop:loop, log:log, // Generic AIOS logging function logAgent:logAgent, // Agent message logging (with details about current) logAIOS:logAIOS, // AIOS logging function related with agent proecssing (with details about current) ml:Ml.agent, off:off, // Remove event handler on:on, // Add event handler options:options, print:Io.out, // Print function for agent messages via Aios.aiosX.log and internal Aios.log; // OR if printAgent is set only AIOS internal messages; can be modified by host app printAgent:undefined, // Print function for agent messages only via Aios.aiosX.log; can be modified by host app schedule:schedule, scheduler:scheduler, ticks:function (v) { if (v!=undefined) ticks=v; else return ticks}, time:time, timeout:function (tmo) { return tmo>0?Aios.time()-current.world.lag+tmo:0 }, // Compute absolute time from relative timeout Chan:Chan, Code:Code, Mobi:Mobi, Name:Name, Node:Node, Proc:Proc, Sec:Sec, Sig:Sig, Simu:Simu, Ts:Ts, World:World, CB:CB, CP:CP, RT:RT, B:B, DIR:Mobi.DIR, I:I, L:L, warn: function (msg) {if (options.verbose>1) log('Warning: '+msg)}, watchdog: undefined } // Builtin watchdog support by JS VM platform? if (watchdog && watchdog.start) Aios.watchdog=watchdog; if (watchdog && watchdog.init) watchdog.init('WATCHDOG'); if (watchdog && watchdog.checkPoint) { // only partial watchdog support by platform aios0.CP=watchdog.checkPoint; aios1.CP=watchdog.checkPoint; aios2.CP=watchdog.checkPoint; aios3.CP=watchdog.checkPoint; Aios.CP=watchdog.checkPoint; } Conf.current(Aios); Code.current(Aios); Sig.current(Aios); Sec.current(Aios); Ts.current(Aios); Proc.current(Aios); Node.current(Aios); World.current(Aios); Mobi.current(Aios); if (Simu) Simu.current(Aios); Chan.current(Aios); Json.current(Aios); Ml.current(Aios); Nn.current(Aios); module.exports = Aios; }; BundleModuleCode['com/pwgen']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Bermi Ferrer, Stefan Bosse ** $INITIAL: (C) 2011-2015 Bermi Ferrer , 2017-2018 Stefan Bosse ** $REVESIO: 1.3.1 ** ** $INFO: * * password-generator using crypto random number generation (slow,HQ) * !using built-in crypto random generators using either native crypto module or polyfill! * * options = {length,memorable,lowercase,uppercase,pattern,number?:boolean,range?:[]} * * Using always twister random byte generator (not random byte array) * * $ENDINFO */ var Crypto = Require('os/crypto.rand'); // Require('crypto'); module.exports.generate = function (options) { function numgen (options) { // assuming byte number range 0-255 var arr = new Uint8Array(options.length||8); getRandomValues(arr); return arr; } function pwgen (options) { var localName, consonant, letter, vowel, pattern = options.pattern, char = "", n, i, validChars = [], prefix=options.prefix; letter = /[a-zA-Z]$/; vowel = /[aeiouAEIOU]$/; consonant = /[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]$/; if (options.length == null) { options.length = 10; } if (pattern == null) { pattern = /\w/; } if (prefix == null) { prefix = ''; } // Non memorable passwords will pick characters from a pre-generated // list of characters if (!options.memorable) { for (i = 33; 126 > i; i += 1) { char = String.fromCharCode(i); if (char.match(pattern)) { validChars.push(char); } } if (!validChars.length) { throw new Error("Could not find characters that match the " + "password pattern " + pattern + ". Patterns must match individual " + "characters, not the password as a whole."); } } while (prefix.length < options.length) { if (options.memorable) { if (prefix.match(consonant)) { pattern = vowel; } else { pattern = consonant; } n = Crypto.randomByte(33,126); // rand(33, 126); char = String.fromCharCode(n); } else { char = validChars[rand(0, validChars.length)]; } if (options.lowercase) char = char.toLowerCase(); else if (options.uppercase) char = char.toUpperCase(); if (char.match(pattern)) { prefix = "" + prefix + char; } } return prefix; }; function rand(min, max) { var key, value, arr = new Uint8Array(max); getRandomValues(arr); for (key in arr) { if (arr.hasOwnProperty(key)) { value = arr[key]; if (value > min && value < max) { return value; } } } return rand(min, max); } function getRandomValues(buf) { var bytes = Crypto.randomBytes(buf.length); buf.set(bytes); } if (options.number) return numgen(options) else return pwgen(options); }; }; BundleModuleCode['os/crypto.rand']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 15-1-16 by sbosse. ** $VERSION: 1.2.4 ** ** $INFO: ** ** Crypto module with HQ random number generators (replacing not available crypto.getRandomValues ** if there is no global crypto module). ** ** $ENDOFINFO */ var crypto = global.crypto || global.msCrypto; if (!crypto && typeof require != 'undefined') try { crypto=global.crypto=require('require') } catch (e) {}; var twister; var MersenneTwister = function(seed) { if (seed == undefined) { /** ** It is not sure that Math.random is seeded randomly ** Thus, a combination of current system time and Math.random ** is used to seed and initialize this random generator */ seed = new Date().getTime(); seed *= Math.random()*91713; seed |= 0; } /* Period parameters */ this.N = 624; this.M = 397; this.MATRIX_A = 0x9908b0df; /* constant vector a */ this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ this.mt = new Array(this.N); /* the array for the state vector */ this.mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */ if (seed.constructor == Array) { this.init_by_array(seed, seed.length); } else { this.init_seed(seed); } } /* initializes mt[N] with a seed */ /* origin name init_genrand */ MersenneTwister.prototype.init_seed = function(s) { this.mt[0] = s >>> 0; for (this.mti=1; this.mti>> 30); this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ this.mt[this.mti] >>>= 0; /* for >32 bit machines */ } } /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ MersenneTwister.prototype.init_by_array = function(init_key, key_length) { var i, j, k; this.init_seed(19650218); i=1; j=0; k = (this.N>key_length ? this.N : key_length); for (; k; k--) { var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30) this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + init_key[j] + j; /* non linear */ this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ i++; j++; if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } if (j>=key_length) j=0; } for (k=this.N-1; k; k--) { var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; /* non linear */ this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ i++; if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } } this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ } /* generates a random number on [0,0xffffffff]-interval */ /* origin name genrand_int32 */ MersenneTwister.prototype.random_int = function() { var y; var mag01 = new Array(0x0, this.MATRIX_A); /* mag01[x] = x * MATRIX_A for x=0,1 */ if (this.mti >= this.N) { /* generate N words at one time */ var kk; if (this.mti == this.N+1) /* if init_seed() has not been called, */ this.init_seed(5489); /* a default initial seed is used */ for (kk=0;kk>> 1) ^ mag01[y & 0x1]; } for (;kk>> 1) ^ mag01[y & 0x1]; } y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1]; this.mti = 0; } y = this.mt[this.mti++]; /* Tempering */ y ^= (y >>> 11); y ^= (y << 7) & 0x9d2c5680; y ^= (y << 15) & 0xefc60000; y ^= (y >>> 18); return y >>> 0; } /* generates a random number on [0,0x7fffffff]-interval */ /* origin name genrand_int31 */ MersenneTwister.prototype.random_int31 = function() { return (this.random_int()>>>1); } /* generates a random number on [0,1]-real-interval */ /* origin name genrand_real1 */ MersenneTwister.prototype.random_incl = function() { return this.random_int()*(1.0/4294967295.0); /* divided by 2^32-1 */ } /* generates a random number on [0,1)-real-interval */ MersenneTwister.prototype.random = function() { return this.random_int()*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on (0,1)-real-interval */ /* origin name genrand_real3 */ MersenneTwister.prototype.random_excl = function() { return (this.random_int() + 0.5)*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on [0,1) with 53-bit resolution*/ /* origin name genrand_res53 */ MersenneTwister.prototype.random_long = function() { var a=this.random_int()>>>5, b=this.random_int()>>>6; return(a*67108864.0+b)*(1.0/9007199254740992.0); } function polyfill () { twister = new MersenneTwister(); // (Math.random()*Number.MAX_SAFE_INTEGER)|0) if (!crypto) crypto=global.crypto={}; crypto.getRandomValues = function getRandomValues (abv) { var l = abv.length while (l--) { abv[l] = Math.floor(twister.random() * 256) } return abv } if (!global.Uint8Array && !Uint8Array) throw new Error('crypto.rand: No Uint8Array found!'); if (!global.Uint8Array) global.Uint8Array=Uint8Array; } function randomByte (min,max) { if (!twister) twister = new MersenneTwister(); return Math.floor(twister.random() * (max-min))+min; } function randomBytes (size, cb) { // phantomjs needs to throw if (size > 65536) throw new Error('requested too many random bytes') if (!crypto || !crypto.getRandomValues) polyfill(); // in case browserify isn't using the Uint8Array version var rawBytes = new global.Uint8Array(size); // This will not work in older browsers. // See https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues if (size > 0) { // getRandomValues fails on IE if size == 0 crypto.getRandomValues(rawBytes); } // phantomjs doesn't like a buffer being passed here var bytes = new Buffer(rawBytes); if (typeof cb === 'function') { cb(null, bytes) } return bytes } module.exports = { randomByte:randomByte, randomBytes:randomBytes } }; BundleModuleCode['jam/conf']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2017 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: conf.js,v 1.2 2017/05/23 07:00:43 sbosse Exp $ ** $VERSION: 1.1.5 ** ** $INFO: ** ** JavaScript AIOS Agent Reconfiguration Sub-System ** ** $ENDOFINFO */ var Json = Require('jam/jsonfn'); var Comp = Require('com/compat'); var current=none; var Aios = none; var act = { add: function (act,code) { current.process.agent.act[act]=code; // Add the new activity to the mask environment of the agent for further referencing. current.process.mask[act]=act; }, delete: function (act) { if(Comp.obj.isArray(act)) Comp.array.iter(act,function (a) {current.process.agent.act[a]=undefined}); else current.process.agent.act[act]=undefined }, update: function (act,code) { current.process.agent.act[act]=code; } }; var trans = { add: function (from,cond) { if (current.process.agent.trans[from]) { var regex1= /"function[\s]*\([\s]*\)[\s]*\{([^\}]+)\}"/; var regex2= /\\n/g; var old=Json.stringify(current.process.agent.trans[from]).replace(regex1,"$1").replace(regex2,""); var next=Json.stringify(cond).replace(regex1,"$1").replace(regex2,""); var merged='(function () {'+old+next+'})'; //console.log(merged) with(current.process.mask) { current.process.agent.trans[from]=eval(merged); } } else current.process.agent.trans[from]=cond; }, delete: function (trans) { if(Comp.obj.isArray(trans)) Comp.array.iter(trans,function (t) {current.process.agent.trans[t]=undefined}); else current.process.agent.trans[trans]=undefined }, update: function (from,cond) { current.process.agent.trans[from]=cond; } } module.exports = { agent:{ act:act, trans:trans }, current:function (module) { current=module.current; Aios=module; } } }; BundleModuleCode['jam/jsonfn']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Vadim Kiryukhin, Stefan Bosse ** $INITIAL: (C) 2006-2017 Vadim Kiryukhin ** $MODIFIED: by sbosse. ** $RCS: $Id: jsonfn.js,v 1.1 2017/05/20 15:56:53 sbosse Exp $ ** $VERSION: 1.1.7 ** ** $INFO: ** ** JSONfn - javascript (both node.js and browser) plugin to stringify, ** parse and clone objects with functions with masked context (mask). ** ** browser: ** JSONfn.stringify(obj); ** JSONfn.parse(str[, date2obj]); ** JSONfn.clone(obj[, date2obj]); ** ** nodejs: ** var JSONfn = require('path/to/json-fn'); ** JSONfn.stringify(obj); ** JSONfn.parse(str[, date2obj]); ** JSONfn.clone(obj[, date2obj]); ** ** ** @obj - Object; ** @str - String, which is returned by JSONfn.stringify() function; ** @date2obj - Boolean (optional); if true, date string in ISO8061 format ** is converted into a Date object; otherwise, it is left as a String. ** ** $ENDOFINFO */ var current=none; (function (exports) { exports.stringify = function (obj) { return JSON.stringify(obj, function (key, value) { if (value instanceof Function || typeof value == 'function') { return value.toString(true); // try minification (true) if supported } if (value instanceof RegExp) { return '_PxEgEr_' + value; } return value; }); }; exports.parse = function (str, mask) { var code; try { with (mask) { code= JSON.parse(str, function (key, value) { var prefix; try { if (typeof value != 'string') { return value; } if (value.length < 8) { return value; } prefix = value.substring(0, 8); if (prefix === 'function') { return eval('(' + value + ')'); } if (prefix === '_PxEgEr_') { return eval(value.slice(8)); } return value; } catch (e) { throw {error:e,value:value}; } }); }; } catch (e) { // within mask there was no current reference if (current) current.error=e.value||str; throw e.error||e; } return code; }; exports.clone = function (obj, date2obj) { return exports.parse(exports.stringify(obj), date2obj); }; exports.current =function (module) { current=module.current; }; }(typeof exports === 'undefined' ? (window.JSONfn = {}) : exports)); }; BundleModuleCode['jam/code']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: code.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.10.7 ** ** $INFO: ** ** JavaScript AIOS Agent Code Management Sub-System ** ** New: Check pointing (CP) can be replaced with JS VM watchdog timer. ** New: Fast sandboxed constructor ** New: Dual mode JSON+/JSOB (with auto detection in toCode) ** New: Dirty fastcopy process copy (JS object copy) ** New: Function toString with minification (if supported by platform) ** ** JSOB: Simplified and compact textual representation of JS object including function code ** ** $ENDOFINFO */ var options = { compactit:true, version:'1.10.7' } try { // Use built-in JS code minimizer if available var M = process.binding('minify'); Function.prototype._toString=Function.prototype.toString; Function.prototype.toString = function (compact) { return compact?M.minify(this._toString()):this._toString(); } } catch (e) {}; var Io = Require('com/io'); var Json = Require('jam/jsonfn'); var Comp = Require('com/compat'); var sandbox = Require('jam/sandbox')(); var current=none; var Aios = none; var util = Require('util'); /* Test if Json.stringify returns compacted code, otherwise text must be compacted here */ function _testac (p1,p2) { /* comment */ this.x = p1; this.y = p2; this.z = 0; this.act = { init: function () { /* comment */ this.z=this.x; this.x++; } } } var _testobj = new _testac(1,2); options.compactit=Json.stringify(_testobj).length>72; var inject = {cp:undefined,rt:undefined}; /** Construct an agent object with given arguments (array) * */ function construct(constructor,argArray) { var inst = Object.create(constructor.prototype); constructor.apply(inst, argArray); return inst; } /** Fast dirty copy (fork): Return a fresh copy of the agent object (i.e., process.agent, instead using ofCode/toCode transf.) * attached to a new process object. * All function and AIOS references will be copied as is. The AIOS level cannot be changed. The mask of the * parent process is now valid for the copied process, too. Any changes in the parent environment effects the child * process, too, and vice versa. * */ function copyProcess(process) { var _process,_agent,agent=process.agent,mask=process.mask; process.node.stats.fastcopy++; agent.process={}; for (var p in process) { switch (p) { case 'schedule': if (process.schedule.length > 0) agent.process[p]=process.schedule; // keep it only if <> [] break; case 'blocked': if (agent.process.suspended==true) agent.process[p]=true; // keep it only if it is true break; case 'gid': case 'pid': break; // ????? // case 'delta': case 'back': case 'dir': // keep it agent.process[p]=process[p]; break; } } if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; agent['self']=undefined; _agent=Comp.obj.copy(agent); if (!_agent.process) _process=Aios.Proc.Proc({mask:mask,agent:_agent}); else { _process=Aios.Proc.Proc(agent.process); _process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:_agent}); _agent['process']=undefined; } agent['self']=agent; return _process; } /** Return name of a class constructor function * */ function className (f) { var name=f.toString().match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); return name?name[1]:"unknown" } /** Create a sandboxed agent object process on the current node * using either a sandboxed agent constructor object {fun,mask} * or a generic agent class constructor function that is sandboxed here. * * Returns process object. * * type create = * function (node:node, * constructor:function|{fun:function,mask:{}}, * args:{}|[],level?:number,className?:string) -> process */ function create(constructor,args,level,className) { return createOn(current.node,constructor,args,level,className); } /** Create a sandboxed agent process on a specified node * using either a sandboxed agent constructor object {fun,mask} * or a generic agent class constructor function that is sandboxed here. * * * Returns process object. * * type createOn = * function (node:node, * constructor:function|{fun:function,mask:{}}, * args:{}|*[],level?:number,className?:string) -> process */ function createOn(node,constructor,args,level,className) { if (!constructor.fun && !Comp.obj.isFunction(constructor)) { Aios.err('Code.create: No valid constructor function specified.'); return; } var code, agent0, agent, process, _process; _process=current.process; current.process={timeout:0}; if (level==undefined) level=1; try { if (!constructor.fun) constructor=makeSandbox(constructor,level); if (!(args instanceof Array)) args=[args]; agent0= construct(constructor.fun,args); if (!agent0) { Aios.err('Code.createOn ('+className+'): Agent constructor failed.'); current.process=_process; return null; } process=makeProcess(agent0,constructor.mask); process.resources.memory=constructor.size||0; current.process=_process; } catch (e) { current.process=_process; Aios.err('Code.createOn ('+className+'): '+e); return; } agent=process.agent; if (!Comp.obj.isArray(args) && Comp.obj.isObject(args)) for (var p in args) { if (Comp.obj.hasProperty(agent,p)) agent[p]=args[p]; } // Test minimal structure requirements if (!agent['next'] || !agent['trans'] || !agent['act']) { Aios.err('Code.createOn: Missing next/trans/act attribute in agent constructor '+className); return none; // must be defined and initialized } process.level=level; agent['self']=agent; if (className) agent['ac']=className; node.register(process); node.stats.create++; return process; } /** Create a compiled agent process in a sandbox environment from an * agent class constructor function on the current node. * Returns JSON+/JSOB representation of agent process snapshot and * the newly created process. * */ function createAndReturn(constructor,ac,args,level) { if (!(args instanceof Array)) args=[args]; /* var code = ofCode({agent:new constructor(args[0],args[1],args[2],args[3], args[4],args[5],args[6],args[7], args[8],args[9])},true); */ var process,agent, code = ofCode({agent:construct(constructor,args)},true); if (level==undefined) level=1; process = toCode(code,level); agent=process.agent; agent.id=Aios.aidgen(); agent.ac=ac; return {code:ofCode(process,false),process:process}; } /** Fork an agent object and return JSON+/JSOB text code. * Note: Forking discards current scheduling blocks (in contrast to migration)!!! * * Returns cleaned code (w/o CP and internal AIOS properties). * */ function forkCode(process) { var code='',p; var agent = process.agent; var self = agent.self; // Clean up current agent process agent['process']=undefined; agent['self']=undefined; code=Aios.options.json?Json.stringify(agent):toString(agent); // Restore current agent process agent.process=process; agent.self=self; // Cleanup required? // CP/RT removal if (inject.cp || inject.rt) code=removeInjection(code); return code; } /** Convert agent object code from a process to text JSON+/JSOB. * Returns cleaned code (w/o CP and internal AIOS properties). * @clean: Code is already clean, no further filtering * */ function ofCode(process,clean) { var code='',p; var agent=process.agent; agent.process={}; for (var p in process) { switch (p) { case 'schedule': if (process.schedule.length > 0) agent.process[p]=process.schedule; // keep it only if <> [] break; case 'blocked': if (agent.process.suspended==true) agent.process[p]=true; // keep it only if it is true break; case 'gid': case 'pid': break; // ????? // case 'delta': case 'back': case 'dir': // keep it agent.process[p]=process[p]; break; } } if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; agent['self']=undefined; code=Aios.options.json?Json.stringify(agent):toString(agent); if (clean && !options.compactit) return code; /* Newline and comment removal is critical. We need to convert '\\''n' to '\n', ** replacing comments, finally removing '\n'. This should only be done one time ** on agent creation with compact=true option. Have top deal with '\\''\\''n', too! ** More complictaed, we must preserve newlines after blocks! */ if (!clean && (inject.cp||inject.rt)) // CP/RT removal; no or only partial watchdog support by platform code=removeInjection(code); if (options.compactit) code=minimize(code); return code; } /** Fast copy agent process creation (virtual, migrate). * All function and AIOS references will remain unchanged. The AIOS level cannot be changed. The mask of the * original (died) process is now valid for the new process, too. */ function ofObject(agent) { var process; if (!agent.process) process=Aios.Proc.Proc({mask:agent.mask,agent:agent}); else { process=Aios.Proc.Proc(agent.process); process.init({timeout:0,schedule:[],blocked:false,mask:agent.mask,agent:agent}); agent['process']=undefined; } agent['mask']=undefined; process.node.stats.fastcopy++; return process; } /** Convert agent text sources to agent code in JSOB format * */ function ofString(source,mask) { var code; try { // execute script in private context with (mask) { eval('"use strict"; code = '+source); } } catch (e) { console.log(e) }; return code; } /** Create an agent process from agent object code * */ function makeProcess (agent,mask) { var process; // Add all activities to the masked environment: if (mask) for(var p in agent.act) { mask[p]=p; } if (!agent.process) process=Aios.Proc.Proc({mask:mask,agent:agent}); else { process=Aios.Proc.Proc(agent.process); process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent}); agent['process']=undefined; } agent['self']=agent; return process; } /** Create a sandboxed agent class constructor object {fun,mask} from * an agent class template constructor function providing * a sandboxed agent constructor function and the sandbox * mask agent environment. * The optional environment object 'env' can contain additional references, e.g., * activitiy references. * * Note: All agents created using the constructor function share the same mask * object! * * typeof constructor = function|string * typeof sac = {fun:function, mask: {}, size:number } */ function makeSandbox (constructor,level,env) { var _process,sac,aios; switch (level) { case 0: aios=Aios.aios0; break; case 1: aios=Aios.aios1; break; case 2: aios=Aios.aios2; break; case 3: aios=Aios.aios3; break; default: aios=Aios.aios0; break; } _process=current.process; current.process={timeout:0}; sac=sandbox(constructor,aios,inject,env); current.process=_process; return sac; } /** Minimize code text * */ function minimize (code) { // Inline and multi-line comments var regex4= /\/\*([\S\s]*?)\*\//g; var regex5= /([^\\}])\\n/g; var regex6= /\/\/[^\n]+/g; // Newline after {},; var regex7= /[ ]*([{},; ]|else)[ ]*\n[\n]*/g; // Filter for string quotes var regex8= /([^\'"]+)|([\'"](?:[^\'"\\]|\\.)+[\'"])/g; // Multi-spaces reduction var regex9= / [ ]+/g; // relax } syntax errors after newline removal; exclude keywords! var regex10= /}\s+(?!else|finally|catch)([a-zA-Z_]+)/g; // relax ) syntax errors after newline removal var regex11= /\)\s+([a-zA-Z_]+)/g; code=code.replace(regex4,"") .replace(regex5,'$1\n') .replace(regex5,'$1\n') .replace(regex6,"") .replace(regex7,"$1") .replace(regex8, function($0, $1, $2) { if ($1) { return $1.replace(regex9,' ').replace(regex10,'};$1').replace(regex11,')\n$1'); } else { return $2; } }); return code; } /** Print agent code */ function print(agent) { var process = agent.process; var self = agent.self; agent['process']=undefined; agent['self']=undefined; var text=Aios.options.json?Json.stringify(agent):toString(agent); agent.process=process; agent.self=self; if (!text) return 'undefined'; var regex4= /\\n/g; if (inject.cp || inject.rt) // CP/RT removal; no or only partial watchdog support by platform text= removeInjection(text); return text.replace(regex4,'\n'); } /** Remove CP/RT injections from code text * */ function removeInjection(text) { // CP removal if (inject.cp) { var regex1= /CP\(\);/g; var regex2= /\(\(([^\)]+)\)\s&&\sCP\(\)\)/g; var regex3= /,CP\(\)/g; text=text.replace(regex1,"").replace(regex2,"($1)").replace(regex3,""); } // RT removal if (inject.rt) { var regex4= /RT\(\);/g; text=text.replace(regex4,""); } return text; } /** Returns size of cleaned code (w/o CP and internal AIOS properties). * */ function size(agent) { var text='',p; var process = agent.process; var self = agent.self; agent['process']=undefined; agent['self']=undefined; text=Aios.options.json?Json.stringify(agent):toString(agent); agent.process=process; agent.self=self; if (inject.cp || inject.rt) { text=removeInjection(text); } return text.length; } /** Convert JSON+/or JSOB text to an agent object process encapsulated in a sandbox (aios access only). * Returns process container with CP injected agent code (process.agent). * * CP Injection (required on generic JS VM platform w/o watchdog, e.g., node.js, browser): * 1. In all loop expressions (for/while) * 2. In all function bodies (start) * * No watchdog: Aios.watchdog == undefined (nodes.js, browser) * Full watchdog implementation: Aios.watchdog && Aios.watchdog.checkPoint==undefined (jvm) * Partial watchdog implementation with checkPoint function: Aios.watchdog.checkPoint (jxcore) * * */ function toCode(text,level) { var agent, process, p, aios, next; switch (level) { case undefined: case 0: aios=Aios.aios0; break; case 1: aios=Aios.aios1; break; case 2: aios=Aios.aios2; break; case 3: aios=Aios.aios3; break; default: aios=Aios.aios0; break; } if (inject.cp) { // CP injection; no or only partial watchdog support var regex1= /while[\s]*\(([^\)]+)\)/g; var regex2= /for[\s]*\(([^\)]+)\)/g; var regex3= /function([^\{]+)\{/g; text=text.replace(regex1,"while (($1) && CP())") .replace(regex2,"for ($1,CP())") .replace(regex3,"function $1{CP();"); } if (inject.rt) { // RT injection var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g; text=text.replace(regex4,'catch ($1) {'+inject.rt+'($1);'); } /* Set up an object to serve as the local context for the code ** being evaluated. The entire global scope must be masked out! ** Additionally, Json defines a variable current, which must be ** masked, too. */ var mask = {current:undefined}; // mask local properties for (p in this) mask[p] = undefined; // mask global properties for (p in global) mask[p] = undefined; // add sandbox content for (p in aios) { mask[p]=aios[p]; } // Auto detect JSON+ / RAWOBJ format var isjson=Comp.string.startsWith(text,'{"'); try {agent=isjson?Json.parse(text,mask):ofString(text,mask);} catch (e) { if (Aios.options.verbose) Aios.log('Aios.code.toCode: '+e+(current.error?(',\nin: '+current.error):'')); return null; } if (!agent) { return Aios.log('Aios.code.toCode: Invalid agent code received (empty or invalid source text?)'); } // Add all activities to the masked environment: for(var p in agent.act) { mask[p]=p; } if (!agent.process) process=Aios.Proc.Proc({mask:mask,agent:agent}); else { process=Aios.Proc.Proc(agent.process); process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent}); agent['process']=undefined; } process.level=level; process.resources.memory=text.length; agent['self']=agent; return process; } /** Convert agent object (i.e., process.agent) to a snapshot object. * */ function toObject(process) { var _process,_agent,agent=process.agent,mask=process.mask; agent.process={}; for (var p in process) { switch (p) { case 'schedule': if (process.schedule.length > 0) agent.process[p]=process.schedule; // keep it only if <> [] break; case 'blocked': if (agent.process.suspended==true) agent.process[p]=true; // keep it only if it is true break; case 'gid': case 'pid': break; // ????? // case 'delta': case 'back': case 'dir': // keep it agent.process[p]=process[p]; break; } } if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; agent['self']=undefined; _agent=Comp.obj.copy(agent); _agent.mask = mask; agent['self']=agent; return _agent; } /** Convert agent object to text source in JSOB format * */ function toString(o) { var p,i,s='',sep; if (Comp.obj.isArray(o)) { s='[';sep=''; for(p in o) { s=s+sep+toString(o[p]); sep=','; } s+=']'; } else if (o instanceof Buffer) { s='[';sep=''; for(i=0;i0) continue; _mask = _mask + ',' + p; } for (p in modules) mask[p]=modules[p]; if (env) for (p in env) mask[p]=env[p]; if (typeof f == 'function') source = f.toString(true); // try minification (true) if supported else source=f; if (inject.cp) { // CP injection var regex1= /while[\s]*\(([^\)]+)\)/g; var regex2= /for[\s]*\(([^\)]+)\)/g; var regex3= /function([^\{]+)\{/g; source=source.replace(regex1,"while (($1) && "+inject.cp+"())") .replace(regex2,"for ($1,"+inject.cp+"())") .replace(regex3,"function $1{"+inject.cp+"();"); } if (inject.rt) { var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g; source=source.replace(regex4,'catch ($1) {'+inject.rt+'($1);'); } mask.eval=undefined;_mask += ',eval' var F = new Function(_mask,'"use strict"; with(this) { f=('+source+').bind(this)} return f') .bind(mask); return {fun:F(),mask:mask,_mask:_mask}; } module.exports = { sandbox:sandbox, Sandbox:Sandbox } module.exports = function () {return sandbox} }; BundleModuleCode['jam/sig']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 15/1/16 by sbosse. ** $RCS: $Id: sig.js,v 1.3 2017/06/19 17:18:39 sbosse Exp sbosse $ ** $VERSION: 1.3.1 ** ** $INFO: ** ** JavaScript AIOS Agent Signal Sub-System ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var current=none; var Aios = none; var options = { version:'1.3.1' } var sig = { broadcast: function (ac,range,sig,arg) { var delivered=0; // Currently only range=0 is supported => local agents if (!Comp.obj.isString(ac)) {current.error='broadcast, invalid class '+ac;throw 'SIGNAL'}; if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='broadcast, invalid signal '+sig;throw 'SIGNAL'}; if (range!=0) {current.error='broadcast, invalid range '+range;throw 'SIGNAL'}; for (var p in current.node.processes.table) { var proc=current.node.processes.table[p]; if (proc && proc.agent.ac == ac && proc.agent.on[sig]) { proc.signals.push([sig,arg,current.process.agent.id]); delivered++; } } return delivered; }, // 'to' is the destination agent id // 'from' indicates source agent id and remote signal propagation (from node.handle) send: function (to,sig,arg,from) { // Local agent? var pid=current.node.processes.lookup(to); if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='send, invalid signal';throw 'SIGNAL'}; current.node.stats.signal++; if (pid!=none) { // [sig,arg,from] current.node.processes.table[pid].signals.push([sig,arg,from||current.process.agent.id]); // ?? Aios.emit('schedule',current.node); return true; } else { // console.log('send',current.node.id,to,sig,arg,current.node.processes.gone[to]) // Agent migrated and still cached? if (current.node.processes.gone[to]) { var curtime=Aios.time()-current.world.lag; current.node.processes.gone[to].timeout=curtime+current.node.TMO; return route(current.node.processes.gone[to].dir, to,sig,arg,from||current.process.agent.id); } else if (current.node.signals[to]) { var curtime=Aios.time()-current.world.lag; current.node.signals[to].timeout=curtime+current.node.TMO; return route(current.node.signals[to].dir, to,sig,arg,from||current.process.agent.id); } } return false; }, // Send a signal to agents on a specific remote destination node, e.g., to=DIR.DELTA([-1,-2]) sendto: function (to,sig,arg) { var delivered=0,i; if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='sendto, invalid signal '+sig;throw 'SIGNAL'}; if ((to.tag||to).indexOf('DIR') != 0) {current.error='sendto, invalid destination '+to; throw 'SIGNAL'}; if (to == Aios.DIR.ORIGIN || (to.delta && Comp.array.zero(to.delta))) { if (sig=='TS.SIG') { // copy/collect from remote TS for(i in arg) { Aios.Ts.agent.out(arg[i]); } } else for (var p in current.node.processes.table) { var proc=current.node.processes.table[p]; if (proc && proc.agent.on && proc.agent.on[sig]) { proc.signals.push([sig,arg,current.process.agent.id]); delivered++; } } return delivered; } else { return route(to, none,sig,arg,current.process.agent.id); } }, sleep: function (tmo) { current.process.suspend(tmo?Aios.time()-current.world.lag+tmo:0); }, // Returns signal name timer: { // Add a oneshot or repeating timer raising a signal 'sig' after timeout 'tmo'. add : function (tmo,sig,arg,repeat) { if (!Comp.obj.isNumber(tmo)) {current.error='timer, invalid timeout '+tmo; throw 'SIGNAL'}; if (!Comp.obj.isString(sig)) {current.error='timer, invalid signal '+sig; throw 'SIGNAL'}; current.node.timers.push([current.process,(Aios.time()-current.world.lag+tmo),sig,arg,repeat]); return sig; }, delete: function (sig) { current.node.timers=current.node.timers.filter(function (t) { return t[2]!=sig }); } }, wakeup: function (process) { if (!process) current.process.wakeup(); else process.wakeup(); } } /** Route signal to next node * */ function route(dir,to,sig,arg,from) { var node1=current.node, chan=none, dest, stat, alive = function () {return 1}, sigobj = {sig:sig,to:to||dir,from:from,arg:arg,back:Aios.DIR.opposite(dir,true)}, msg; // console.log('route',node1.id,dir,sigobj) switch (dir.tag||dir) { case Aios.DIR.NORTH: chan=node1.connections.north; break; case Aios.DIR.SOUTH: chan=node1.connections.south; break; case Aios.DIR.WEST: chan=node1.connections.west; break; case Aios.DIR.EAST: chan=node1.connections.east; break; case Aios.DIR.UP: chan=node1.connections.up; break; case Aios.DIR.DOWN: chan=node1.connections.down; break; case Aios.DIR.NW: chan=node1.connections.nw; break; case Aios.DIR.NE: chan=node1.connections.ne; break; case Aios.DIR.SE: chan=node1.connections.se; break; case Aios.DIR.SW: chan=node1.connections.sw; break; case 'DIR.IP': chan=node1.connections.ip; dest=dir.ip; break; case 'DIR.DELTA': // Simple Delta routing: Minimize [x,y,..] -> [0,0,..] with {x,y,..} sigobj.to=Comp.obj.copy(sigobj.to); if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status()) sigobj.to.delta[0]--,chan=node1.connections.east; else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status()) sigobj.to.delta[0]++,chan=node1.connections.west; else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status()) sigobj.to.delta[1]--,chan=node1.connections.south; else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status()) sigobj.to.delta[1]++,chan=node1.connections.north; else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status()) sigobj.to.delta[2]--,chan=node1.connections.up; else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status()) sigobj.to.delta[2]++,chan=node1.connections.down; break; case 'DIR.PATH': chan=node1.connections.path; dest=dir.path; break; case 'DIR.CAP': if (!current.network) {current.error='No connection to server '+dir.cap; return false;}; chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap; break; default: return false; } switch (dir.tag||dir) { // One hop to next neighbour only? case Aios.DIR.NORTH: case Aios.DIR.SOUTH: case Aios.DIR.WEST: case Aios.DIR.EAST: case Aios.DIR.UP: case Aios.DIR.DOWN: case Aios.DIR.NW: case Aios.DIR.NE: case Aios.DIR.SE: case Aios.DIR.SW: sigobj.to=Aios.DIR.ORIGIN; // After messaging signal has arrived break; } if (chan==none || !chan.status(dest) /* OLDCOMM || !chan.signal*/) { current.error='No connection to direction '+dir; return false; }; node1.stats.signal++; if (Aios.options.fastcopy && chan.virtual) msg=sigobj; else msg=Aios.Code.toString(sigobj); /** OLDCOMM chan.signal(msg,dest); */ /* NEWCOMM */ chan.send({signal:msg,to:dest}); return true; } module.exports = { agent:sig, options:options, route:route, current:function (module) { current=module.current; Aios=module; } } }; BundleModuleCode['jam/node']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: node.js,v 1.3 2017/06/06 14:53:57 sbosse Exp $ ** $VERSION: 1.10.3 ** ** $INFO: ** ** JavaScript AIOS Agent Node Sub-System ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var Security = Require('jam/security'); var current=none; var Aios = none; var options = { version:'1.10.3' } function aid(process) { return process.agent.id+':'+process.pid } function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; /** Create a node. * typeof options = {id,maxpro,maxts,position:{x,y},defaultLevel?,TMO?} * */ var node= function (options) { var self=this; options=checkOptions(options,{}); this.options=options; this.id = checkOption(this.options.id,Aios.aidgen()); this.position = checkOption(this.options.position,Aios.DIR.ORIGIN); this.type = checkOption(this.options.type,'generic'); this.verbose = checkOption(this.options.verbose,0); // Default AIOS privilege level for received agent snapshots this.defaultLevel=checkOption(this.options.defaultLevel,1); this.processes={ free:none, max:checkOption(this.options.maxpro,100), // (proc|undefined) [] table:[], // number|undefined [] hash:[], top:0, used:0, // a cache of migrated agents [id]={dir,timeout} gone:[] }; this.processes.lookup = function (aid) { if(self.processes.hash[aid]!=undefined) return self.processes.hash[aid]; else return none; }; this.processes.process = function (aid) { if (self.processes.hash[aid]!=_) return self.processes.table[self.processes.hash[aid]]; else return none; }; // Signal propagation cache [from]={dir:timeout} this.signals=[]; // [agent,tmo,sig,arg] this.timers=[]; /** Connections to other nodes using P2P/IP/DOS links * type link = {recv: function (callback),send:function(data), * status: function() -> bool,count:number} */ this.connections={north:none,south:none,west:none,east:none}; // tuple spaces this.ts = Aios.Ts.create({maxn:checkOption(this.options.maxts,8), id:this.id,node:self}); // Random ports for negotiation and node security this.random = {}; this.port = Security.Port.unique(); this.random[this.port]=Security.Port.unique(); // Code dictionary shared by agents this.library = {}; // Location (geo) and position (virtual) information // location : { ip:string, // gps:{lat:number, lon:number}, // geo:{city:string,country:string,countryCode:string,region:string,zip:string} } this.location = null; this.position = options.position; this.stats = { cpu:0, create:0, fastcopy:0, fork:0, handled:0, migrate:0, received:0, signal:0, error:0, tsout:0, tsin:0, agents:0, } // Agent migration (gone) cache timeout this.TMO = checkOption(this.options.TMO,100000); // Needs node's service? this.timeout = 0; Aios.emit('node+',self); }; /** Clean-up and destroy this node. Terminate all agent processes. */ node.prototype.destroy = function () { var p,pro,_node=current.node,self=this; this.connections={}; current.node=this; for(p in this.processes.table) { pro=this.processes.table[p]; if (!pro) continue; pro.kill=true; this.unregister(pro); } this.processes.gone=[]; this.ts = none; current.node=_node; Aios.emit('node-',self); } /** Export of code library * */ node.prototype.export = function (name,code) { // copy and sandbox code if (!this.library[name]) this.library[name]=Aios.Code.toString(code); } /** Find an agent of the node by it's id and/or class, or agents matching a regular expression. * */ node.prototype.getAgentProcess = function (id,ac) { var matches=Comp.obj.isRegex(id)?[]:undefined, table=this.processes.table,p; if (!matches && this.processes.hash[id]!=undefined) { p=table[this.processes.hash[id]]; if (!ac || p.agent.ac==ac) return p; } for(var p in table) { if (!table[p]) continue; if (!matches && table[p].agent,id==id && (!ac || table[p].agent,ac=ac)) return table[p]; if (matches && id.test(table[p].agent.id)) matches.push(table[p]); } return matches; }; node.prototype.getAgent = function (id,ac) { var pros=this.getAgentProcess(id,ac); if (pros && Comp.obj.isArray(pros)) return Comp.array.map(pros,function (pro) {return pro.agent}); else if (pros) return pros.agent; }; /** Receive a signal to be passed to an agent located here or routed to another node. * Message is in JSOB text format or a JS object (fastcopy mode). * * typeof sigobj = {to,sig,arg,from,back?} * */ node.prototype.handle = function (msg) { var delivered,tmo,curtime=Aios.time()-current.world.lag, _node=current.node,self=this, sigobj=(typeof msg == 'string')?Aios.Code.ofString(msg,{}):msg; if (!sigobj) return; // Error current.node=this; // console.log('handler',this.id,sigobj); delivered=(Aios.Mobi.DIR.isDir(sigobj.to)?Aios.Sig.agent.sendto:Aios.Sig.agent.send) (sigobj.to,sigobj.sig,sigobj.arg,sigobj.from); if (delivered && sigobj.back) { // Update signal route cache tmo=curtime+this.TMO; this.signals[sigobj.from]={dir:sigobj.back,timeout:tmo}; this.timeout=min0(this.timeout,tmo); }; this.stats.handled++; current.node=_node; Aios.emit('schedule',self); } /** Import code from library. * Returns a sandboxed code copy. * */ node.prototype.import = function (name) { var code; if (this.library[name]) code=Aios.Code.ofString(this.library[name],current.process.mask); return code; } /** Get node statistics * */ node.prototype.info = function () { var self=this, p, obj = {}; ovj.stats = this.stats; obj.id = this.id; obj.position = this.position; obj.agents={}; var update=function (obj) { var p; for (p in obj) { if (p != '_update') delete obj[p]; } for (p in self.processes.hash) { if (self.processes.hash[p]!=_) obj[p]=self.processes.table[self.processes.hash[p]]; }; } obj.agents._update=update; update(obj.agents); obj.signals=this.signals; obj.timers=this.timers; obj.ts=this.ts; obj.connections=this.connections; return obj; } /** Print node statistics * */ node.prototype.print = function (summary) { var i,blocked,pending,total,ghost=0; var str='==== NODE '+this.id+' ===='+NL; str += 'SYSTIME='+Aios.time()+NL; str += 'PROCESS TABLE >>'+NL; if (summary) { blocked=0; pending=0; total=0; ghost=0; for (i in this.processes.table) { if (this.processes.table[i]!=_) { total++; if (this.processes.table[i].blocked) blocked++; if (this.processes.table[i].signals.length>0) pending++; if (this.processes.table[i].agent.next==undefined) ghost++; }; } str += ' TOTAL='+total+' BLOCKED='+blocked+' DYING='+ghost+' SIGPEND='+pending+NL; } else { for (i in this.processes.table) { if (this.processes.table[i]!=_) { str += ' ['+aid(this.processes.table[i])+'] '+ 'NEXT='+this.processes.table[i].agent.next+' '+ this.processes.table[i].print(); }; } } if (this.timers.length>0) { str += 'TIMER TABLE >>'+NL; for (i in this.timers) { str += ' ['+aid(this.timers[i][0])+'] TMO='+this.timers[i][1]+' SIG='+this.timers[i][2]+NL; } } str += 'TUPLE SPACES >>'+NL; if (summary) str += ' '+this.ts.print(summary); else str += this.ts.print(summary); return str; } /** Receive migrated agent text code and create a process container registered on this node. * If start=false then the next activity is computed here. * */ node.prototype.receive = function (msg,start,from) { // Save context var _process=current.process, _node=current.node, self=this, process,agent; if (this.verbose>1) Io.log ('Received (start='+start+'):\n'+msg); if (typeof msg !== 'object') process=Aios.Code.toCode(msg,this.defaultLevel); else process=Aios.Code.ofObject(msg); // Virtual migration, same physical JAM if (!process) return; // Error agent=process.agent; agent['self']=agent; this.register(process); this.stats.received++; if (process.dir || process.delta) { /* TODO migration if this node is not the destination */ }; if (!process.back && from && from.address && from.port) process.back=Aios.DIR.IP(from.address+':'+from.port); if (process.back && process.agent.parent) { // register child-to-parent signal path tmo=Aios.time()-current.world.lag+this.TMO; this.signals[process.agent.parent]={dir:process.back,timeout:tmo}; this.timeout=min0(this.timeout,tmo); } // console.log('node.receive '+this.position.x+','+this.position.y); if (process.schedule.length == 0) { // Compute next activity on THIS node current.node=this; current.process=process; try { if (!start) agent.next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent): agent.trans[agent.next]; if (process.blocked) throw 'BLOCKING'; //console.log(agent.next); } catch (e) { Aios.aios.log ('Node.receive: Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+ ' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+Aios.Code.print(agent.trans[agent.next]))+ '\nat:\n'+Io.sprintstack(e)); this.unregister(process); }; // Restore context current.node=_node; current.process=_process; } } /** Register agent code and assign a process container. * */ node.prototype.register = function (process) { var i,p, self=this, agent=process.agent; if (this.processes.free==none) { loop: for (i in this.processes.table) { if (this.processes.table[i]==_) { this.processes.free=i; break loop}; } } if (this.processes.free!=none) { this.processes.table[this.processes.free]=process; process.pid=this.processes.free; process.agent=agent; this.processes.free=none; } else { this.processes.table[this.processes.top]=process; process.agent=agent; process.pid=this.processes.top; this.processes.top++; } if (agent.id==undefined) agent.id=Aios.aidgen(); this.processes.hash[agent.id]=process.pid; this.processes.used++; this.stats.agents++; if (this.processes.gone[process.agent.id]) // Agent returned again! this.processes.gone[process.agent.id]=undefined; process.node=this; Aios.emit('agent+',process,self); Aios.emit('schedule',self); } /** Node Garbage Collection and Timeout Service * */ node.prototype.service = function (curtime) { var nexttime=0,p,pro,sig; // TS cleanup management this.ts.service(curtime); if (curtime 5) key[i] = 0; else { if ((get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0) key[i] = 1; else key[i] = 0; j++; } } Des48.des_OWsetkey(key); /* ** Now go encrypt constant 0 */ block=Des48.des_OWcrypt48(block); /* ** and put the bits in the destination port */ var pb = 0; for (i = 0; i < PORT_SIZE;i++) { var pbyte = 0; for (j = 0; j < 8; j++) { pbyte = pbyte | (block[pb] << j); pb++; } pubport=set_portbyte(pubport, i, pbyte); } return pubport; } function pad(str,size) { while (str.length < (size || 2)) {str = "0" + str;} return str; } function port_cmp(port1,port2) { if (port1==undefined || port2==undefined) return (port1==port2); else return String.equal(port1,port2); } function port_copy(port) { return String.copy(port); } // Expected format: XX:XX:XX:XX:XX function port_of_string(str,compact) { var tokens=str.split(':'),i,port=''; for (i=0;i 0) str = str + ':'; str = str + pad(num.toString(16).toUpperCase(), 2); } } else str='undefined'; return str; } function prv2pub (port) { var putport; if (priv2pub_cache[port] == undefined) { putport=one_way(port); priv2pub_cache[port] = putport; } else putport = priv2pub_cache[port]; return putport; } function prv_cmp(prv1,prv2) { return (prv1==undefined&&prv2==undefined) || (prv1.prv_obj==prv2.prv_obj && prv1.prv_rights==prv2.prv_rights && port_cmp(prv1.prv_rand,prv2.prv_rand)) } /** ** Decode a private structure (check for a valid private field) * * typeof @prv = privat * typeof @rand = port * returns boolean */ function prv_decode (prv,rand) { if (prv.prv_rights == PRV_ALL_RIGHTS) return port_cmp(prv.prv_rand,rand); else { var tmp_port = port_copy(rand), pt0 = get_portbyte(tmp_port, 0), pr0 = prv.prv_rights; tmp_port = set_portbyte(tmp_port, 0, (pt0 ^ pr0)); tmp_port = one_way(tmp_port); return port_cmp(prv.prv_rand, tmp_port) } } /* ** Encode a private part from the object number, the rights field ** and the random port. ** Returns the created private structure. */ function prv_encode(obj,rights,rand) { var tmp_port = port_copy(rand), r1 = rights, rmask = PRV_ALL_RIGHTS; if (rights == PRV_ALL_RIGHTS) return Private(obj,r1 & rmask,tmp_port); else { var pt0 = get_portbyte(tmp_port,0); tmp_port = set_portbyte(tmp_port,0,pt0 ^ r1); tmp_port = one_way(tmp_port); return Private(obj,r1 & rmask,tmp_port) } } function prv_of_string(str) { var pp=prv_parse(str,0); return pp?pp.priv:undefined } /* ** Return the private object number form a private structure */ function prv_number(prv) { return prv.prv_obj; } // Expected format: obj(right)[port] function prv_parse(str,offset) { var priv=Private(); var sv; var len=str.length,pos=offset; if (str[pos]=='(') pos++; sv=''; while(str[pos]!='(') { sv=sv+str[pos]; pos++; } priv.prv_obj=Perv.int_of_string(sv); sv=''; if (str[pos]=='(') pos++; while(str[pos]!=')') { sv=sv+str[pos]; pos++; } priv.prv_rights=Perv.int_of_string('0x'+sv); if (str[pos]==')') pos++; var pp=port_parse(str,pos); if (pp==undefined) return undefined; priv.prv_rand=pp.port; pos=pp.pos; return {priv:priv,pos:pos}; } function prv_to_string(priv) { var str=''; if (priv==undefined) return 'undefined'; str=priv.prv_obj; str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')['; str=str+port_to_string(priv.prv_rand)+']'; return str; } /** Restrict a private field (rights&mask) of a capability. * * @param {privat} priv * @param {number} mask rights restriction mask * @param {port} random secret server random port */ function prv_restrict(priv,mask,random) { var pr = prv_encode(priv.prv_obj, priv.prv_rights & mask, random); return pr; } /* ** Return the private rights field. */ function prv_rights(prv) { return prv.prv_rights & Rights.PRV_ALL_RIGHTS; } /* ** Check the private rights field: 1. Validation, 2: Required rights. */ function prv_rights_check(prv,rand,required) { if (!Net.prv_decode(prv,rand)) return false; return (prv.prv_rights & required)==required; } /* * Return a new random unique port. * * Warning: the quality of the random ports are strongly * related to JSVMs underlying random generator. * * typeof return = port */ function uniqport() { var port = String.create (PORT_SIZE); var i,values; do { values = Rnd.generate({number:true,length:PORT_SIZE}); for (i = 0; i <= (PORT_SIZE - 1); i++) port = String.set(port, i, (Perv.char_of_int(values[i]))); if (uniquePorts[port]) uniquePorts[port]++; else uniquePorts[port]=1; } while (uniquePorts[port]>1); return port; } Port.equal = port_cmp Port.toString = port_to_string Port.ofString = port_of_string Port.prv2pub = prv2pub Port.unique = uniqport Private.decode = prv_decode Private.encode = prv_encode Private.equal = prv_cmp Private.number = prv_number Private.ofString = prv_of_string Private.restrict = prv_restrict Private.rights = prv_rights Private.rights_check = prv_rights_check Private.toString = prv_to_string Capability.toString = cap_to_string Capability.ofString = cap_of_string var Security = { current:function (module) { current=module.current; Aios=module; }, PORT_SIZE:PORT_SIZE, PRIV_SIZE:PRIV_SIZE, Private:Private, Capability: Capability, Port: Port, nilport: Port(), nilpriv: Private(0,0,Port()), nilcap: Capability(Port(),Private(0,0,Port())), one_way : one_way, prv2pub : prv2pub, } module.exports = Security; }; BundleModuleCode['dos/des48']=function (module,exports,global,process){ /** ** ================================== ** OOOO OOOO OOOO O O OOOO ** O O O O O O O O O ** O O O O O O O O O ** OOOO OOOO OOOO O OOO OOOO ** O O O O O O O O O ** O O O O O O O O O ** OOOO OOOO OOOO OOOO O O OOOO ** ================================== ** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR. ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2016 BSSLAB ** $CREATED: 3/30/15 by sbosse. ** $VERSION: 1.1.4 ** ** $INFO: ** ** DOS: Encryption 48bit ** ** $ENDOFINFO */ var util = Require('util'); var Io = Require('com/io'); var Comp = Require('com/compat'); var Array = Comp.array; var assert = Comp.assert; const des_HBS = 24; const des_BS = des_HBS * 2; /* ** Initial permutation, */ var des_IP = [ 23, 27, 34, 44, 37, 17, 12, 42, 3, 32, 41, 29, 20, 2, 1, 10, 0, 28, 40, 6, 7, 11, 16, 8, 25, 30, 14, 26, 47, 38, 19, 43, 18, 5, 35, 39, 36, 21, 4, 45, 24, 22, 13, 33, 31, 9, 15, 46 ]; /* ** Final permutation, FP = IP^(-1) */ var des_FP = [ 16, 14, 13, 8, 38, 33, 19, 20, 23, 45, 15, 21, 6, 42, 26, 46, 22, 5, 32, 30, 12, 37, 41, 0, 40, 24, 27, 1, 17, 11, 25, 44, 9, 43, 2, 34, 36, 4, 29, 35, 18, 10, 7, 31, 3, 39, 47, 28 ]; /* ** Permuted-choice 1 from the key bits ** to yield C and D. ** Note that bits 8,16... are left out: ** They are intended for a parity check. */ var des_PC1_C = [ 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, 10, 2,59,51,43,35,27, 19,11, 3,60,52,44,36 ]; var des_PC1_D = [ 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, 14, 6,61,53,45,37,29, 21,13, 5,28,20,12, 4 ]; /* ** Sequence of shifts used for the key schedule. */ var des_shifts = [ 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 ]; /* ** Permuted-choice 2, to pick out the bits from ** the CD array that generate the key schedule. */ var des_PC2_C = [ 14,17,11,24, 1, 5, 3,28,15, 6,21,10, 23,19,12, 4,26, 8, 16, 7,27,20,13, 2 ]; var des_PC2_D = [ 41,52,31,37,47,55, 30,40,51,45,33,48, 44,49,39,56,34,53, 46,42,50,36,29,32 ]; /* ** The C and D arrays used to calculate the key schedule. */ var des_C = Array.create(56,0); // des_D = des_C[28] var des_D_get = function (i) {return des_C[i+28]}; var des_D_set = function (i,sval) { des_C[i+28] = sval }; /* ** The key schedule. ** Generated from the key. */ var des_KS= Array.create_matrix(16,48,0); var des_OWsetkey = function(key) { var ks = []; var t = 0; var i,j,k; /* ** First, generate C and D by permuting ** the key. The low order bit of each ** 8-bit char is not used, so C and D are only 28 ** bits apiece. */ for(i = 0;i < 28;i++) { var index1 = des_PC1_C[i] - 1; var index2 = des_PC1_D[i] - 1; des_C[i] = key[index1]; des_D_set(i,key[index2]); } /* ** To generate Ki, rotate C and D according ** to schedule and pick up a permutation ** using PC2. */ for (i = 0 ;i< 16;i++) { ks = des_KS[i]; // rotate for (k = 0; k < des_shifts[i]; k++) { t = des_C[0]; for (j = 0; j < 27; j++) { des_C[j] = -des_C[j + 1]; } des_C[27] = t; t = des_D_get(0); for (j = 0; j < 27; j++) { des_D_set(j, des_D_get(j + 1)); } des_D_set(27, t); } /* ** get Ki. Note C and D are concatenated. */ for (j = 0; j < 24; j++) { ks[j] = des_C[des_PC2_C[j] - 1]; ks[j + 24] = des_D_get(des_PC2_D[j] - 28 - 1); } } }; /* ** The E bit-selection table. */ var des_E = [ 22, 15, 12, 3, 8, 2, 23, 16, 14, 13, 9, 10, 0, 1, 21, 19, 18, 6, 11, 7, 17, 4, 20, 5, 5, 17, 11, 13, 12, 14, 8, 7, 19, 22, 18, 9, 3, 4, 1, 6, 16, 2, 20, 15, 10, 23, 0, 21 ]; /* ** The 8 selection functions. ** For some reason, they give a 0-origin ** index, unlike everything else. */ var des_S = [ [ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13 ], [ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9 ], [ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12 ], [ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14 ], [ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3 ], [ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13 ], [ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12 ], [ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11 ] ]; /* ** P is a permutation on the selected combination ** of the current L and key. */ var des_P = [ 3, 13, 9, 12, 8, 20, 21, 7, 5, 23, 16, 1, 14, 18, 4, 15, 22, 10, 2, 0, 11, 19, 17, 6 ]; var des_L = Array.create(des_BS,0); var des_R_get = function (i) { return des_L[(i+des_HBS)]}; var des_R_set = function (i,sval) { des_L[i+des_HBS]= sval}; var des_tempL = Array.create(des_HBS,0); var des_f = Array.create (32,0); /* ** Warning!! ** ** f[] used to be HBS for some years. ** 21/6/1990 cbo and sater discovered that inside the loop where f is computed ** indices are used from 0 to 31. These overlapped the preS array which is ** declared hereafter on all compilers upto that point, but only those ** values that were not used anymore. But the values of f are only used ** upto HBS. Makes you wonder about the one-way property. ** Then came ACK, and reversed the order of the arrays in the image. ** ** As a short term solution f[] was increased to 32, but in the long run ** someone should have a good look at our "oneway" function */ /* ** The combination of the key and the input, before selection. */ var des_preS = Array.create (48,0); /* ** The payoff: encrypt a block. (Now 48 bytes, 1 bit/byte) */ var des_OWcrypt48 = function(block) { var ks = []; var t1 = 0; var t2 = 0; var i, j, k; /* ** First, permute the bits in the input */ for (j = 0; j <= (des_BS - 1); j++) { des_L[j] = block[des_IP[j]]; } /* ** Perform an encryption operation 16 times. */ for (i = 0; i <= 15; i++) { ks = des_KS[i]; /* ** Save the R array, ** which will be the new L. */ for (j = 0; j < (des_HBS - 1); j++) { des_tempL[j] = des_R_get(j); } /* ** Expand R to 48 bits using the E selector; ** exclusive-or with the current key bits. */ for (j = 0; j <= 47; j++) { des_preS[j] = (des_R_get(des_E[j])) ^ ks[j]; } /* ** The pre-select bits are now considered ** in 8 groups of 6 bits each. ** The 8 selection functions map these ** 6-bit quantities into 4-bit quantities ** and the results permuted ** to make an f(R, K). ** The indexing into the selection functions ** is peculiar; it could be simplified by ** rewriting the tables. */ t1 = 0; t2 = 0; for (j = 0; j <= 7; j++) { var sind2 = ((des_preS[t1 + 0] << 5) & 0xff) + ((des_preS[t1 + 1] << 3) & 0xff) + ((des_preS[t1 + 2] << 2) & 0xff) + ((des_preS[t1 + 3] << 1) & 0xff) + ((des_preS[t1 + 4] << 0) & 0xff) + ((des_preS[t1 + 5] << 4) & 0xff); k = des_S[j][sind2]; des_f[t2 + 0] = (k >> 3) & 0x1; des_f[t2 + 1] = (k >> 2) & 0x1; des_f[t2 + 2] = (k >> 1) & 0x1; des_f[t2 + 3] = (k >> 0) & 0x1; // 3 .. 31 !!! t1 = t1 + 6; t2 = t2 + 4; } /* ** The new R is L ^ f(R, K). ** The f here has to be permuted first, though. */ for (j = 0; j < des_HBS; j++) { des_R_set(j, (des_L[j] ^ des_f[des_P[j]])); } /* ** Finally, the new L (the original R) ** is copied back. */ for (j = 0; j < des_HBS; j++) { des_L[j] = des_tempL[j]; } } /* ** The output L and R are reversed. */ for (j = 0; j < des_HBS; j++) { t1 = des_L[j]; des_L[j] = des_R_get(j); des_R_set(j, t1); } /* ** The final output ** gets the inverse permutation of the very original. */ for (j = 0; j < des_BS; j++) { block[j] = des_L[des_FP[j]]; } return block; }; module.exports = { des_OWsetkey:des_OWsetkey, des_OWcrypt48:des_OWcrypt48 }; }; BundleModuleCode['jam/proc']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: proc.js,v 1.1 2017/05/20 15:56:53 sbosse Exp $ ** $VERSION: 1.5.1 ** ** $INFO: ** ** JavaScript AIOS Agent Process Module ** ** $ENDOFINFO */ var Comp = Require('com/compat'); var current=none; var Aios = none; var options = { version:'1.4.5' } var PRIO = { LOW:0, NORMAL:1, HIGH:2 } /* ** Agent process - must be compatible with scheduler context process! */ var proc = function (properties) { // Agent code this.agent={}; // Internal scheudling blocks - can'tmigrate - if any // Handled by global scheduler (DOS) this.block=[]; // Process execution suspended? this.blocked=false; // Process blocking timeout this.timeout=0; // For soft checkpointing this.runtime=0; // Ressource control (node constraints) this.resources = { consumed:0, // total processing time consumed memory:0, // total memory (code+data) consumed tuples:0, // total tuple generation agent:0, // total agents created } this.level=undefined; // Dynamic process priority effecting scheduling order this.priority = PRIO.NORMAL; // Agent scheduling blocks - can migrate! // Handled by AIOS scheduler only! // function [] this.schedule=[]; // Agent activity suspended, waiting for an event? this.suspended=false; this.suspendedIn=undefined; this.error=none; // process id this.pid=none; // process parent id this.gid=none; this.id='agent'; this.mask={}; // [sig,arg,from] [] this.signals=[]; // Did we moved? this.move=none; // Killing state this.kill=false; // Dead state this.dead=false; // Pending next transition computatuion? this.transition=false; for (var p in properties) { if (properties[p]!=undefined) this[p]=properties[p]; } // Used in simulators only: A geometric shape object this.shape=undefined; if (current.world) this.parent = current.world.context; this.node=current.node; } /** Execute a callback function in this agent process context immediately (should invoke scheduler and CB!) * */ proc.prototype.callback = function (cb,args) { var _process=current.process,_node=current.node, res; current.node=this.node; current.process=this; try { res=cb.apply(this.agent,args||[]); } catch (e) { Aios.aios.log('Caught callback error: '+e); } current.process=_process; current.node=_node; return res; } /** Execute this process immediately * */ proc.prototype.exec = function() { var _process=current.process,_node=current.node, res; current.node=this.node; res = Aios.schedule(this); current.process=_process; current.node=_node; return res; } /** Finalize this process * */ proc.prototype.finalize = function() { this.kill=true; this.suspended=false; current.node.unregister(this); } /** Fork an agent process. * Returns child process. * If level is not specified, the parent process level is used. */ proc.prototype.fork = function(parameters,level,dirty) { var code, _process=current.process, process_, agent_, p; if (dirty && level!=undefined) dirty=false; // Dirty process copy with level change not possible! if (level==undefined) level=current.process.mask.privilege(); else level=Math.min(current.process.mask.privilege(),level); if (!dirty) { code = Aios.Code.forkCode(current.process); process_ = Aios.Code.toCode(code,level); } else { process_ = Aios.Code.copyProcess(current.process); } agent_ = process_.agent agent_.id=Aios.aidgen(); agent_.parent=current.process.agent.id; process_.init({gid:current.process.pid}); current.process=process_; current.node.register(process_); // Update forked child agent parameters only if they already exist for (p in parameters) { if (Comp.obj.hasProperty(agent_,p)) agent_[p]=parameters[p]; } // Should next activity computed in scheduler by setting process.transition ??? // compute next activity after fork if there is no scheduling block, // no parameter next set, // and forkCode should always discard all current schedule blocks! if (!parameters.next) try { agent_.next=(typeof agent_.trans[agent_.next] == 'function')?agent_.trans[agent_.next].call(agent_): agent_.trans[agent_.next]; } catch (e) { /*kill agent?*/ process_.kill=true; }; this.node.stats.fork++; current.process=_process; return process_; } proc.prototype.init = function (properties) { for (var p in properties) { if (this[p]!=undefined) this[p]=properties[p]; } } proc.prototype.print = function () { var str='', agent=this.agent; str = 'PID='+this.pid+ (this.gid?' GID='+this.gid:'')+ (this.timeout?(' TMO='+this.timeout):'')+ (this.blocked?' BLOCKED':'')+ (this.suspended?' SUSP':'')+ (this.kill?' KILL':'')+ (this.dead?' DEAD':''); if (this.schedule.length>0) str += ' SCHEDULE='+this.schedule.length; if (this.block.length>0) str += ' BLOCK='+this.block.length; if (this.signals.length>0) str += ' SIGNALS('+this.signals.length+'):'+ Comp.printf.list(this.signals,function (s) {return s[0]}); if (this.transition) str += ' TRANS'; if (this.consumed|0) str += ' CONS='+(this.consumed|0); if (agent) str += ' AGENT '+agent.id+' next='+agent.next; return str; } /** * Suspend agent activity processing, but not internal block scheduling! */ proc.prototype.suspend = function (timeout,transition,suspendedIn){ if (!this.kill && !this.dead) { this.suspended=true; if (timeout!=undefined) this.timeout=timeout; if (transition) this.transition=true; // pending next computation this.suspendedIn = suspendedIn; } } proc.prototype.update = function (properties) { for (var p in properties) { this[p]=properties[p]; } } /** * Wakeup agent process from a previous suspend call (sleep) */ proc.prototype.wakeup = function (immediate){ this.suspended=false; this.timeout=0; if (!this.kill && !this.dead && (immediate || this.schedule.length == 0)) { var _process=current.process,_node=current.node; current.node=this.node; if (this.suspendedIn=='ts') this.node.ts.cleanup(this,true); // Have to call callback handler to inform about timeout!? this.suspendedIn=undefined; this.transition=this.schedule.length == 0; // Re-entering the scheduler is a bad idea!? Aios.schedule(this); current.process=_process; current.node=_node; } } function Proc(properties) { var obj = new proc(properties); return obj; } module.exports = { agent: { fork:function fork(parameters) { return current.process.fork(parameters); } }, isProc: function (o) { return o instanceof Proc }, Proc:Proc, PRIO:PRIO, current:function (module) { current=module.current; Aios=module; }, options:options } }; BundleModuleCode['jam/ts']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: ts.js,v 1.3 2017/06/19 17:18:39 sbosse Exp sbosse $ ** $VERSION: 1.8.2 ** ** $INFO: ** ** JavaScript Agent Tuple-Space Sub-System ** ** New: xx.try function synonyms (inp.try, rd.try,..) and try functions now fire callback on timeout ** New: testandset ** New: eval/listen ** New: Patterns can contain regular expression! (p_i instanceof RegExp) ** New: A rd/inp operation can return all matching tuples ** New: alt operation supporting listening on multiple patterns ** New: Distributed TS with collect, copyto, store ** ** Exeception: 'TS' ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var current=none; var Aios = none; var verbose=false; var options = { version:'1.8.2', } function aid(process) { return process.agent.id+':'+process.pid } function log(tsi,process,msg) { if (verbose && process) Aios.aios.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] Ag ' + aid(process)+ ' '+msg); else if (verbose) Io.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] SYS'+msg); } function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; /******************************************* ** Waiter management *******************************************/ /** Add a waiter to a tuple space data base * */ var addwaiter = function (tsi,waiter) { var index,key; if (tsi.waiters.free.length==0) { index=tsi.waiters.length; tsi.waiters.push(waiter); } else { index=tsi.waiters.free[0]; tsi.waiters[index]=waiter; tsi.waiters.free.shift(); } if (typeof (key=waiter.pat[0]) == 'string') switch (waiter.op) { case 'listen': tsi.waiters.hash[key]=index; break; } } /** Check for waiting agent processes and try to match the provided tuple. * Readers can read multiple copies of the tuple, whereby consumers can only read the tuple one time. * Consumers (in-op) can be in a waiting list (next/prev). If one waiter in a list consumes * a tuple, all waiters must be removed. The other waiters (the same process, but different patterns; alt-op) * can be in different tuple data bases! * */ var checkwaiter = function (tsi,tuple,callback) { var res,consumed=false, i,waiter, _process=current.process; // Create agent callback function cb(waiter,res) { Aios.CB(waiter.pro,function () {waiter.cb.call(waiter.pro.agent,res)}); } for(i=0;i0) { consumed = Comp.array.findmap(current.node.ts.consumers,function (consumer) { return consumer(tuple); }); } return consumed; } var findwaiter = function (tsi,waiter) { var i; for(i=0;icurrent.node.ts.n || nary==0) return; tsi=current.node.ts.db[nary]; if (!all && Comp.isString(pat[0]) && tsi.hash[pat[0]]!=undefined) { // Speedup trial with hash key res=match(tsi.data[tsi.hash[pat[0]]],pat); } if (res==none) { res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple) { if (tuple==_) return none; else return match(tuple,pat); }); if (res && res.length==0) res=none; if (res == none && current.node.ts.providers.length>0) { res = Comp.array.findmap(current.node.ts.providers,function (provider) { return provider(pat); }); } } return res; } /******************************************* ** Tuple management *******************************************/ /** * Compare two values, check equiality */ var equal = function(x,y) { var i; if(x==y) return true; if (Comp.obj.isArray(x) && Comp.obj.isArray(y)) { if (x.length!=y.length) return false; for(i in x) { if (x[i] != y[i]) return false; } return true; } return false; } /** Match a tuple element with a template pattern element y. * */ var match1 = function (x,y) { if (y==any) return true; if (x==y) return true; if ((x instanceof Array) && (y instanceof Array)) return match(x,y)!=none; if (y instanceof RegExp && typeof x == 'string' && y.test(x)) return true; return false; } /** Match a tuple with a template and return none or the original tuple (equivalence result?) * */ var match = function (tuple,templ) { var i; if (tuple.length != templ.length) return none; for(i in tuple) { if (!match1(tuple[i],templ[i])) return none; }; return tuple; } /** Find and remove one/all matching tuple(s) from the database based on pattern matching * */ var remove = function (pat,all) { var tsi,nary=pat.length,res=none,removed=false,hashed=_; if (nary>current.node.ts.n || nary==0) return; tsi=current.node.ts.db[nary]; if (!all && Comp.isString(pat[0])) hashed=tsi.hash[pat[0]]; if (hashed != _) { // Speedup trial with hash key res=match(tsi.data[hashed],pat); if (res) { // invalidate matching tuple in data list removed=true; tsi.data[hashed]=_; tsi.tmo[hashed]=0; // remember the free slot in the data list if (tsi.free==none) tsi.free=hashed; // invalidate hash entry - tuple is consumed delete tsi.hash[pat[0]]; } } if (res==none || removed==false) { res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple,i) { if (tuple==_) return none; var res_=match(tuple,pat); if (res_!=none) { if (Comp.isString(pat[0]) && tsi.hash[pat[0]]==i) { // Invalidate hash - tuple is consumed delete tsi.hash[pat[0]]; } tsi.data[i]=_; tsi.tmo[i]=0; if (tsi.free==none) tsi.free=i; return res_; } else return none; }); if (res && res.length==0) res=none; } return res; } /******************************************* ** Tuple Space Agent/Client API *******************************************/ var ts = { // consuming - tmo <> 0 => try_alt alt: function (pats,callback,all,tmo) { var tsi,nary, i,p,pat,waiters=none,last=none; for(i in pats) { pat=pats[i]; nary=pat.length; if (nary>current.node.ts.n || nary==0) return none; res = remove(pat,all); if (res && res.length) current.node.stats.tsin += (all?res.length:1); if (res && callback) { callback.call(current.process.agent,res); return; } else if (callback && (tmo==undefined||tmo>0)) { if (waiters==none) waiters={pat:pat, pro:current.process, cb:callback, op:'in'+(all?'-all':''), tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 },last=waiters; else { last.next={pat:pat, pro:current.process, cb:callback, op:'in'+(all?'-all':''), tmo:tmo>0?Aios.time()-current.world.lag+tmo:0, prev:last },last=last.next; } } } if (waiters!=none) { p=waiters; while(p) { tsi=current.node.ts.db[p.pat.length]; addwaiter(tsi,p); p=p.next; } log(tsi,current.process,' +waiter'); current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); } }, // The collect primitive moves tuples from this source TS that match template // pattern into destination TS specified by path 'to' (a node destination). collect: function (to,pat) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0) return none; tsi=current.node.ts.db[nary]; res = remove(pat,true); if (res.length>0) { current.node.stats.tsin += res.length; Aios.Sig.agent.sendto(to,'TS.SIG',res); } return res.length; }, // Copy all matching tuples form this source TS to a remote destination TS // specified by path 'to' (a node destination). copyto: function (to,pat) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0) return 0; tsi=current.node.ts.db[nary]; res = lookup(pat,true); if (res.length>0) { Aios.Sig.agent.sendto(to,'TS.SIG',res); } return res.length; }, // Access a tuple evaluator - non-blocking: no listener -> callback(null) // TODO blocking/tmo evaluate: function (pat,callback,tmo) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0) return none; tsi=current.node.ts.db[nary]; consumed=checkwaiter(tsi,pat,callback); if (!consumed && callback) callback.call(current.process.agent,null); }, // Test tuple existence exists: function (pat) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0) return none; tsi=current.node.ts.db[nary]; res = lookup(pat); return res!=none; }, // consuming - tmo <> 0 => try_in inp: function (pat,callback,all,tmo) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; res = remove(pat,all); log(tsi,current.process,' in? '+res+' []='+count(tsi)); if (res && res.length) current.node.stats.tsin += (all?res.length:1); if (res==none && callback && (tmo==undefined||tmo>0)) { addwaiter(tsi,{pat:pat, pro:current.process, cb:callback, op:'in'+(all?'-all':''), tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 }); log(tsi,current.process,' +waiter'); current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); return none; } else if (callback) callback.call(current.process.agent,res); else return res; }, // Provide a tuple evaluator listen: function (pat,callback) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; addwaiter(tsi,{pat:pat, pro:current.process, cb:callback, op:'listen', tmo:0 }); }, // Store time-limited tuples mark: function (tuple,tmo) { var p,tsi,nary=tuple.length,consumed=false; if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; current.node.stats.tsout++; // Check waiters consumed=checkwaiter(tsi,tuple); if (!consumed) { if (tsi.free==none) { loop: for (var i in tsi.data) { if (tsi.data[i]==_) {tsi.free=i; break loop} } } if (tsi.free!=none) { tsi.data[tsi.free]=tuple; tsi.tmo[tsi.free]=Aios.time()-current.world.lag+tmo; current.node.ts.timeout=min0(current.node.ts.timeout,tsi.tmo[tsi.free]); if (Comp.obj.isString(tuple[0])) tsi.hash[tuple[0]]=tsi.free; tsi.free=none; } else { tsi.data.push(tuple); tsi.tmo.push(Aios.time()-current.world.lag+tmo); // hash is only a first guess to find a tuple if (Comp.obj.isString(tuple[0])) tsi.hash[tuple[0]]=tsi.data.length-1; } } else current.node.stats.tsin++; }, // Store a tuple in this TS out: function (tuple) { var tsi,nary=tuple.length,consumed=false,res; if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; current.node.stats.tsout++; // Check waiters consumed=checkwaiter(tsi,tuple); if (!consumed) { if (tsi.free==none) { loop: for (var i in tsi.data) { if (tsi.data[i]==_) {tsi.free=i; break loop} } } if (tsi.free!=none) { tsi.data[tsi.free]=tuple; tsi.tmo[tsi.free]=0; if (Comp.obj.isString(tuple[0])) tsi.hash[tuple[0]]=tsi.free; tsi.free=none; } else { tsi.data.push(tuple); tsi.tmo.push(0); // hash is only a first guess to find a tuple if (Comp.obj.isString(tuple[0])) tsi.hash[tuple[0]]=tsi.data.length-1; } } else current.node.stats.tsin++; log(tsi,current.process,' out '+tuple+' ['+nary+'] consumed='+consumed+' []='+count(tsi)); }, // not consuming - tmo <> undefined => try_rd [0: immed.] rd: function (pat,callback,all,tmo) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; res = lookup(pat,all); if (res==none && callback && (tmo==_||tmo>0)) { addwaiter(tsi,{pat:pat, pro:current.process, cb:callback, op:'rd'+(all?'-all':''), tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 }); current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); return none; } else if (callback) callback.call(current.process.agent,res); else return res; }, // consuming rm: function (pat,all) { var tsi,nary=pat.length,res; if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; tsi=current.node.ts.db[nary]; res = remove(pat,all); if (res && res.length) current.node.stats.tsin += (all?res.length:1); return (res!=none); }, // Remote tuple storage store: function (to,tuple) { Aios.Sig.agent.sendto(to,'TS.SIG',[tuple]); return 1; }, // Test and Set: Atomic modification of a tuple - non blocking // typeof @callback: function (tuple) -> tuple ts: function (pat,callback) { var tsi,nary=pat.length,res,ret; if (nary>current.node.ts.n || nary==0 || !Comp.obj.isArray(pat)) throw 'TS'; tsi=current.node.ts.db[nary]; res = lookup(pat,false); log(tsi,current.process,' test? '+res+' []='+count(tsi)); if (res) current.node.stats.tsin += 1; if (callback) { if (current.process) ret=callback.call(current.process.agent,res); else ret=callback(res); // update the modified tuple if (ret && ret.length==res.length) Comp.array.copy(ret,res); res=ret; } else if (res) { // restore the originally consumed tuple ts.out(res); } return res; }, try : { alt : function (tmo,pats,callback,all) { return ts.alt(pats,callback,all,tmo); }, evaluate : function (tmo,pat,callback) { return ts.evaluate(pat,callback,tmo); }, inp : function (tmo,pat,callback,all) { return ts.inp(pat,callback,all,tmo); }, rd : function (tmo,pat,callback,all) { return ts.rd(pat,callback,all,tmo); } } } // Synonyms ts.alt.try = ts.try.alt ts.evaluate.try = ts.try.evaluate ts.inp.try = ts.try.inp ts.rd.try = ts.try.rd /******************************************* ** Tuple Space Data Base *******************************************/ var tsd = function (options) { var self=this; if (!options) options={}; this.n=options.maxn||8; this.id=options.id||'TS'; this.timeout=0; this.db=Comp.array.init(this.n+1,function (i) { var tsi; if (i==0) return none; tsi = { i:i, hash:[], // number|none free:none, // [*] [] data:[], // number [] tmo:[], // [pattern,agent,callback,kind] waiters:[] }; tsi.waiters.free=[]; tsi.waiters.hash={}; // Hash tuple key for consuming waiter return tsi; }); /* ** Additional external tuple providers implementing a match function. */ this.providers=[]; /* ** Additional external tuple consumers implementing a match function. */ this.consumers=[]; this.node=options.node; // External API w/o blocking and callbacks (i.e., try_ versions with tmo=0) // Can be called from any context this.extern = { inp: function (pat,all) { var res,tsi,nary=pat.length,_node=current.node; current.node=self.node||_node; if (nary>current.node.ts.n || nary==0) return none; tsi=current.node.ts.db[nary]; res = remove(pat,all); if (res && res.length) current.node.stats.tsin += (all?res.length:1); current.node=_node; return res; }, mark: function (pat,tmo) { var res,_node=current.node; current.node=self.node||_node; res = ts.mark(pat,tmo); current.node=_node; return res; }, out: function (pat) { var res,_node=current.node; current.node=self.node||_node; res = ts.out(pat) current.node=_node; return res; }, rd: function (pat,all) { var res,tsi,nary=pat.length,_node=current.node; current.node=self.node||_node; if (nary>current.node.ts.n || nary==0) return none; tsi=current.node.ts.db[nary]; res = lookup(pat,all); if (res && res.length) current.node.stats.tsin += (all?res.length:1); current.node=_node; return res; }, rm: function (pat,all) { var res,_node=current.node; current.node=self.node||_node; res=ts.rm(pat,all); current.node=_node; return res; }, ts: function (pat,callback) { var res,_node=current.node; current.node=self.node||_node; res=ts.ts(pat,callback); current.node=_node; return res; }, } } var create = function (options) { return new tsd(options); } tsd.prototype.checkwaiter = function (tuple) { var tsi,nary=tuple.length; if (nary>this.n || nary==0) return none; tsi=current.node.ts.db[nary]; return checkwaiter(tsi,tuple); } /** Remove an agent process from waiter queues. * If doCallback is set, a pending operation callback handler is executed here (e.g., on timeout or interruption). * */ tsd.prototype.cleanup = function (process,doCallback) { var i,j,tsi,p,waiter; for (i in current.node.ts.db) { if (i==0) continue; tsi=current.node.ts.db[i]; for(j=0;j 'tuple * type consumer : function ('pat) -> boolean */ tsd.prototype.register = function (func,consumer) { if (consumer) this.consumers.push(func) else this.providers.push(func); }; tsd.prototype.print = function (summary) { var i,tsi,num,str='',sep=''; if (summary) { str += '['; for (i in current.node.ts.db) { if (i==0) continue; tsi=current.node.ts.db[i]; num = count(tsi); if (num>0) { str += sep+'TS'+(int(i)+1)+'='+num; sep=' '; } } str += ']'+NL; } else for (i in current.node.ts.db) { if (i==0) continue; tsi=current.node.ts.db[i]; str += '['+Comp.printf.sprintf('%2d',tsi.i)+ ' free='+(tsi.free?Comp.printf.sprintf('%4d',tsi.free):'none')+ ' data='+Comp.printf.sprintf('%4d(%4d)',count(tsi),tsi.data.length)+ ' waiters='+Comp.printf.sprintf('%4d',tsi.waiters.length)+']'+NL; } return str; } /** Tuple Space Garbage Collection and Timeout Service * */ tsd.prototype.service = function (curtime) { var i,hashed,tsi,nexttime=0; // TODO: if (curtime1) main.out(' .. nexttime = '+thr.nexttime+ ' ('+(thr.nexttime>0?thr.nexttime-thr.curtime:0)+')'); }; this.sleep = function () { var delta; thr.curtime=Aios.time(); delta=thr.nexttime>0?thr.nexttime-thr.curtime:1000; if (main.verbose>2) main.out(' .. sleeping for '+delta+' ms'); main.scheduler.Delay(delta); }; this.transitions = function () { var trans; trans = [ [undefined, this.init, function (thr) { return true }], [this.init, this.run, function (thr) { return true }], [this.run, this.run, function (thr) { return thr.nexttime<0; }], [this.run, this.sleep, function (thr) { return !dying; }], [this.run, this.terminate, function (thr) { return dying }], [this.sleep, this.run, function (thr) { return true; }] ]; return trans; }; this.context = main.scheduler.TaskContext('JAM World'+main.id, thr); } }; // Add an agent class constructor (@env can contain resolved constructor function variables). // typepf constructor = function|string world.prototype.addClass = function (name,constructor,env) { this.classes[name]=[ Aios.Code.makeSandbox(constructor,0,env), Aios.Code.makeSandbox(constructor,1,env), Aios.Code.makeSandbox(constructor,2,env), Aios.Code.makeSandbox(constructor,3,env) ]; } /** Add a node to the world. * */ world.prototype.addNode = function (node) { this.nodes.push(node); if (node.id) this.hash[node.id]=node; }; /** Connect two nodes in directions dir:node1->node2 and dir':node2->node1 * with two virtual channel links that are created here. * */ world.prototype.connect = function (dir,node1,node2,options) { if (!options) options={}; var chan=Aios.Chan.Virtual(node1,node2,dir,options); switch (dir) { case Aios.DIR.NORTH: node1.connections.north=chan.link1; node2.connections.south=chan.link2; break; case Aios.DIR.SOUTH: node1.connections.south=chan.link1; node2.connections.north=chan.link2; break; case Aios.DIR.WEST: node1.connections.west=chan.link1; node2.connections.east=chan.link2; break; case Aios.DIR.EAST: node1.connections.east=chan.link1; node2.connections.west=chan.link2; break; case Aios.DIR.NE: node1.connections.ne=chan.link1; node2.connections.sw=chan.link2; break; case Aios.DIR.NW: node1.connections.nw=chan.link1; node2.connections.se=chan.link2; break; case Aios.DIR.SE: node1.connections.se=chan.link1; node2.connections.nw=chan.link2; break; case Aios.DIR.SW: node1.connections.sw=chan.link1; node2.connections.ne=chan.link2; break; case Aios.DIR.UP: node1.connections.up=chan.link1; node2.connections.down=chan.link2; break; case Aios.DIR.DOWN: node1.connections.down=chan.link1; node2.connections.up=chan.link2; break; default: if (current) current.error='EINVALID'; throw 'CONNECT'; } chan.link2.on('agent',node1.receive.bind(node2)); chan.link1.on('agent',node2.receive.bind(node1)); chan.link2.on('signal',node1.handle.bind(node2)); chan.link1.on('signal',node2.handle.bind(node1)); chan.link1.end=node2.id; chan.link2.end=node1.id; return chan; }; /** Connect node via a port in direction dir:node->*. The endpoint node * will be * connected if the @snd parameter is specified. Otherwise only an unconnected port is created. * An endpoint can be later connected using the world.connectTo method (if provided by the interface). * * One uni- or bidirectional physical link is created and attached to the given node. * * typeof options={ * compress?:boolean, * oneway?:boolean, * proto:'udp'|'tcp'|'http'|'device', * device?:string, * rcv:url is node endpoint, * snd?:url is remote endpoint * } * with type url = ":" | ":" | "" * and ipport = (1-65535) | "*" */ world.prototype.connectPhy = function (dir,node,options) { var self=this,chan,name=Aios.DIR.to(dir); if (!options) options={}; chan=Aios.Chan.Physical(node,dir,options); switch (dir.tag||dir) { case 'DIR.IP': // Update routing table of router! if (!node.connections.ip) node.connections.ip=new Aios.Chan.iprouter(); node.connections.ip.addLink(chan.link); chan.router=node.connections.ip; break; default: if (!name) { if (current) current.error='ENOCHANNEL'; throw 'CONNECT'; } node.connections[name]=chan.link; } chan.link.on('agent',node.receive.bind(node)); chan.link.on('signal',node.handle.bind(node)); chan.link.on('class',function (obj){ for(var p in obj) self.addClass(p,obj[p].fun,obj[p].env)}); return chan; }; /** Connect a physical link of node @node to a remote endpoint (if curerently not connected) specified by the @dir parameter. * typeof @dir = {tag,ip?,device?} with tag='DIR.IP'|'DIR.NORTH',.. * */ world.prototype.connectTo = function (dir,node,options) { var chan,tokens,to=dir.ip,name=Aios.DIR.to(dir); if (!node) node=current.node; chan=node.connections[name]; if (chan && (chan.status(to) || !chan.connect)) chan=undefined; if (chan) chan.connect(to,options); } /** Check connectivity to a specific node or a set of nodes * */ world.prototype.connected = function (dir,node) { var name=Aios.DIR.to(dir),list; chan=node.connections[name]; switch (dir.tag||dir) { case Aios.DIR.tag.PATH: return chan && chan.status(dir.path); break; case Aios.DIR.tag.IP: // DIR.IP('*') returns all linked IP routes // DIR.IP('%') returns all linked nodes (names) return chan && chan.status(dir.ip); break; case Aios.DIR.tag.NODE: // DIR.NODE('*') returns all linked nodes on all connections! if (dir.node=='*') { // Check all conenctions for remote node information list=[]; if (node.connections.ip) list=list.concat(node.connections.ip.status('%')); return list; } else if (typeof dir.node == 'string') { // Return link (IP) if (node.connections.ip && node.connections.ip.lookup) { found=node.connections.ip.lookup(dir.node); return found?Aios.DIR.IP(found):none; } } break; case Aios.DIR.tag.DELTA: // a rough guess (no nw/sw/se/ne) if (dir.delta[0]==1) chan=node.connections.east; else if (dir.delta[0]==-1) chan=node.connections.west; else if (dir.delta[1]==1) chan=node.connections.north; else if (dir.delta[1]==-1) chan=node.connections.south; else if (dir.delta[2]==1) chan=node.connections.up; else if (dir.delta[2]==-1) chan=node.connections.down; return chan && chan.status(); break; default: return (chan && chan.status())||false; } } /** Disconnect a physical link of node @node to a remote endpoint (if curerently connected) specified by the @dir parameter. * */ world.prototype.disconnect = function (dir,node) { var chan; switch (dir.tag||dir) { case 'DIR.IP': if (node.connections.ip && node.connections.ip.status(dir.ip) && node.connections.ip.disconnect) node.connections.ip.disconnect(dir.ip); break; } } /** Find an agent in the world by it's id and class, or agents matching a regular expression. * */ world.prototype.getAgent = function (id,ac) { var res=Comp.obj.isRegex(id)?[]:undefined; for(var n in this.nodes) { var table=this.nodes[n].processes.table; for(var p in table) { if (!table[p]) continue; if (!res && table[p].agent,id==id && table[p].agent,ac=ac) return table[p].agent; if (res && id.test(table[p].agent.id)) res.push(table[p].agent); } } return res; }; /** Find a node in the world by it's id or nodes matching a regular expression. * */ world.prototype.getNode = function (nodeid) { if (Comp.obj.isRegex(nodeid)) { var res=[]; for(var n in this.nodes) { if (nodeid.test(this.nodes[n].id)) res.push(this.nodes[n]); } return res; } else return this.hash[nodeid]; }; world.prototype.info = function () { var obj={}; obj.agents=0; obj.transferred=0; for(var n in this.nodes) { obj.agents += this.nodes[n].processes.used; for (var l in this.nodes[n].connections) { if (this.nodes[n].connections[l]) obj.transferred += this.nodes[n].connections[l].count(); } } return obj; } world.prototype.init = function () { } /** Lookup nodes (using patterns and providing broker support) * */ world.prototype.lookup = function (dir,callback,node) { switch (dir.tag||dir) { case Aios.DIR.tag.PATH: if (node.connections.ip && node.connections.ip.lookup) return node.connections.ip.lookup(dir.path,callback); break; default: if (callback) callback(); } } world.prototype.print = function (summary) { var str='**** WORLD '+this.id+' ****'+NL; var res = Io.mem(); str += 'DATA='+int(res.data/1024)+' MB HEAP='+int(res.heap/1024)+' MB'+NL; for(var n in this.nodes) { str += this.nodes[n].print(summary); } return str; } /** Disconnect and remove a node from the world. * The node must be destroyed explicitly. * */ world.prototype.removeNode = function (nodeid) { var c,c2,conn,thenode,chan,node2; this.nodes=Comp.array.filter(this.nodes,function (node) { if (node.id==nodeid) thenode=node; return node.id!=nodeid; }); this.hash[nodeid]=undefined; if (thenode) for(c in thenode.connections) { conn=thenode.connections[c]; if (conn && conn.end) { node2=this.getNode(conn.end); if (node2) for (c2 in node2.connections) { // Unlink? if (node2.connections[c2] && node2.connections[c2].end==nodeid) node2.connections[c2]=undefined; } } } }; world.prototype.start = function () { var self=this; if (this.scheduler) { proc = new this.thread(0); this.context=proc.context; this.scheduler.Add(proc.context); } this.gc = setInterval(function () { var node,n,p; if (self.verbose>2) self.out('GC'); for(n in self.nodes) { node=self.nodes[n]; for(p in node.processes.gone) { if (node.processes.gone[p]) { node.processes.gone[p].tmo -= 500; if (node.processes.gone[p].tmo<=0) node.processes.gone[p]=undefined; } } } },500); } world.prototype.stop = function () { } var World = function (nodes,options) { var obj=new world(nodes,options); current.world=obj; return obj; } module.exports = { options:options, World:World, current:function (module) { current=module.current; Aios=module} } }; BundleModuleCode['jam/chan']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 09-02-16 by sbosse. ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.13.2 ** ** $INFO: ** ** JavaScript AIOS Agent Node Communication Module offering P2P communication with another nodes ** ** 1. Virtual Link: Connecting virtual (logical) nodes using buffers ** ** 2. Physical Link: Connecting physical nodes (on the same physical host or remote hosts) ** using AMP protocol and IP communication (including endpoint pairing across NAT routers ** using a rendezvous broker service) ** ** 3. Physical Link: Connecting node processes (in a cluster on the same physical host) using process streams ** ** For IP-based communication ports an internal IP router is provided offering operation ** of multiple ports and connections. ** ** Communciation link object provided by 1.-3.: ** ** type link = { ** on: method (@event,@handler) with @event={'agent'|'signal'|'class'}, ** send: method (@msg) with @msg:{agent:string|object,to:dir}|{signal:string|object},to:dir}, ** status: method (dir) -> boolean, ** count: method () -> number is returning number of received (phy only) and sent bytes, ** connect?:method (@to), ** disconnect?:method (@to), ** start?:method, ** stop?:method ** } ** ** ** Events, emitter: link+ link- error(err="link"|string,arg?) ** ** ** TODO: ** - Phy capability protected communication and operations ** ** $ENDOFINFO */ var Io = Require('com/io'); var Lz = Require('os/lz-string'); var Comp = Require('com/compat'); var Buf = Require('dos/buf'); var Net = Require('dos/network'); var Command = Net.Command; var Status = Net.Status; var current=none; var Aios=none; var CBL = Require('com/cbl'); var Amp = Require('jam/amp'); var options = { verbose:1, version:'1.13.2' } module.exports.options=options; var SLINK = { INIT:'INIT', INITED:'INITED', RUNNING:'RUNNING' } /******************** * Virtual Circuit ******************** */ var virtual= function (node1,node2,dir,options) { var self=this; this.node1=node1; this.node2=node2; this.dir=dir; // node1 -> node2 this.buffer1=[]; this.buffer2=[]; this.count1={rcv:0,snd:0}; this.count2={rcv:0,snd:0}; this.compress=options.compress; /* NEWCOMM */ this.handler1=[]; this.handler2=[]; // External API this.link1 = { on: function (event,callback) { var data; self.handler1[event]=callback; if (event=='agent' && self.buffer2.length>0) { // Agent receiver data=Comp.array.pop(self.buffer2); if (self.compress) data=Lz.decompress(data); callback(data); } }, send: function (msg) { var data; if (msg.agent) { // Agent migration data=msg.agent; if (self.compress) data=Lz.compress(data); if (self.handler2.agent) self.handler2.agent(self.compress?Lz.decompress(data):data); else self.buffer1.push(data); if (data.length) self.count1.snd += data.length; else self.count1.snd++; } else if (msg.signal) { // Signal propagation - signals are not queued data=msg.signal; if (data.length) self.count1.snd += data.length; else self.count1.snd++; if (self.handler2.signal) self.handler2.signal(data); } }, count: function () {return self.count1.snd}, status: function () {return true}, // Linked? virtual:true } this.link2 = { on: function (event,callback) { var data; self.handler2[event]=callback; if (event=='agent' && self.buffer1.length>0) { // Agent receiver data=Comp.array.pop(self.buffer1); if (self.compress) data=Lz.decompress(data); callback(data); } }, send: function (msg) { var data; if (msg.agent) { // Agent migration data=msg.agent; if (self.compress) data=Lz.compress(data); if (self.handler1.agent) self.handler1.agent(self.compress?Lz.decompress(data):data); else self.buffer2.push(data); if (data.length) self.count2.snd += data.length; else self.count2.snd++; } else if (msg.signal) { // Signal propagation - signals are not queued data=msg.signal; if (data.length) self.count2.snd += data.length; else self.count2.snd++; if (self.handler1.signal) self.handler1.signal(data); } }, count: function () {return self.count2.snd}, status: function () {return true}, // Linked? virtual:true } }; virtual.prototype.init = function () {}; virtual.prototype.start = function () {}; virtual.prototype.stop = function () {}; var Virtual = function (node1,node2,dir,options) { var obj=new virtual(node1,node2,dir,options); return obj; } module.exports.Virtual=Virtual; module.exports.current=function (module) { current=module.current; Aios=module; Amp.current(module); }; if (global.config.nonetwork) return; /******************************* PHYSICAL *************************************/ /********************* ** Physical Circuit ********************* * * Using UDP-AMP or process stream connections (TODO) * typeof options={ * broker?:url is UDP hole punching rendezvous broker * compress?:boolean, * device?:string, * name?:string is optional name of the comm. port e.g. the JAM node name, * on?: { } is event handler object, * oneway?:boolean, * out?:function, * proto?:'udp'|'tcp'|'http'|'hardware', * rcv:url is this endpoint address, * snd?:url is remote endpoint address, * stream?:boolean, * verbose? * } * with type url = ":" | ":" | "" * and type ipport = (1-65535) | "*" */ var physical= function (node,dir,options) { var self=this; options=checkOptions(options,{}); this.options=options; this.ip=none; if (options.rcv) this.ip=url2addr(options.rcv); else this.ip={address:'localhost',port:undefined}; this.node=node; this.dir=dir; // outgoing port (node -> dst), e.g., IP this.count=0; this.broker=options.broker; this.mode=this.options.compress?Amp.AMMode.AMO_COMPRESS:0; this.state = SLINK.INIT; this.events = []; this.out=Aios.print; this.amp= Amp.Amp({ broker:options.broker?url2addr(options.broker,this.ip.address):undefined, dir:this.dir, mode:this.options.mode, name:this.options.name, node:node, oneway:this.options.oneway, multicast:this.options.multicast, proto:this.options.proto, rcv:this.ip, snd:options.snd?url2addr(options.snd,'127.0.0.1'):undefined, sock:options.sock, verbose:options.verbose, }); // External API this.link = { on: function (event,callback) { self.events[event]=callback; }, send: function (msg,to) { var buf,data,addr=to?url2addr(to,'127.0.0.1'):{}; if (msg.agent) { data=msg.agent; // string of JSON+ buf=Buf.Buffer(); if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data); Buf.buf_put_string(buf,data); // function request(cmd:integer,msg:Buffer,snd?:address) self.amp.request(Command.PS_MIGRATE, buf, self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined); } else if (msg.signal) { data=msg.signal; // string of JSON // Signal propagation buf=Buf.Buffer(); if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data); Buf.buf_put_string(buf,data); // function request(cmd:integer,msg:Buffer,snd?:address) self.amp.request(Command.PS_SIGNAL, buf, self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined); } }, count: function () {return self.amp.count.rcv+self.amp.count.snd}, status : function (to) { if (self.amp) { if (to) to=url2addr(to); return to?self.amp.status(to.address,to.port):self.amp.status(); } }, // Linked? ip:this.ip, mode:this.amp.mode } /** Connect to remote endpoint with optional capability key protection * typeof @to = "" | ":" | "" * typeof @key = string "()[]" */ this.link.connect=function (to,key) { var addr=url2addr(to,self.ip.address); self.amp.link(addr,true,key); }; // Disconnect remote endpoint this.link.disconnect=function (to) { var tokens; if (!to){ if (self.amp.snd && self.amp.snd.address && self.amp.snd.port) self.amp.unlink(self.amp.snd); } else { var addr=url2addr(to,self.ip.address); self.amp.unlink(addr); } }; this.link.init=function (cb) { if (self.state!=SLINK.INIT) return cb?cb():null; self.state=SLINK.INITED; return self.amp.init(cb); } this.link.start=function (cb) { if (self.state!=SLINK.INITED) return cb?cb():null; self.state=SLINK.RUNNING; return self.amp.start(cb); } this.link.stop=function (cb) { if (self.state!=SLINK.RUNNING) return cb?cb():null; self.state=SLINK.INITED; return self.amp.stop(cb); } if (this.broker) this.link.lookup = function (path,callback) { if (self.amp.lookup) self.amp.lookup(path,callback); else if (callback) callback([]); } // Install route notification propagation to router (if installed) this.amp.on('route+',function (arg,arg2) { if (self.router) self.router.add(arg,self.link,arg2); self.emit('link+',arg,arg2); Aios.emit('link+',arg,arg2); }); this.amp.on('route-',function (arg) { if (self.router) self.router.delete(arg,self.link); self.emit('link-',arg); Aios.emit('link-',arg); }); this.amp.on('error',function (err,arg) { self.emit('error',err,arg); }); if (options.on) { for(var p in options.on) this.on(p,options.on[p]); } // Register message receiver handler this.amp.receiver(function (handler) { var code,name,env,agentid,stat,obj; if (!handler) return; if (self.options.verbose>2) { self.out('AMP: got request:'+ Io.inspect(handler)) }; switch (handler.cmd) { case Command.PS_MIGRATE: code = Buf.buf_get_string(handler.buf); // console.log(code); // console.log(myJam.amp.url(handler.remote)) if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); if (self.events.agent) self.events.agent(code,false,handler.remote); break; case Command.PS_CREATE: code = Buf.buf_get_string(handler.buf); // console.log(code); // console.log(myJam.amp.url(handler.remote)) if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); if (self.events.agent) self.events.agent(code,true); break; case Command.PS_WRITE: name = Buf.buf_get_string(handler.buf); code = Buf.buf_get_string(handler.buf); env = Buf.buf_get_string(handler.buf); // console.log(code); // console.log(myJam.amp.url(handler.remote)) if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); obj={}; try {eval("env = "+env)} catch (e) {}; obj[name]={ fun:code, env:env } if (self.events['class']) self.events['class'](obj); break; case Command.PS_SIGNAL: // TODO code = Buf.buf_get_string(handler.buf); // console.log(code); if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); if (self.events.signal) self.events.signal(code,handler.remote); break; } }); }; physical.prototype.emit = function (event,arg,aux1,aux2) { if (this.events[event]) this.events[event](arg,aux1,aux2)}; physical.prototype.on = function (event,handler) {this.events[event]=handler}; physical.prototype.init = function (callback) { return this.link.init(callback)}; physical.prototype.start = function (callback) {return this.link.start(callback)}; physical.prototype.stop = function () {return this.link.stop()}; var Physical = function (node,dir,options) { var obj=new physical(node,dir,options); return obj; } module.exports.Physical=Physical; /************************* ** IP UTILS *************************/ /* url = ":" | ":" | "" * and ipport = (1-65535) | "*" */ function url2addr(url,defaultIP) { var addr={address:defaultIP||'localhost',proto:'UDP',port:undefined}, parts = url.toString().split(':'); if (parts.length==1) { if (Comp.string.isNumeric(parts[0])) addr.port=Number(parts[0]); // port number else if (parts[0].indexOf('-') != -1) addr.port=parts[0]; // port range p0-p1 else if (parts[0]=='*') addr.port=undefined; // any port else addr.address=parts[0]; // ip/url } else return {address:parts[0],port:parts[1]=='*'?undefined:Number(parts[1])||parts[1]}; return addr; }; function addr2url(addr) { return addr.address+':'+(addr.port?addr.port:'*') }; function addrequal(addr1,addr2) { return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port; } function resolve (url,defaultIP) { var addr=url2addr(url,defaultIP); if (defaultIP=='127.0.0.1' && addr.address=='localhost') addr.address=defaultIP; return addr2url(addr) } function ipequal(ip1,ip2) { if (ip1==undefined || ip2==undefined) return false; else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) && (Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true; else return ip1==ip2; } /*********************************************** * IP Router using AMP/UDP/HTTP links * Entry point for move and send operations DIR.IP *********************************************** */ function iprouter() { this.routingTable={}; this.nodeTable={}; this.links=[]; } // Add route and link to be used for the route (and optional remote node id) iprouter.prototype.add = function (to,link,node) { to=resolve(to,'127.0.0.1'); if (options.verbose) Aios.print('[IP] iprouter: add route '+addr2url(link.ip)+' -> '+to+(node?'#'+node:'')); this.routingTable[to]=link; this.nodeTable[to]=node; } // Add link device iprouter.prototype.addLink = function (link) { if (!link.ip) link.ip='*'; if (options.verbose) Aios.print('[IP] iprouter: add link '+addr2url(link.ip)); this.links.push(link); } // Connect to a remote endpoint iprouter.prototype.connect = function (to,key) { var link,p; to=resolve(to,'127.0.0.1'); // Search for an unconnected port!? for(p in this.links) { if (this.links[p].status(to)) return; if (!(this.links[p].mode&Amp.AMMode.AMO_MULTICAST) && this.links[p].status()) continue; link=this.links[p]; break; } if (link && link.connect) { link.connect(to,key); } } // iprouter.prototype.count = function (dest) { var res=0; for(var i in this.links) { res += this.links[i].count(); } return res; } // Remove route iprouter.prototype.delete = function (to) { to=resolve(to,'127.0.0.1'); if (this.routingTable[to]) { if (options.verbose) Aios.print('[IP] iprouter: remove route '+addr2url(this.routingTable[to].ip)+ ' -> ' + to); delete this.routingTable[to]; delete this.nodeTable[to]; } } // Disconnect a remote endpoint iprouter.prototype.disconnect = function (to) { // Search for a connected port! to=resolve(to,'127.0.0.1'); if (this.routingTable[to] && this.routingTable[to].status(to)) { this.routingTable[to].disconnect(to); } } /** Lookup a IP:PORT address pair of a nodeid OR contact a broker to get reachable * nodeid-IP address pairs * */ iprouter.prototype.lookup = function (nodeid,callback) { var p,result=[],n=0; // Broker lookup with a pattern like /domain/* (DIR.PATH) if (nodeid.indexOf('*')!=-1) { // TODO for (p in this.links) { if (this.links[p].lookup) { n++; this.links[p].lookup(nodeid,function (_result) { if (_result && _result.length) result=result.concat(_result); n--; if (n==0) callback(result); }); } } } else for(p in this.nodeTable) { if (this.nodeTable[p] == nodeid && this.routingTable[p]) return p; } } /** Try to find our local IP address. * */ iprouter.prototype.ip = function () { for(var i in this.links) { if (this.links[i].ip) return this.links[i].ip; } } /** Reverse lookup: Get the nodeid from an IP:PORT address * typeof @ip = string */ iprouter.prototype.reverse = function (ip) { return this.nodeTable[ip]; } /** Send a message * */ iprouter.prototype.send = function (msg) { msg.to=resolve(msg.to,'127.0.0.1'); if (this.routingTable[msg.to]) { this.routingTable[msg.to].send(msg,msg.to); } else { } } /** Start all attached devices * */ iprouter.prototype.start = function (callback) { var cbl=CBL(callback||function(){}); this.links.forEach(function (link) { cbl.push(function (next) {link.start(next)}); }); cbl.start(); } // Check status of link in given direction (or any direction dest==undefined) // OR return all current registered routes string [] (dest=='*')! // OR return all current connected nodes string [] (dest=='%')! // OR return all current registered links (ip) string [] (dest=='$')! iprouter.prototype.status = function (dest) { var res,p; if (dest==undefined) { // Any registered routes? for(p in this.routingTable) { if (this.routingTable[p]) return true } } else if (dest=='*') { res=[]; for(p in this.routingTable) { if (this.routingTable[p]) res.push(p) } return res; } else if (dest=='%') { res=[]; for(p in this.nodeTable) { if (this.nodeTable[p] && this.routingTable[p]) res.push(this.nodeTable[p]); } return res; } else { dest=resolve(dest,'127.0.0.1'); if (this.routingTable[dest]) return this.routingTable[dest].status(dest); else return false; } return false; } // Stop all attached devices iprouter.prototype.stop = function (callback) { var cbl=CBL(callback||function(){}); this.links.forEach(function (link) { cbl.push(function (next) {link.stop(next)}); }); cbl.start(); } module.exports.iprouter=iprouter; module.exports.Command=Command module.exports.Status=Status module.exports.url2addr=url2addr; module.exports.addr2url=addr2url; }; BundleModuleCode['os/lz-string']=function (module,exports,global,process){ // Copyright (c) 2013 Pieroxy // This work is free. You can redistribute it and/or modify it // under the terms of the WTFPL, Version 2 // For more information see LICENSE.txt or http://www.wtfpl.net/ // // For more information, the home page: // http://pieroxy.net/blog/pages/lz-string/testing.html // // LZ-based compression algorithm, version 1.4.4 var LZString = (function() { // private property var f = String.fromCharCode; var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; var baseReverseDic = {}; function getBaseValue(alphabet, character) { if (!baseReverseDic[alphabet]) { baseReverseDic[alphabet] = {}; for (var i=0 ; i>> 8; buf[i*2+1] = current_value % 256; } return buf; }, //decompress from uint8array (UCS-2 big endian format) decompressFromUint8Array:function (compressed) { if (compressed===null || compressed===undefined){ return LZString.decompress(compressed); } else { var buf=new Array(compressed.length/2); // 2 bytes per character for (var i=0, TotalLen=buf.length; i> 1; } } else { value = 1; for (i=0 ; i> 1; } } context_enlargeIn--; if (context_enlargeIn == 0) { context_enlargeIn = Math.pow(2, context_numBits); context_numBits++; } delete context_dictionaryToCreate[context_w]; } else { value = context_dictionary[context_w]; for (i=0 ; i> 1; } } context_enlargeIn--; if (context_enlargeIn == 0) { context_enlargeIn = Math.pow(2, context_numBits); context_numBits++; } // Add wc to the dictionary. context_dictionary[context_wc] = context_dictSize++; context_w = String(context_c); } } // Output the code for w. if (context_w !== "") { if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { if (context_w.charCodeAt(0)<256) { for (i=0 ; i> 1; } } else { value = 1; for (i=0 ; i> 1; } } context_enlargeIn--; if (context_enlargeIn == 0) { context_enlargeIn = Math.pow(2, context_numBits); context_numBits++; } delete context_dictionaryToCreate[context_w]; } else { value = context_dictionary[context_w]; for (i=0 ; i> 1; } } context_enlargeIn--; if (context_enlargeIn == 0) { context_enlargeIn = Math.pow(2, context_numBits); context_numBits++; } } // Mark the end of the stream value = 2; for (i=0 ; i> 1; } // Flush the last char while (true) { context_data_val = (context_data_val << 1); if (context_data_position == bitsPerChar-1) { context_data.push(getCharFromInt(context_data_val)); break; } else context_data_position++; } return context_data.join(''); }, decompress: function (compressed) { if (compressed == null) return ""; if (compressed == "") return null; return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); }, _decompress: function (length, resetValue, getNextValue) { var dictionary = [], next, enlargeIn = 4, dictSize = 4, numBits = 3, entry = "", result = [], i, w, bits, resb, maxpower, power, c, data = {val:getNextValue(0), position:resetValue, index:1}; for (i = 0; i < 3; i += 1) { dictionary[i] = i; } bits = 0; maxpower = Math.pow(2,2); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } switch (next = bits) { case 0: bits = 0; maxpower = Math.pow(2,8); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } c = f(bits); break; case 1: bits = 0; maxpower = Math.pow(2,16); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } c = f(bits); break; case 2: return ""; } dictionary[3] = c; w = c; result.push(c); while (true) { if (data.index > length) { return ""; } bits = 0; maxpower = Math.pow(2,numBits); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } switch (c = bits) { case 0: bits = 0; maxpower = Math.pow(2,8); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } dictionary[dictSize++] = f(bits); c = dictSize-1; enlargeIn--; break; case 1: bits = 0; maxpower = Math.pow(2,16); power=1; while (power!=maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { data.position = resetValue; data.val = getNextValue(data.index++); } bits |= (resb>0 ? 1 : 0) * power; power <<= 1; } dictionary[dictSize++] = f(bits); c = dictSize-1; enlargeIn--; break; case 2: return result.join(''); } if (enlargeIn == 0) { enlargeIn = Math.pow(2, numBits); numBits++; } if (dictionary[c]) { entry = dictionary[c]; } else { if (c === dictSize) { entry = w + w.charAt(0); } else { return null; } } result.push(entry); // Add w+entry[0] to the dictionary. dictionary[dictSize++] = w + entry.charAt(0); enlargeIn--; w = entry; if (enlargeIn == 0) { enlargeIn = Math.pow(2, numBits); numBits++; } } } }; return LZString; })(); if (typeof define === 'function' && define.amd) { define(function () { return LZString; }); } else if( typeof module !== 'undefined' && module != null ) { module.exports = LZString } }; BundleModuleCode['dos/buf']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 29-4-15 by sbosse. ** $RCS: $Id$ ** $VERSION: 1.1.5 ** ** $INFO: ** ** DOS: Buffer Management ** ** $ENDOFINFO */ "use strict"; var log = 0; var util = Require('util'); var Io = Require('com/io'); var Comp = Require('com/compat'); var String = Comp.string; var Array = Comp.array; var Perv = Comp.pervasives; var des48 = Require('dos/des48'); var Rand = Comp.random; var Net = Require('dos/network'); var Status = Net.Status; var Fs = Require('fs'); var SIZEOF_INT16 = 2; var SIZEOF_INT32 = 4; var PORT_SIZE = 6; var PRIV_SIZE = PORT_SIZE+SIZEOF_INT32; var CAP_SIZE = PORT_SIZE+PRIV_SIZE; /** Generic buffer, union with rpcio object ** Argument: optional, hex-ascci string or number (size), passed to Buffer instantiation * * * @param {number|string|Buffer} [data] * @constructor */ var buffer = function (data) { var size; this.pos=0; if (Comp.isNumber(data)) { this.data=new Buffer(data); } else if (Comp.isString(data)) { this.data=new Buffer(''); buf_of_hex(this,data) } else if (Comp.isArray(data)) { this.data=new Buffer(data); } else if (typeof data == "object" && data.constructor === Buffer) { this.data=data; } else this.data=new Buffer(''); }; /** Extend a buffer to new size (buf.pos+off). * * @param buf * @param off */ function buf_extend(buf,off) { if (buf.data.length<(buf.pos+off)) { buf.data=Buffer.concat([buf.data,new Buffer(off-(buf.data.length-buf.pos))]); } } /** Expand buffer to new size. * * @param buf * @param size */ function buf_expand(buf,size) { if (buf.data.lengthsize) { buf.data=Buffer.slice(buf.data,size); } } /* ** BUFFER encoding and decoding of native data types ** Supported objects: rpcio, buffer. ** Supported native data types: int16, int32, string, float, port, private, capability, ... ** ALL buffer data is stored in byte buffers that extends automatically (buf_put_XX). */ function buf_put_string (buf,str) { buf_extend(buf,str.length+1); for(var i=0;i> 8) & 0xff; buf.pos=buf.pos+2; } function buf_get_int16 (buf) { var n=0; var end=buf.data.length; if (buf.pos+2 <= end) { n = buf.data[buf.pos]; n = n | (buf.data[buf.pos+1] << 8); buf.pos = buf.pos + 2; if (n&0x8000) return (n-0x10000); else return (n); } else throw Status.BUF_OVERFLOW; } function buf_put_int32 (buf,n) { buf_extend(buf,4); buf.data[buf.pos]=n & 0xff; buf.data[buf.pos+1]=(n >> 8) & 0xff; buf.data[buf.pos+2]=(n >> 16) & 0xff; buf.data[buf.pos+3]=(n >> 24) & 0xff; buf.pos=buf.pos+4; } function buf_get_int32 (buf) { var n=0; var end=buf.data.length; if (buf.pos+4 <= end) { n = buf.data[buf.pos]; n = n | (buf.data[buf.pos+1] << 8); n = n | (buf.data[buf.pos+2] << 16); n = n | (buf.data[buf.pos+3] << 24); buf.pos = buf.pos + 4; // TBD: Sign check??? return (n); } else throw Status.BUF_OVERFLOW; } function buf_put_port (buf,port) { buf_extend(buf,Net.PORT_SIZE); for(var i=0;i> 8) & 0xff; buf.data[buf.pos+2]=(priv.prv_obj >> 16) & 0xff; buf.data[buf.pos+3]=priv.prv_rights & 0xff; buf.pos=buf.pos+4; buf_put_port(buf,priv.prv_rand); } function buf_get_priv (buf,priv) { var n; var end=buf.data.length; if (buf.pos+(Net.PRIV_SIZE) <= end) { if (priv == undefined) priv = Net.Private(); n = buf.data[buf.pos]; n = n | (buf.data[buf.pos+1] << 8); n = n | (buf.data[buf.pos+2] << 16); priv.prv_obj=n; priv.prv_rights=buf.data[buf.pos+3]; buf.pos=buf.pos+4; priv.prv_rand=buf_get_port(buf); return priv; } else throw Status.BUF_OVERFLOW; } function buf_put_cap (buf,cap) { buf_put_port(buf,cap.cap_port); buf_put_priv(buf,cap.cap_priv); } function buf_get_cap (buf,cap) { var end=buf.data.length; if (buf.pos+(Net.CAP_SIZE) <= end) { if (cap == undefined) cap = Net.Capability(); cap.cap_port=buf_get_port(buf); buf_get_priv(buf,cap.cap_priv); return cap; } else throw Status.BUF_OVERFLOW; } function buf_put_hdr (buf,hdr) { buf_put_port(buf,hdr.h_port); buf_put_priv(buf,hdr.h_priv); buf_put_int32(buf,hdr.h_command); buf_put_int32(buf,hdr.h_status); } function buf_get_hdr (buf,hdr) { if (hdr==undefined) hdr=Net.Header(); hdr.h_port=buf_get_port(buf); buf_get_priv(buf,hdr.h_priv); hdr.h_command=buf_get_int32(buf); hdr.h_status=buf_get_int32(buf); return hdr; } /** TODO: buf blit * * @param buf * @param bufsrc * @param [srcoff] * @param [len] */ function buf_put_buf (buf,bufsrc,srcoff,len) { if (srcoff==undefined) srcoff=0; if (len==undefined) len=bufsrc.data.length; buf_extend(buf,len); for(var i=0;i= buf.data.length) buf_expand(buf,off+1); buf.pos=off; } /** * @param {file} fd * @param {buffer} buf * @param {number} [off] file offset * @param {number} [len] * @returns {number} n */ function buf_write (fd,buf,off,len) { var n; if (off==undefined) n=Io.write_buf(fd,buf.data,0,buf.data.length); else { if (len==undefined) len=buf.data.length; n=Io.write_buf(fd,buf.data,0,len,off); } return n; } /** * @param {file} fd * @param {buffer} buf * @param {number} off file offset * @param {number} len * @returns {number} n */ function buf_read (fd,buf,off,len) { var n; buf_expand(buf,len); n=Io.read_buf(fd,buf.data,0,len,off); buf.pos=0; return n; } function buf_print(buf) { var str='['; for(var i=0;i0) str=str+','+buf.data[i]; else str=str+buf.data[i]; } return str+']'+buf.pos+':'+buf.data.length; } function buf_set (buf,off,byte) { if (off >= buf.data.length) buf_expand(buf,off+1); buf.data[off]=byte; } function buf_get (buf,off) { return buf.data[off]; } /** Reset buffer * * @param buf */ function buf_init (buf) { buf.data=new Buffer(''); buf.pos=0; } function buf_copy (dst,src) { dst.data=new Buffer(src.data); dst.pos=0; } function buf_blit (dst,dstoff,src,srcoff,len) { buf_expand(dst,dstoff+len); src.data.copy(dst.data,dstoff,srcoff,srcoff+len); dst.pos=0; } /** * * @type {{SIZEOF_INT16: number, SIZEOF_INT32: number, PORT_SIZE: number, PRIV_SIZE: number, CAP_SIZE: number, Buffer: Function, buf_put_string: buf_put_string, buf_put_int16: buf_put_int16, buf_put_int32: buf_put_int32, buf_put_port: buf_put_port, buf_put_priv: buf_put_priv, buf_put_cap: buf_put_cap, buf_put_hdr: buf_put_hdr, buf_put_buf: buf_put_buf, buf_put_bytes: buf_put_bytes, buf_get_string: buf_get_string, buf_get_int16: buf_get_int16, buf_get_int32: buf_get_int32, buf_get_port: buf_get_port, buf_get_priv: buf_get_priv, buf_get_cap: buf_get_cap, buf_get_hdr: buf_get_hdr, buf_get_buf: buf_get_buf, buf_get_bytes: buf_get_bytes, buf_pad: buf_pad, buf_set: buf_set, buf_get: buf_get, buf_set_pos: buf_set_pos, buf_init: buf_init, buf_blit: buf_blit, buf_copy: buf_copy, buf_extend: buf_extend, buf_expand: buf_expand, buf_shrink: buf_shrink, buf_read: buf_read, buf_write: buf_write, buf_print: buf_print, buf_to_hex: buf_to_hex, buf_of_hex: buf_of_hex, buf_to_str: buf_to_str, buf_of_str: buf_of_str}} */ module.exports = { SIZEOF_INT16: SIZEOF_INT16, SIZEOF_INT32: SIZEOF_INT32, PORT_SIZE: PORT_SIZE, PRIV_SIZE: PRIV_SIZE, CAP_SIZE: CAP_SIZE, /** * * @param {number|string|Buffer} [data] * @returns {buffer} */ Buffer: function Buffer(data) { var obj = new buffer(data); Object.preventExtensions(obj); return obj; }, // Buffer data operations buf_put_string:buf_put_string, buf_put_int16:buf_put_int16, buf_put_int32:buf_put_int32, buf_put_port:buf_put_port, buf_put_priv:buf_put_priv, buf_put_cap:buf_put_cap, buf_put_hdr:buf_put_hdr, buf_put_buf:buf_put_buf, buf_put_bytes:buf_put_bytes, buf_get_string:buf_get_string, buf_get_int16:buf_get_int16, buf_get_int32:buf_get_int32, buf_get_port:buf_get_port, buf_get_priv:buf_get_priv, buf_get_cap:buf_get_cap, buf_get_hdr:buf_get_hdr, buf_get_buf:buf_get_buf, buf_get_bytes:buf_get_bytes, buf_pad:buf_pad, buf_set:buf_set, buf_get:buf_get, buf_set_pos:buf_set_pos, buf_init:buf_init, buf_blit:buf_blit, buf_copy:buf_copy, buf_extend:buf_extend, buf_expand:buf_expand, buf_shrink:buf_shrink, // Buffer IO buf_read:buf_read, buf_write:buf_write, buf_print:buf_print, // Conversion buf_to_hex:buf_to_hex, buf_of_hex:buf_of_hex, buf_to_str:buf_to_str, buf_of_str:buf_of_str, length: function(buf) { if (buf.data==undefined) return 0; else return buf.data.length; } }; }; BundleModuleCode['dos/network']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR. ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 3-5-15 by sbosse. ** $VERSION: 1.2.6 ** ** $INFO: ** ** DOS: Networking, Commands, Status/Error codes, .. ** ** $ENDOFINFO */ "use strict"; var log = 0; var util = Require('util'); var Io = Require('com/io'); var Comp = Require('com/compat'); var String = Comp.string; var Array = Comp.array; var Perv =Comp.pervasives; var Des48 = Require('dos/des48'); var Base64 = Require('os/base64'); var Rand = Comp.random; var Fs = Require('fs'); //var xmldoc = Require('xmldoc'); function pad(str,size) { while (str.length < (size || 2)) {str = "0" + str;} return str; } /** Direction * var Direction = { NORTH:1, WEST:2, EAST:3, SOUTH:4, ORIGIN:5, tostring:function (i) { switch (i) { case 1: return 'NORTH'; case 2: return 'WEST'; case 3: return 'EAST'; case 4: return 'SOUTH'; case 5: return 'ORIGIN'; default: return 'Direction?'; } } }; */ // Standard Object Service var STD_FIRST_COM = 1000; var STD_LAST_COM = 1999; var STD_FIRST_ERR = (-STD_FIRST_COM); var STD_LAST_ERR = (-STD_LAST_COM); // File Server var AFS_FIRST_COM = 2000; var AFS_LAST_COM = 2099; var AFS_FIRST_ERR = (-AFS_FIRST_COM); var AFS_LAST_ERR = (-AFS_LAST_COM); var AFS_REQBUFSZ = 1024*32; // Directory and Name Server var DNS_FIRST_COM = 2100; var DNS_LAST_COM = 2199; var DNS_FIRST_ERR = (-DNS_FIRST_COM); var DNS_LAST_ERR = (-DNS_LAST_COM); var DNS_MAXCOLUMNS = 4; // System Process Server (incl. Agent Platform Manager) var PS_FIRST_COM = 2200; var PS_LAST_COM = 2299; var PS_FIRST_ERR = (-PS_FIRST_COM); var PS_LAST_ERR = (-PS_LAST_COM); // Broker Server var BR_FIRST_COM = 2300; var BR_LAST_COM = 2399; var BR_FIRST_ERR = (-BR_FIRST_COM); var BR_LAST_ERR = (-BR_LAST_COM); /** RPC Status * * @enum {number} */ var Status = { STD_OK:0, STD_CAPBAD : STD_FIRST_ERR, STD_COMBAD : (STD_FIRST_ERR-1), STD_ARGBAD : (STD_FIRST_ERR-2), STD_NOTNOW : (STD_FIRST_ERR-3), STD_NOSPACE : (STD_FIRST_ERR-4), STD_DENIED : (STD_FIRST_ERR-5), STD_NOMEM : (STD_FIRST_ERR-6), STD_EXISTS : (STD_FIRST_ERR-7), STD_NOTFOUND : (STD_FIRST_ERR-8), STD_SYSERR : (STD_FIRST_ERR-9), STD_INTR : (STD_FIRST_ERR-10), STD_OVERFLOW : (STD_FIRST_ERR-11), STD_WRITEPROT : (STD_FIRST_ERR-12), STD_NOMEDIUM : (STD_FIRST_ERR-13), STD_IOERR : (STD_FIRST_ERR-14), STD_WRONGSRV : (STD_FIRST_ERR-15), STD_OBJBAD : (STD_FIRST_ERR-16), STD_UNKNOWN : (STD_FIRST_ERR-17), DNS_UNAVAIL : (DNS_FIRST_ERR -1), DNS_NOTEMPTY : (DNS_FIRST_ERR -2), DNS_UNREACH : (DNS_FIRST_ERR -3), DNS_CLASH : (DNS_FIRST_ERR -4), RPC_FAILURE : -1, BUF_OVERFLOW : -2, print: function(stat) { switch(stat) { case Status.STD_OK : return 'STD_OK'; case Status.STD_CAPBAD : return 'STD_CAPBAD'; case Status.STD_COMBAD : return 'STD_COMBAD'; case Status.STD_ARGBAD : return 'STD_ARGBAD'; case Status.STD_NOTNOW : return 'STD_NOTNOW'; case Status.STD_NOSPACE : return 'STD_NOSPACE'; case Status.STD_DENIED : return 'STD_DENIED'; case Status.STD_NOMEM : return 'STD_NOMEM'; case Status.STD_EXISTS : return 'STD_EXISTS'; case Status.STD_NOTFOUND : return 'STD_NOTFOUND'; case Status.STD_SYSERR : return 'STD_SYSERR'; case Status.STD_INTR : return 'STD_INTR'; case Status.STD_OVERFLOW : return 'STD_OVERFLOW'; case Status.STD_WRITEPROT : return 'STD_WRITEPROT'; case Status.STD_NOMEDIUM : return 'STD_NOMEDIUM'; case Status.STD_IOERR : return 'STD_IOERR'; case Status.STD_WRONGSRV : return 'STD_WRONGSRV'; case Status.STD_OBJBAD : return 'STD_OBJBAD'; case Status.STD_UNKNOWN : return 'STD_UNKNOWN'; case Status.DNS_UNAVAIL : return 'DNS_UNAVAIL'; case Status.DNS_NOTEMPTY : return 'DNS_NOTEMPTY'; case Status.DNS_UNREACH : return 'DNS_UNREACH'; case Status.DNS_CLASH : return 'DNS_CLASH'; case Status.RPC_FAILURE : return 'RPC_FAILURE'; case Status.BUF_OVERFLOW : return 'BUF_OVERFLOW'; default : return '"'+stat+'"'; } } }; /** RPC Command * * @enum {number} */ var Command = { /* ** Standard Commands */ STD_MONITOR : STD_FIRST_COM, STD_AGE : (STD_FIRST_COM+1), STD_COPY : (STD_FIRST_COM + 2), STD_DESTROY : (STD_FIRST_COM + 3), STD_INFO : (STD_FIRST_COM + 4), STD_RESTRICT : (STD_FIRST_COM + 5), STD_STATUS : (STD_FIRST_COM + 6), STD_TOUCH : (STD_FIRST_COM + 7), STD_GETPARAMS : (STD_FIRST_COM + 8), STD_SETPARAMS : (STD_FIRST_COM + 9), STD_NTOUCH : (STD_FIRST_COM + 10), STD_EXIT : (STD_FIRST_COM + 11), STD_RIGHTS : (STD_FIRST_COM + 12), STD_EXEC : (STD_FIRST_COM + 13), STD_LOCATION : (STD_FIRST_COM + 20), STD_LABEL : (STD_FIRST_COM + 21), /* ** AFC Commands */ AFS_CREATE : (AFS_FIRST_COM + 1), AFS_DELETE : (AFS_FIRST_COM + 2), AFS_FSCK : (AFS_FIRST_COM + 3), AFS_INSERT : (AFS_FIRST_COM + 4), AFS_MODIFY : (AFS_FIRST_COM + 5), AFS_READ : (AFS_FIRST_COM + 6), AFS_SIZE : (AFS_FIRST_COM + 7), AFS_DISK_COMPACT : (AFS_FIRST_COM + 8), AFS_SYNC : (AFS_FIRST_COM + 9), AFS_DESTROY : (AFS_FIRST_COM + 10), /* ** DNS Commands */ DNS_CREATE : (DNS_FIRST_COM), DNS_DISCARD : (DNS_FIRST_COM + 1), DNS_LIST : (DNS_FIRST_COM + 2), DNS_APPEND : (DNS_FIRST_COM + 3), DNS_CHMOD : (DNS_FIRST_COM + 4), DNS_DELETE : (DNS_FIRST_COM + 5), DNS_LOOKUP : (DNS_FIRST_COM + 6), DNS_SETLOOKUP : (DNS_FIRST_COM + 7), DNS_INSTALL : (DNS_FIRST_COM + 8), DNS_REPLACE : (DNS_FIRST_COM + 10), DNS_GETMASKS : (DNS_FIRST_COM + 11), DNS_GETSEQNR : (DNS_FIRST_COM + 12), DNS_RENAME : (DNS_FIRST_COM + 13), DNS_GETROOT : (DNS_FIRST_COM + 14), DNS_GETDEFAFS : (DNS_FIRST_COM + 15), PS_STUN : (PS_FIRST_COM), // Kill a process/ create a snapshot PS_MIGRATE : (PS_FIRST_COM+1), // Execute a process from a snapshot after migration (->next+) PS_EXEC : (PS_FIRST_COM+2), // Execute a process from a snapshot (->next) PS_WRITE : (PS_FIRST_COM+4), // Store a process class template PS_READ : (PS_FIRST_COM+5), // Get a process class template PS_CREATE : (PS_FIRST_COM+6), // Create a process from a template and execute PS_FORK : (PS_FIRST_COM+7), // Fork a process from a running process PS_SIGNAL : (PS_FIRST_COM+8), // Send a signal to a process BR_CONNECT : (BR_FIRST_COM), BR_DISCONN : (BR_FIRST_COM+1), print: function(cmd) { switch(cmd) { case Command.STD_MONITOR : return 'STD_MONITOR'; case Command.STD_AGE : return 'STD_AGE'; case Command.STD_COPY : return 'STD_COPY'; case Command.STD_DESTROY : return 'STD_DESTROY'; case Command.STD_INFO : return 'STD_INFO'; case Command.STD_RESTRICT : return 'STD_RESTRICT'; case Command.STD_STATUS : return 'STD_STATUS'; case Command.STD_TOUCH : return 'STD_TOUCH'; case Command.STD_GETPARAMS : return 'STD_GETPARAMS'; case Command.STD_SETPARAMS : return 'STD_SETPARAMS'; case Command.STD_NTOUCH : return 'STD_NTOUCH'; case Command.STD_EXIT : return 'STD_EXIT'; case Command.STD_RIGHTS : return 'STD_RIGHTS'; case Command.STD_EXEC : return 'STD_EXEC'; case Command.STD_LOCATION : return 'STD_LOCATION'; case Command.STD_LABEL : return 'STD_LABEL'; case Command.AFS_CREATE : return 'AFS_CREATE'; case Command.AFS_DELETE : return 'AFS_DELETE'; case Command.AFS_FSCK : return 'AFS_FSCK'; case Command.AFS_INSERT : return 'AFS_INSERT'; case Command.AFS_MODIFY : return 'AFS_MODIFY'; case Command.AFS_READ : return 'AFS_READ'; case Command.AFS_SIZE : return 'AFS_SIZE'; case Command.AFS_DISK_COMPACT : return 'AFS_DISK_COMPACT'; case Command.AFS_SYNC : return 'AFS_SYNC'; case Command.AFS_DESTROY : return 'AFS_DESTROY'; case Command.DNS_CREATE : return 'DNS_CREATE'; case Command.DNS_DISCARD : return 'DNS_DISCARD'; case Command.DNS_LIST : return 'DNS_LIST'; case Command.DNS_APPEND : return 'DNS_APPEND'; case Command.DNS_CHMOD : return 'DNS_CHMOD'; case Command.DNS_DELETE : return 'DNS_DELETE'; case Command.DNS_LOOKUP : return 'DNS_LOOKUP'; case Command.DNS_SETLOOKUP : return 'DNS_SETLOOKUP'; case Command.DNS_INSTALL : return 'DNS_INSTALL'; case Command.DNS_REPLACE : return 'DNS_REPLACE'; case Command.DNS_GETMASKS : return 'DNS_GETMASKS'; case Command.DNS_GETSEQNR : return 'DNS_GETSEQNR'; case Command.DNS_RENAME : return 'DNS_RENAME'; case Command.DNS_GETROOT : return 'DNS_GETRROT'; case Command.DNS_GETDEFAFS : return 'DNS_GETDEFAFS'; case Command.PS_STUN : return 'PS_STUN'; case Command.PS_EXEC : return 'PS_EXEC'; case Command.PS_MIGRATE : return 'PS_MIGRATE'; case Command.PS_READ : return 'PS_READ'; case Command.PS_WRITE : return 'PS_WRITE'; case Command.PS_CREATE : return 'PS_CREATE'; case Command.PS_FORK : return 'PS_FORK'; case Command.PS_SIGNAL : return 'PS_SIGNAL'; case Command.BR_CONNECT : return 'BR_CONNECT'; case Command.BR_DISCONN : return 'BR_DISCONN'; default: return '"'+cmd+'"'; } } }; var PORT_SIZE = 6; var PRIV_SIZE = 4+PORT_SIZE; var CAP_SIZE = 16; /** Object Rights * * @enum {number} */ var Rights = { AFS_RGT_READ : 0x1, AFS_RGT_CREATE : 0x2, AFS_RGT_MODIFY : 0x4, AFS_RGT_DESTROY : 0x8, AFS_RGT_ADMIN : 0x80, DNS_COLMASK : ((1 << DNS_MAXCOLUMNS) - 1), // Rights to access specific columns of a directory row, one bit, one column. DNS_RGT_COLALL : ((1 << DNS_MAXCOLUMNS) - 1), DNS_RGT_COL1 : 0x01, DNS_RGT_OWNER : 0x01, DNS_RGT_COL2 : 0x02, DNS_RGT_GROUP : 0x02, DNS_RGT_COL3 : 0x04, DNS_RGT_OTHERS : 0x04, DNS_RGT_COL4 : 0x08, DNS_RGT_READ : 0x10, DNS_RGT_CREATE : 0x20, DNS_RGT_MODIFY : 0x40, DNS_RGT_DELETE : 0x80, HOST_INFO : 0x01, HOST_READ : 0x02, HOST_WRITE : 0x04, HOST_EXEC : 0x08, PSR_READ : 0x01, PSR_WRITE : 0x02, PSR_CREATE : 0x04, PSR_DELETE : 0x08, PSR_EXEC : 0x10, PSR_KILL : 0x20, PSR_ALL : 0xff, NEG_SCHED : 0x08, NEG_CPU : 0x10, NEG_RES : 0x20, NEG_LEVEL : 0x40, PRV_ALL_RIGHTS : 0xff }; var DEF_RPC_MAX_HOP = 4; var priv2pub_cache = []; /** * * @param {number []} [port_vals] * @returns {string} */ var port = function (port_vals) { if (port_vals==undefined) port_vals=[0,0,0,0,0,0]; var port=''; for(var i = 0; i< PORT_SIZE;i++) { port=port+Perv.char_of_int(port_vals[i]); } return port; }; /** * * @param {number} [obj] * @param {number} [rights] * @param {port} [rand] * @constructor */ var privat = function (obj,rights,rand) { if (obj==undefined) { // Create empty private field this.prv_obj=0; this.prv_rights=0; this.prv_rand=port(); } else { this.prv_obj = obj; // Integer this.prv_rights = rights; // Integer this.prv_rand = rand; // Port=string } }; /** * * @param {port} [cap_port] * @param {privat} [cap_priv] * @constructor */ var capability = function(cap_port, cap_priv) { if (cap_port==undefined) { // Create empty capability this.cap_port = port(); this.cap_priv = new privat(); } else { this.cap_port = cap_port; // Port=string if (cap_priv==undefined) this.cap_priv = new privat(); else this.cap_priv = cap_priv; // Private } }; /* ** RPC communication is XML based using the HTTP interface. ** RPC communication is synchronous, hence a callback ** function is used to handle the reply (acknowledge). */ /** * * @param {port} [h_port] * @param {privat} [h_priv] * @param {Command} [h_command] * @param {(Status.STD_OK|*)} [h_status] * @constructor */ var header = function(h_port,h_priv,h_command,h_status) { if (h_port==undefined) { // Create empty header this.h_port = port(); this.h_priv = new privat(); this.h_command = undefined; this.h_status = undefined; } else { this.h_port = h_port; this.h_priv = h_priv; this.h_command = h_command; this.h_status = h_status; } }; /** * * @param {number} [obj] * @param {number} [rights] * @param {port} [rand] * @returns {privat} */ function Private(obj,rights,rand) { var _obj = new privat(obj,rights,rand); Object.preventExtensions(_obj); return _obj; } /** * * @param {port} [cap_port] * @param {privat} [cap_priv] * @returns {capability} */ function Capability (cap_port, cap_priv) { var obj = new capability(cap_port, cap_priv); Object.preventExtensions(obj); return obj; } /** * * @param {port} [h_port] * @param {privat} [h_priv] * @param {Command} [h_command] * @param {(Status.STD_OK|*)} [h_status] * @returns {header} */ function Header(h_port,h_priv,h_command,h_status) { var obj = new header(h_port,h_priv,h_command,h_status); Object.preventExtensions(obj); return obj; } /* ** Hash table of all locally created unique ports. */ var uniqports=[]; /** * */ var Net = { // Direction:Direction, PORT_SIZE:PORT_SIZE, PRIV_SIZE:PRIV_SIZE, AFS_REQBUFSZ:AFS_REQBUFSZ, CAP_SIZE:CAP_SIZE, DNS_MAXCOLUMNS:DNS_MAXCOLUMNS, TIMEOUT:5000, DEF_RPC_MAX_HOP:DEF_RPC_MAX_HOP, Status:Status, Command:Command, Rights:Rights, Private:Private, Capability: Capability, Header: Header, Port: port, /** * @type {port} */ nilport: port(), nilpriv: Private(0,0,this.nilport), nilcap: Capability(this.nilport,this.nilpriv), /* ** Utils to get and set single bytes of a port */ get_portbyte: function(port,i) { return Perv.int_of_char(String.get(port,i)) }, set_portbyte: function(port,i,byte) { return String.set(port, i, (Perv.char_of_int(byte))); }, /* * Return a unique key of a capability that can be used for hash tables */ key: function (cap) { return cap.cap_port+ cap.cap_priv.prv_obj+ cap.cap_priv.prv_rights+ cap.cap_priv.prv_rand; }, /* ** Encryption function */ one_way: function (port) { var key = Array.create(64,0); var block = Array.create(48,0); var pubport = String.make (PORT_SIZE,'\0'); var i, j, k; /* ** We actually need 64 bit key. ** Throw some zeroes in at bits 6 and 7 mod 8 ** The bits at 7 mod 8 etc are not used by the algorithm */ j=0; for (i = 0; i< 64; i++) { if ((i & 7) > 5) key[i] = 0; else { if ((this.get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0) key[i] = 1; else key[i] = 0; j++; } } Des48.des_OWsetkey(key); /* ** Now go encrypt constant 0 */ block=Des48.des_OWcrypt48(block); /* ** and put the bits in the destination port */ var pb = 0; for (i = 0; i < PORT_SIZE;i++) { var pbyte = 0; for (j = 0; j < 8; j++) { pbyte = pbyte | (block[pb] << j); pb++; } pubport=this.set_portbyte(pubport, i, pbyte); } return pubport; }, /* ** Check whether the required rights [R1;R2;..] are ** present in the rights field rg. Return a boolean value. */ rights_req : function(rights,required) { var all=true; Array.iter(required,function(rq) { if (rq & rights == 0) all = false; }); return all; }, port_cmp: function(port1,port2) { var i; var eq=true; for(i=0;i 0) str = str + ':'; str = str + pad(num.toString(16).toUpperCase(), 2); } } else str='undefined'; return str; }, port_of_str: function (str,compact) { var tokens=str.split(':'),i,port=''; for (i=0;i 0) str = str + ':'; str = str + pad(num.toString(16).toUpperCase(), 2); } } else str='undefined'; return str; }, /** * * @param priv * @returns {string} */ private: function(priv) { var str=''; if (priv==undefined) return 'undefined'; str=priv.prv_obj; str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')['; str=str+this.port(priv.prv_rand)+']'; return str; }, status: Status.print } }; module.exports = Net; }; BundleModuleCode['com/cbl']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2017 bLAB ** $CREATED: 27-11-17 by sbosse. ** $VERSION: 1.1.1 ** ** $INFO: ** ** JavaScript Callback List ** ** Assume there is a set of non-blocking IO operations with callbacks io1,io2,.., and there is a final ** callback function that should be called after all io operations have finished. ** ** $ENDOFINFO */ function CBL(callback) { if (!(this instanceof CBL)) return new CBL(callback); this.schedules=[]; this.callback=callback; } // Next schedule CBL.prototype.next = function (status) { var f=this.schedules.shift(); // if (f) console.log('next '+f.toString()) if (f) { f(this.next.bind(this),status); } else if (this.callback) this.callback(status); } // Add next IO callback at the end of the list CBL.prototype.push = function (f) { this.schedules.push(f); } // Execute CBL CBL.prototype.start = function () { this.next(); } // Add next IO callback at the top of the list CBL.prototype.top = function (f) { this.schedules.unshift(f); } module.exports=CBL; }; BundleModuleCode['jam/amp']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 09-02-16 by sbosse. ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.11.1 ** ** $INFO: ** ** JAM Agent Management Port (AMP) over UDP/HTTP/devices/streams ** ** ** New: Fully negotiated IP Multicast Ports (P2N) ** ** $ENDOFINFO */ var Io = Require('com/io'); var Lz = Require('os/lz-string'); var Comp = Require('com/compat'); var Buf = Require('dos/buf'); var Net = Require('dos/network'); var Command = Net.Command; var Status = Net.Status; var current=none; var Aios=none; var CBL = Require('com/cbl'); var COM = Require('jam/ampCOM'), AMMode=COM.AMMode, AMMessageType=COM.AMMessageType, AMState=COM.AMState, amp=COM.amp, options=COM.options, url2addr=COM.url2addr, addr2url=COM.addr2url, addrequal=COM.addrequal, resolve=COM.resolve, ipequal=COM.ipequal, getNetworkIP=COM.getNetworkIP, doUntilAck=COM.doUntilAck; options.localhost='localhost'; options.version='1.10.1', options.AMC_MAXLIVE=5, options.TIMER=500, options.TRIES=10; options.REGTMO=1000; /*********************** ** AMP ************************/ var ampMAN = Require('jam/ampMAN'); if (global.TARGET!= 'browser') { /******************************* AMP *************************************/ var ampUDP = Require('jam/ampUDP'); var ampTCP = Require('jam/ampTCP'); var ampStream = Require('jam/ampStream'); } var ampHTTP = Require('jam/ampHTTP'); /** Main AMP constructor * ==================== * */ var Amp = function (options) { var obj; switch (options.proto) { case 'stream': obj=new amp.stream(options); break; case 'http': obj=new amp.http(options); break; case 'tcp': obj=new amp.tcp(options); break; case 'udp': default: obj=new amp.udp(options); } return obj; } module.exports.current=function (module) { current=module.current; Aios=module; if (ampMAN) ampMAN.current(module); if (ampUDP) ampUDP.current(module); if (ampHTTP) ampHTTP.current(module); if (ampTCP) ampTCP.current(module); if (ampStream) ampStream.current(module); }; module.exports.Amp=Amp; module.exports.AMMessageType=AMMessageType; module.exports.AMState=AMState; module.exports.AMMode=AMMode; module.exports.url2addr=url2addr; module.exports.addr2url=addr2url; module.exports.Command=Command module.exports.Status=Status module.exports.options=options; }; BundleModuleCode['jam/ampCOM']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 09-02-16 by sbosse. ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.13.1 ** ** $INFO: ** ** JAM Agent Management Port (AMP) - Common Types and Utils ** ** ** ** $ENDOFINFO */ var options = { peekIP: '134.102.22.124', // used by getnetworkip, must be an HTTP server } var Comp = Require('com/compat'); // Channel mode flags var AMMode = { AMO_UNICAST: 1, // P2P AMO_MULTICAST: 2, // P2N AMO_STATIC: 4, // AMO_BUFFER: 8, // Transfer buffer data AMO_OBJECT: 16, // Transfer objects instead of buffer data AMO_COMPRESS: 32, // Compress data AMO_SERVER: 64, // This is HTTP server mode AMO_CLIENT: 128, // This is HTTP client mode AMO_ONEWAY:256, // Other side can be reache dw/o link negotiation print: function (m) { var s='',sep=''; if (m & AMMode.AMO_UNICAST) s += (sep+'UNI'),sep='|'; if (m & AMMode.AMO_MULTICAST) s += (sep+'MUL'),sep='|'; if (m & AMMode.AMO_STATIC) s += (sep+'STA'),sep='|'; if (m & AMMode.AMO_BUFFER) s += (sep+'BUF'),sep='|'; if (m & AMMode.AMO_OBJECT) s += (sep+'OBJ'),sep='|'; if (m & AMMode.AMO_COMPRESS) s += (sep+'ZIP'),sep='|'; if (m & AMMode.AMO_CLIENT) s += (sep+'CLI'),sep='|'; if (m & AMMode.AMO_SERVER) s += (sep+'SRV'),sep='|'; if (m & AMMode.AMO_ONEWAY) s += (sep+'ONE'),sep='|'; return s; } } // Message type var AMMessageType = { AMMACK:0, AMMPING:1, AMMPONG:2, AMMLINK:3, AMMUNLINK:4, AMMRPCHEAD:6, // Header followed by multiple data requests AMMRPCDATA:7, // Broker Rendezvous support AMMCONTROL:8, AMMRPC:9, // Header + data in one message AMMCOLLECT:10, // Collect messages AMMRPCHEADDATA:11, // Header with embedded data print:function(op) { switch (op) { case AMMessageType.AMMACK: return "AMMACK"; case AMMessageType.AMMPING: return "AMMPING"; case AMMessageType.AMMPONG: return "AMMPONG"; case AMMessageType.AMMLINK: return "AMMLINK"; case AMMessageType.AMMUNLINK: return "AMMUNLINK"; case AMMessageType.AMMRPCHEAD: return "AMMRPCHEAD"; case AMMessageType.AMMRPCHEADDATA: return "AMMRPCHEADDATA"; case AMMessageType.AMMRPCDATA: return "AMMRPCDATA"; case AMMessageType.AMMRPC: return "AMMRPC"; case AMMessageType.AMMCOLLECT: return "AMMCOLLECT"; // Rendezvous Broker Message case AMMessageType.AMMCONTROL: return "AMMCONTROL"; default: return "Chan.AMMessageType?"; } } }; // Channel state var AMState = { AMS_NOTINIT:1, // AMP Not initialized conenction AMS_INIT:2, // AMP Server started, but not confirmed AMS_READY:3, // AMP Server initialized and confirmed (other end point not connected) AMS_NEGOTIATE:4, // AMP Server intiialized, in negotiation state (other end point not connected) AMS_CONNECTED:5, // AMP Other side connected AMS_AWAIT:6, // AMP waits for event (pairing) AMS_NOTCONNECTED:10, // AMP Other side not connected // Optional IP broker service AMS_RENDEZVOUS:7, // Broker IP P2P rendezvous; starting AMS_REGISTERED:8, // Broker IP P2P rendezvous; registered; expecting pairing AMS_PAIRING:9, // Broker IP P2P rendezvous; now pairing; send punches until paired AMS_PAIRED:10, // Broker IP P2P rendezvous; acknowldeged and paired -> NOTCONNECTED print:function(op) { switch (op) { case AMState.AMS_NOTINIT: return "AMS_NOTINIT"; case AMState.AMS_INIT: return "AMS_INIT"; case AMState.AMS_READY: return "AMS_READY"; case AMState.AMS_NEGOTIATE: return "AMS_NEGOTIATE"; case AMState.AMS_CONNECTED: return "AMS_CONNECTED"; case AMState.AMS_AWAIT: return "AMS_AWAIT"; case AMState.AMS_NOTCONNECTED: return "AMS_NOTCONNECTED"; case AMState.AMS_RENDEZVOUS: return "AMS_RENDEZVOUS"; case AMState.AMS_REGISTERED: return "AMS_REGISTERED"; case AMState.AMS_PAIRING: return "AMS_PAIRING"; case AMState.AMS_PAIRED: return "AMS_PAIRED"; default: return "Chan.AMState?"; } } }; var amp={ AMMessageType:AMMessageType, AMState:AMState }; /************************* ** IP UTILS *************************/ function isLocal(addr) { return addr=='localhost'|| addr=='127.0.0.1' } /* typeof @url = ":" | ":" | ":" | "" * and @ipport = (1-65535) | "*" * and @port = string */ function url2addr(url,defaultIP) { var addr={address:defaultIP||options.localhost,proto:'UDP',port:undefined}, parts = url.split(':'); if (parts.length==1) { if (Comp.string.isNumeric(parts[0])) addr.port=Number(parts[0]); else if (parts[0]=='*') addr.port=undefined; else addr.address=parts[0]; } else return {address:parts[0],port:parts[1]=='*'?undefined:Number(parts[1])||parts[1]}; return addr; }; function addr2url(addr) { return (isLocal(addr.address)?options.localhost:addr.address)+':'+(addr.port?addr.port:'*') }; function obj2url(obj) { if (!obj) return '*'; if (obj.name && !obj.address) return obj.name+':*'; if (!obj.address) return '*'; return (isLocal(obj.address)?options.localhost:obj.address)+':'+(obj.port?obj.port:'*') }; function addrequal(addr1,addr2) { return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port; } function addrempty(addr) { return !(addr && addr.address && addr.port); } function resolve (url) {return addr2url(url2addr(url)) } function ipequal(ip1,ip2) { if (ip1==undefined || ip2==undefined) return false; else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) && (Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true; else return ip1==ip2; } // Use remote TCP connection to get this host IP (private address if behind NAT) var ipnet = Require('net'); var myip; function getNetworkIP(server,callback) { var socket; if (!ipnet) return callback('Not supported','error'); if (myip) return callback(undefined,myip); if (!server) server={address:options.peekIP,port:80}; socket = ipnet.createConnection(server.port, server.address); socket.on('connect', function() { myip=socket.address().address; callback(undefined, socket.address().address); socket.end(); }); socket.on('error', function(e) { callback(e, 'error'); }); } function doUntilAck(interval, fn, ack, arg) { if (ack()) return; fn(arg); return setTimeout(function() { doUntilAck(interval, fn, ack, arg); }, interval); } module.exports = { AMMode:AMMode, AMMessageType:AMMessageType, AMState:AMState, amp:amp, options:options, addrempty:addrempty, url2addr:url2addr, addr2url:addr2url, obj2url:obj2url, addrequal:addrequal, resolve:resolve, ipequal:ipequal, getNetworkIP:getNetworkIP, doUntilAck:doUntilAck } }; BundleModuleCode['jam/ampMAN']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 30-01-18 by sbosse. ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.14.4 ** ** $INFO: ** ** JAM Agent Management Port (AMP) - General Management Operations ** ** ** New: ** - Single message transfers (HEADER+DATA) ** ** ** $ENDOFINFO */ var Io = Require('com/io'); var Lz = Require('os/lz-string'); var Comp = Require('com/compat'); var Buf = Require('dos/buf'); var Net = Require('dos/network'); var Command = Net.Command; var Status = Net.Status; var current=none; var Aios=none; var CBL = Require('com/cbl'); var COM = Require('jam/ampCOM'), AMMode=COM.AMMode, AMMessageType=COM.AMMessageType, AMState=COM.AMState, amp=COM.amp, options=COM.options, url2addr=COM.url2addr, addr2url=COM.addr2url, addrequal=COM.addrequal, resolve=COM.resolve, ipequal=COM.ipequal, addrempty=COM.addrempty, getNetworkIP=COM.getNetworkIP; // Get data from message function msgData(msg) { // typeof msg.data = Array | Buffer | { type: 'Buffer', data: Array } return msg.data && msg.data.data?msg.data.data:msg.data; } module.exports.current=function (module) { current=module.current; Aios=module; }; amp.man = function (options) { } // Message logger amp.man.prototype.LOG = function (op,msg) { if (!this.logging) return; switch (op) { case 'print': for(var i in this.logs) { Aios.log(this.logs[i].op,this.logs[i].time,this.logs[i].msg,AMState.print(this.logs[i].state)); } this.logs=[]; break; case 'enable': this.logging=true; break; case 'disable': this.logging=false; break; default: var date = new Date(); var time = Math.floor(date.getTime()); this.logs.push({op:op,time:time,msg:msg,state:(this.url && this.links[this.url].state)}); } } /** Transation cache for receiving data fragments that can be out of order. * typeof @data = [handler:{tid,remote,cmd,size,frags,buf},data:[],timeout:number] * */ amp.man.prototype.addTransaction = function (remote,tid,data) { if (this.mode & AMMode.AMO_MULTICAST) this.transactions[remote.address+remote.port+tid]=data; else this.transactions[tid]=data; } amp.man.prototype.deleteTransaction = function (remote,tid) { if (this.mode & AMMode.AMO_MULTICAST) delete this.transactions[remote.address+remote.port+tid]; else delete this.transactions[tid]; } amp.man.prototype.findTransaction = function (remote,tid) { if (this.mode & AMMode.AMO_MULTICAST) return this.transactions[remote.address+remote.port+tid]; else return this.transactions[tid]; } /** Check the state of a link * */ amp.man.prototype.checkState = function (state,addr) { switch (state) { case AMState.AMS_CONNECTED: if (this.mode & AMMode.AMO_ONEWAY) return true; if (this.mode & AMMode.AMO_MULTICAST) return this.links[addr2url(addr)]; if (this.url && this.links[this.url].state == AMState.AMS_CONNECTED) return true; break; } return false; } /** Update AMP object configuration * */ amp.man.prototype.config = function(options) { for(var p in options) this[p]=options[p]; } /** Handle events * */ amp.man.prototype.emit = function(event,arg,aux,aux2) { if (this.events[event]) this.events[event](arg,aux,aux2); } /** Handler for incoming messages (proecssed by receiver) * */ amp.man.prototype.handle = function (msg,remote,response) { var handler,thisnum,ipport,cmsg,url,ack; if (this.verbose > 1) this.out('handle '+AMMessageType.print(msg.type)+' from '+addr2url(remote)); switch (msg.type) { case AMMessageType.AMMRPCHEAD: case AMMessageType.AMMRPCHEADDATA: if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; handler={}; handler.tid=msg.tid; // handler.remote=remote.address+':'+Buf.buf_get_int16(buf); handler.remote=remote; handler.cmd=msg.cmd; handler.size=msg.size; handler.frags=msg.frags; // console.log(handler) if (handler.size>0 && handler.frags>0) { // AMMRPCDATA messages are following handler.buf=Buf.Buffer(); dlist = Comp.array.range(0, handler.frags - 1); // Add transaction to cache for pending data this.addTransaction(remote, handler.tid, [handler,dlist,1000]); } else if (handler.size>0) { // Single message transfer; message contains all data handler.buf=msgData(msg); this.callback(handler); } else { // No data; control message handler.buf=Buf.Buffer(); this.callback(handler); } break; case AMMessageType.AMMRPCDATA: if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; thisnum = msg.off/this.dlimit; transaction = this.findTransaction(remote,msg.tid); if (transaction!=undefined) { handler=transaction[0]; if (this.verbose>1) this.out('receiver: adding data num='+ thisnum+' off='+msg.off+' size='+msg.size+' dlist='+transaction[1]); Buf.buf_get_buf(msgData(msg),handler.buf,msg.off,msg.size); transaction[1]=Comp.array.filter(transaction[1],function(num) {return (num!=thisnum)}); if (Comp.array.empty(transaction[1])) { if (this.verbose>1) this.out('[AMP] receiver: finalize '+addr2url(remote)); // Io.out(handler.data.toString()); // Deliver this.callback(handler); this.deleteTransaction(remote,msg.tid); } } break; case AMMessageType.AMMRPC: if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; // Complete RPC message handler={}; handler.tid=msg.tid; // handler.remote=remote.address+':'+Buf.buf_get_int16(buf); handler.remote=remote; handler.cmd=msg.cmd; handler.size=msg.size; handler.frags=msg.frags; handler.buf=Buf.Buffer(msgData(msg)); this.callback(handler); if (this.ack && response) this.ack(response); break; case AMMessageType.AMMPING: url=addr2url(remote); ipport=remote.port; if (this.mode&AMMode.AMO_MULTICAST) { if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return; } else if (this.url) { if (this.links[this.url].state!=AMState.AMS_CONNECTED) return; } // Send back a PONG message only if we're connected this.pong({address:remote.address,port:ipport},response); break; case AMMessageType.AMMPONG: ipport=remote.port; if (this.mode&AMMode.AMO_MULTICAST) { url=addr2url(remote); if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED) { this.links[url].live = options.AMC_MAXLIVE; } } else if (this.url && this.links[this.url].state==AMState.AMS_CONNECTED) { this.links[this.url].live = options.AMC_MAXLIVE; } if (this.ack && response) this.ack(response); break; case AMMessageType.AMMACK: if (msg.status=="ELINKED") { if (this.mode&AMMode.AMO_MULTICAST) { // Multicast mode url=addr2url(remote); if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) { // Ad-hoc remote connect if (!this.links[url]) this.links[url]={}; this.links[url].snd=remote; this.links[url].live=options.AMC_MAXLIVE; this.links[url].port=msg.port; this.links[url].ipport=remote.port; this.links[url].state=AMState.AMS_CONNECTED; this.links[url].node=msg.node; this.emit('route+',url,msg.node); this.watchdog(true); if (this.verbose) this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+ Net.Print.port(msg.port)+', Node '+msg.node); } } } break; case AMMessageType.AMMLINK: ipport=remote.port; if (this.mode&AMMode.AMO_MULTICAST) { // Multicast mode url=addr2url(remote); if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) { // Ad-hoc remote connect if (!this.links[url]) this.links[url]={}; this.links[url].snd=remote; this.links[url].live=options.AMC_MAXLIVE; this.links[url].port=msg.port; this.links[url].ipport=remote.port; // back link acknowledge this.link(this.links[url].snd,false,none,response); // no ack="EOK" -- ack send by link response!; this.links[url].state=AMState.AMS_CONNECTED; this.links[url].node=msg.node; // if (this.mode&AMMode.AMO_UNICAST) this.snd=remote,this.url=url; this.emit('route+',url,msg.node); this.watchdog(true); if (this.verbose) this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+ Net.Print.port(msg.port)+', Node '+msg.node); } else if (this.links[url].state==AMState.AMS_CONNECTED) { // Already linked! Just acknowledge ack="ELINKED"; } } else { url=addr2url(remote); // Unicast mode; only one connection if (this.links[url] && !addrempty(this.links[url].snd) && this.links[url].state==AMState.AMS_NOTCONNECTED && ipequal(this.links[url].snd.address,remote.address) && this.links[url].snd.port==ipport) // ipport or remote.port?? { // Preferred / expected remote connect this.links[url].snd=remote; this.links[url].port=msg.port; this.links[url].ipport=remote.port; this.links[url].node=msg.node; this.links[url].live=options.AMC_MAXLIVE; // back link acknowledge this.link(this.links[url].snd); this.links[url].state=AMState.AMS_CONNECTED; // Inform router this.emit('route+',url,msg.node); this.watchdog(true); if (this.verbose) this.out('Linked with preferred '+this.proto+' '+ url +', '+ Net.Print.port(msg.port)); } else if ((!this.links[url] && !this.url) || (this.links[url] && this.links[url].state==AMState.AMS_NOTCONNECTED) || (this.broker && this.url && this.links[this.url].state==AMState.AMS_NOTCONNECTED)) { if (!this.links[url]) this.links[url]={}; this.links[url].snd=remote; this.links[url].live=options.AMC_MAXLIVE; this.links[url].port=msg.port; this.links[url].ipport=remote.port; this.links[url].node=msg.node; // back link acknowledge this.link(this.links[url].snd,false,none,response); // no ack="EOK"; - ack was send with link message! this.links[url].state=AMState.AMS_CONNECTED; this.url=url; // remember this link // Inform router this.emit('route+',url,msg.node); this.watchdog(true); if (this.verbose) this.out('Linked with ad-hoc ' + this.proto +' '+ url +', '+ Net.Print.port(msg.port)); } } if (ack && this.ack && response) this.ack(response,ack); break; case AMMessageType.AMMUNLINK: ipport=remote.port; if (this.mode&AMMode.AMO_MULTICAST) { // Multicast mode url=addr2url(remote); // ipport or remote.port?? if (this.links[url] && !addrempty(this.links[url].snd) && ipequal(this.links[url].snd.address,remote.address) && this.links[url].snd.port==ipport && this.links[url].state==AMState.AMS_CONNECTED) { this.links[url].state=AMState.AMS_NOTCONNECTED; // Not negotiated. Just close the link! if (this.verbose) this.out('Unlinked ' +url+', '+ Net.Print.port(msg.port)); // Inform router this.emit('route-',url); if (!this.links[url].snd.connect) this.links[url].snd={}; if (this.cleanup) this.cleanup(url); } } else { // Unicast mode if (this.url && !addrempty(this.links[this.url].snd) && ipequal(this.links[this.url].snd.address,remote.address) && this.links[this.url].snd.port==ipport && this.links[this.url].state==AMState.AMS_CONNECTED) { this.links[this.url].state=AMState.AMS_NOTCONNECTED; addr=this.links[this.url].snd; // Not negotiated. Just close the link! if (this.verbose) this.out('Unlinked ' + this.url +', '+ Net.Print.port(msg.port)); // Inform router this.emit('route-',addr2url(addr)); if (!this.links[this.url].snd.connect) this.links[this.url].snd=null; if (this.cleanup) this.cleanup(url); } } if (this.ack && response) this.ack(response); break; // optional rendezvous brokerage ; remote is broker IP!!! case AMMessageType.AMMCONTROL: cmsg = JSON.parse(msgData(msg)); if (this.verbose>1) this.out('# got message '+msgData(msg)); this.LOG('rcv',cmsg); // All brokerage and pairing is handled by the root path '*'! if (this.control && this.links['*']) this.control(this.links['*'],cmsg,remote); break; default: this.out('handle: Unknown message type '+msg.type); } } /** Install event handler * */ amp.man.prototype.on = function(event,handler) { this.events[event]=handler; } // Status of link, optionally checking destination amp.man.prototype.status = function (ip,ipport) { var p,url; if (this.mode&AMMode.AMO_MULTICAST) { if (!ip) { for(p in this.links) if (this.links[p] && this.links[p].state==AMState.AMS_CONNECTED) return true; return false; } else { url=addr2url({address:ip,port:ipport}); if (!this.links[url]) return false; return this.links[url].state==AMState.AMS_CONNECTED; } } if (!ip && this.url) return this.links[this.url].state==AMState.AMS_CONNECTED || (this.mode&AMMode.AMO_ONEWAY)==AMMode.AMO_ONEWAY; return (this.url && this.links[this.url].snd.address==ip && this.links[this.url].snd.port==ipport); } }; BundleModuleCode['jam/ampHTTP']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 09-02-16 by sbosse. ** $RCS: $Id:$ ** $VERSION: 1.12.6 ** ** $INFO: ** ** JAM Agent Management Port (AMP) over HTTP ** Only Mulitcast IP(*) mode is supported! ** ** Events out: 'error','route-' ** ** TODO: Garbage collection ** ** $ENDOFINFO */ var Io = Require('com/io'); var Lz = Require('os/lz-string'); var Comp = Require('com/compat'); var Buf = Require('dos/buf'); var Net = Require('dos/network'); var Command = Net.Command; var Status = Net.Status; var current=none; var Aios=none; var CBL = Require('com/cbl'); var Bas64 = Require('os/base64'); var COM = Require('jam/ampCOM'), AMMode=COM.AMMode, AMMessageType=COM.AMMessageType, AMState=COM.AMState, amp=COM.amp, options=COM.options, url2addr=COM.url2addr, addr2url=COM.addr2url, addrequal=COM.addrequal, resolve=COM.resolve, ipequal=COM.ipequal, getNetworkIP=COM.getNetworkIP; var debug = false; module.exports.current=function (module) { current=module.current; Aios=module; }; /* ** Parse query string '?attr=val&attr=val... and return parameter record */ function parseQueryString( url ) { var queryString = url.substring( url.indexOf('?') + 1 ); if (queryString == url) return []; var params = {}, queries, temp, i, l; // Split into key/value pairs queries = queryString.split("&"); // Convert the array of strings into an object for ( i = 0, l = queries.length; i < l; i++ ) { temp = queries[i].split('='); if (temp[1]==undefined) temp[1]='true'; params[temp[0]] = temp[1].replace('%20',' '); } return params; } /* ** Format a query string from a parameter record */ function formatQueryString (msg) { var path= '/?'; path += "type="+AMMessageType.print(msg.type); if (msg.cmd) path += '&cmd='+msg.cmd; if (msg.tid) path += '&tid='+msg.tid; if (msg.port) path += '&port='+Net.port_to_str(msg.port); if (msg.timeout) path += '&timeout='+msg.timeout; if (msg.node) path += '&node='+msg.node.replace(' ','%20'); if (msg.index) path += '&index='+msg.index; return path; } function msg2JSON(msg) { if (msg.port) msg.port=Net.port_to_str(msg.port); if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) { if (msg.port) msg.port=Net.port_to_str(msg.port); }); return JSON.stringify(msg); } function JSON2msg(data) { var msg=JSON.parse(data); if (msg.port) msg.port=Net.port_of_str(msg.port); if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) { if (msg.port) msg.port=Net.port_of_str(msg.port); }); return msg; } /** Get XML data * */ function getData(data) { if (data==undefined) return undefined; else if (data.val!='') return data.val; else return data.children.toString(); } function is_error(data,err) { if (data==undefined) return true; if (err==undefined) return (data.length > 0 && Comp.string.get(data,0)=='E'); else return (Comp.string.equal(data,err)); }; /** AMP port using HTTP * =================== * * No negotiation is performed. Data transfer can be fragmented. * Each time a remote endpoint sends a GET/PUT request, we stall the request until * a timeout occurs or we have to send data to the remote endpoint. A link is established. * The routing table is refreshed each time the same client send a * GET/PUT request again. If the client do not send requests anymore after a timeout, it is considered to be * unlinked and the route is removed. * * type amp.http = function (options:{rcv:address,snd?:address,verbose?,logging?,out?:function,log?}) */ var http = Require('http'); amp.http = function (options) { var self=this; this.proto = 'http'; this.options=checkOptions(options,{}); this.verbose=checkOption(this.options.verbose,0); this.dir = options.dir; // attached to JAM port this.rcv = options.rcv; // Local HTTP Server Port; Server Mode this.mode = AMMode.AMO_MULTICAST; // We can handle multiple links at once this.node = options.node; // Attached to this node if (options.rcv && options.rcv.address!='*' && options.rcv.port) this.mode |= AMMode.AMO_SERVER; else this.mode |= AMMode.AMO_CLIENT; this.options.keepalive=true; this.port = options.port||Net.uniqport(); // Connection Link Port (this side) this.id = Net.Print.port(this.port); // Stream socket; can be a process object! this.out = function (msg) { var time=Io.Time()+' '; Aios.print(time+'[AMP '+Net.Print.port(self.port)+ (self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg); } this.err = function (msg) { var time=Io.Time()+' '; Aios.print(time+'[AMP '+Net.Print.port(self.port)+ (self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg); throw 'AMP'; } this.events = []; // typeof linkentry = {snd:address,tries:number,state:amstate,collect?,collecting?,msgqueue?:{} []} this.links = {}; this.count={rcv:0,snd:0,lnk:0,png:0}; if (options.snd) { url=addr2url(options.snd,'127.0.0.1'); this.links[url]={snd:options.snd,tries:0,state:AMState.AMS_NOTCONNECTED,live:options.AMC_MAXLIVE}; //this.out(url) } // Collector thread collecting messages from server (AMO_CLIENT mode) this.collector=undefined; this.logs=[]; this.logging=options.logging||false; if (this.logging) { setInterval(function () { self.LOG('print') },5000); } this.index=0; }; amp.http.prototype.LOG = amp.man.prototype.LOG; amp.http.prototype.checkState = amp.man.prototype.checkState; amp.http.prototype.config = amp.man.prototype.config; amp.http.prototype.emit = amp.man.prototype.emit; amp.http.prototype.on = amp.man.prototype.on; amp.http.prototype.handle = amp.man.prototype.handle; amp.http.prototype.status = amp.man.prototype.status; /** Acknowledge reply * */ amp.http.prototype.ack=function(snd,status) { this.reply(snd,{type:AMMessageType.AMMACK,status:status||"EOK", port:this.port,node:this.node?this.node.id:'*'}); } /** Callback from ampMAN handler to inform about remote unlink event * */ amp.http.prototype.cleanup=function(url,keep) { // Cleanup link var obj=this.links[url]; if (!obj) return; obj.state=AMState.AMS_NOTCONNECTED if (obj.collect) clearTimeout(obj.collect), obj.collect=undefined; if (obj.collecting) this.reply(obj.collecting,{status:'ENOENTRY'}),obj.collecting=undefined; // Link was initiated on remote side // Remove link! if (!keep) { obj.snd={}; this.links[url]=undefined; } } /** Collect request * */ amp.http.prototype.collect=function(snd) { var self=this, url=addr2url(snd), msg={type:AMMessageType.AMMCOLLECT,port:this.port,index:this.index++}; if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED) this.send(snd,msg,function (reply) { var err=is_error(reply); if (err) return; // self.cleanup(url,true); if (reply.msg) Comp.array.iter(reply.msg,function (msg) { self.handle(msg,snd); }); if (!self.links[url]) return; // unlinked? self.links[url].collect=setTimeout(function () { self.collect(snd); },0); }); } /** Service collect request * */ amp.http.prototype.collecting=function(msg,remote,response) { var url; if (this.verbose>2) this.out('handle AMMCOLLECT from '+addr2url(remote)); url=addr2url(remote); // ipport or remote.port?? if (this.links[url] && this.links[url].msgqueue && this.links[url].msgqueue.length) { this.reply(response,{msg:this.links[url].msgqueue}); this.links[url].msgqueue=[]; } else if (this.links[url]) this.links[url].collecting=response; else this.reply(response,{status:'ENOENTRY'}); } /** HTTP GET request to send a messageto the server broker returning data on reply. * * @param path * @param callback */ amp.http.prototype.get = function (snd,path,callback) { var body,req, self=this; if (this.verbose>2) this.out('get '+addr2url(snd)+ path); this.count.snd = this.count.snd + path.length; if (!http.xhr) { req = http.request({ host: snd.address, port: snd.port, path: path, method: 'GET', keepAlive: this.options.keepalive, headers: { } } , function(res) { if (self.verbose>2) self.out('got '+addr2url(snd)+ path); if (res.setEncoding != null) res.setEncoding('utf8'); body = ''; res.on('data', function (chunk) { body = body + chunk; }); res.once('end', function () { self.count.rcv += body.length; if (callback) callback(body); }); }); req.once('error', function(err) { if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err); self.emit('error',err); if (callback) callback(); }); req.end(); } else { // XHR Browser http.request({ host: snd.address, port: snd.port, path:path, proto:'http', method: 'GET', headers: { } } , function(err,xhr,body) { if (err) { if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err); self.emit('error',err); if (callback) callback(); } else { self.count.rcv += body.length; if (callback) callback(body); } }); } }; /** Initialize AMP * */ amp.http.prototype.init = function(callback) { if (callback) callback(); }; /** Negotiate a virtual communication link (peer-to-peer). * In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori! * * typeof @snd = address * typeof @callback = function * typeof @connect = boolean is indicating an initial connect request and not an acknowledge * typeof @key = private * typeof @response = object * * +------------+ * VCMessageType (int16) * Connection Port (port) * Node ID (string) * // Receiver IP Port (int32) * +------------+ * */ amp.http.prototype.link=function(snd,connect,key,response) { var self = this, msg, url; if (this.verbose>1) this.out('amp.link: to '+addr2url(snd)); // MULTICAST mode // Add new link to cache of links if (!snd) this.err('link: no destinataion set in MULTICAST mode'); url=addr2url(snd); if (!this.links[url] || !this.links[url].snd.address) { if (connect) snd.connect=true; this.links[url]={ snd:snd, state:AMState.AMS_NOTCONNECTED, tries:0, connect:connect, live:options.AMC_MAXLIVE}; } // Let watchdog handle connect request link messages if (!this.inwatchdog && connect) return this.watchdog(true); // if (this.verbose>1) this.out('send link '+Io.inspect(snd)); msg={type:AMMessageType.AMMLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++}; this.count.lnk++; if (response) this.reply(response,msg); else this.send(snd,msg,function (reply) { if (is_error(reply)) return; // error // start message collector thread after first link reply! if ((self.mode & AMMode.AMO_CLIENT) && !self.links[url].collect) { self.links[url].collect=setTimeout(function () { self.collect(snd); },0); } // handle reply self.handle(reply,snd); }); }; amp.http.prototype.ping=function(snd,response) { var self = this,msg={}; msg.type=AMMessageType.AMMPING; msg.port=this.port; msg.index=this.index++; // Buf.buf_put_int32(buf, self.rcv.port); if (this.verbose>1) this.out('amp.ping: to '+addr2url(snd)); this.count.png++; if (response) this.reply(response,msg); else this.send(snd,msg,function (reply) { if (is_error(reply)) return; // error // handle reply self.handle(reply,snd); }); } amp.http.prototype.pong=function(snd,response) { var self = this,msg={}; msg.type=AMMessageType.AMMPONG; msg.port=this.port; msg.index=this.index++; // Buf.buf_put_int32(buf, self.rcv.port); if (this.verbose>1) this.out('amp.pong: to '+addr2url(snd)); this.count.png++; if (response) this.reply(response,msg); else this.send(snd,msg,function (reply) { if (is_error(reply)) { self.emit('error',reply); } }); } /** HTTP PUT request to send a message and data to the AMP HTTP server. * * @param path * @param data */ amp.http.prototype.put = function (snd,path,data) { var self=this, req,body; this.count.snd = this.count.snd + path.length + data.length; if (!http.xhr) { req = http.request({ host: snd.address, port: snd.port, path: path, method: 'POST', keepAlive: this.options.keepalive, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': data.length } } , function(res) { if (res.setEncoding != null) res.setEncoding('utf8'); // TODO body=+chunk, res.on('end') ..?? res.once('data', function (chunk) { // TODO }); }); req.once('error', function(err) { self.out('Warning: request to '+addr2url(snd)+' failed: '+err); self.emit('error',err); }); // write data to request body req.write(data); req.end(); } else { // XHR Browser http.request({ host: snd.address, port: snd.port, path: path, method: 'POST', body:data, keepAlive: this.options.keepalive, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': data.length } } , function(err,xhr,body) { if (err) { if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' failed: '+err); self.emit('error',err); } // TODO }) } }; amp.http.prototype.receiver = function (callback,rcv) { var self = this; if (callback) this.callback=callback; if (this.mode & AMMode.AMO_SERVER) { // Only if this is a public or locally visible network node this node // should provide a server port! if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address; if (rcv.port==undefined) rcv.port=this.rcv.port; this.server=http.createServer(function (request,response) { if(parseQueryString(request.url).length==0) return response.end('EINVALID'); // accidental access by WEB browser var i,body, msg = parseQueryString(request.url), remote = {address:request.connection.remoteAddress.replace(/^::ffff:/,''), port:'['+msg.port.replace(/:/g,'')+']' /* unique remote identifier */}; if (self.verbose>2) console.log(request.method,request.url,msg,addr2url(remote),url2addr(addr2url(remote))); self.count.rcv += msg.length; msg.type=AMMessageType[msg.type]; if (debug) console.log(Io.Time(),msg) response.origin=request.headers.origin||request.headers.Origin; Comp.string.match(request.method,[ ['GET',function() { if (msg.type==AMMessageType.AMMCOLLECT) self.collecting(msg,remote,response); else self.handle(msg,remote,response); }], ['POST',function() { body = ''; request.on('data', function (chunk) { body = body + chunk; }); request.on('end', function () { msg.data=Buffer(body,'hex'); self.count.rcv += msg.data.length; if (msg.cmd) msg.cmd=Number(msg.cmd); self.handle(msg,remote,response); }); }] ]) }); this.server.on("connection", function (socket) { socket.setNoDelay(true); }); this.server.on("error", function (err) { self.out('Warning: receiver failed: '+err); if (err) self.err(err); }); this.server.listen(rcv.port,function (err) { // Try to get network IP address of this host if (!err) getNetworkIP(undefined,function (err,ip) { if (!err) self.rcv.address=ip; if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+')'); if (err) return self.out("! Unable to obtain network connection information: "+err); }); if (callback) callback(err); }); } if (this.mode & AMMode.AMO_CLIENT) { // If this is a hidden node (e.g., inside a WEB browser), we have to connect to a remote public server // by using stalled GET requests. if (callback) this.callback=callback; } } /** Send a reply for a pending HTTP GET/PUT request (AMO_SERVER) * */ amp.http.prototype.reply = function (response,msg) { var data=msg2JSON(msg), header; if (response.origin!=undefined) header={'Access-Control-Allow-Origin': response.origin, 'Access-Control-Allow-Credentials': 'true', 'Content-Type': 'text/plain'}; else header={'Content-Type': 'text/plain'}; if (this.options.keepalive) header["Connection"]="keep-alive"; response.writeHead(200,header); response.write(data); if (debug) console.log(Io.Time(),msg) response.end(); } /** Send a request message to a remote node endpoint * * function (cmd:integer,msg:Buffer,snd:address) */ amp.http.prototype.request = function (cmd,msg,snd) { var self=this,req={}, size = msg.data.length, tid = Comp.random.int(65536/2); if (snd==undefined) this.err('request: snd=null'); req.type=AMMessageType.AMMRPC; req.tid=tid; // Transaction Message ID req.port=this.port; // This AMP id // Buf.buf_put_int16(buf,self.rcv.port); // For reply req.cmd=cmd; req.size=size; req.data=msg.data; this.send(snd,req); } /** Main entry for requests with JSON interface. Multiplexer for HTTP GET/PUT. * * msg: JSON * callback : function (reply:object) */ amp.http.prototype.send = function (snd,msg,callback) { var path, url, body, self=this; // Create query selector path = formatQueryString(msg); if (typeof snd.port == 'string') { url=addr2url(snd); // If Pending get from client // Else queue message, client will collect them later (or never) if (this.links[url]) { if (!this.links[url].msgqueue) this.links[url].msgqueue=[]; if (this.links[url].collecting) {// pending AMMCOLLECT request if (this.verbose>1) this.out('REPLY msg '+AMMessageType.print(msg.type)+' to '+url); this.reply(this.links[url].collecting,{msg:[msg]}); this.links[url].collecting=undefined; } else { if (this.verbose>1) this.out('QUEUE msg '+AMMessageType.print(msg.type)+' for '+url); this.links[url].msgqueue.push(msg); } } } else if (msg.data!=undefined) { // Convert buffer data to hex formatted string body=msg.data.toString('hex'); this.put(snd,path,body,function (body) { if (is_error(body)) self.emit('error',body); else if (!is_status(body)) self.emit('error','EINVALID'); // No reply expected! }); } else { this.get(snd,path,function (body) { var xml,i, reply; if (!body || is_error(body)) { self.emit('error','EINVALID'); } else { reply=JSON2msg(body); // { status:string,reply:*,msg?:{}[],..} } if (callback) callback(reply); }); } } // Start AMP watchdog and receiver amp.http.prototype.start = function(callback) { var self=this; if (this.verbose>0 && this.mode & AMMode.AMO_SERVER) this.out('Starting ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'); if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT) this.out('Starting ['+AMMode.print(this.mode)+'] (proto http)'); this.watchdog(true); if (!this.server && this.mode & AMMode.AMO_SERVER) { // After stop? Restart receiver. this.receiver(); } if (callback) callback(); } // Stop AMP amp.http.prototype.stop = function(callback) { for(var p in this.links) { if (this.links[p]) { // Try to unlink remote endpoint this.unlink(this.links[p].snd); this.links[p].state=AMState.AMS_NOTCONNECTED; if (this.links[p].collect) clearTimeout(this.links[p].collect),this.links[p].collect=undefined; } } if (this.timer) clearTimeout(this.timer),this.timer=undefined; if (this.server) this.server.close(),this.server=undefined; if (callback) callback(); } // Unlink remote endpoint amp.http.prototype.unlink=function(snd) { var self = this,msg, url = snd?addr2url(snd):null; if (this.mode&AMMode.AMO_MULTICAST) { if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return; } else { if (this.links.state!=AMState.AMS_CONNECTED) return; } msg={type:AMMessageType.AMMUNLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++}; this.send(snd,msg,function (reply) { // handle reply if (reply) {} }); this.emit('route-',addr2url(snd)); if (this.mode&AMMode.AMO_MULTICAST) { this.links[url].state=AMState.AMS_NOTCONNECTED; if (!this.links[url].snd.connect) this.links[url].snd={}; } else { this.links.state=AMState.AMS_NOTCONNECTED; if (!this.links.snd.connect) this.links.snd={}; } this.cleanup(url); } /** Install a watchdog timer. * * 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set. * 2. If link state is AMS_CONNECTED, check link end point. * 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker * * @param run */ amp.http.prototype.watchdog = function(run,immedOrDelay) { var self=this; if (this.timer) clearTimeout(self.timer),this.timer=undefined; if (run) self.timer=setTimeout(function () { if (!self.timer || self.inwatchdog) return; // stopped or busy? self.timer = undefined; self.inwatchdog=true; function handle(obj,url) { if (self.verbose>1) self.out('Watchdog: handle link '+ (obj.snd?addr2url(obj.snd):'')+' in state '+ AMState.print(obj.state)+' live '+obj.live); switch (obj.state) { case AMState.AMS_CONNECTED: if (obj.live == 0) { // No PING received, disconnect... if (self.verbose>0) self.out('Endpoint ' + addr2url(obj.snd) + ' not responding, propably dead. Unlinking...'); obj.state = AMState.AMS_NOTCONNECTED; self.emit('route-',addr2url(obj.snd)); self.cleanup(url,obj.snd.connect); if (obj.snd.connect) self.watchdog(true,2000); } else { obj.tries=0; obj.live--; self.watchdog(true); if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd); else self.ping(); } break; case AMState.AMS_NOTCONNECTED: case AMState.AMS_PAIRED: if (obj.snd.port && typeof obj.snd.port == 'number') { // Try link to specified remote endpoint obj.snd if (self.verbose>0 && obj.tries==0) self.out('Trying link to ' + addr2url(obj.snd)); self.link(obj.snd); obj.tries++; if (obj.tries < options.TRIES) self.watchdog(true); else { self.out('Giving up to link '+addr2url(obj.snd)); self.emit('error','link',addr2url(obj.snd)); obj.snd={},obj.tries=0; } } break; // AMP P2P Control case AMState.AMS_RENDEZVOUS: obj.send( {type:'register',name: self.rcv.name, linfo: self.rcv}, self.broker, function () {} ); self.watchdog(true); break; case AMState.AMS_REGISTERED: if (obj.tries < options.TRIES && obj.snd.name) { obj.tries++; self.send( {type:'pair', from:self.rcv.name, to: obj.snd.name}, self.broker, function () { } ); } if (obj.tries < options.TRIES) self.watchdog(true); break; } } for(var p in self.links) if (self.links[p]) handle(self.links[p],p); self.inwatchdog=false; },immedOrDelay==true?0:immedOrDelay||options.TIMER); }; }; BundleModuleCode['os/http.browser']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: Stefan Bosse ** $VERSION: 1.1.2 ** ** $INFO: ** * ================================ * Browser HTTP Request (Simplified version) * ================================ * ** ** $ENDOFINFO */ var XHR = XMLHttpRequest if (!XHR) throw new Error('missing XMLHttpRequest') else console.log('HTTP Browser Module Ver. 1.1.1 initialized.'); var DEFAULT_TIMEOUT = 2000; /** request * typeof @options = { host: string, port:number, path:string, method:"GET"|"PUT", body?:string, headers:{} } * typeof @callback = function (err, xhr, body) */ function request(options, callback) { var xhr = new XHR(), err, url = options.uri || ('http://'+options.host+':'+(options.port?options.port:80)+'/'+options.path), is_cors = is_crossDomain(url), supports_cors = ('withCredentials' in xhr) if(is_cors && !supports_cors) { err = new Error('Browser does not support cross-origin request: ' + options.uri) err.cors = 'unsupported' return callback(err, xhr) } options.headers = options.headers || {}; options.timeout = options.timeout || DEFAULT_TIMEOUT; options.headers = options.headers || {}; options.body = options.body || null; if(is_cors) xhr.withCredentials = !! options.withCredentials; xhr.timeout = options.timeout; xhr.onopen = function () { for (var key in options.headers) xhr.setRequestHeader(key, options.headers[key]) } xhr.onload = function () { if(xhr.status === 0) { err = new Error('EREQUEST') callback(err, xhr) } else callback(null,xhr,xhr.responseText) } xhr.ontimeout = function () { // XMLHttpRequest timed out. Do something here. err = new Error('ETIMEOUT') err.duration = options.timeout callback(err,xhr, null) }; xhr.onrror = function () { // XMLHttpRequest failed. Do something here. err = new Error('ESERVER') callback(err,xhr, null) }; xhr.onreadystatechange = function () { if (xhr.readyState === XHR.DONE) { if(xhr.status === 0) { err = new Error('ENETWORK') callback(err, xhr) } } }; switch (options.method) { case 'GET': case 'get': xhr.open('GET', url, true /* async */); xhr.send() break; case 'PUT': case 'POST': case 'put': case 'post': xhr.open('POST', url, true /* async */); xhr.send(options.body) break; } } function is_crossDomain(url) { var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/ // jQuery #8138, IE may throw an exception when accessing // a field from window.location if document.domain has been set var ajaxLocation try { ajaxLocation = location.href } catch (e) { // Use the href attribute of an A element since IE will modify it given document.location ajaxLocation = document.createElement( "a" ); ajaxLocation.href = ""; ajaxLocation = ajaxLocation.href; } if (ajaxLocation.match('file:')) return true; var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || [] , parts = rurl.exec(url.toLowerCase() ) var result = !!( parts && ( parts[1] != ajaxLocParts[1] || parts[2] != ajaxLocParts[2] || (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443)) ) ) //console.debug('is_crossDomain('+url+') -> ' + result) return result } module.exports = { request:request, xhr: true }; }; BundleModuleCode['jam/mobi']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 15-1-16 by sbosse. ** $RCS: $Id: mobi.js,v 1.2 2017/05/27 18:20:36 sbosse Exp $ ** $VERSION: 1.9.3 ** ** $INFO: ** ** JavaScript AIOS Agent Mobilityn Module ** ** $ENDOFINFO */ var Comp = Require('com/compat'); var Net; var options = { version:'1.9.2' } if (global.config.dos) Net=Require('dos/network'); var current=none; var Aios = none; /** Direction type; used with move and link? operations * The link operation can eitehr return a boolean value or * a list of reachable destiantions (PATH/IP only). * NORTH, ..., are used for P2P connections only. */ var DIRS= ['NORTH','SOUTH','WEST','EAST','LEFT','RIGHT','UP','DOWN','ORIGIN','NW','NE','SW','SE', 'DELTA','RANGE','NODE','IP','PATH','CAP']; /* enum DIR = { NORTH , SOUTH , .. , IP(ip:string) , .. } : dir tyoe dir = NORTH | SOUTH | .. | IP {tag,ip:string } | .. */ var DIR = { NORTH:'DIR.NORTH', SOUTH:'DIR.SOUTH', WEST:'DIR.WEST', EAST:'DIR.EAST', LEFT:'DIR.LEFT', RIGHT:'DIR.RIGHT', UP:'DIR.UP', DOWN:'DIR.DOWN', ORIGIN:'DIR.ORIGIN', NW:'DIR.NW', NE:'DIR.NE', SW:'DIR.SW', SE:'DIR.SE', // Assuming: z-> x N // | W+E U(+z)/D(-z) // v y S DELTA: function (addr) { return {tag:"DIR.DELTA",delta:addr} }, // Only for link? operation RANGE: function (r) { return {tag:"DIR.RANGE",radius:r} }, // Address a node (identifier name) directly NODE: function (node) { return {tag:"DIR.NODE",node:node} }, IP:function (addr) { return {tag:"DIR.IP",ip:addr} }, /* ** ** Path can contain filter, e.g. range /distance[0-5], /distance[5], .. ** or sets of destinations, e.g., /node* ** or a hopping array [dest1,dest2,..] ** type of path = string | string array */ PATH:function (path) { return {tag:"DIR.PATH",path:path} }, CAP:function (cap) { return {tag:"DIR.CAP",cap:cap} } } DIR.tag = { NORTH:'DIR.NORTH', SOUTH:'DIR.SOUTH', WEST:'DIR.WEST', EAST:'DIR.EAST', LEFT:'DIR.LEFT', RIGHT:'DIR.RIGHT', UP:'DIR.UP', DOWN:'DIR.DOWN', ORIGIN:'DIR.ORIGIN', NW:'DIR.NW', NE:'DIR.NE', SW:'DIR.SW', SE:'DIR.SE', DELTA:'DIR.DELTA', RANGE:'DIR.RANGE', NODE:'DIR.NODE', IP:'DIR.IP', PATH:'DIR.PATH', CAP:'DIR.CAP', } /** Back direction. In case of IP, the remote address on receiving agent code is used. */ function opposite (dir,next) { var chan; switch (dir.tag||dir) { case DIR.NORTH: return DIR.SOUTH; case DIR.SOUTH: return DIR.NORTH; case DIR.WEST: return DIR.EAST; case DIR.EAST: return DIR.WEST; case DIR.LEFT: return DIR.RIGHT; case DIR.RIGHT: return DIR.LEFT; case DIR.UP: return DIR.DOWN; case DIR.DOWN: return DIR.UP; case DIR.NW: return DIR.SE; case DIR.NE: return DIR.SW; case DIR.SE: return DIR.NW; case DIR.SW: return DIR.NE; case DIR.tag.DELTA: if (!next) return DIR.DELTA(dir.delta.map(function (v) {return -v})); else return; case DIR.tag.IP: // try to use current process back attribute containing remote IP address upon receiving if (current.process && current.process.back && current.process.back.tag==DIR.tag.IP) return current.process.back; else return none; case DIR.tag.NODE: // try to use current process back attribute containing remote IP address upon receiving if (current.process && current.process.back) { switch (current.process.back.tag) { case DIR.tag.IP: // Try to resolve node name if (current.node && current.node.connections.ip && current.node.connections.ip.reverse) return DIR.NODE(current.node.connections.ip.reverse(current.process.back.ip)); else return current.process.back; case DIR.tag.NODE: return current.process.back; default: return none; } } else return none; case 'DIR.PATH': // TODO: this node name/path! return none; case 'DIR.CAP': // TODO: this node capability! return none; default: return none; } }; // Create a valid DIR compatible type from a lowercase name specifier (e.g., north -> DIR.NORTH DIR.from = function (name) { var Dir=name.toUpperCase(); if (DIRS.indexOf(Dir) == -1) return; return {tag:'DIR.'+Dir} } // Create a valid lowercase name specifier from DIR (e.g. DIR.NORTH -> north) DIR.to = function (dir) { if ((dir.tag||dir).substr(0,4)!='DIR.') return; return (dir.tag||dir).substr(4).toLowerCase(); } DIR.isDir = function (o) { return (o.tag||o).indexOf('DIR')==0; } DIR.opposite=opposite; DIR.print = function (dir) { if (!dir) return 'undefined'; var name=(dir.tag||dir).substring(4); switch (dir.tag||dir) { case 'DIR.DELTA': return name+'('+Comp.printf.list(dir.delta)+')'; case 'DIR.RANGE': return name+'('+dir.radius+')'; case 'DIR.NODE': return name+'('+dir.node+')'; case 'DIR.IP': return name+'('+(dir.ip==undefined?'*':dir.ip)+')'; case 'DIR.PATH': return name+'('+dir.path+')'; case 'DIR.CAP': return name+'('+dir.cao+')'; default: return name } }; /** Search a channel that is connected to node 'destnode' * */ function lookup(node,destnode) { var chan,path; if (node.connections.ip && node.connections.ip.lookup) { path=node.connections.ip.lookup(destnode); if (path) return {chan:node.connections.ip,dest:path}; } } /** Move current agent to new node * */ function move(dir) { var node1=current.node, chan=none, dest, stat, path, alive = function () {return 1}, nokill=false, name=DIR.to(dir), msg; switch (dir.tag||dir) { case 'DIR.IP': chan=node1.connections.ip; dest=dir.ip; break; case 'DIR.DELTA': current.process.dir=Comp.obj.copy(dir); if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status()) current.process.dir.delta[0]--,chan=node1.connections.east; else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status()) current.process.dir.delta[0]++,chan=node1.connections.west; else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status()) current.process.dir.delta[1]--,chan=node1.connections.south; else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status()) current.process.dir.delta[1]++,chan=node1.connections.north; else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status()) current.process.dir.delta[2]--,chan=node1.connections.up; else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status()) current.process.dir.delta[2]++,chan=node1.connections.down; break; case 'DIR.NODE': if (node1.connections.range && node1.connections.range[dir.node] && node1.connections.range[dir.node].status()) chan=node1.connections.range[dir.node],dest=dir.node; else { // Find node name -> channel mapping dest=lookup(node1,dir.node); if (dest) chan=dest.chan,dest=dest.dest; } break; case 'DIR.PATH': // TODO // if (!current.network) {current.error='No connection to path '+dir.path; throw 'MOVE'}; if (Comp.obj.isArray(dir.path)) { path=Comp.array.pop(dir.path); } else path = dir.path; chan=node1.connections.path; dest=path; nokill=true; break; case 'DIR.CAP': // TODO if (!current.network) {current.error='No connection to server '+dir.cap; throw 'MOVE'}; chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap; nokill=true; break; default: if (!name) { current.error='ENOCHANNEL'; throw 'MOVE'; } chan=node1.connections[name]; } // print(node1.connections); // print(chan) if (chan==none || !chan.status(dest)) { current.error='No connection to direction '+DIR.print(dir); throw 'MOVE' }; if (!current.process.back) current.process.back=Aios.DIR.opposite(dir); node1.stats.migrate++; if (Aios.options.fastcopy && chan.virtual) msg=Aios.Code.toObject(current.process); else msg=Aios.Code.ofCode(current.process,false); current.process.move=dir; /* NEWCOMM | context is current process !!!! */ chan.send({agent:msg,to:dest,context:current.process}); // kill or supend ghost agent if (!nokill) current.process.kill=true; // discard process now else current.process.suspended=true; // discard process after send op finished //print(current.process.print()); } module.exports = { agent:{ move:move, opposite:opposite, DIR:DIR }, current:function (module) { current=module.current; Aios=module; }, DIR:DIR, DIRS:DIRS, options:options } }; BundleModuleCode['ml/ml']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2019 BSSLAB ** $CREATED: 8-2-16 by sbosse. ** $VERSION: 1.8.1 ** ** $INFO: ** ** JavaScript AIOS Machine Learning API ** ** type algorithm = {'dti','dt','knn','mlp','svm'} ** ** ** dti: interval decision tree algorithm ** ------------------------------------- ** ** General feature variable set: ** ** typeof @options = { ** algorithm='dti', ** data:{x1:number,x2:number,..,y:*} [] ** target:string is e.g. 'y' ** features: string [] is e.g. ['x1','x2',..] ** eps:number is e.g. '5', ** maxdepth:number, ** } ** ** Or vector feature variables (i.e., features=[0,1,2,...n-1], target=n): ** ** typeof @options = { ** algorithm='dti', ** x:* [] [], ** y:* [], ** eps:number is e.g. '5', ** maxdepth:number, ** } ** ** knn: k-Nearest-Neighbour Algorithm ** ---------------------------------- ** ** typeof @options = { ** algorithm='knn', ** x: number [][], ** y: * [] ** } ** ** mlp: multi layer perceptron Algorithm ** ---------------------------------- ** ** typeof @options = { ** algorithm='mlp', ** x: number [][], ** y: number [] [] | * [], ** hidden_layers?:number [], ** lr?:number, ** epochs?:number, ** labels?:string [], ** features?: string [], ** normalize?, ** verbose?:number ** } ** ** ** text: text analysis (similarity checking) ** ----------------------------------------- ** classify(model,string) -> {match:number [0..1],string:string } ** learn({algorithm:ML.TXT, data:string []]) -> model ** test({algorithm:ML.TXT,string:string}|model,string) -> number [0..1] ** similarity(string,string) -> number [0..1] ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var DT = Require('ml/dt'); var DTI = Require('ml/dti'); var KNN = Require('ml/knn'); var SVM = Require('ml/svm'); var MLP = Require('ml/mlp'); var C45 = Require('ml/C45'); var TXT = Require('ml/text'); var current=none; var Aios=none; var options = { version: '1.8.1' } // Some definitions var ML = { // Algorithms C45:'c45', DT:'dt', DTI:'dti', KNN:'knn', KNN2:'knn2', MLP:'mlp', SVM:'svm', TXT:'txt', // Some Functions EUCL:'euclidean', PEAR:'pearson' }; /** * Computes Log with base-2 * @private */ function log2(n) { return Math.log(n) / Math.log(2); } // Agent AIOS API var ml = { /** Classification: Apply sample data to learned model. * Returns prediction result. * */ classify: function (model,samples) { var x; switch (model.algorithm) { case ML.TXT: // typeof options = {data: string []} if (Comp.obj.isArray(samples)) return samples.map(function (sample) { return TXT.classify(model,sample) }); else return TXT.classify(model,samples); break; case ML.KNN: return model.predict(samples); break; case ML.SVM: return model.predict(samples); break; case ML.MLP: if (Comp.obj.isMatrix(samples)) { x=samples; if (model.scale) x=x.map(function (row) { return row.map(function (col) { return -1+(col-model.scale.off)*model.scale.k })}); return model.labels?model.predict(x).map(function (r) { var o={}; r.forEach(function (v,i) { o[model.labels[i]]=v }); return o; }):model.predict(x); } else if (Comp.obj.isArray(samples)) { x=samples; if (model.scale) x=x.map(function (col) { return (col-model.scale.off)*model.scale.k }); return model.labels?model.predict([x]).map(function (r) { var o={}; r.forEach(function (v,i) { o[model.labels[i]]=v }); return o; })[0]:model.predict([x])[0]; } else if (Comp.obj.isObj(samples) && model.features) { x=model.features.map(function (f) { return samples[f] }); if (model.scale) x=x.map(function (col,i) { return model.scale.shift+ (col-model.scale.off[i])*model.scale.k[i] }); //console.log(x) return model.labels?model.predict([x]).map(function (r) { var o={}; r.forEach(function (v,i) { o[model.labels[i]]=v }); return o; })[0]:model.predict([x])[0]; } break; case ML.C45: // Sample row format: [x1,x2,..,xn] if (Comp.obj.isMatrix(samples)) { return samples.map(function (sample) { return C45.classify(model,sample); }); } else if (Comp.obj.isArray(samples)) { return C45.classify(model,samples); } else if (Comp.obj.isObj(samples) && model.features) { } break; case ML.DTI: default: if (Comp.obj.isMatrix(samples)) return samples.map(function (sample) { return DTI.predict(model,sample) }); else return DTI.predict(model,samples); } }, column: function (data,key) { return data.map(function (row) { return row[key] }) }, compact: function (model) { switch (model.algorithm) { case ML.DTI: default: return DTI.compactTree(model); } }, depth: function (model) { switch (model.algorithm) { case ML.DTI: return DTI.depth(model); } }, // Information entropy of a set of values entropy: function (vals) { return C45.entropy(none,vals) }, // Information entropy of a value distribution entropyN: function (counts) { var e=0,sum=counts.reduce(function (a,b) { return a+b},0); counts.forEach(function (n) { var p=n/sum; if (p==0) return; e=e-p*C45.log2(p); }); return e; }, // Dependent entropy of a data column of a table with rows grouped by // unique target column values entropyDep: function (data,datacol,targetcol) { var target = ml.column(data,targetcol); var target_values = ml.unique(target); var column = ml.column(data,datacol); var column_values = ml.unique(column); var e = 0; column_values.forEach(function (v) { var countv=0; column.forEach(function (cv,i) { if (cv==v) countv++; }); var occurences = target_values.map(function (t) { var countd=0; column.forEach(function (cv,i) { if (target[i]==t && cv==v) countd++; }); return countd; }); // console.log(v,countv,column.length,occurences) e = e + (countv/column.length)*ml.entropyN(occurences); }); return e; }, evaluate: function (model,target,samples) { switch (model.algorithm) { case ML.DTI: default: return DTI.evaluate(model,target,samples); } }, /** Learning: Create a classification model from training data * */ learn: function (options) { var model,data,features,featureTypes,target,cols,n_ins,n_outs,x,y,scale,offset,shift; if (options==_) options={}; switch (options.algorithm) { case ML.TXT: // typeof options = {data: string []} model = TXT.create(options.data,{ }); model.algorithm=options.algorithm return model; break; case ML.KNN: // typeof options = {x: number [][], y: * [], distance?:function|string,k?:number} model = KNN.create(options.x,options.y,{ distance:options.distance, k:options.k }); model.algorithm=options.algorithm return model; break; case ML.SVM: // typeof options = {x: number [][], y: {-1,1} []} model = SVM.create({ x:options.x, y:options.y, }); model.algorithm=options.algorithm model.train({ C:options.C, tol:options.tol, max_passes:options.max_passes, alpha_tol:options.alpha_tol, kernel:options.kernel }); return model; break; case ML.MLP: // typeof options = {x: number [][], // y: number [][] | * [], // hidden_layers?:[],epochs?:number, // labels?:string [], features?: string [], // normalize?, bipolar?, verbose?} // // y and MLP(learn) requires [[p1,p2,..],[p1,p2,..],..] with 0>=p>=1 // p:label probability x=options.x; if (Comp.obj.isMatrix(options.y)) y=options.y; else if (Comp.obj.isArray(options.y) && options.labels) { y=options.y.map(function (l1) { return options.labels.map(function (l2) { return l1==l2?1:0; }); }); } else throw 'ML.learn.MLP: invalid options'; if (options.normalize) { var max=x[0].map(function (col) { return col}), min=x[0].map(function (col) { return col}); x.forEach(function (row) { row.forEach(function (col,i) { max[i]=Math.max(max[i],col); min[i]=Math.min(min[i],col) }) }); shift=options.bipolar?-1:0; scale=max.map(function (x,i) { return (shift?2:1)/((x-min[i])==0?1:x-min[i])}); offset=min; x=x.map(function (row) { return row.map(function (col,i) { return shift+(col-offset[i])*scale[i] }) }); } //console.log(x) model = new MLP({ input:x, label:y, n_ins:x[0].length, n_outs:y[0].length, hidden_layer_sizes:options.hidden_layers }); model.algorithm=options.algorithm; model.labels=options.labels; model.features=options.features; model.scale=options.normalize?{k:scale,off:offset,shift:shift}:undefined; model.set('log level',options.verbose||0); // 0 : nothing, 1 : info, 2 : warning. model.train({ epochs : options.epochs||20000 }); return model; break; case ML.C45: // typeof options = {data: {}[], target:string, features: string []} | // {data: [][], target?:string, features?: string []} | // {x: number [][], y:[]} var model = C45.create(); if (options.x && options.y) { data=options.x.slice().map(function (row,i) { row.push(options.y[i])}); features=options.x[0].map(function (col,i) { return 'x'+i }); featureTypes=options.x[0].map(function (col,i) { return 'number' }); target='y'; } else if (options.data && Comp.obj.isMatrix(options.data)) { data=options.data; features=options.features||options.data[0].slice(0,-1).map(function (col,i) { return String(i) }); featureTypes=options.data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' }); target=options.target||'y'; } else if (options.data && Comp.obj.isArray(options.data) && Comp.obj.isObj(options.data[0]) && options.target && options.features) { rowNames=options.features.concat(options.target); data=options.data.map(function (row) { rowNames.map(function (attr) { return row[attr] })}); features=options.features; featureTypes=data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' }); target=options.target; } else throw 'ML.learn.C45: Invalid options'; // console.log(data,target,features,featureTypes); C45.train(model,{ data: data, target: target, features: features, featureTypes: featureTypes }); model.algorithm=options.algorithm return model; break; case ML.DTI: default: // typeof options = {data: {}[], target:string, features: string [], eps;number, maxdepth} | // {x: number [][], y:[], eps;number, maxdepth} if (options.eps==_) options.eps=0; if (options.maxdepth==_) options.maxdepth=20; if (options.data && options.target && options.features) model = DTI.create(options); else if (options.x && options.y) { if (options.x.length != options.y.length) throw 'ML.learn.DTI: X and Y vector have different length'; data=options.x.map(function (row,i) {row.push(options.y[i]); return row}); features=Comp.array.init(data[0].length-1,function (i) { return String(i)}); target=String(data[0].length-1); console.log(data,features,target) model = DTI.create({ data:data, features:features, target:target, eps:options.eps, maxdepth:options.maxdepth }); } else throw 'ML.learn.DTI: Invalid options'; model.algorithm=options.algorithm; return model; } }, print: function (model,indent,compact) { switch (model.algorithm) { case ML.DTI: return DTI.print(model,indent,compact); case ML.C45: return C45.print(model,indent); } }, // Only text module similarity : TXT.similarity, // Check model consistency test: function (model,samples) { var x,y,res,p=0.0; switch (model.algorithm) { case ML.TXT: var model = model.string?{ data : [model.string] }:model; if (Comp.obj.isArray(samples)) return samples.map(function (sample) { return TXT.classify(model,sample).match }); else return TXT.classify(model,samples).match; break; case ML.C45: // Sample row format: [x1,x2,..,y] if (Comp.obj.isMatrix(samples)) { samples.forEach(function (sample) { x=sample.slice(0,sample.length-1); y=sample[sample.length-1]; res= C45.classify(model,x); if (res==y) p += 1; }); return p/samples.length; } else if (Comp.obj.isArray(samples)) { x=samples.slice(0,samples.length-1); y=samples[samples.length-1]; res = C45.classify(model,x); return res==y?1.0:0.0 } else if (Comp.obj.isObj(samples) && model.features) { } break; } }, /** Return unique values of a set * */ unique: C45.unique, /** Update a learned model * */ update: function (model,options) { switch (model.algorithm||options.algorithm) { case ML.DTI: default: var model; // typeof @options = {data: number [][], target:string, features: string [], eps?:number, maxdepth?:number} | // {x: number [][], y:[], eps?:number, maxdepth?:number} if (options.eps==_) options.eps=0; if (options.maxdepth==_) options.maxdepth=20; if (options.data && options.target && options.features) model = DTI.update(model,options); else if (options.x && options.y) { if (options.x.length != options.y.length) throw 'ML.update.DTI: X and Y vector have different length'; data=options.x.slice(); data=data.map(function (row,i) {row.push(options.y[i]); return row}); features=Comp.array.init(data[0].length-1,function (i) { return String(i)}); target=String(data[0].length-1); console.log(data,features,target) model = DTI.update(model,{ data:data, features:features, target:target, eps:options.eps, maxdepth:options.maxdepth }); } else throw 'ML.update.DTI: Invalid options'; model.algorithm=options.algorithm; return model; } }, ML:ML, }; module.exports = { agent:ml, classify:ml.classify, column:ml.column, compact:ml.compact, depth:ml.depth, entropy:ml.entropy, entropyN:ml.entropyN, entropyDep:ml.entropyDep, evaluate:ml.evaluate, learn:ml.learn, print:ml.print, test:ml.test, unique:ml.unique, update:ml.update, ML:ML, current:function (module) { current=module.current; Aios=module; } } }; BundleModuleCode['ml/dt']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Ankit Kuwadekar, Stefan Bosse ** $INITIAL: (C) 2014, Ankit Kuwadekar ** $MODIFIED: (C) 2006-2018 bLAB by sbosse ** $VERSION: 1.2.1 ** ** $INFO: ** ** ID3 Decision Tree Algorithm ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var current=none; var Aios=none; /** * Map of valid tree node types * @constant * @static */ var NODE_TYPES = { RESULT: 'result', FEATURE: 'feature', FEATURE_VALUE: 'feature_value' }; function depth(model) { switch (model.type) { case NODE_TYPES.RESULT: return 1; case NODE_TYPES.FEATURE: return 1+Comp.array.max(model.vals,function (val) { return depth(val); }); case NODE_TYPES.FEATURE_VALUE: return 1+depth(model.child); } return 0; } function print(model) { var line='',sep; switch (model.type) { case NODE_TYPES.RESULT: return ' -> '+model.name; case NODE_TYPES.FEATURE: line='('+model.name+'?'; sep=''; Comp.array.iter(model.vals,function (v) { line += sep+print(v); sep=','; }); return line+')'; case NODE_TYPES.FEATURE_VALUE: return ' '+model.name+':'+print(model.child); } return 0; } /** * Predicts class for sample */ function predict(model,sample) { var root = model; while (root.type !== NODE_TYPES.RESULT) { var attr = root.name; var sampleVal = sample[attr]; var childNode = Comp.array.find(root.vals, function(node) { return node.name == sampleVal }); if (childNode){ root = childNode.child; } else { root = root.vals[0].child; } } return root.val; }; /** * Evalutes prediction accuracy on samples */ function evaluate(model,target,samples) { var total = 0; var correct = 0; Comp.array.iter(samples, function(s) { total++; var pred = predict(model,s); var actual = s[target]; if (pred == actual) { correct++; } }); return correct / total; }; /** * Creates a new tree */ function createTree(data, target, features, eps) { var targets = Comp.array.unique(Comp.array.pluck(data, target)); // Aios.aios.log('createTree:'+targets.length); if (targets.length == 1) { return { type: NODE_TYPES.RESULT, val: targets[0], name: targets[0], alias: targets[0] + randomUUID() }; } if (features.length == 0) { var topTarget = mostCommon(targets); return { type: NODE_TYPES.RESULT, val: topTarget, name: topTarget, alias: topTarget + randomUUID() }; } var bestFeature = maxGain(data, target, features, eps); var remainingFeatures = Comp.array.without(features, bestFeature); var possibleValues = Comp.array.unique(Comp.array.pluck(data, bestFeature)); var node = { name: bestFeature, alias: bestFeature + randomUUID() }; node.type = NODE_TYPES.FEATURE; node.vals = Comp.array.map(possibleValues, function(v) { var _newS = data.filter(function(x) { return x[bestFeature] == v }); var child_node = { name: v, alias: v + randomUUID(), type: NODE_TYPES.FEATURE_VALUE }; child_node.child = createTree(_newS, target, remainingFeatures, eps); return child_node; }); return node; } /** * Computes Max gain across features to determine best split * @private */ function maxGain(data, target, features,eps) { var gains=[]; var maxgain= Comp.array.max(features, function(element) { var g = gain(data, target, element, eps); gains.push(element+':'+g); return g; }); //Aios.aios.log(gains); // console.log(gains); return maxgain; } /** * Computes entropy of a list * @private */ function entropy(vals) { var uniqueVals = Comp.array.unique(vals); var probs = uniqueVals.map(function(x) { return prob(x, vals) }); var logVals = probs.map(function(p) { return -p * log2(p) }); return logVals.reduce(function(a, b) { return a + b }, 0); } function entropyEps(vals,eps) { var uniqueVals = Comp.array.unique(vals); var probs = uniqueVals.map(function(x) { return probEps(x, vals, eps) }); var logVals = probs.map(function(p) { return -p * log2(p) }); return logVals.reduce(function(a, b) { return a + b }, 0); } /** * Computes gain * @private */ function gain(data, target, feature) { var attrVals = Comp.array.unique(Comp.array.pluck(data, feature)); var setEntropy = entropy(Comp.array.pluck(data, target)); var setSize = data.length; var entropies = attrVals.map(function(n) { var subset = data.filter(function(x) { return x[feature] === n }); return (subset.length / setSize) * entropy(Comp.array.pluck(subset, target)); }); // var entropyData = entropyV(Comp.array.pluck(data, feature),eps); // console.log('Feat '+feature+':'+entropyData); var sumOfEntropies = entropies.reduce(function(a, b) { return a + b }, 0); return setEntropy - sumOfEntropies; } function uniqueEps(data,eps) { var result=[]; data.forEach(function (x) { var found; if (!results.length) results.push(x); else { results.forEach(function (y) { if (found) return; found = Math.abs(x-y)= (value-eps)) && (element <= (value+eps)); }); var numOccurrences = occurrences.length; var numElements = list.length; return numOccurrences / numElements; } /** * Computes Log with base-2 * @private */ function log2(n) { return Math.log(n) / Math.log(2); } /** * Finds element with highest occurrence in a list * @private */ function mostCommon(list) { var elementFrequencyMap = {}; var largestFrequency = -1; var mostCommonElement = null; list.forEach(function(element) { var elementFrequency = (elementFrequencyMap[element] || 0) + 1; elementFrequencyMap[element] = elementFrequency; if (largestFrequency < elementFrequency) { mostCommonElement = element; largestFrequency = elementFrequency; } }); return mostCommonElement; } /** * Generates random UUID * @private */ function randomUUID() { return "_r" + Math.random().toString(32).slice(2); } module.exports = { NODE_TYPES:NODE_TYPES, createTree:createTree, depth:depth, entropy:entropy, evaluate:evaluate, predict:predict, print:print, current:function (module) { current=module.current; Aios=module;} }; }; BundleModuleCode['ml/dti']=function (module,exports,global,process){ /** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2018 bLAB ** $CREATED: 03-03-16 by sbosse. ** $VERSION: 1.4.2 ** ** $INFO: ** ** Interval Decision Tree Learner ** ** Modified ID3-based Decision Tree Algorithm that wraps all data with 2-eps intervals and uses ** interval instead single value arithmetic for entropy calculation and feature selection. ** The classification bases on a nearest-neighbourhood look-up of best matching results. ** ** Two different algorithms are supported: ** ** 1. Static (using learn), the DTI learner using attribute selection based on entropy. ** The training data must be available in advance. ** 2. Dynamic (using update), the DTI learrner using attribute selection based on significance. ** The training data is applied sequentielly (stream learning) updating the model. ** ** Though in principle the both algrotihms can be mixed (first static, then dynamic updating), ** the resulting model will have poor classification quality. Either use static or only dynamic ** (stream) learning. ** ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var current=none; var Aios=none; var min = Comp.pervasives.min; var max = Comp.pervasives.max; /** * Map of valid tree node types * @constant * @static */ var NODE_TYPES = { RESULT: 'result', FEATURE: 'feature', FEATURE_VALUE: 'feature_value' }; function Result(key) { return { type:NODE_TYPES.RESULT, name:key } } function Feature(name,vals) { return { type:NODE_TYPES.FEATURE, name:name, vals:vals } } // A value can be a scalar or a range {a,b} object function Value(val,child) { return { type:NODE_TYPES.FEATURE_VALUE, val:val, child:child } } /** Add a new training set with optional data set merging and value interval expansion. * */ function add_training_set(data,set,merge) { if (merge) { // Merge a data set with an existing for a specific key; create value ranges } else data.push(set); } /** * Computes Log with base-2 * @private */ function log2(n) { return Math.log(n) / Math.log(2); } function results(model) { var line='',sep; if (!model) return ''; switch (model.type) { case NODE_TYPES.RESULT: return model.name; case NODE_TYPES.FEATURE: sep=''; line=''; Comp.array.iter(model.vals,function (v) { line += sep+results(v); sep=','; }); return line; case NODE_TYPES.FEATURE_VALUE: return results(model.child); } return 'result?'; } /** * Finds element with highest occurrence in a list * @private */ function mostCommon(list) { var elementFrequencyMap = {}; var largestFrequency = -1; var mostCommonElement = null; list.forEach(function(element) { var elementFrequency = (elementFrequencyMap[element] || 0) + 1; elementFrequencyMap[element] = elementFrequency; if (largestFrequency < elementFrequency) { mostCommonElement = element; largestFrequency = elementFrequency; } }); return mostCommonElement; } function addVal(v1,v2) { if (v1.a!=undefined) { if (v2.a!=undefined) return {a:v1.a+v2.a,b:v1.b+v2.b}; else return {a:v1.a+v2,b:v2.b+v2}; } else if (v2.a!=undefined) return {a:v2.a+v1,b:v2.b+v1}; else return v1+v2; } function lowerBound(v) { if (v.a==undefined) return v; else return v.a; } function upperBound(v) { if (v.b==undefined) return v; else return v.b; } function equal(v1,v2) { return (v1==v2 || (upperBound(v1) == upperBound(v2) && (lowerBound(v1) == lowerBound(v2)))) } function overlap(v1,v2) { return (upperBound(v1) >= lowerBound(v2) && upperBound(v1) <= upperBound(v2)) || (upperBound(v2) >= lowerBound(v1) && upperBound(v2) <= upperBound(v1)) } function containsVal(vl,v) { for (var i in vl) { var v2=vl[i]; if (overlap(v,v2)) return true; } return false; } function centerVal(v) { if (v.a==undefined) return v; else return (v.a+v.b)/2; } function distanceVal (v1,v2) { return Math.abs(centerVal(v1)-centerVal(v2)); } function Bounds(vl,v) { if (vl.length==0) return {a:v,b:v}; else if (v==undefined) return {a:Min(vl),b:Max(vl)}; else return {a:Min([Min(vl),v]),b:Max([Max(vl),v])}; } function Min(vals) { var min=none; Comp.array.iter(vals, function (val) { if (min==none) min=(val.a==undefined?val:val.a); else min=val.a==undefined?(valmax?val:max):(val.b>max?val.a:max)); }); return max; } // Return interval of a value x with a<=x_center-eps, b>=x_center+eps function epsVal(x,eps) { if (x.a == undefined) return {a:x-eps,b:x+eps}; else if ((x.b-x.a) < 2*eps) return {a:centerVal(x)-eps,b:centerVal(x)+eps}; else return x; } /** Filter out unique values that are spaced at least by eps * */ function uniqueEps(data,eps) { var results=[]; Comp.array.iter(data,function (x) { var found; if (!results.length) results.push(x); else { Comp.array.iter(results,function (y,i) { if (found) return; found = Math.abs(centerVal(x)-centerVal(y)) lowerBound(_vals[i+1].val)) { if (_vals[i].val.b) _vals[i].val.b=lowerBound(_vals[i+1].val)-1; else _vals[i+1].val.a=upperBound(_vals[i].val)+1; } } } } model.vals=_vals; return model; break; case NODE_TYPES.FEATURE_VALUE: return model; break; } } /** Creates a new tree from training data (data) * * data is {x1:v1,x2:v2,..,y:vn} [] * target is classification key name * features is ['x1','x2,',..] w/o target variable * eps is interval applied to all data values * */ function createTree(data, target, features, options) { var _newS,child_node,bounds; var targets = Comp.array.unique(Comp.array.pluck(data, target)); // console.log(targets) if (options.maxdepth==undefined) options.maxdepth=1; if (options.maxdepth==0) return Result('-'); // console.log(data); // console.log(features); //Aios.aios.log('createTree:'+targets.length); //try {Aios.aios.CP();} catch (e) {throw 'DTI.createTree: '+options.maxdepth }; if (Aios) Aios.aios.CP(); if (targets.length == 1) return Result(targets[0]); if (features.length == 0) { var topTarget = mostCommon(targets); return Result(topTarget) } var bestFeatures = getBestFeatures(data, target, features, options.eps); var bestFeature = bestFeatures[0]; var remainingFeatures = Comp.array.filtermap(bestFeatures,function (feat) { if (feat.name!=bestFeature.name) return feat.name; else return none; }); /* var possibleValues = Comp.array.sort(Comp.array.pluck(data, bestFeature.name), function (x,y) { if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order }); */ var possibleValues = getPossibleVals(data,bestFeature.name); var vals=[]; //console.log(bestFeatures); //console.log(possibleValues); var partitions=partitionVals(possibleValues,options.eps); // Aios.aios.log(partitions); //console.log(bestFeatures); //console.log(possibleValues); if (partitions.length==1) { // no further 2*eps separation possible, find best feature by largest distance // resort best feature list with respect to value deviation bestFeatures.sort(function (ef1,ef2) { if (ef1.d > ef2.d) return -1; else return 1; }); bestFeature = bestFeatures[0]; possibleValues = getPossibleVals(data,bestFeature.name); Comp.array.iter(mergeVals(possibleValues),function (val,i) { _newS = data.filter(function(x) { // console.log(x[bestFeature.name],val,overlap(val,x[bestFeature.name])) return overlap(val,x[bestFeature.name]); }); child_node = Value(val); options.maxdepth--; child_node.child = createTree(_newS, target, remainingFeatures, options); //console.log(_newS); vals.push(child_node); }) } else Comp.array.iter(partitions,function (partition,i) { _newS = data.filter(function(x) { // console.log(x[bestFeature.name],v,overlap(x[bestFeature.name],v)) return containsVal(partition,x[bestFeature.name]); }); bounds = Bounds(partition); child_node = Value(options.eps==0?{a:bounds.a,b:bounds.b}:{a:bounds.a-options.eps,b:bounds.b+options.eps}); options.maxdepth--; child_node.child = createTree(_newS, target, remainingFeatures, options); //console.log(_newS); vals.push(child_node); }); return Feature(bestFeature.name,vals); } /** Return the depth of the tree * */ function depth(model) { switch (model.type) { case NODE_TYPES.RESULT: return 0; case NODE_TYPES.FEATURE: return 1+Comp.array.max(model.vals,function (val) { return depth(val); }); case NODE_TYPES.FEATURE_VALUE: return depth(model.child); } return 0; } /** Computes entropy of a list with 2-epsilon intervals * */ function entropyEps(vals,eps) { // TODO: overlapping value intervals var uniqueVals = Comp.array.unique(vals); var probs = uniqueVals.map(function(x) { return probEps(x, vals, eps) }); var logVals = probs.map(function(p) { return -p * log2(p) }); return logVals.reduce(function(a, b) { return a + b }, 0); } function entropyEps2(vals,eps) { // TODO: overlapping value intervals var uniqueVals = uniqueEps(vals,eps); var probs = uniqueVals.map(function(x) { return probEps2(x, vals, eps) }); var logVals = probs.map(function(p) { return -p * log2(p) }); return logVals.reduce(function(a, b) { return a + b }, 0); } function getBestFeatures(data,target,features,eps) { var bestfeatures=[]; function deviation(vals) { var n = vals.length; var mu=Comp.array.sum(vals,function (val) { return (lowerBound(val)+upperBound(val))/2; })/n; var dev=Comp.array.sum(vals,function (val) { return Math.pow(((lowerBound(val)+upperBound(val))/2)-mu,2); })/n; return dev; } for (var feature in features) { if (features[feature]==undefined) throw 'DTI.getBestFeatures: invalid feature vector'; var vals=Comp.array.pluck(data, features[feature]).map(function (val) {return val==undefined?0:val}); var e = entropyEps(vals,eps); var d = deviation(vals); var min = Min(vals); var max = Max(vals); bestfeatures.push({e:e,d:d,range:{a:min,b:max},name:features[feature]}); } bestfeatures.sort(function (ef1,ef2) { if (ef1.e > ef2.e) return -1; else return 1; }); return bestfeatures; } /** Find in one data set the most significant feature variable (i.e., with highest value) */ function getSignificantFeature(data,features) { var f,sig; for (f in features) { if (sig==undefined || sig.val < data[features[f]]) sig={name:features[f],val:data[features[f]]}; } return sig; } function getPossibleVals(data,feature) { return Comp.array.sort(Comp.array.pluck(data, feature), function (x,y) { if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order }); } /** Merge values and intervals */ function mergeVals(vals) { var _vals, merged,i,j; for (i in vals) { var vi = vals[i]; if (!_vals) _vals=[vi]; else { // Find overlapping values and merge merged=false; loopj: for (j in _vals) { var vj = _vals[j]; if (equal(vi,vj)) { merged=true; break loopj; } else if (overlap(vi,vj)) { merged=true; _vals[j]={a:Min([vi,vj]),b:Max([vi,vj])}; break loopj; } } if (!merged) _vals.push(vi); } } //Aios.aios.log(_vals); return _vals||[]; } /** * Predicts class for sample */ function nearestVal(vals,sample,fun) { var best=none; for (var v in vals) { var d=fun?distanceVal(fun(vals[v]),sample):distanceVal(vals[v],sample); if (best==none) best={v:vals[v],d:d}; else if (best.d > d) best={v:vals[v],d:d}; } if (best) return best.v; else return none; } /** Parttition an ordered set of values * Each partition of values has at least 2*eps distance to the next partition. * */ function partitionVals(vals,eps) { var last=none; var partitions=[]; var partition=[]; for(var i in vals) { var val0=vals[i]; var val1=vals[i-1]; if (val1==undefined) partition.push(val0); else if ( upperBound(val0) < upperBound(addVal(val1,2*eps))) partition.push(val0); else { partitions.push(partition); partition=[val0]; } } if (partition.length>0) partitions.push(partition); return partitions; } /** Make a predicition with sample data * */ function predict(model,sample) { var root = model; while (root && root.type !== NODE_TYPES.RESULT) { var attr = root.name; var sampleVal = sample[attr]; var childNode = nearestVal(root.vals,sampleVal,function (node) { return node.val; }); if (childNode){ root = childNode.child; } else { root = none; } } if (root) return root.name||root.val; else return none; }; /** Print the tree * */ function print(model,indent, compact) { var line='',sep; if (compact) return results(model); if (indent==undefined) indent=0; if (!model) return ''; var sp = function () {return Comp.string.create(indent);}; switch (model.type) { case NODE_TYPES.RESULT: return sp()+'-> '+model.name+NL; case NODE_TYPES.FEATURE: line=sp()+'$'+model.name+'?'+NL; Comp.array.iter(model.vals,function (v) { line += print(v,indent+2); }); return line; case NODE_TYPES.FEATURE_VALUE: line=sp()+'='+(model.val.a==undefined?model.val:'['+model.val.a+','+model.val.b+']')+NL; return line+print(model.child,indent+2); } return 'model?'; } /** * Computes probability of of a given value existing in a given list * with additional 2*epsilon interval, only applicable to numerical values. */ function probEps(value, list, eps) { // TODO: ranges var occurrences = Comp.array.filter(list, function(element) { return (element >= (value-eps)) && (element <= (value+eps)); }); var numOccurrences = occurrences.length; var numElements = list.length; return numOccurrences / numElements; } function probEps2(value, list, eps) { // TODO: ranges var occurrences = Comp.array.filter(list, function(element) { return overlap(epsVal(value), epsVal(element)); }); var numOccurrences = occurrences.length; var numElements = list.length; return numOccurrences / numElements; } /** Incremental update of the model with new training set(s). Can be executed with an empty model. * The current tree can be week for a new training set (new target). * This can result in a classification of the new target with insignificant variables. * Therefore, the last tree node must be exapnded with an additional strong (most significant) * variable of the new data set (but it is still a heuristic for future updates). */ function updateTree(model,data, target, features, options) { var eps = options.eps, maxdepth = options.maxdepth, verbose = options.verbose; var featuresINm={}, // All current tree feature variables and their value interval results=[], // All current tree result leafs set,i,v,feature,remainingFeatures,exists,sigFeature; // 1. Analysis of existing model var analyze = function (model,feature) { var feature2; if (!model) return; switch (model.type) { case NODE_TYPES.RESULT: if (!Comp.array.contains(results,model.name)) results.push(model.name); break; case NODE_TYPES.FEATURE: feature2={name:model.name}; if (!featuresINm[model.name]) featuresINm[model.name]=feature2; Comp.array.iter(model.vals,function (v) { analyze(v,featuresINm[model.name]) }); break; case NODE_TYPES.FEATURE_VALUE: if (!feature.val) feature.val={ a:(model.val.a==undefined?model.val:model.val.a), b:(model.val.a==undefined?model.val:model.val.b) }; else { feature.val.a=min(feature.val.a, (model.val.a==undefined?model.val:model.val.a)); feature.val.b=max(feature.val.b, (model.val.a==undefined?model.val:model.val.b)); } analyze(model.child); break; } } analyze(model); // console.log(featuresINm); // console.log(results); exists=Comp.array.contains(results,data[target]); // 2a. Empty model, add first training set with two significant feature variable nodes function init(set) { set=data[i]; sigFeature1=getSignificantFeature(set,features); remainingFeatures=Comp.array.filter(features,function (feat) { return sigFeature1.name!=feat; }); sigFeature2=getSignificantFeature(set,remainingFeatures); featuresINm[sigFeature1.name]={name:sigFeature1.name, val:{a:sigFeature1.val-eps,b:sigFeature1.val+eps}}; featuresINm[sigFeature2.name]={name:sigFeature2.name, val:{a:sigFeature2.val-eps,b:sigFeature2.val+eps}}; results.push(set[target]); model=Feature(sigFeature1.name,[ Value({a:set[sigFeature1.name]-eps,b:set[sigFeature1.name]+eps}, Feature(sigFeature2.name,[ Value({a:sigFeature2.val-eps,b:sigFeature2.val+eps}, Result(set[target])) ]))]); return model; } remainingFeatures=Comp.array.filter(features,function (feat) { return !featuresINm[feat]; }); // 2b. Update the tree with the new training set var update = function (model,set,feature) { var feature2,p; if (!model) return; switch (model.type) { case NODE_TYPES.RESULT: if (model.name != set[target] && verbose) console.log('Cannot insert new training set '+set[target]+' in tree. No more separating variables!'); break; case NODE_TYPES.FEATURE: // console.log(set[target]+': '+ model.name+'='+set[model.name]); if (set[model.name]<(featuresINm[model.name].val.a-eps) || set[model.name]>(featuresINm[model.name].val.b+eps)) { // add new training set; done // the current decision tree can be week, thus add another strong variable node, too! sigFeature=getSignificantFeature(set,remainingFeatures); featuresINm[sigFeature.name]={name:sigFeature.name, val:{a:sigFeature.val-eps,b:sigFeature.val+eps}}; featuresINm[model.name].val.a=min(featuresINm[model.name].val.a,set[model.name]-eps); featuresINm[model.name].val.b=max(featuresINm[model.name].val.b,set[model.name]+eps); if (!Comp.array.contains(results,set[target])) results.push(set[target]); model.vals.push(Value({a:set[model.name]-eps,b:set[model.name]+eps}, Feature(sigFeature.name,[ Value({a:sigFeature.val-eps,b:sigFeature.val+eps}, Result(set[target])) ]))); model.vals=Comp.array.sort(model.vals,function (v1,v2) {return (lowerBound(v1.val), 2012 * @author Martin Kleppe , 2012 * @author Ubilabs http://ubilabs.net, 2012 * @license MIT License */ function Node(obj, dimension, parent) { this.obj = obj; this.left = null; this.right = null; this.parent = parent; this.dimension = dimension; } /* KDTree * */ function KDTree(points, metric) { if (!(this instanceof KDTree)) return new KDTree(points, metric); // If points is not an array, assume we're loading a pre-built tree if (!Array.isArray(points)) { this.dimensions = points.dimensions; this.root = points; restoreParent(this.root); } else { this.dimensions = new Array(points[0].length); for (var i = 0; i < this.dimensions.length; i++) { this.dimensions[i] = i; } this.root = buildTree(points, 0, null, this.dimensions); } this.metric = metric; } // Convert to a JSON serializable structure; this just requires removing // the `parent` property KDTree.prototype.toJSON = function() { var result = toJSONImpl(this.root, true); result.dimensions = this.dimensions; return result; } KDTree.prototype.nearest = function(point, maxNodes, maxDistance) { var metric = this.metric; var dimensions = this.dimensions; var i; var bestNodes = new BinaryHeap( function (e) { return -e[1]; } ); function nearestSearch(node) { var dimension = dimensions[node.dimension]; var ownDistance = metric(point, node.obj); var linearPoint = {}; var bestChild, linearDistance, otherChild, i; function saveNode(node, distance) { bestNodes.push([node, distance]); if (bestNodes.size() > maxNodes) { bestNodes.pop(); } } for (i = 0; i < dimensions.length; i += 1) { if (i === node.dimension) { linearPoint[dimensions[i]] = point[dimensions[i]]; } else { linearPoint[dimensions[i]] = node.obj[dimensions[i]]; } } linearDistance = metric(linearPoint, node.obj); if (node.right === null && node.left === null) { if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { saveNode(node, ownDistance); } return; } if (node.right === null) { bestChild = node.left; } else if (node.left === null) { bestChild = node.right; } else { if (point[dimension] < node.obj[dimension]) { bestChild = node.left; } else { bestChild = node.right; } } nearestSearch(bestChild); if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { saveNode(node, ownDistance); } if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) { if (bestChild === node.left) { otherChild = node.right; } else { otherChild = node.left; } if (otherChild !== null) { nearestSearch(otherChild); } } } if (maxDistance) { for (i = 0; i < maxNodes; i += 1) { bestNodes.push([null, maxDistance]); } } if (this.root) { nearestSearch(this.root); } var result = []; for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) { if (bestNodes.content[i][0]) { result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]); } } return result; } function toJSONImpl(src) { var dest = new Node(src.obj, src.dimension, null); if (src.left) dest.left = toJSONImpl(src.left); if (src.right) dest.right = toJSONImpl(src.right); return dest; } function buildTree(points, depth, parent, dimensions) { var dim = depth % dimensions.length; if (points.length === 0) { return null; } if (points.length === 1) { return new Node(points[0], dim, parent); } points.sort(function (a, b) { a[dimensions[dim]] - b[dimensions[dim]]}); var median = Math.floor(points.length / 2); var node = new Node(points[median], dim, parent); node.left = buildTree(points.slice(0, median), depth + 1, node, dimensions); node.right = buildTree(points.slice(median + 1), depth + 1, node, dimensions); return node; } function restoreParent(root) { if (root.left) { root.left.parent = root; restoreParent(root.left); } if (root.right) { root.right.parent = root; restoreParent(root.right); } } /** BinaryHeap * */ // Binary heap implementation from: // http://eloquentjavascript.net/appendix2.html function BinaryHeap (scoreFunction) { if (!(this instanceof BinaryHeap)) return new BinaryHeap (scoreFunction); this.content = []; this.scoreFunction = scoreFunction; } BinaryHeap.prototype.push = function(element) { // Add the new element to the end of the array. this.content.push(element); // Allow it to bubble up. this.bubbleUp(this.content.length - 1); } BinaryHeap.prototype.pop = function() { // Store the first element so we can return it later. var result = this.content[0]; // Get the element at the end of the array. var end = this.content.pop(); // If there are any elements left, put the end element at the // start, and let it sink down. if (this.content.length > 0) { this.content[0] = end; this.sinkDown(0); } return result; } BinaryHeap.prototype.peek = function() { return this.content[0]; } BinaryHeap.prototype.size = function() { return this.content.length; } BinaryHeap.prototype.bubbleUp = function(n) { // Fetch the element that has to be moved. var element = this.content[n]; // When at 0, an element can not go up any further. while (n > 0) { // Compute the parent element's index, and fetch it. var parentN = Math.floor((n + 1) / 2) - 1; var parent = this.content[parentN]; // Swap the elements if the parent is greater. if (this.scoreFunction(element) < this.scoreFunction(parent)) { this.content[parentN] = element; this.content[n] = parent; // Update 'n' to continue at the new position. n = parentN; } else { // Found a parent that is less, no need to move it further. break; } } } BinaryHeap.prototype.sinkDown = function(n) { // Look up the target element and its score. var length = this.content.length; var element = this.content[n]; var elemScore = this.scoreFunction(element); while (true) { // Compute the indices of the child elements. var child2N = (n + 1) * 2; var child1N = child2N - 1; // This is used to store the new position of the element, // if any. var swap = null; // If the first child exists (is inside the array)... if (child1N < length) { // Look it up and compute its score. var child1 = this.content[child1N]; var child1Score = this.scoreFunction(child1); // If the score is less than our element's, we need to swap. if (child1Score < elemScore) { swap = child1N; } } // Do the same checks for the other child. if (child2N < length) { var child2 = this.content[child2N]; var child2Score = this.scoreFunction(child2); if (child2Score < (swap === null ? elemScore : child1Score)) { swap = child2N; } } // If the element needs to be moved, swap it, and continue. if (swap !== null) { this.content[n] = this.content[swap]; this.content[swap] = element; n = swap; } else { // Otherwise, we are done. break; } } } /** KNN * */ /** * @param {Array} dataset * @param {Array} labels * @param {object} options * @param {number} [options.k=numberOfClasses + 1] - Number of neighbors to classify. * @param {function} [options.distance=euclideanDistance] - Distance function that takes two parameters. */ function KNN(dataset, labels, options) { if (!options) options={}; if (!(this instanceof KNN)) return new KNN(dataset, labels, options); if (dataset === true) { var model = labels; this.kdTree = new KDTree(model.kdTree, options); this.k = model.k; this.classes = new Set(model.classes); this.isEuclidean = model.isEuclidean; return; } var classes = new Set(labels); var distance = getDistanceFunction(options.distance), k = options.k||classes.size + 1; var points = new Array(dataset.length); for (var i = 0; i < points.length; ++i) { points[i] = dataset[i].slice(); } for (i = 0; i < labels.length; ++i) { points[i].push(labels[i]); } this.kdTree = new KDTree(points, distance); this.k = k; this.classes = classes; this.isEuclidean = distance === euclideanDistance; } /** * Create a new KNN instance with the given model. * @param {object} model * @param {function} distance=euclideanDistance - distance function must be provided if the model wasn't trained with euclidean distance. * @return {KNN} */ function load(model, distance) { if (!distance) distance = euclideanDistance; if (model.name !== 'KNN') { throw new Error('invalid model: ' + model.name); } if (!model.isEuclidean && distance === euclideanDistance) { throw new Error('a custom distance function was used to create the model. Please provide it again'); } if (model.isEuclidean && distance !== euclideanDistance) { throw new Error('the model was created with the default distance function. Do not load it with another one'); } return new KNN(true, model, distance); } /** * Return a JSON containing the kd-tree model. * @return {object} JSON KNN model. */ KNN.prototype.toJSON = function() { return { name: 'KNN', kdTree: this.kdTree, k: this.k, classes: Array.from(this.classes), isEuclidean: this.isEuclidean }; } /** * Predicts the output given the matrix to predict. * @param {Array} dataset * @return {Array} predictions */ KNN.prototype.predict = function(dataset) { if (Array.isArray(dataset)) { if (typeof dataset[0] === 'number') { return getSinglePrediction(this, dataset); } else if (Array.isArray(dataset[0]) && typeof dataset[0][0] === 'number') { var predictions = new Array(dataset.length); for (var i = 0; i < dataset.length; i++) { predictions[i] = getSinglePrediction(this, dataset[i]); } return predictions; } } throw new TypeError('dataset to predict must be an array or a matrix'); } function getSinglePrediction(knn, currentCase) { var nearestPoints = knn.kdTree.nearest(currentCase, knn.k); var pointsPerClass = {}; var predictedClass = -1; var maxPoints = -1; var lastElement = nearestPoints[0][0].length - 1; //for (var element of knn.classes) { // pointsPerClass[element] = 0; //} forof(knn.classes,function (element) { pointsPerClass[element] = 0; }); for (var i = 0; i < nearestPoints.length; ++i) { var currentClass = nearestPoints[i][0][lastElement]; var currentPoints = ++pointsPerClass[currentClass]; if (currentPoints > maxPoints) { predictedClass = currentClass; maxPoints = currentPoints; } } return predictedClass; } /** Create a KNN * * typeof @options = {data:number [] [],result: * []} * */ var KNN2 = function (options) { if (!(this instanceof KNN)) return new KNN(options); this.data = options.data; this.result = options.result; } /** Make a prediction * * typeof @options = {x,k?,weightf?:function,distance?:function} */ KNN2.prototype.predict = function(options) { var x = options.x; var k = options.k || 3; var weightf = getWeightedFunction(options.weightf); var distance = getDistanceFunction(options.distance); var distanceList = []; var i; for(i=0; i= 1 || s == 0); s = Math.sqrt( (-2 * Math.log(s)) / s ); return v1 * s; } m.shape = function(mat) { var row = mat.length; var col = mat[0].length; return [row,col]; }; m.addVec = function(vec1, vec2) { if(vec1.length === vec2.length) { var result = []; var i; for(i=0;i max) max = vec[i]; } return max; } m.minMat = function(mat) { var min = mat[0][0]; var i = mat.length; while (i--) { for(var j=0;j tol && self.alphas[i] >0)) { // Randomly selects j (i != j) var j = math.randInt(0,self.x.length-1); if(i==j) j = (j+1) % self.x.length; var E_j = self.f(self.x[j]) - self.y[j]; var alpha_i_old = self.alphas[i], alpha_j_old = self.alphas[j]; // Compute L,H var L,H; if(self.y[i] !== self.y[j]) { L = Math.max(0, self.alphas[j] - self.alphas[i]); H = Math.min(C, C + self.alphas[j] - self.alphas[i]); } else { L = Math.max(0, self.alphas[j] + self.alphas[i] - C); H = Math.min(C, self.alphas[j] + self.alphas[i]); } if(L === H) continue; // Compute ETA var ETA = 2 * self.kernel(self.x[i],self.x[j]) - self.kernel(self.x[i],self.x[i]) - self.kernel(self.x[j],self.x[j]); if(ETA >= 0) continue; // Clip new value to alpha_j self.alphas[j] -= 1.*self.y[j] * (E_i - E_j) / ETA; if(self.alphas[j] > H) self.alphas[j] = H; else if(self.alphas[j] < L) self.alphas[j] = L; if(Math.abs(self.alphas[j] - alpha_j_old) < alphatol) continue; // Clip new value to alpha_i self.alphas[i] += self.y[i] * self.y[j] * (alpha_j_old - self.alphas[j]); // update b var b1 = self.b - E_i - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[i]) - self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[i],self.x[j]); var b2 = self.b - E_j - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[j]) - self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[j],self.x[j]); if(0 < self.alphas[i] && self.alphas[i] < C) self.b = b1; else if(0 < self.alphas[j] && self.alphas[j] < C) self.b = b2; else self.b = (b1+b2)/2.0; numChangedAlphas ++ ; } // end-if } // end-for if(numChangedAlphas == 0) passes++; else passes = 0; } } SVM.prototype.predict = function(x) { var self = this; if(self.f(x) >= 0) return 1; else return -1; } SVM.prototype.f = function(x) { var self = this; var f = 0, j; for(j=0; j