UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

105 lines 5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const astring_1 = require("astring"); const utils_1 = require("../cse-machine/utils"); const ast = require("../utils/ast/astCreator"); const interpreter_1 = require("./interpreter"); const instrCreator_1 = require("./instrCreator"); const closureToJS = (value, context) => { function DummyClass() { const args = [...arguments]; const node = ast.callExpression(ast.literal(value, value.node.loc), args.map(arg => ast.primitive(arg))); // Create a new CSE Machine with the same context as the current one, but with // the control reset to only contain the call expression, and the stash emptied. const newContext = { ...context, runtime: { ...context.runtime, // Only environments are intended to be mutated by the new CSE Machine, the // rest of the runtime properties should stay the same nodes: [...context.runtime.nodes], breakpointSteps: [...context.runtime.breakpointSteps], changepointSteps: [...context.runtime.changepointSteps], debuggerOn: false } }; newContext.runtime.control = new interpreter_1.Control(); // Also need the env instruction to return back to the current environment at the end. // The call expression won't create one as there is only one item in the control. newContext.runtime.control.push((0, instrCreator_1.envInstr)((0, utils_1.currentEnvironment)(context), (0, utils_1.currentTransformers)(context), node), node); newContext.runtime.stash = new interpreter_1.Stash(); newContext.runtime.transformers = context.runtime.transformers; const gen = (0, interpreter_1.generateCSEMachineStateStream)(newContext, newContext.runtime.control, newContext.runtime.stash, -1, -1); // Run the new CSE Machine fully to obtain the result in the stash for (const _ of gen) { } // Also don't forget to update object count in original context context.runtime.objectCount = newContext.runtime.objectCount; return newContext.runtime.stash.peek(); } Object.defineProperty(DummyClass, 'name', { value: value.functionName }); Object.setPrototypeOf(DummyClass, () => undefined); Object.defineProperty(DummyClass, 'Inherits', { value: (Parent) => { DummyClass.prototype = Object.create(Parent.prototype); DummyClass.prototype.constructor = DummyClass; } }); DummyClass.toString = () => (0, astring_1.generate)(value.originalNode); DummyClass.call = (thisArg, ...args) => { return DummyClass.apply(thisArg, args); }; return DummyClass; }; class Callable extends Function { constructor(f) { super(); return Object.setPrototypeOf(f, new.target.prototype); } } /** * Models function value in the CSE machine. */ class Closure extends Callable { static makeFromArrowFunction(node, environment, transformers, context, dummyReturn, predefined) { const functionBody = !(0, utils_1.isBlockStatement)(node.body) && !(0, utils_1.isStatementSequence)(node.body) ? ast.blockStatement([ast.returnStatement(node.body, node.body.loc)], node.body.loc) : dummyReturn && !(0, utils_1.hasReturnStatement)(node.body) ? ast.blockStatement([ ...node.body.body, ast.returnStatement(ast.identifier('undefined', node.body.loc), node.body.loc) ], node.body.loc) : node.body; const closure = new Closure(ast.blockArrowFunction(node.params, functionBody, node.loc), environment, transformers, context, predefined); // Set the closure's node to point back at the original one closure.originalNode = node; return closure; } constructor(node, environment, transformers, context, isPredefined) { super(function (...args) { return funJS.apply(this, args); }); this.node = node; this.environment = environment; this.transformers = transformers; this.originalNode = node; this.id = (0, utils_1.uniqueId)(context); (0, utils_1.currentEnvironment)(context).heap.add(this); const params = this.node.params.map((o) => o.type === 'RestElement' ? '...' + o.argument.name : o.name); this.functionName = params.join(', '); if (params.length !== 1 || params[0].startsWith('...')) { this.functionName = '(' + this.functionName + ')'; } this.functionName += ' => ...'; const funJS = closureToJS(this, context); this.fun = funJS; this.predefined = isPredefined ?? false; } toString() { return (0, astring_1.generate)(this.originalNode); } } exports.default = Closure; //# sourceMappingURL=closure.js.map