js-slang
Version:
Javascript-based implementations of Source, written in Typescript
105 lines • 5 kB
JavaScript
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
;