UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

316 lines 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getProp = exports.setProp = exports.wrap = exports.callIteratively = exports.evaluateBinaryExpression = exports.binaryOp = exports.evaluateUnaryExpression = exports.unaryOp = exports.boolOrErr = exports.callIfFuncAndRightArgs = exports.makeLazyFunction = exports.wrapLazyCallee = exports.delayIt = exports.forceIt = exports.throwIfTimeout = void 0; const createContext_1 = require("../createContext"); const errors_1 = require("../errors/errors"); const runtimeSourceError_1 = require("../errors/runtimeSourceError"); const timeoutErrors_1 = require("../errors/timeoutErrors"); const astCreator_1 = require("./ast/astCreator"); const create = require("./ast/astCreator"); const makeWrapper_1 = require("./makeWrapper"); const rttc = require("./rttc"); function throwIfTimeout(nativeStorage, start, current, line, column, source) { if (current - start > nativeStorage.maxExecTime) { throw new timeoutErrors_1.PotentialInfiniteLoopError(create.locationDummyNode(line, column, source), nativeStorage.maxExecTime); } } exports.throwIfTimeout = throwIfTimeout; function forceIt(val) { if (val !== undefined && val !== null && val.isMemoized !== undefined) { if (val.isMemoized) { return val.memoizedValue; } const evaluatedValue = forceIt(val.f()); val.isMemoized = true; val.memoizedValue = evaluatedValue; return evaluatedValue; } else { return val; } } exports.forceIt = forceIt; function delayIt(f) { return { isMemoized: false, value: undefined, f }; } exports.delayIt = delayIt; function wrapLazyCallee(candidate) { candidate = forceIt(candidate); if (typeof candidate === 'function') { const wrapped = (...args) => candidate(...args.map(forceIt)); (0, makeWrapper_1.makeWrapper)(candidate, wrapped); wrapped[Symbol.toStringTag] = () => candidate.toString(); wrapped.toString = () => candidate.toString(); return wrapped; } else if (candidate instanceof createContext_1.LazyBuiltIn) { if (candidate.evaluateArgs) { const wrapped = (...args) => candidate.func(...args.map(forceIt)); (0, makeWrapper_1.makeWrapper)(candidate.func, wrapped); wrapped[Symbol.toStringTag] = () => candidate.toString(); wrapped.toString = () => candidate.toString(); return wrapped; } else { return candidate; } } // doesn't look like a function, not our business to error now return candidate; } exports.wrapLazyCallee = wrapLazyCallee; function makeLazyFunction(candidate) { return new createContext_1.LazyBuiltIn(candidate, false); } exports.makeLazyFunction = makeLazyFunction; function callIfFuncAndRightArgs(candidate, line, column, source, ...args) { const dummy = create.callExpression(create.locationDummyNode(line, column, source), args, { start: { line, column }, end: { line, column } }); if (typeof candidate === 'function') { const originalCandidate = candidate; if (candidate.transformedFunction !== undefined) { candidate = candidate.transformedFunction; } const expectedLength = candidate.length; const receivedLength = args.length; const hasVarArgs = candidate.minArgsNeeded !== undefined; if (hasVarArgs ? candidate.minArgsNeeded > receivedLength : expectedLength !== receivedLength) { throw new errors_1.InvalidNumberOfArguments(dummy, hasVarArgs ? candidate.minArgsNeeded : expectedLength, receivedLength, hasVarArgs); } try { const forcedArgs = args.map(forceIt); return originalCandidate(...forcedArgs); } catch (error) { // if we already handled the error, simply pass it on if (!(error instanceof runtimeSourceError_1.RuntimeSourceError || error instanceof errors_1.ExceptionError)) { throw new errors_1.ExceptionError(error, dummy.loc); } else { throw error; } } } else if (candidate instanceof createContext_1.LazyBuiltIn) { try { if (candidate.evaluateArgs) { args = args.map(forceIt); } return candidate.func(...args); } catch (error) { // if we already handled the error, simply pass it on if (!(error instanceof runtimeSourceError_1.RuntimeSourceError || error instanceof errors_1.ExceptionError)) { throw new errors_1.ExceptionError(error, dummy.loc); } else { throw error; } } } else { throw new errors_1.CallingNonFunctionValue(candidate, dummy); } } exports.callIfFuncAndRightArgs = callIfFuncAndRightArgs; function boolOrErr(candidate, line, column, source) { candidate = forceIt(candidate); const error = rttc.checkIfStatement(create.locationDummyNode(line, column, source), candidate); if (error === undefined) { return candidate; } else { throw error; } } exports.boolOrErr = boolOrErr; function unaryOp(operator, argument, line, column, source) { argument = forceIt(argument); const error = rttc.checkUnaryExpression(create.locationDummyNode(line, column, source), operator, argument); if (error === undefined) { return evaluateUnaryExpression(operator, argument); } else { throw error; } } exports.unaryOp = unaryOp; function evaluateUnaryExpression(operator, value) { if (operator === '!') { return !value; } else if (operator === '-') { return -value; } else if (operator === 'typeof') { return typeof value; } else { return +value; } } exports.evaluateUnaryExpression = evaluateUnaryExpression; function binaryOp(operator, chapter, left, right, line, column, source) { left = forceIt(left); right = forceIt(right); const error = rttc.checkBinaryExpression(create.locationDummyNode(line, column, source), operator, chapter, left, right); if (error === undefined) { return evaluateBinaryExpression(operator, left, right); } else { throw error; } } exports.binaryOp = binaryOp; function evaluateBinaryExpression(operator, left, right) { switch (operator) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return left / right; case '%': return left % right; case '===': return left === right; case '!==': return left !== right; case '<=': return left <= right; case '<': return left < right; case '>': return left > right; case '>=': return left >= right; default: return undefined; } } exports.evaluateBinaryExpression = evaluateBinaryExpression; /** * Limitations for current properTailCalls implementation: * Obviously, if objects ({}) are reintroduced, * we have to change this for a more stringent check, * as isTail and transformedFunctions are properties * and may be added by Source code. */ const callIteratively = (f, nativeStorage, ...args) => { let line = -1; let column = -1; let source = null; const startTime = Date.now(); const pastCalls = []; while (true) { const dummy = (0, astCreator_1.locationDummyNode)(line, column, source); f = forceIt(f); if (typeof f === 'function') { if (f.transformedFunction !== undefined) { f = f.transformedFunction; } const expectedLength = f.length; const receivedLength = args.length; const hasVarArgs = f.minArgsNeeded !== undefined; if (hasVarArgs ? f.minArgsNeeded > receivedLength : expectedLength !== receivedLength) { throw new errors_1.InvalidNumberOfArguments((0, astCreator_1.callExpression)(dummy, args, { start: { line, column }, end: { line, column }, source }), hasVarArgs ? f.minArgsNeeded : expectedLength, receivedLength, hasVarArgs); } } else if (f instanceof createContext_1.LazyBuiltIn) { if (f.evaluateArgs) { args = args.map(forceIt); } f = f.func; } else { throw new errors_1.CallingNonFunctionValue(f, dummy); } let res; try { res = f(...args); if (Date.now() - startTime > nativeStorage.maxExecTime) { throw new timeoutErrors_1.PotentialInfiniteRecursionError(dummy, pastCalls, nativeStorage.maxExecTime); } } catch (error) { // if we already handled the error, simply pass it on if (!(error instanceof runtimeSourceError_1.RuntimeSourceError || error instanceof errors_1.ExceptionError)) { throw new errors_1.ExceptionError(error, dummy.loc); } else { throw error; } } if (res === null || res === undefined) { return res; } else if (res.isTail === true) { f = res.function; args = res.arguments; line = res.line; column = res.column; source = res.source; pastCalls.push([res.functionName, args]); } else if (res.isTail === false) { return res.value; } else { return res; } } }; exports.callIteratively = callIteratively; const wrap = (f, stringified, hasVarArgs, nativeStorage) => { if (hasVarArgs) { // @ts-ignore f.minArgsNeeded = f.length; } const wrapped = (...args) => (0, exports.callIteratively)(f, nativeStorage, ...args); (0, makeWrapper_1.makeWrapper)(f, wrapped); wrapped.transformedFunction = f; wrapped[Symbol.toStringTag] = () => stringified; wrapped.toString = () => stringified; return wrapped; }; exports.wrap = wrap; const setProp = (obj, prop, value, line, column, source) => { const dummy = (0, astCreator_1.locationDummyNode)(line, column, source); const error = rttc.checkMemberAccess(dummy, obj, prop); if (error === undefined) { return (obj[prop] = value); } else { throw error; } }; exports.setProp = setProp; const getProp = (obj, prop, line, column, source) => { const dummy = (0, astCreator_1.locationDummyNode)(line, column, source); const error = rttc.checkMemberAccess(dummy, obj, prop); if (error === undefined) { if (obj[prop] !== undefined && !obj.hasOwnProperty(prop)) { throw new errors_1.GetInheritedPropertyError(dummy, obj, prop); } else { return obj[prop]; } } else { throw error; } }; exports.getProp = getProp; //# sourceMappingURL=operators.js.map