UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

234 lines 8.49 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.throwIfTimeout = void 0; 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 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 { return originalCandidate(...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) { 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) { 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) { 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); 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 { 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