UNPKG

dcp-client

Version:

Core libraries for accessing DCP network

182 lines (163 loc) 6.42 kB
/** @file sa-ww-simulation.js * A simulated WebWorker evaluator environment. * * Takes onreadln, writeln & more from the native & node evaluators * and implements web worker API functionality. * This environment is designed so that no I/O etc is permitted, beyond * communication with stdin and stdout using a well-defined protocol, exposed * via a WebWorker-like API using postMessage etc. * * The native & node evaluators provide the following API on the global object when this * program is evaluated: * - writeln('string') Write a message to stdout. * - onreadln(function) Dispatch function, with a single string * argument, when a message is received on stdin. * Each string is a single JSON-serialized object. * - die() Tell the host environment that it's time to end * it all. * * @author Wes Garland, wes@sparc.network * @date March 2018 * * @note Unusual function scoping is done to eliminate spurious symbols * from being accessible from the global object, to mitigate * certain classes of security risks. The global object here is * the top of the scope chain (ie global object) for all code run * by hosts in this environment. */ /* globals writeln, onreadln, die */ // @ts-nocheck /** * self is a localscope reference to the global object. All subsequent files run * after this one will have localscope access to 'self'. This is done to emulate * web workers which already have self defined. */ const self = this; delete self.console; try { (function privateScope(writeln, onreadln, die) { /* Implement console.log which propagates messages to stdout. */ var console = { log: function workerControl$$log () { writeln('LOG:' + Array.prototype.slice.call(arguments).join(' ').replace(/\n/g,"\u2424")) } } self._console = console try { self.console = console console.debug = console.log console.error = console.log console.warn = console.log } catch (e) {} var inMsg, outMsg var eventListeners = {} var onHandlerTypes = ['message', 'error'] var onHandlers = {} //Will be removing JSON and KVIN in access-lists.js, so need an alias for them var serialize = JSON.stringify var deserialize = JSON.parse var unmarshal = KVIN.unmarshal self.postMessage = function workerControl$$Worker$postMessage (message) { send({type: 'workerMessage', message }); } self.close = function close() { writeln('DIE: worker close called'); die(); } self.addEventListener = function workerControl$$Worker$addEventListener (type, listener) { if (typeof eventListeners[type] === 'undefined') { eventListeners[type] = [] } eventListeners[type].push(listener) } self.removeEventListener = function workerControl$$Worker$removeEventListener (type, listener) { if (typeof eventListeners[type] === 'undefined') { return } const i = eventListeners[type].indexOf(listener) if (i !== -1) { eventListeners[type].splice(i, 1) } } for (let i = 0; i < onHandlerTypes.length; i++) { let onHandlerType = onHandlerTypes[i] Object.defineProperty(self, 'on' + onHandlerType, { enumerable: true, configurable: false, set: function (cb) { this.removeEventListener(onHandlerType, onHandlers[onHandlerType]) this.addEventListener(onHandlerType, cb) onHandlers[onHandlerType] = cb }, get: function () { return onHandlers[onHandlerType] } }) } /** Emit an event */ function emitEvent(eventName, argument) { if (eventListeners[eventName]) { for (let i = 0; i < eventListeners[eventName].length; i++) { eventListeners[eventName][i].call(self, argument) } } } /** Send a message to stdout. * This defines the "from evaluator" half of the protocol. */ function send (outMsg) { outMsg = serialize(outMsg) writeln('MSG:' + outMsg) } /** Receive a line from stdin. * This defines the "to evaluator" half of the protocol. */ onreadln(function receiveLine(line) { try { outMsg = { type: 'result', step: 'parseInput::' + deserialize.name, success: false } inMsg = deserialize(line) outMsg.origin = inMsg.type outMsg.step = inMsg.type || 'parseMessage' switch (inMsg.type) { default: throw new Error("Invalid message type '" + (typeof inMsg.type === 'string' ? inMsg.type : JSON.stringify(inMsg.type)) + "'") case 'newSerializer': outMsg.step = 'changeSerializer' let newSerializer = eval(inMsg.payload) outMsg.success = true send(outMsg) // acknowledge change in old format serialize = newSerializer.serialize deserialize = newSerializer.deserialize outMsg = { type: 'nop', success: true } send(outMsg); break case 'workerMessage': // if (inMsg.message.request === 'main') { // inMsg.message.data = unmarshal(inMsg.message.data); // } // if (inMsg.message.request === 'assign') { // inMsg.message.job.arguments = unmarshal(inMsg.message.job.arguments); // } emitEvent('message', {data: inMsg.message}) outMsg.success = true send(outMsg) break case 'die': writeln('DIE: ' + Date()) outMsg.success = true die(); break; } } catch (e) { /* Return exceptions thrown in this engine (presumably the host code) to stdout for reporting. */ outMsg.success = false outMsg.exception = { name: e.name, message: e.message, fileName: e.fileName, lineNumber: e.lineNumber, stack: e.stack } outMsg.e = e send(outMsg) } }) /* receiveLine */ })(writeln, onreadln, die) /* privateScope */ /* Remove symbols from global scope that may be security leaks*/ writeln = onreadln = die = undefined delete self.writeln delete self.onreadln delete self.die } catch (e) { writeln('DIE: ' + e.message); die(); }