UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

1,695 lines 48.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.runWithProgram = exports.show_heap_value = exports.show_heap = exports.NEW_FUNCTION = exports.show_registers = void 0; const constants_1 = require("../constants"); const timeoutErrors_1 = require("../errors/timeoutErrors"); const vm_prelude_1 = require("../stdlib/vm.prelude"); const astCreator_1 = require("../utils/ast/astCreator"); const stringify_1 = require("../utils/stringify"); const opcodes_1 = require("./opcodes"); const svml_scheduler_1 = require("./svml-scheduler"); const util_1 = require("./util"); const LDCI_VALUE_OFFSET = 1; const LDCF32_VALUE_OFFSET = 1; const LDCF64_VALUE_OFFSET = 1; const LGCS_VALUE_OFFSET = 1; const FUNC_MAX_STACK_SIZE_OFFSET = 0; const FUNC_ENV_SIZE_OFFSET = 1; const FUNC_NUM_ARGS_OFFSET = 2; const FUNC_CODE_OFFSET = 3; const INS_OPCODE_OFFSET = 0; const BR_OFFSET = 1; const LD_ST_INDEX_OFFSET = 1; const LD_ST_ENV_OFFSET = 2; const CALL_NUM_ARGS_OFFSET = 1; const CALLT_NUM_ARGS_OFFSET = 1; const CALLP_ID_OFFSET = 1; const CALLP_NUM_ARGS_OFFSET = 2; const CALLTP_ID_OFFSET = 1; const CALLTP_NUM_ARGS_OFFSET = 2; const CALLV_ID_OFFSET = 1; const CALLV_NUM_ARGS_OFFSET = 2; const CALLTV_ID_OFFSET = 1; const CALLTV_NUM_ARGS_OFFSET = 2; const NEWC_ADDR_OFFSET = 1; const ADDR_FUNC_INDEX_OFFSET = 0; const NEWENV_NUM_ARGS_OFFSET = 1; const NEWCP_ID_OFFSET = 1; const NEWCV_ID_OFFSET = 1; // VIRTUAL MACHINE // "registers" are the global variables of our machine. // These contain primitive values (numbers or boolean // values) or arrays of primitive values // PROG contains the entire SVML JSON formatted program let PROG; // FUNC contains the function array, for easier access let FUNC; // INTERNAL is the function array for internal functions const INTERNAL = vm_prelude_1.INTERNAL_FUNCTIONS; const INTERNAL_OPCODE_SLOT = 1; const INTERNAL_NUM_ARGS_SLOT = 2; const INTERNAL_HAS_RETURN_VAL_SLOT = 3; // GLOBAL_ENV is the env that contains all the primitive functions let GLOBAL_ENV = -1; // HEAP is array containing all dynamically allocated data structures let HEAP = []; // next free slot in heap let FREE = 0; // temporary value, used by PUSH and POP; initially a dummy value let RES = -Infinity; // THREAD STATE // P contains the instructions to be executed in the current function call let P; // PC is program counter: index of the next instruction in P let PC = 0; // ENV is address of current environment in HEAP; initially a dummy value let ENV = -1; // OS is address of current operand stack in HEAP; initially a dummy value let OS = -Infinity; // RTS contains the call stack let RTS = []; // top index of RTS stack let TOP_RTS = -1; /** * when executing concurrent code */ // TO is timeout counter: how many instructions are left for a thread to run let TO = 0; // some general-purpose registers let A = 0; let B = 0; let C = 0; let D = 0; let E = 0; let F = 0; let G = 0; let H = 0; let I = 0; let J = 0; let K = 0; function show_executing(s) { let str = ''; str += '--- RUN ---' + s + '\n'; str += 'PC :' + PC + '\n'; str += 'instr:' + (0, util_1.getName)(P[PC][INS_OPCODE_OFFSET]); return str; } // for debugging: show all registers function show_registers(s, isShowExecuting = true) { let str = ''; if (isShowExecuting) { str = show_executing(s) + '\n'; } str += '--- REGISTERS ---' + s + '\n'; str += 'RES:' + RES + '\n'; str += 'A :' + A + '\n'; str += 'B :' + B + '\n'; str += 'C :' + C + '\n'; str += 'D :' + D + '\n'; str += 'E :' + E + '\n'; str += 'F :' + F + '\n'; str += 'G :' + G + '\n'; str += 'H :' + H + '\n'; str += 'I :' + I + '\n'; str += 'OS :' + OS + '\n'; str += 'ENV:' + ENV + '\n'; str += 'RTS:' + RTS + '\n'; str += 'TOP_RTS:' + TOP_RTS + '\n'; str += 'TO:' + TO + '\n'; str += 'scheduler_state:' + scheduler_state_string() + '\n'; return str; } exports.show_registers = show_registers; // register that says if machine is running let RUNNING = true; const NORMAL = 0; const DIV_ERROR = 1; const TYPE_ERROR = 2; const NUM_ARGS_ERROR = 3; const CALL_NON_FUNCTION_ERROR = 4; let ERROR_MSG_ARGS = []; let STATE = NORMAL; // general node layout const TAG_SLOT = 0; const SIZE_SLOT = 1; const FIRST_CHILD_SLOT = 2; const LAST_CHILD_SLOT = 3; // NEW expects tag in A and size in B function NEW() { HEAP[FREE + TAG_SLOT] = A; HEAP[FREE + SIZE_SLOT] = B; RES = FREE; FREE = FREE + B; } // boxed nodes const BOXED_VALUE_SLOT = 4; // number nodes layout // // 0: tag = -100 // 1: size = 5 // 2: offset of first child from the tag: 6 (no children) // 3: offset of last child from the tag: 5 (must be less than first) // 4: value const NUMBER_TAG = -100; const NUMBER_SIZE = 5; const NUMBER_VALUE_SLOT = 4; // changes A, B, C, expects number in A function NEW_NUMBER() { C = A; A = NUMBER_TAG; B = NUMBER_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 6; HEAP[RES + LAST_CHILD_SLOT] = 5; // no children HEAP[RES + NUMBER_VALUE_SLOT] = C; } // bool nodes layout // // 0: tag = -101 // 1: size = 5 // 2: offset of first child from the tag: 6 (no children) // 3: offset of last child from the tag: 5 (must be less than first) // 4: value const BOOL_TAG = -101; const BOOL_SIZE = 5; const BOOL_VALUE_SLOT = 4; // changes A, B, C, expects boolean value in A function NEW_BOOL() { C = A; A = BOOL_TAG; B = BOOL_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 6; HEAP[RES + LAST_CHILD_SLOT] = 5; // no children HEAP[RES + BOOL_VALUE_SLOT] = C; } // string nodes layout // // 0: tag = -107 // 1: size = 5 // 2: offset of first child from the tag: 6 (no children) // 3: offset of last child from the tag: 5 (must be less than first) // 4: value const STRING_TAG = -107; const STRING_SIZE = 5; const STRING_VALUE_SLOT = 4; // changes A, B, C, expects string literal in A function NEW_STRING() { C = A; A = STRING_TAG; B = STRING_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 6; HEAP[RES + LAST_CHILD_SLOT] = 5; // no children HEAP[RES + STRING_VALUE_SLOT] = C; } // array nodes layout // // 0: tag = -108 // 1: size = 5 // 2: offset of first child from the tag: 6 (no children) // 3: offset of last child from the tag: 5 (must be less than first) // 4: value (JS array, each element is the address of the element's node in the heap) // 5: current size of array (largest index assigned) const ARRAY_TAG = -108; const ARRAY_SIZE = 6; const ARRAY_VALUE_SLOT = 4; const ARRAY_SIZE_SLOT = 5; // changes A, B function NEW_ARRAY() { A = ARRAY_TAG; B = ARRAY_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 6; HEAP[RES + LAST_CHILD_SLOT] = 5; // no children HEAP[RES + ARRAY_VALUE_SLOT] = []; HEAP[RES + ARRAY_SIZE_SLOT] = 0; } // undefined nodes layout // // 0: tag = -106 // 1: size = 4 // 2: offset of first child from the tag: 5 (no children) // 3: offset of last child from the tag: 4 (must be less than first) const UNDEFINED_TAG = -106; const UNDEFINED_SIZE = 4; function NEW_UNDEFINED() { A = UNDEFINED_TAG; B = UNDEFINED_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 5; HEAP[RES + LAST_CHILD_SLOT] = 4; // no children } // null nodes layout // // 0: tag = -109 // 1: size = 4 // 2: offset of first child from the tag: 5 (no children) // 3: offset of last child from the tag: 4 (must be less than first) const NULL_TAG = -109; const NULL_SIZE = 4; // changes A, B. function NEW_NULL() { A = NULL_TAG; B = NULL_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 5; HEAP[RES + LAST_CHILD_SLOT] = 4; // no children } // operandstack nodes layout // // 0: tag = -105 // 1: size = maximal number of entries + 4 // 2: first child slot = 4 // 3: last child slot = current top of stack; initially 3 (empty stack) // 4: first entry // 5: second entry // ... const OS_TAG = -105; // changes A, B, C, expects max size in A function NEW_OS() { C = A; A = OS_TAG; B = C + 4; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 4; // operand stack initially empty HEAP[RES + LAST_CHILD_SLOT] = 3; } // PUSH and POP are convenient subroutines that operate on // the operand stack OS // PUSH expects its argument in A // changes B function PUSH_OS() { B = HEAP[OS + LAST_CHILD_SLOT]; // address of current top of OS B = B + 1; HEAP[OS + LAST_CHILD_SLOT] = B; // update address of current top of OS HEAP[OS + B] = A; } // POP puts the top-most value into RES // changes B function POP_OS() { B = HEAP[OS + LAST_CHILD_SLOT]; // address of current top of OS HEAP[OS + LAST_CHILD_SLOT] = B - 1; // update address of current top of OS RES = HEAP[OS + B]; } // closure nodes layout // // 0: tag = -103 // 1: size = 7 // 2: offset of first child from the tag: 6 (only environment) // 3: offset of last child from the tag: 6 // 4: type of function (normal, primitive, internal) // 5: index = index of function in program function array // 6: environment const CLOSURE_TAG = -103; const CLOSURE_SIZE = 7; const CLOSURE_NORMAL_TYPE = 0; // not necessary right now due to the way primitives are handled // const CLOSURE_PRIMITIVE_TYPE = 1 const CLOSURE_INTERNAL_TYPE = 2; const CLOSURE_TYPE_SLOT = 4; const CLOSURE_FUNC_INDEX_SLOT = 5; const CLOSURE_ENV_SLOT = 6; // changes A, B, E, F // expects index of function in FUNC / INTERNAL in A // expects type of function in B function NEW_FUNCTION() { E = A; F = B; A = CLOSURE_TAG; B = CLOSURE_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = CLOSURE_ENV_SLOT; HEAP[RES + LAST_CHILD_SLOT] = CLOSURE_ENV_SLOT; HEAP[RES + CLOSURE_TYPE_SLOT] = F; HEAP[RES + CLOSURE_FUNC_INDEX_SLOT] = E; HEAP[RES + CLOSURE_ENV_SLOT] = ENV; } exports.NEW_FUNCTION = NEW_FUNCTION; // stackframe nodes layout // // 0: tag = -104 // 1: size = 7 // 2: offset of first child from the tag: 5 (environment) // 3: offset of last child from the tag: 6 (operand stack) // 4: program counter = return address // 5: environment // 6: operand stack // 7: current function code array const RTS_FRAME_TAG = -104; const RTS_FRAME_SIZE = 8; const RTS_FRAME_PC_SLOT = 4; const RTS_FRAME_ENV_SLOT = 5; const RTS_FRAME_OS_SLOT = 6; const RTS_FRAME_FUNC_INS_SLOT = 7; // changes A, B, expects current PC, ENV, OS, P, TOP_RTS in their registers function NEW_RTS_FRAME() { A = RTS_FRAME_TAG; B = RTS_FRAME_SIZE; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = RTS_FRAME_ENV_SLOT; HEAP[RES + LAST_CHILD_SLOT] = RTS_FRAME_OS_SLOT; HEAP[RES + RTS_FRAME_PC_SLOT] = PC + 1; // next instruction! HEAP[RES + RTS_FRAME_ENV_SLOT] = ENV; HEAP[RES + RTS_FRAME_OS_SLOT] = OS; HEAP[RES + RTS_FRAME_FUNC_INS_SLOT] = P; } // expects stack frame in A function PUSH_RTS() { TOP_RTS = TOP_RTS + 1; RTS[TOP_RTS] = A; } // places stack frame into RES function POP_RTS() { RES = RTS[TOP_RTS]; TOP_RTS = TOP_RTS - 1; } // environment nodes layout // // 0: tag = -102 // 1: size = number of entries + 5 // 2: first child = 5 // 3: last child // 4: previous env // 5: first entry // 6: second entry // ... const ENV_TAG = -102; // Indicates previous environment const PREVIOUS_ENV_SLOT = 4; const NIL = -1; // expects number of env entries in A, previous env in B // changes A, B, C function NEW_ENVIRONMENT() { C = A; D = B; A = ENV_TAG; B = C + 5; NEW(); HEAP[RES + FIRST_CHILD_SLOT] = 5; HEAP[RES + LAST_CHILD_SLOT] = 4 + C; HEAP[RES + PREVIOUS_ENV_SLOT] = D; } // expect operands to check equality for in C and D // return result as boolean literal in A function CHECK_EQUAL() { A = C === D; // same reference (for arrays and normal functions) B = HEAP[C + TAG_SLOT] === HEAP[D + TAG_SLOT]; // check same type E = HEAP[C + TAG_SLOT] === UNDEFINED_TAG; A = A || (B && E); // check undefined E = HEAP[C + TAG_SLOT] === NULL_TAG; A = A || (B && E); // check null E = HEAP[C + TAG_SLOT] === CLOSURE_TAG; // check functions if (B && E) { // if internal, compare index E = HEAP[C + CLOSURE_TYPE_SLOT] === HEAP[D + CLOSURE_TYPE_SLOT]; E = E && HEAP[C + CLOSURE_FUNC_INDEX_SLOT] === HEAP[D + CLOSURE_FUNC_INDEX_SLOT]; A = A || E; } E = HEAP[C + TAG_SLOT] === NUMBER_TAG; E = E || HEAP[C + TAG_SLOT] === STRING_TAG; E = E || HEAP[C + TAG_SLOT] === BOOL_TAG; E = E && B; // check same type and has boxed value C = HEAP[C + BOXED_VALUE_SLOT]; D = HEAP[D + BOXED_VALUE_SLOT]; E = E && C === D; A = A || E; } const NORMAL_CALL = 0; const TAIL_CALL = 1; const PRIMITIVE_CALL = 2; const PRIMITIVE_TAIL_CALL = 3; const INTERNAL_CALL = 4; const INTERNAL_TAIL_CALL = 5; // expect number of arguments in G, closure (index for internal) in F, // type of call in J // currently only checks number of arguments for variadic functions // uses A,B,C,D,E,F,G,H,I,J,K function FUNCTION_CALL() { if (J === INTERNAL_CALL || J === INTERNAL_TAIL_CALL || ((J === NORMAL_CALL || J === TAIL_CALL) && HEAP[F + CLOSURE_TYPE_SLOT] === CLOSURE_INTERNAL_TYPE)) { if (J === NORMAL_CALL || J === TAIL_CALL) { F = HEAP[F + CLOSURE_FUNC_INDEX_SLOT]; } INTERNAL_FUNCTION_CALL(); } else { // prep for new environment B = HEAP[F + CLOSURE_ENV_SLOT]; // A is now env to be extended H = HEAP[F + CLOSURE_FUNC_INDEX_SLOT]; H = FUNC[H]; // H is now the function header of the function to call I = H[FUNC_NUM_ARGS_OFFSET]; A = H[FUNC_ENV_SIZE_OFFSET]; // A is now the environment extension count NEW_ENVIRONMENT(); // after this, RES is new env E = RES; // for varargs (-1), put all elements into an array. hacky implementation if (I === vm_prelude_1.VARARGS_NUM_ARGS) { NEW_ARRAY(); I = RES; for (C = G - 1; C >= 0; C = C - 1) { POP_OS(); HEAP[I + ARRAY_VALUE_SLOT][C] = RES; } HEAP[I + ARRAY_SIZE_SLOT] = G; // manually update array length D = E + HEAP[E + FIRST_CHILD_SLOT]; HEAP[D] = I; } else if (I === G) { D = E + HEAP[E + FIRST_CHILD_SLOT] + G - 1; // D is now address where last argument goes in new env for (C = D; C > D - G; C = C - 1) { POP_OS(); // now RES has the address of the next arg HEAP[C] = RES; // copy argument into new env } } else { STATE = NUM_ARGS_ERROR; ERROR_MSG_ARGS[0] = I; ERROR_MSG_ARGS[1] = G; RUNNING = false; } if (J === NORMAL_CALL || J === TAIL_CALL) { POP_OS(); // closure is on top of OS; pop it as not needed } if (J === NORMAL_CALL || J === PRIMITIVE_CALL) { // normal calls need to push to RTS NEW_RTS_FRAME(); // saves PC+1, ENV, OS, P A = RES; PUSH_RTS(); } PC = 0; P = H[FUNC_CODE_OFFSET]; A = H[FUNC_MAX_STACK_SIZE_OFFSET]; NEW_OS(); OS = RES; ENV = E; } } // expects type of call in J, internal function id in F // number of arguments in G // actually no difference between tail and normal function INTERNAL_FUNCTION_CALL() { F = INTERNAL[F]; K = F; // save the internal function if (K[INTERNAL_NUM_ARGS_SLOT] !== vm_prelude_1.VARARGS_NUM_ARGS && K[INTERNAL_NUM_ARGS_SLOT] !== G) { STATE = NUM_ARGS_ERROR; ERROR_MSG_ARGS[0] = K[INTERNAL_NUM_ARGS_SLOT]; ERROR_MSG_ARGS[1] = G; RUNNING = false; } else { M[K[INTERNAL_OPCODE_SLOT]](); // call subroutine directly if (K[INTERNAL_HAS_RETURN_VAL_SLOT]) { // pop return value if present POP_OS(); D = RES; } else { NEW_UNDEFINED(); D = RES; } if (J === NORMAL_CALL || J === TAIL_CALL) { POP_OS(); // pop closure } A = D; PUSH_OS(); // push return value back PC = PC + 1; } } let scheduler = new svml_scheduler_1.RoundRobinScheduler(); const threads = new Map(); let currentThreadId = -1; // Initialize the scheduler (do this before running code) function INIT_SCHEDULER() { scheduler = new svml_scheduler_1.RoundRobinScheduler(); threads.clear(); } // Schedule new thread for later execution using the thread state currently in VM // You will want to pop another thread, restore thread state, etc. // after calling this, as the current thread state should not be running function NEW_THREAD() { const newId = scheduler.newThread(); threads.set(newId, [OS, ENV, PC, P, RTS, TOP_RTS]); } // Schedule current thread for later execution // You will want to pop another thread, restore thread state, etc. // after calling this, as the current thread state should not be running function PAUSE_THREAD() { // Save state to threads map threads.set(currentThreadId, [OS, ENV, PC, P, RTS, TOP_RTS]); // Pause thread in scheduler scheduler.pauseThread(currentThreadId); } function DELETE_CURRENT_THREAD() { // Clear state from threads map threads.delete(currentThreadId); // Delete thread from scheduler scheduler.deleteCurrentThread(currentThreadId); currentThreadId = -1; } // Get thread from scheduler and run it function RUN_THREAD() { ; [currentThreadId, TO] = scheduler.runThread(); [OS, ENV, PC, P, RTS, TOP_RTS] = threads.get(currentThreadId); } // Returns the number of threads in the scheduler function GET_NUM_IDLE_THREADS() { RES = scheduler.numIdle(); } function scheduler_state_string() { return new Array(scheduler.idleThreads).toString(); } // debugging: show current heap function is_node_tag(x) { return x !== undefined && x <= -100 && x >= -110; } function node_kind(x) { return x === NUMBER_TAG ? 'number' : x === BOOL_TAG ? 'boolean' : x === CLOSURE_TAG ? 'closure' : x === RTS_FRAME_TAG ? 'RTS frame' : x === OS_TAG ? 'OS' : x === ENV_TAG ? 'environment' : x === UNDEFINED_TAG ? 'undefined' : x === NULL_TAG ? 'null' : x === STRING_TAG ? 'string' : x === ARRAY_TAG ? 'array' : ' (unknown node kind)'; } function show_heap(s) { const len = HEAP.length; let i = 0; let str = ''; str += '--- HEAP --- ' + s + '\n'; while (i < len) { str += i + ': ' + HEAP[i] + // TODO is_number(HEAP[i]) && (is_node_tag(HEAP[i]) ? ' (' + node_kind(HEAP[i]) + ')' : '') + '\n'; i = i + 1; } return str; } exports.show_heap = show_heap; function show_heap_value(address) { return ('result: heap node of type = ' + node_kind(HEAP[address]) + ', value = ' + HEAP[address + NUMBER_VALUE_SLOT]); } exports.show_heap_value = show_heap_value; // SVML implementation // We implement our machine with an array M that // contains subroutines. Each subroutine implements // a machine instruction, using a nullary function. // The machine can then index into M using the op-codes // of the machine instructions. To be implementable on // common hardware, the subroutines have the // following structure: // * they have no parameters // * they do not return any results // * they do not have local variables // * they do not call other functions except the // subroutines PUSH and POP // * each line is very simple, for example an array access // Ideally, each line can be implemented directly with a // machine instruction of a real computer. In that case, // the subroutines could become machine language macros, // and the compiler could generate real machine code. // There are some exceptions due to the need to support // primitive functions or certain behavior const M = []; M[opcodes_1.default.NOP] = () => { PC = PC + 1; }; M[opcodes_1.default.LGCI] = () => { A = P[PC][LDCI_VALUE_OFFSET]; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCF32] = () => { A = P[PC][LDCF32_VALUE_OFFSET]; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCF64] = () => { A = P[PC][LDCF64_VALUE_OFFSET]; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCB0] = () => { A = false; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCB1] = () => { A = true; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCU] = () => { NEW_UNDEFINED(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCN] = () => { NEW_NULL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LGCS] = () => { A = P[PC][LGCS_VALUE_OFFSET]; NEW_STRING(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.POPG] = () => { POP_OS(); PC = PC + 1; }; // type check here as we need to know whether number or string M[opcodes_1.default.ADDG] = () => { POP_OS(); I = RES; POP_OS(); G = RES; H = HEAP[I + TAG_SLOT] === HEAP[G + TAG_SLOT]; D = HEAP[I + TAG_SLOT] === NUMBER_TAG; F = H && D; if (F) { A = HEAP[I + NUMBER_VALUE_SLOT]; A = HEAP[G + NUMBER_VALUE_SLOT] + A; NEW_NUMBER(); } E = HEAP[I + TAG_SLOT] === STRING_TAG; F = H && E; if (F) { A = HEAP[I + STRING_VALUE_SLOT]; A = HEAP[G + STRING_VALUE_SLOT] + A; NEW_STRING(); } A = RES; PUSH_OS(); PC = PC + 1; J = D || E; J = !(J && H); if (J) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'string and string or number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[G + TAG_SLOT])} and ${node_kind(HEAP[I + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '+'; RUNNING = false; } }; M[opcodes_1.default.SUBG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + NUMBER_VALUE_SLOT]; A = HEAP[E + NUMBER_VALUE_SLOT] - A; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '-'; RUNNING = false; } }; M[opcodes_1.default.MULG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + NUMBER_VALUE_SLOT]; A = HEAP[E + NUMBER_VALUE_SLOT] * A; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '*'; RUNNING = false; } }; M[opcodes_1.default.DIVG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + NUMBER_VALUE_SLOT]; F = A; A = HEAP[E + NUMBER_VALUE_SLOT] / A; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '/'; RUNNING = false; } F = G && F === 0; if (F) { STATE = DIV_ERROR; RUNNING = false; } }; M[opcodes_1.default.MODG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + NUMBER_VALUE_SLOT]; A = HEAP[E + NUMBER_VALUE_SLOT] % A; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = E && HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; RUNNING = false; } }; M[opcodes_1.default.NEGG] = () => { POP_OS(); D = RES; A = -HEAP[D + NUMBER_VALUE_SLOT]; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '-'; RUNNING = false; } }; M[opcodes_1.default.NOTG] = () => { POP_OS(); D = RES; A = !HEAP[D + BOOL_VALUE_SLOT]; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === BOOL_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'boolean'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '!'; RUNNING = false; } }; // for comparisons, assume both string or both nums M[opcodes_1.default.LTG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + BOXED_VALUE_SLOT]; A = HEAP[E + BOXED_VALUE_SLOT] < A; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && (HEAP[D + TAG_SLOT] === NUMBER_TAG || HEAP[D + TAG_SLOT] === STRING_TAG); if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'string and string or number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '<'; RUNNING = false; } }; M[opcodes_1.default.GTG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + BOXED_VALUE_SLOT]; A = HEAP[E + BOXED_VALUE_SLOT] > A; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && (HEAP[D + TAG_SLOT] === NUMBER_TAG || HEAP[D + TAG_SLOT] === STRING_TAG); if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'string and string or number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '>'; RUNNING = false; } }; M[opcodes_1.default.LEG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + BOXED_VALUE_SLOT]; A = HEAP[E + BOXED_VALUE_SLOT] <= A; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && (HEAP[D + TAG_SLOT] === NUMBER_TAG || HEAP[D + TAG_SLOT] === STRING_TAG); if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'string and string or number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '<='; RUNNING = false; } }; M[opcodes_1.default.GEG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[D + BOXED_VALUE_SLOT]; A = HEAP[E + BOXED_VALUE_SLOT] >= A; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === HEAP[E + TAG_SLOT]; G = G && (HEAP[D + TAG_SLOT] === NUMBER_TAG || HEAP[D + TAG_SLOT] === STRING_TAG); if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'string and string or number and number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])} and ${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = '>='; RUNNING = false; } }; // check type here as undefined and null need to be differentiated by nodes // unless if we add one more slot to undefined and null M[opcodes_1.default.EQG] = () => { POP_OS(); C = RES; POP_OS(); D = RES; CHECK_EQUAL(); NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.NEQG] = () => { POP_OS(); C = RES; POP_OS(); D = RES; CHECK_EQUAL(); A = !A; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.NEWC] = () => { A = P[PC][NEWC_ADDR_OFFSET][ADDR_FUNC_INDEX_OFFSET]; B = CLOSURE_NORMAL_TYPE; NEW_FUNCTION(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.NEWA] = () => { NEW_ARRAY(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.LDLG] = () => { C = ENV; A = HEAP[C + HEAP[C + FIRST_CHILD_SLOT] + P[PC][LD_ST_INDEX_OFFSET]]; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.STLG] = () => { POP_OS(); C = ENV; HEAP[C + HEAP[C + FIRST_CHILD_SLOT] + P[PC][LD_ST_INDEX_OFFSET]] = RES; PC = PC + 1; }; M[opcodes_1.default.LDPG] = () => { B = P[PC][LD_ST_ENV_OFFSET]; // index of env to lookup C = ENV; for (; B > 0; B = B - 1) { C = HEAP[C + PREVIOUS_ENV_SLOT]; } A = HEAP[C + HEAP[C + FIRST_CHILD_SLOT] + P[PC][LD_ST_INDEX_OFFSET]]; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.STPG] = () => { POP_OS(); B = P[PC][LD_ST_ENV_OFFSET]; // index of env to lookup C = ENV; for (; B > 0; B = B - 1) { C = HEAP[C + PREVIOUS_ENV_SLOT]; } HEAP[C + HEAP[C + FIRST_CHILD_SLOT] + P[PC][LD_ST_INDEX_OFFSET]] = RES; PC = PC + 1; }; M[opcodes_1.default.LDAG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; G = HEAP[E + TAG_SLOT] === ARRAY_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'array'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'array access'; RUNNING = false; return; } A = HEAP[D + NUMBER_VALUE_SLOT]; A = HEAP[E + ARRAY_VALUE_SLOT][A]; if (A === undefined) { NEW_UNDEFINED(); A = RES; } PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'array index'; RUNNING = false; } }; M[opcodes_1.default.STAG] = () => { POP_OS(); D = RES; POP_OS(); E = RES; A = HEAP[E + NUMBER_VALUE_SLOT]; // index POP_OS(); F = RES; G = HEAP[F + TAG_SLOT] === ARRAY_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'array'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[F + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'array access'; RUNNING = false; return; } HEAP[F + ARRAY_VALUE_SLOT][A] = D; // update array size D = HEAP[F + ARRAY_SIZE_SLOT]; if (D < A + 1) { D = A + 1; } HEAP[F + ARRAY_SIZE_SLOT] = D; PC = PC + 1; G = HEAP[E + TAG_SLOT] === NUMBER_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'number'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[E + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'array index'; RUNNING = false; } }; M[opcodes_1.default.BRT] = () => { POP_OS(); A = HEAP[RES + BOOL_VALUE_SLOT]; if (A) { PC = PC + P[PC][BR_OFFSET]; } else { PC = PC + 1; } }; M[opcodes_1.default.BRF] = () => { POP_OS(); A = HEAP[RES + BOOL_VALUE_SLOT]; if (!A) { PC = PC + P[PC][BR_OFFSET]; } else { PC = PC + 1; } }; M[opcodes_1.default.BR] = () => { PC = PC + P[PC][BR_OFFSET]; }; M[opcodes_1.default.CALL] = () => { G = P[PC][CALL_NUM_ARGS_OFFSET]; // lets keep number of arguments in G // we peek down OS to get the closure F = HEAP[OS + HEAP[OS + LAST_CHILD_SLOT] - G]; E = HEAP[F + TAG_SLOT] === CLOSURE_TAG; if (E) { J = NORMAL_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.CALLT] = () => { G = P[PC][CALLT_NUM_ARGS_OFFSET]; // lets keep number of arguments in G // we peek down OS to get the closure F = HEAP[OS + HEAP[OS + LAST_CHILD_SLOT] - G]; E = HEAP[F + TAG_SLOT] === CLOSURE_TAG; if (E) { J = TAIL_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.CALLP] = () => { G = P[PC][CALLP_NUM_ARGS_OFFSET]; // lets keep number of arguments in G F = P[PC][CALLP_ID_OFFSET]; // lets keep primitiveCall Id in F F = HEAP[GLOBAL_ENV + HEAP[GLOBAL_ENV + FIRST_CHILD_SLOT] + F]; // get closure E = HEAP[F + TAG_SLOT] === CLOSURE_TAG; if (E) { J = PRIMITIVE_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.CALLTP] = () => { G = P[PC][CALLTP_NUM_ARGS_OFFSET]; // lets keep number of arguments in G F = P[PC][CALLTP_ID_OFFSET]; // lets keep primitiveCall Id in F F = HEAP[GLOBAL_ENV + HEAP[GLOBAL_ENV + FIRST_CHILD_SLOT] + F]; // get closure E = HEAP[F + TAG_SLOT] === CLOSURE_TAG; if (E) { J = PRIMITIVE_TAIL_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.CALLV] = () => { G = P[PC][CALLV_NUM_ARGS_OFFSET]; F = P[PC][CALLV_ID_OFFSET]; E = F < vm_prelude_1.INTERNAL_FUNCTIONS.length; if (E) { J = INTERNAL_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.CALLTV] = () => { G = P[PC][CALLTV_NUM_ARGS_OFFSET]; F = P[PC][CALLTV_ID_OFFSET]; E = F < vm_prelude_1.INTERNAL_FUNCTIONS.length; if (E) { J = INTERNAL_TAIL_CALL; FUNCTION_CALL(); } else { STATE = CALL_NON_FUNCTION_ERROR; ERROR_MSG_ARGS[0] = convertToJsFormat(F); RUNNING = false; } }; M[opcodes_1.default.RETG] = () => { POP_RTS(); H = RES; PC = HEAP[H + RTS_FRAME_PC_SLOT]; ENV = HEAP[H + RTS_FRAME_ENV_SLOT]; P = HEAP[H + RTS_FRAME_FUNC_INS_SLOT]; POP_OS(); A = RES; OS = HEAP[H + RTS_FRAME_OS_SLOT]; PUSH_OS(); }; M[opcodes_1.default.DUP] = () => { POP_OS(); A = RES; PUSH_OS(); PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.NEWENV] = () => { B = ENV; A = P[PC][NEWENV_NUM_ARGS_OFFSET]; // lets keep number of arguments in A NEW_ENVIRONMENT(); // after this, RES is new env ENV = RES; PC = PC + 1; }; M[opcodes_1.default.POPENV] = () => { ENV = HEAP[ENV + PREVIOUS_ENV_SLOT]; // restore to parent env PC = PC + 1; }; // for now, we treat all primitive functions as normal functions // until we find a way to deal with streams. // problem with streams is that they create nullary functions, which // will be called using CALL, and are considered normal functions by // the compiler and machine M[opcodes_1.default.NEWCP] = () => { A = P[PC][NEWCP_ID_OFFSET]; A = HEAP[GLOBAL_ENV + HEAP[GLOBAL_ENV + FIRST_CHILD_SLOT] + A]; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.NEWCV] = () => { A = P[PC][NEWCV_ID_OFFSET]; B = CLOSURE_INTERNAL_TYPE; NEW_FUNCTION(); A = RES; PUSH_OS(); PC = PC + 1; }; // all opcodes from here onwards are custom to this implementation (3 Concurrent) M[opcodes_1.default.ARRAY_LEN] = () => { POP_OS(); D = RES; A = HEAP[D + ARRAY_SIZE_SLOT]; NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; G = HEAP[D + TAG_SLOT] === ARRAY_TAG; if (!G) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'array'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'array_length'; RUNNING = false; } }; M[opcodes_1.default.DISPLAY] = () => { POP_OS(); C = RES; POP_OS(); D = RES; externalFunctions.get(opcodes_1.default.DISPLAY)(convertToJsFormat(D), convertToJsFormat(C)); A = D; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.DRAW_DATA] = () => { POP_OS(); externalFunctions.get(opcodes_1.default.DRAW_DATA)(...convertToJsFormat(RES)); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.ERROR] = () => { POP_OS(); C = RES; POP_OS(); D = RES; externalFunctions.get(opcodes_1.default.ERROR)(convertToJsFormat(D), convertToJsFormat(C)); // terminates so don't do anything else // A = D // PUSH_OS() // PC = PC + 1 }; M[opcodes_1.default.IS_ARRAY] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === ARRAY_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_BOOL] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === BOOL_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_FUNC] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === CLOSURE_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_NULL] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === NULL_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_NUMBER] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === NUMBER_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_STRING] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === STRING_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.IS_UNDEFINED] = () => { POP_OS(); A = HEAP[RES + TAG_SLOT] === UNDEFINED_TAG; NEW_BOOL(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.MATH_HYPOT] = () => { POP_OS(); A = Math.hypot(...convertToJsFormat(RES)); NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.STRINGIFY] = () => { POP_OS(); A = (0, stringify_1.stringify)(convertToJsFormat(RES)); NEW_STRING(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.PROMPT] = () => { POP_OS(); A = externalFunctions.get(opcodes_1.default.PROMPT)(convertToJsFormat(RES)); NEW_STRING(); A = RES; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.DISPLAY_LIST] = () => { POP_OS(); C = RES; POP_OS(); D = RES; externalFunctions.get(opcodes_1.default.DISPLAY_LIST)(convertToJsFormat(D), convertToJsFormat(C)); A = D; PUSH_OS(); PC = PC + 1; }; M[opcodes_1.default.ARITY] = () => { POP_OS(); D = RES; G = HEAP[D + TAG_SLOT] === CLOSURE_TAG; if (G) { H = HEAP[D + CLOSURE_FUNC_INDEX_SLOT]; H = FUNC[H]; A = H[FUNC_NUM_ARGS_OFFSET]; if (A === vm_prelude_1.VARARGS_NUM_ARGS) { A = 0; } NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; } else { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'closure'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'arity'; RUNNING = false; } }; addPrimitiveOpCodeHandlers(); // Internal functions. They are called directly in internal function calls // All internal functions should not use register J or K, and can find // the number of arguments in G. They should also not increment PC. // expects num args in G M[opcodes_1.default.EXECUTE] = () => { I = G; E = OS; // we need the values in OS, so store in E first G = [OS, ENV, PC, P, RTS, TOP_RTS]; // store current state first // Keep track of registers first to restore present state after saving threads for (; I > 0; I = I - 1) { RTS = []; TOP_RTS = -1; OS = E; POP_OS(); H = RES; // store closure in H F = HEAP[H + CLOSURE_FUNC_INDEX_SLOT]; F = FUNC[F]; // store function header in F A = F[FUNC_MAX_STACK_SIZE_OFFSET]; NEW_OS(); OS = RES; A = F[FUNC_ENV_SIZE_OFFSET]; B = HEAP[H + CLOSURE_ENV_SLOT]; NEW_ENVIRONMENT(); ENV = RES; P = F[FUNC_CODE_OFFSET]; // enqueue to thread queue PC = 0; NEW_THREAD(); } ; [OS, ENV, PC, P, RTS, TOP_RTS] = G; // restore state }; M[opcodes_1.default.TEST_AND_SET] = () => { POP_OS(); D = RES; // array E = HEAP[D + TAG_SLOT] === ARRAY_TAG; if (!E) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'array'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'test_and_set'; RUNNING = false; } else { E = HEAP[D + ARRAY_VALUE_SLOT][0]; // get old boolean value A = true; NEW_BOOL(); HEAP[D + ARRAY_VALUE_SLOT][0] = RES; A = E; PUSH_OS(); // push old value to os } }; M[opcodes_1.default.CLEAR] = () => { POP_OS(); D = RES; // array E = HEAP[D + TAG_SLOT] === ARRAY_TAG; if (!E) { STATE = TYPE_ERROR; ERROR_MSG_ARGS[0] = 'array'; ERROR_MSG_ARGS[1] = `${node_kind(HEAP[D + TAG_SLOT])}`; ERROR_MSG_ARGS[2] = 'clear'; RUNNING = false; } else { A = false; NEW_BOOL(); HEAP[D + ARRAY_VALUE_SLOT][0] = RES; } }; // called whenever the machine is first run function INITIALIZE() { D = FUNC[PROG[0]]; // put function header in D A = D[FUNC_MAX_STACK_SIZE_OFFSET]; P = D[FUNC_CODE_OFFSET]; NEW_OS(); OS = RES; A = D[FUNC_ENV_SIZE_OFFSET]; B = NIL; NEW_ENVIRONMENT(); ENV = RES; GLOBAL_ENV = ENV; PC = 0; } // called during concurrent execution function RUN_INSTRUCTION() { if (TOP_RTS > -1 || P[PC][INS_OPCODE_OFFSET] !== opcodes_1.default.RETG) { // execute normally if (M[P[PC][INS_OPCODE_OFFSET]] === undefined) { throw Error('unknown op-code: ' + P[PC][INS_OPCODE_OFFSET]); } M[P[PC][INS_OPCODE_OFFSET]](); TO = TO - 1; } else { // end of current thread, try to setup another thread DELETE_CURRENT_THREAD(); GET_NUM_IDLE_THREADS(); if (RES === 0) { // end if no more threads RUNNING = false; } else { // setup next thread RUN_THREAD(); } } } function TIMEOUT_THREAD() { // enqueue to thread queue PAUSE_THREAD(); RUN_THREAD(); } function run() { const MAX_TIME = constants_1.JSSLANG_PROPERTIES.maxExecTime; const startTime = Date.now(); // startup INITIALIZE(); while (RUNNING) { // infinite loop protection if (Date.now() - startTime > MAX_TIME) { throw new timeoutErrors_1.PotentialInfiniteLoopError((0, astCreator_1.locationDummyNode)(-1, -1, null), MAX_TIME); } if (TO > 0) { // show_registers("run loop"); // show_heap("run loop"); // show_executing('') RUN_INSTRUCTION(); } else if (TO === 0) { // when exhausted time quanta TIMEOUT_THREAD(); } else { throw Error('TO cannot be negative'); } } // handle errors if (STATE !== NORMAL) { throw Error('execution aborted: ' + getErrorType()); } POP_OS(); // show_heap_value(RES) // return convertToJsFormat(RES) // Source 3 Concurrent programs do not return anything. return 'all threads terminated'; } function getErrorType() { switch (STATE) { case DIV_ERROR: return 'division by 0'; case TYPE_ERROR: // 0: expected types // 1: received types // 2: operator return `Expected ${ERROR_MSG_ARGS[0]}, got ${ERROR_MSG_ARGS[1]} for ${ERROR_MSG_ARGS[2]}.`; case NUM_ARGS_ERROR: return `Expected ${ERROR_MSG_ARGS[0]} arguments, but got ${ERROR_MSG_ARGS[1]}.`; case CALL_NON_FUNCTION_ERROR: return `calling non-function value ${ERROR_MSG_ARGS[0]}.`; default: throw Error('invalid error type'); } } function convertToJsFormat(node, refs) { if (refs !== undefined && refs.has(node)) { return refs.get(node); } const kind = node_kind(HEAP[node + TAG_SLOT]); switch (kind) { case 'undefined': return undefined; case 'null': return null; case 'number': case 'string': case 'boolean': return HEAP[node + BOXED_VALUE_SLOT]; case 'array': { if (refs === undefined) { refs = new Map(); } const arr = HEAP[node + BOXED_VALUE_SLOT]; const res = []; refs.set(node, res); for (let i = 0; i < arr.length; i++) { res[i] = convertToJsFormat(arr[i], refs); } return res; } case 'closure': return '<Function>'; default: throw Error('Encountered unexpressible type: ' + kind); } } // if program has primitive calls, prelude must be included. // this implementation also assumes a correct program, and does not // currently check for type correctness // an incorrect program will have undefined behaviors function runWithProgram(p, context) { PROG = p; FUNC = PROG[1]; // list of SVMFunctions P = []; PC = -1; HEAP = []; FREE = 0; GLOBAL_ENV = NIL; ENV = NIL; OS = -Infinity; RES = -Infinity; RTS = []; TO = 0; TOP_RTS = -1; STATE = NORMAL; RUNNING = true; ERROR_MSG_ARGS = []; INIT_SCHEDULER(); A = 0; B = 0; C = 0; D = 0; E = 0; F = 0; G = 0; H = 0; I = 0; J = 0; K = 0; // setup externalBuiltins // certain functions are imported from cadet-frontend // so import them first every time const externals = context.nativeStorage.builtins; if (externals.size > 0) { vm_prelude_1.EXTERNAL_PRIMITIVES.forEach(func => extractExternalBuiltin(func, externals)); } return run(); } exports.runWithProgram = runWithProgram; function addPrimitiveOpCodeHandlers() { function addNullaryHandler(opcode, f) { M[opcode] = () => { A = f(); NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; } function addUnaryHandler(opcode, f) { M[opcode] = () => { POP_OS(); A = HEAP[RES + NUMBER_VALUE_SLOT]; A = f(A); NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; } // string as well due to parseInt. Only works due to current // representation of strings. Must change if the machine changes // to a more authentic representation of strings function addBinaryHandler(opcode, f) { M[opcode] = () => { POP_OS(); C = HEAP[RES + NUMBER_VALUE_SLOT]; POP_OS(); D = HEAP[RES + BOXED_VALUE_SLOT]; A = f(D, C); NEW_NUMBER(); A = RES; PUSH_OS(); PC = PC + 1; }; } vm_prelude_1.NULLARY_PRIMITIVES.forEach(func => { if (func[2]) addNullaryHandler(func[1], func[2]); }); vm_prelude_1.UNARY_PRIMITIVES.forEach(func => { if (func[2]) addUnaryHandler(func[1], func[2]); }); vm_prelude_1.BINARY_PRIMITIVES.concat([ ['', opcodes_1.default.MATH_MAX, Math.max], ['', opcodes_1.default.MATH_MIN, Math.min] ]).forEach(func => { if (func[2]) addBinaryHandler(func[1], func[2]); }); } const externalFunctions = new Map(); function extractExternalBuiltin(func, externals) { const name = func[0]; const opcode = func[1]; externalFunctions.set(opcode, externals.get(name)); } //# sourceMappingURL=svml-machine.js.map