mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-08-17 02:20:31 +03:00
Do full joins (#2402)
Summary: Release note: Rewrote the joining logic to always do a full join at every join point Closes #2151 #2222 #2279 I've spent a lot of time in the last few months trying to sort out problems that arise from effects being applied too many or too few times. Fixing these feel a bit like playing wack a mole and in the end no fix goes unpunished. Stepping back a bit from the fray, it seems to me that the root cause of all this pain is the fact that joins of different kinds of completions get delayed. Before we had path conditions and the simplifier this seemed like a rather good thing since exceptional paths did not contribute values to the normal paths and we thus had fewer abstract values to deal with and fewer places where Prepack would grind to a halt. In the current state of things, however, it seems perfectly possible to join in all branches at every join point. I've had to decrease some limits, in particular the number of times we go around a loop with conditional exits. I've also had to make the test runner impose a limit on how many times the simplifier can invoke Path.implies. Nevertheless, the tests seem to pass and hopefully this will also fix quite a lot of bugs that have been unresolved for many months already. Pull Request resolved: https://github.com/facebook/prepack/pull/2402 Differential Revision: D9236263 Pulled By: hermanventer fbshipit-source-id: 92a25b591591297afeba536429226c5a0291f451
This commit is contained in:
parent
671ea300ba
commit
7157849a44
@ -59,7 +59,6 @@ jobs:
|
||||
yarn test-react
|
||||
yarn test-sourcemaps
|
||||
yarn test-std-in
|
||||
yarn test-residual
|
||||
yarn test-test262 --expectedCounts 11944,5641,0 --statusFile ~/artifacts/test262-status.txt --timeout 120 --cpuScale 0.25 --verbose
|
||||
#yarn test-test262-new --statusFile ~/artifacts/test262-new-status.txt --timeout 120 --verbose
|
||||
- store_artifacts:
|
||||
|
@ -31,8 +31,6 @@
|
||||
"lint": "eslint src scripts",
|
||||
"flow": "flow",
|
||||
"flow-ci": "flow version; flow check",
|
||||
"test-residual": "babel-node scripts/test-residual.js",
|
||||
"test-residual-with-coverage": "./node_modules/.bin/istanbul cover ./lib/test-residual.js --dir coverage.residual && ./node_modules/.bin/remap-istanbul -i coverage.residual/coverage.json -o coverage-sourcemapped.residual -t html",
|
||||
"test-serializer": "babel-node --stack_trace_limit=200 --stack_size=10000 scripts/test-runner.js",
|
||||
"test-serializer-single": "yarn test-serializer --debugNames --verbose --fast --filter",
|
||||
"test-serializer-with-coverage": "./node_modules/.bin/istanbul cover ./lib/test-error-handler.js --dir coverage.error && ./node_modules/.bin/istanbul cover ./lib/test-runner.js && ./node_modules/.bin/remap-istanbul -i coverage.error/coverage.json -i coverage/coverage.json -o coverage-sourcemapped -t html",
|
||||
@ -47,7 +45,7 @@
|
||||
"test-std-in": "bash < scripts/test-std-in.sh",
|
||||
"test-react": "jest",
|
||||
"test-react-fast": "SKIP_REACT_JSX_TESTS=true jest",
|
||||
"test": "yarn test-residual && yarn test-serializer && yarn test-sourcemaps && yarn test-error-handler && yarn test-std-in && yarn test-test262 && yarn test-internal && yarn test-internal-react && yarn test-react",
|
||||
"test": "yarn test-serializer && yarn test-sourcemaps && yarn test-error-handler && yarn test-std-in && yarn test-test262 && yarn test-internal && yarn test-internal-react && yarn test-react",
|
||||
"test-coverage-most": "./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover ./lib/multi-runner.js --dir coverage.most && ./node_modules/.bin/remap-istanbul -i coverage.most/coverage.json -o coverage-sourcemapped -t html",
|
||||
"test-all-coverage": "./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover ./lib/multi-runner.js --dir coverage.most && ./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover ./lib/test262-runner.js --timeout 50 --singleThreaded && ./node_modules/.bin/remap-istanbul -i coverage/coverage.json -i coverage.most/coverage.json -o coverage-sourcemapped -t html",
|
||||
"repl": "node lib/repl-cli.js",
|
||||
|
@ -8,9 +8,7 @@
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
// This file just runs the 4 test runners in one file for coverage
|
||||
require("./test-residual.js");
|
||||
|
||||
// This file just runs the 3 test runners in one file for coverage
|
||||
require("./test-runner.js");
|
||||
|
||||
require("./generate-sourcemaps-test.js");
|
||||
|
@ -54,7 +54,6 @@ function runTest(name: string, code: string): boolean {
|
||||
console.log(chalk.inverse(name));
|
||||
|
||||
let recover = code.includes("// recover-from-errors");
|
||||
let delayUnsupportedRequires = code.includes("// delay unsupported requires");
|
||||
let compatibility = code.includes("// jsc") ? "jsc-600-1-4-17" : undefined;
|
||||
|
||||
let expectedErrors = code.match(/\/\/\s*expected errors:\s*(.*)/);
|
||||
@ -68,7 +67,6 @@ function runTest(name: string, code: string): boolean {
|
||||
try {
|
||||
let options = {
|
||||
internalDebug: false,
|
||||
delayUnsupportedRequires,
|
||||
mathRandomSeed: "0",
|
||||
errorHandler: errorHandler.bind(null, recover ? "Recover" : "Fail", errors),
|
||||
serialize: true,
|
||||
|
@ -68,7 +68,6 @@ function runTest(name: string, code: string): boolean {
|
||||
let options = {
|
||||
internalDebug: true,
|
||||
compatibility: "jsc-600-1-4-17",
|
||||
delayUnsupportedRequires: true,
|
||||
accelerateUnsupportedRequires: true,
|
||||
mathRandomSeed: "0",
|
||||
errorHandler,
|
||||
|
@ -1,262 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
let construct_realm = require("../lib/construct_realm.js").default;
|
||||
let initializeGlobals = require("../lib/globals.js").default;
|
||||
let AbruptCompletion = require("../lib/completions.js").AbruptCompletion;
|
||||
let ThrowCompletion = require("../lib/completions.js").ThrowCompletion;
|
||||
let FatalError = require("../lib/errors.js").FatalError;
|
||||
|
||||
let chalk = require("chalk");
|
||||
let path = require("path");
|
||||
let fs = require("fs");
|
||||
let vm = require("vm");
|
||||
let os = require("os");
|
||||
let minimist = require("minimist");
|
||||
const EOL = os.EOL;
|
||||
|
||||
function search(dir, relative) {
|
||||
let tests = [];
|
||||
|
||||
for (let name of fs.readdirSync(dir)) {
|
||||
let loc = path.join(dir, name);
|
||||
let stat = fs.statSync(loc);
|
||||
|
||||
if (stat.isFile()) {
|
||||
tests.push({
|
||||
file: fs.readFileSync(loc, "utf8"),
|
||||
name: path.join(relative, name),
|
||||
});
|
||||
} else if (stat.isDirectory()) {
|
||||
tests = tests.concat(search(loc, path.join(relative, name)));
|
||||
}
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
let tests = search(`${__dirname}/../test/residual`, "test/residual");
|
||||
|
||||
function exec(code) {
|
||||
let script = new vm.Script(
|
||||
`var global = this; var self = this; var __result = ${code} // keep newline here as code may end with comment
|
||||
; report(__result);`,
|
||||
{ cachedDataProduced: false }
|
||||
);
|
||||
|
||||
let result = "";
|
||||
let logOutput = "";
|
||||
|
||||
function write(prefix, values) {
|
||||
logOutput += "\n" + prefix + values.join("");
|
||||
}
|
||||
|
||||
script.runInNewContext({
|
||||
setTimeout: setTimeout,
|
||||
setInterval: setInterval,
|
||||
clearTimeout: clearTimeout,
|
||||
clearInterval: clearInterval,
|
||||
report: function(s) {
|
||||
result = s;
|
||||
},
|
||||
console: {
|
||||
log(...s) {
|
||||
write("", s);
|
||||
},
|
||||
warn(...s) {
|
||||
write("WARN:", s);
|
||||
},
|
||||
error(...s) {
|
||||
write("ERROR:", s);
|
||||
},
|
||||
},
|
||||
});
|
||||
return result + logOutput;
|
||||
}
|
||||
|
||||
function runTest(name, code, args) {
|
||||
let realmOptions = { residual: true };
|
||||
let sources = [{ filePath: name, fileContents: code }];
|
||||
console.log(chalk.inverse(name));
|
||||
if (code.includes("// throws introspection error")) {
|
||||
try {
|
||||
let realm = construct_realm(realmOptions);
|
||||
initializeGlobals(realm);
|
||||
let result = realm.$GlobalEnv.executePartialEvaluator(sources);
|
||||
if (result instanceof ThrowCompletion) throw result.value;
|
||||
} catch (err) {
|
||||
if (err instanceof FatalError) return true;
|
||||
console.error(err);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
let expected, actual;
|
||||
let codeIterations = [];
|
||||
let markersToFind = [];
|
||||
for (let [positive, marker] of [[true, "// does contain:"], [false, "// does not contain:"]]) {
|
||||
if (code.includes(marker)) {
|
||||
let i = code.indexOf(marker);
|
||||
let value = code.substring(i + marker.length, code.indexOf("\n", i));
|
||||
markersToFind.push({ positive, value, start: i + marker.length });
|
||||
}
|
||||
}
|
||||
try {
|
||||
try {
|
||||
expected = exec(`(function () { ${code}; // keep newline here as code may end with comment
|
||||
return __result; }).call(this);`);
|
||||
} catch (e) {
|
||||
expected = "" + e;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
let max = 4;
|
||||
let oldCode = code;
|
||||
for (; i < max; i++) {
|
||||
let realm = construct_realm(realmOptions);
|
||||
initializeGlobals(realm);
|
||||
let result = realm.$GlobalEnv.executePartialEvaluator(sources);
|
||||
if (result instanceof ThrowCompletion) throw result.value;
|
||||
if (result instanceof AbruptCompletion) throw result;
|
||||
let newCode = result.code;
|
||||
if (args.verbose && i === 0) console.log(newCode);
|
||||
codeIterations.push(newCode);
|
||||
let markersIssue = false;
|
||||
for (let { positive, value, start } of markersToFind) {
|
||||
let found = newCode.indexOf(value, start) !== -1;
|
||||
if (found !== positive) {
|
||||
console.error(chalk.red(`Output ${positive ? "does not contain" : "contains"} forbidden string: ${value}`));
|
||||
markersIssue = true;
|
||||
}
|
||||
}
|
||||
if (markersIssue) break;
|
||||
try {
|
||||
actual = exec(`(function () { ${newCode}; // keep newline here as code may end with comment
|
||||
return __result; }).call(this);`);
|
||||
} catch (e) {
|
||||
actual = "" + e;
|
||||
}
|
||||
if (expected !== actual) {
|
||||
console.error(chalk.red("Output mismatch!"));
|
||||
break;
|
||||
}
|
||||
if (oldCode === newCode) {
|
||||
// The generated code reached a fixed point!
|
||||
return true;
|
||||
}
|
||||
oldCode = newCode;
|
||||
}
|
||||
if (i === max) {
|
||||
console.error(chalk.red(`Code generation did not reach fixed point after ${max} iterations!`));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
console.log(chalk.underline("original code"));
|
||||
console.log(code);
|
||||
console.log(chalk.underline("output of inspect() on original code"));
|
||||
console.log(expected);
|
||||
for (let i = 0; i < codeIterations.length; i++) {
|
||||
console.log(chalk.underline(`generated code in iteration ${i}`));
|
||||
console.log(codeIterations[i]);
|
||||
}
|
||||
console.log(chalk.underline("output of inspect() on last generated code iteration"));
|
||||
console.log(actual);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function run(args) {
|
||||
let failed = 0;
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
|
||||
for (let test of tests) {
|
||||
// filter hidden files
|
||||
if (path.basename(test.name)[0] === ".") continue;
|
||||
if (test.name.endsWith("~")) continue;
|
||||
if (test.file.includes("// skip")) continue;
|
||||
if (!test.name.includes(args.filter)) continue;
|
||||
|
||||
total++;
|
||||
if (runTest(test.name, test.file, args)) passed++;
|
||||
else failed++;
|
||||
}
|
||||
|
||||
console.log("Passed:", `${passed}/${total}`, (Math.floor((passed / total) * 100) || 0) + "%");
|
||||
return failed === 0;
|
||||
}
|
||||
|
||||
// Object to store all command line arguments
|
||||
class ProgramArgs {
|
||||
verbose: boolean;
|
||||
filter: string;
|
||||
constructor(verbose: boolean, filter: string) {
|
||||
this.verbose = verbose;
|
||||
this.filter = filter; //lets user choose specific test files, runs all tests if omitted
|
||||
}
|
||||
}
|
||||
|
||||
// Execution of tests begins here
|
||||
function main(): number {
|
||||
try {
|
||||
let args = argsParse();
|
||||
if (!run(args)) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof ArgsParseError) {
|
||||
console.error("Illegal argument: %s.\n%s", e.message, usage());
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper function to provide correct usage information to the user
|
||||
function usage(): string {
|
||||
return `Usage: ${process.argv[0]} ${process.argv[1]} ` + EOL + `[--verbose] [--filter <string>]`;
|
||||
}
|
||||
|
||||
// NOTE: inheriting from Error does not seem to pass through an instanceof
|
||||
// check
|
||||
class ArgsParseError {
|
||||
message: string;
|
||||
constructor(message: string) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
// Parses through the command line arguments and throws errors if usage is incorrect
|
||||
function argsParse(): ProgramArgs {
|
||||
let parsedArgs = minimist(process.argv.slice(2), {
|
||||
string: ["filter"],
|
||||
boolean: ["verbose"],
|
||||
default: {
|
||||
verbose: false,
|
||||
filter: "",
|
||||
},
|
||||
});
|
||||
if (typeof parsedArgs.verbose !== "boolean") {
|
||||
throw new ArgsParseError("verbose must be a boolean (either --verbose or not)");
|
||||
}
|
||||
if (typeof parsedArgs.filter !== "string") {
|
||||
throw new ArgsParseError(
|
||||
"filter must be a string (relative path from serialize directory) (--filter abstract/Residual.js)"
|
||||
);
|
||||
}
|
||||
let programArgs = new ProgramArgs(parsedArgs.verbose, parsedArgs.filter);
|
||||
return programArgs;
|
||||
}
|
||||
|
||||
main();
|
@ -307,7 +307,7 @@ function verifyFunctionOrderings(code: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function unescapleUniqueSuffix(code: string, uniqueSuffix?: string) {
|
||||
function unescapeUniqueSuffix(code: string, uniqueSuffix?: string) {
|
||||
return uniqueSuffix != null ? code.replace(new RegExp(uniqueSuffix, "g"), "") : code;
|
||||
}
|
||||
|
||||
@ -341,7 +341,6 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
if (!args.fast && args.filter === "") console.log(chalk.inverse(name) + " " + JSON.stringify(options));
|
||||
let compatibility = code.includes("// jsc") ? "jsc-600-1-4-17" : undefined;
|
||||
let initializeMoreModules = code.includes("// initialize more modules");
|
||||
let delayUnsupportedRequires = code.includes("// delay unsupported requires");
|
||||
if (args.verbose || code.includes("// inline expressions")) options.inlineExpressions = true;
|
||||
options.invariantLevel = code.includes("// omit invariants") || args.verbose ? 0 : 99;
|
||||
if (code.includes("// emit concrete model")) options.emitConcreteModel = true;
|
||||
@ -354,11 +353,11 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
let compileJSXWithBabel = code.includes("// babel:jsx");
|
||||
let functionCloneCountMatch = code.match(/\/\/ serialized function clone count: (\d+)/);
|
||||
options = ((Object.assign({}, options, {
|
||||
abstractValueImpliesMax: 2000,
|
||||
compatibility,
|
||||
debugNames: args.debugNames,
|
||||
debugScopes: args.debugScopes,
|
||||
initializeMoreModules,
|
||||
delayUnsupportedRequires,
|
||||
errorHandler: diag => "Fail",
|
||||
internalDebug: true,
|
||||
serialize: true,
|
||||
@ -381,7 +380,6 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
initializeGlobals(realm);
|
||||
let serializerOptions = {
|
||||
initializeMoreModules,
|
||||
delayUnsupportedRequires,
|
||||
internalDebug: true,
|
||||
lazyObjectsRuntime: options.lazyObjectsRuntime,
|
||||
};
|
||||
@ -459,7 +457,6 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
addedCode = code.substring(i + injectAtRuntime.length, code.indexOf("\n", i));
|
||||
options.residual = false;
|
||||
}
|
||||
if (delayUnsupportedRequires) options.residual = false;
|
||||
if (args.es5) {
|
||||
code = transformWithBabel(code, [], [["@babel/env", { forceAllTransforms: true, modules: false }]]);
|
||||
}
|
||||
@ -590,7 +587,7 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
codeToRun = augmentCodeWithLazyObjectSupport(codeToRun, args.lazyObjectsRuntime);
|
||||
}
|
||||
if (args.verbose) console.log(codeToRun);
|
||||
codeIterations.push(unescapleUniqueSuffix(codeToRun, options.uniqueSuffix));
|
||||
codeIterations.push(unescapeUniqueSuffix(codeToRun, options.uniqueSuffix));
|
||||
if (args.es5) {
|
||||
codeToRun = transformWithBabel(
|
||||
codeToRun,
|
||||
@ -640,9 +637,7 @@ function runTest(name, code, options: PrepackOptions, args) {
|
||||
}
|
||||
if (singleIterationOnly) return Promise.reject({ type: "RETURN", value: true });
|
||||
if (
|
||||
unescapleUniqueSuffix(oldCode, oldUniqueSuffix) ===
|
||||
unescapleUniqueSuffix(newCode, newUniqueSuffix) ||
|
||||
delayUnsupportedRequires
|
||||
unescapeUniqueSuffix(oldCode, oldUniqueSuffix) === unescapeUniqueSuffix(newCode, newUniqueSuffix)
|
||||
) {
|
||||
// The generated code reached a fixed point!
|
||||
return Promise.reject({ type: "RETURN", value: true });
|
||||
@ -922,7 +917,7 @@ function argsParse(): ProgramArgs {
|
||||
es5: false, // if true test marked as es6 only are not run
|
||||
filter: "",
|
||||
outOfProcessRuntime: "", // if set, assumed to be a JS runtime and is used
|
||||
// to run tests. If not a seperate node context used.
|
||||
// to run tests. If not a separate node context used.
|
||||
lazyObjectsRuntime: LAZY_OBJECTS_RUNTIME_NAME,
|
||||
noLazySupport: false,
|
||||
fast: false,
|
||||
|
@ -11,78 +11,108 @@
|
||||
|
||||
import type { BabelNodeSourceLocation } from "@babel/types";
|
||||
import invariant from "./invariant.js";
|
||||
import { Effects, Realm } from "./realm.js";
|
||||
import type { Effects } from "./realm.js";
|
||||
import { AbstractValue, EmptyValue, Value } from "./values/index.js";
|
||||
|
||||
export class Completion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
let e = precedingEffects;
|
||||
if (e !== undefined) {
|
||||
if (e.result === undefined) e.result = this;
|
||||
else e = e.shallowCloneWithResult(this);
|
||||
}
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
this.value = value;
|
||||
this.effects = e;
|
||||
this.target = target;
|
||||
this.location = location;
|
||||
invariant(this.constructor !== Completion, "Completion is an abstract base class");
|
||||
}
|
||||
|
||||
value: Value;
|
||||
effects: void | Effects;
|
||||
target: ?string;
|
||||
location: ?BabelNodeSourceLocation;
|
||||
|
||||
shallowCloneWithoutEffects(): Completion {
|
||||
invariant(false, "Completion.shallowCloneWithoutEffects is an abstract base method and should not be called");
|
||||
containsSelectedCompletion(selector: Completion => boolean): boolean {
|
||||
return selector(this);
|
||||
}
|
||||
|
||||
toDisplayString(): string {
|
||||
return "[" + this.constructor.name + " value " + (this.value ? this.value.toDisplayString() : "undefined") + "]";
|
||||
}
|
||||
|
||||
static makeAllNormalCompletionsResultInUndefined(completion: Completion): void {
|
||||
let undefinedVal = completion.value.$Realm.intrinsics.undefined;
|
||||
if (completion instanceof SimpleNormalCompletion) completion.value = undefinedVal;
|
||||
else if (completion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
if (completion.composedWith !== undefined)
|
||||
Completion.makeAllNormalCompletionsResultInUndefined(completion.composedWith);
|
||||
Completion.makeAllNormalCompletionsResultInUndefined(completion.consequent);
|
||||
Completion.makeAllNormalCompletionsResultInUndefined(completion.alternate);
|
||||
}
|
||||
}
|
||||
|
||||
static makeSelectedCompletionsInfeasible(selector: Completion => boolean, completion: Completion): void {
|
||||
let bottomValue = completion.value.$Realm.intrinsics.__bottomValue;
|
||||
if (selector(completion)) completion.value = bottomValue;
|
||||
else if (completion instanceof JoinedNormalAndAbruptCompletions || completion instanceof JoinedAbruptCompletions) {
|
||||
if (completion instanceof JoinedNormalAndAbruptCompletions && completion.composedWith !== undefined)
|
||||
Completion.makeSelectedCompletionsInfeasible(selector, completion.composedWith);
|
||||
Completion.makeSelectedCompletionsInfeasible(selector, completion.consequent);
|
||||
Completion.makeSelectedCompletionsInfeasible(selector, completion.alternate);
|
||||
}
|
||||
}
|
||||
|
||||
static normalizeSelectedCompletions(selector: Completion => boolean, completion: Completion): Completion {
|
||||
if (selector(completion)) return new SimpleNormalCompletion(completion.value);
|
||||
let normalizedComposedWith;
|
||||
if (completion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
invariant(completion.savedEffects === undefined); // caller should not used a still saved completion for this
|
||||
if (completion.composedWith !== undefined)
|
||||
normalizedComposedWith = Completion.normalizeSelectedCompletions(selector, completion.composedWith);
|
||||
}
|
||||
if (completion instanceof JoinedNormalAndAbruptCompletions || completion instanceof JoinedAbruptCompletions) {
|
||||
let nc = Completion.normalizeSelectedCompletions(selector, completion.consequent);
|
||||
let na = Completion.normalizeSelectedCompletions(selector, completion.alternate);
|
||||
if (normalizedComposedWith === undefined) {
|
||||
if (nc === completion.consequent && na === completion.alternate) return completion;
|
||||
if (nc instanceof AbruptCompletion && na instanceof AbruptCompletion) return completion;
|
||||
if (nc instanceof SimpleNormalCompletion && na instanceof SimpleNormalCompletion)
|
||||
return new SimpleNormalCompletion(
|
||||
AbstractValue.createFromConditionalOp(completion.value.$Realm, completion.joinCondition, nc.value, na.value)
|
||||
);
|
||||
invariant(nc instanceof AbruptCompletion || nc instanceof NormalCompletion);
|
||||
invariant(na instanceof AbruptCompletion || na instanceof NormalCompletion);
|
||||
return new JoinedNormalAndAbruptCompletions(completion.joinCondition, nc, na);
|
||||
}
|
||||
invariant(nc instanceof AbruptCompletion || nc instanceof NormalCompletion);
|
||||
invariant(na instanceof AbruptCompletion || na instanceof NormalCompletion);
|
||||
let result = new JoinedNormalAndAbruptCompletions(completion.joinCondition, nc, na);
|
||||
invariant(normalizedComposedWith instanceof JoinedNormalAndAbruptCompletions);
|
||||
result.composedWith = normalizedComposedWith;
|
||||
return result;
|
||||
}
|
||||
return completion;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal completions are returned just like spec completions
|
||||
export class NormalCompletion extends Completion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
super(value, precedingEffects, location, target);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
super(value, location, target);
|
||||
invariant(this.constructor !== NormalCompletion, "NormalCompletion is an abstract base class");
|
||||
}
|
||||
|
||||
shallowCloneWithoutEffects(): NormalCompletion {
|
||||
invariant(false, "NormalCompletion.shallowCloneWithoutEffects is an abstract base method and should not be called");
|
||||
}
|
||||
}
|
||||
|
||||
// SimpleNormalCompletions are returned just like spec completions. This class exists as the parallel for
|
||||
// PossiblyNormalCompletion to make comparisons easier.
|
||||
export class SimpleNormalCompletion extends NormalCompletion {
|
||||
shallowCloneWithoutEffects(): SimpleNormalCompletion {
|
||||
return new SimpleNormalCompletion(this.value, undefined, this.location, this.target);
|
||||
}
|
||||
}
|
||||
// SimpleNormalCompletions are returned just like spec completions.
|
||||
// They chiefly exist for use in joined completions.
|
||||
export class SimpleNormalCompletion extends NormalCompletion {}
|
||||
|
||||
// Abrupt completions are thrown as exeptions, to make it a easier
|
||||
// to quickly get to the matching high level construct.
|
||||
export class AbruptCompletion extends Completion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
super(value, precedingEffects, location, target);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, target?: ?string) {
|
||||
super(value, location, target);
|
||||
invariant(this.constructor !== AbruptCompletion, "AbruptCompletion is an abstract base class");
|
||||
}
|
||||
|
||||
shallowCloneWithoutEffects(): AbruptCompletion {
|
||||
invariant(false, "AbruptCompletion.shallowCloneWithoutEffects is an abstract base method and should not be called");
|
||||
}
|
||||
}
|
||||
|
||||
export class ThrowCompletion extends AbruptCompletion {
|
||||
constructor(
|
||||
value: Value,
|
||||
precedingEffects: void | Effects,
|
||||
location: ?BabelNodeSourceLocation,
|
||||
nativeStack?: ?string
|
||||
) {
|
||||
super(value, precedingEffects, location);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, nativeStack?: ?string) {
|
||||
super(value, location);
|
||||
this.nativeStack = nativeStack || new Error().stack;
|
||||
let realm = value.$Realm;
|
||||
if (realm.isInPureScope()) {
|
||||
@ -93,48 +123,32 @@ export class ThrowCompletion extends AbruptCompletion {
|
||||
}
|
||||
|
||||
nativeStack: string;
|
||||
|
||||
shallowCloneWithoutEffects(): ThrowCompletion {
|
||||
return new ThrowCompletion(this.value, undefined, this.location, this.nativeStack);
|
||||
}
|
||||
}
|
||||
|
||||
export class ContinueCompletion extends AbruptCompletion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation, target: ?string) {
|
||||
super(value, precedingEffects, location, target || null);
|
||||
}
|
||||
|
||||
shallowCloneWithoutEffects(): ContinueCompletion {
|
||||
return new ContinueCompletion(this.value, undefined, this.location, this.target);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, target: ?string) {
|
||||
super(value, location, target || null);
|
||||
}
|
||||
}
|
||||
|
||||
export class BreakCompletion extends AbruptCompletion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation, target: ?string) {
|
||||
super(value, precedingEffects, location, target || null);
|
||||
}
|
||||
|
||||
shallowCloneWithoutEffects(): BreakCompletion {
|
||||
return new BreakCompletion(this.value, undefined, this.location, this.target);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation, target: ?string) {
|
||||
super(value, location, target || null);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReturnCompletion extends AbruptCompletion {
|
||||
constructor(value: Value, precedingEffects: void | Effects, location: ?BabelNodeSourceLocation) {
|
||||
super(value, precedingEffects, location);
|
||||
constructor(value: Value, location: ?BabelNodeSourceLocation) {
|
||||
super(value, location);
|
||||
if (value instanceof EmptyValue) {
|
||||
this.value = value.$Realm.intrinsics.undefined;
|
||||
}
|
||||
}
|
||||
|
||||
shallowCloneWithoutEffects(): ReturnCompletion {
|
||||
return new ReturnCompletion(this.value, undefined, this.location);
|
||||
}
|
||||
}
|
||||
|
||||
export class ForkedAbruptCompletion extends AbruptCompletion {
|
||||
constructor(realm: Realm, joinCondition: AbstractValue, consequent: AbruptCompletion, alternate: AbruptCompletion) {
|
||||
super(realm.intrinsics.empty, undefined, consequent.location);
|
||||
export class JoinedAbruptCompletions extends AbruptCompletion {
|
||||
constructor(joinCondition: AbstractValue, consequent: AbruptCompletion, alternate: AbruptCompletion) {
|
||||
super(joinCondition.$Realm.intrinsics.empty, consequent.location);
|
||||
this.joinCondition = joinCondition;
|
||||
this.consequent = consequent;
|
||||
this.alternate = alternate;
|
||||
@ -144,36 +158,16 @@ export class ForkedAbruptCompletion extends AbruptCompletion {
|
||||
consequent: AbruptCompletion;
|
||||
alternate: AbruptCompletion;
|
||||
|
||||
shallowCloneWithoutEffects(): ForkedAbruptCompletion {
|
||||
return new ForkedAbruptCompletion(this.value.$Realm, this.joinCondition, this.consequent, this.alternate);
|
||||
}
|
||||
|
||||
// For convenience, this.consequent.effects should always be defined, but accessing it directly requires
|
||||
// verifying that with an invariant.
|
||||
get consequentEffects(): Effects {
|
||||
invariant(this.consequent.effects);
|
||||
return this.consequent.effects;
|
||||
}
|
||||
|
||||
get alternateEffects(): Effects {
|
||||
invariant(this.alternate.effects);
|
||||
return this.alternate.effects;
|
||||
}
|
||||
|
||||
updateConsequentKeepingCurrentEffects(newConsequent: AbruptCompletion): AbruptCompletion {
|
||||
let e = this.consequent.effects;
|
||||
invariant(e);
|
||||
newConsequent.effects = e.shallowCloneWithResult(newConsequent);
|
||||
this.consequent = newConsequent;
|
||||
return this;
|
||||
}
|
||||
|
||||
updateAlternateKeepingCurrentEffects(newAlternate: AbruptCompletion): AbruptCompletion {
|
||||
let e = this.alternate.effects;
|
||||
invariant(e);
|
||||
newAlternate.effects = e.shallowCloneWithResult(newAlternate);
|
||||
this.alternate = newAlternate;
|
||||
return this;
|
||||
containsSelectedCompletion(selector: Completion => boolean): boolean {
|
||||
if (selector(this.consequent)) return true;
|
||||
if (selector(this.alternate)) return true;
|
||||
if (this.consequent instanceof JoinedAbruptCompletions) {
|
||||
if (this.consequent.containsSelectedCompletion(selector)) return true;
|
||||
}
|
||||
if (this.alternate instanceof JoinedAbruptCompletions) {
|
||||
if (this.alternate.containsSelectedCompletion(selector)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toDisplayString(): string {
|
||||
@ -182,112 +176,46 @@ export class ForkedAbruptCompletion extends AbruptCompletion {
|
||||
superString + " c: [" + this.consequent.toDisplayString() + "] a: [" + this.alternate.toDisplayString() + "]]"
|
||||
);
|
||||
}
|
||||
|
||||
containsCompletion(CompletionType: typeof Completion): boolean {
|
||||
if (this.consequent instanceof CompletionType) return true;
|
||||
if (this.alternate instanceof CompletionType) return true;
|
||||
if (this.consequent instanceof ForkedAbruptCompletion) {
|
||||
if (this.consequent.containsCompletion(CompletionType)) return true;
|
||||
}
|
||||
if (this.alternate instanceof ForkedAbruptCompletion) {
|
||||
if (this.alternate.containsCompletion(CompletionType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
containsBreakOrContinue(): boolean {
|
||||
if (this.consequent instanceof BreakCompletion || this.consequent instanceof ContinueCompletion) return true;
|
||||
if (this.alternate instanceof BreakCompletion || this.alternate instanceof ContinueCompletion) return true;
|
||||
if (this.consequent instanceof ForkedAbruptCompletion) {
|
||||
if (this.consequent.containsBreakOrContinue()) return true;
|
||||
}
|
||||
if (this.alternate instanceof ForkedAbruptCompletion) {
|
||||
if (this.alternate.containsBreakOrContinue()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
transferChildrenToPossiblyNormalCompletion(): PossiblyNormalCompletion {
|
||||
invariant(this.consequent.value instanceof EmptyValue || this.alternate.value instanceof EmptyValue);
|
||||
return new PossiblyNormalCompletion(
|
||||
this.value.$Realm.intrinsics.empty,
|
||||
this.joinCondition,
|
||||
this.consequent,
|
||||
this.alternate,
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly normal completions have to be treated like normal completions
|
||||
// and are thus never thrown. At the end of a try block or loop body, however,
|
||||
// action must be taken to deal with the possibly abrupt case of the completion.
|
||||
export class PossiblyNormalCompletion extends NormalCompletion {
|
||||
// This should never be thrown, therefore it is treated as a NormalCompletion even though it is also Abrupt.
|
||||
export class JoinedNormalAndAbruptCompletions extends NormalCompletion {
|
||||
constructor(
|
||||
value: Value,
|
||||
joinCondition: AbstractValue,
|
||||
consequent: Completion,
|
||||
alternate: Completion,
|
||||
savedPathConditions: Array<AbstractValue>,
|
||||
savedEffects: void | Effects = undefined
|
||||
consequent: AbruptCompletion | NormalCompletion,
|
||||
alternate: AbruptCompletion | NormalCompletion
|
||||
) {
|
||||
invariant(consequent instanceof NormalCompletion || alternate instanceof NormalCompletion);
|
||||
super(value, undefined, consequent.location);
|
||||
super(consequent instanceof NormalCompletion ? consequent.value : alternate.value, consequent.location);
|
||||
this.joinCondition = joinCondition;
|
||||
this.consequent = consequent;
|
||||
this.alternate = alternate;
|
||||
this.savedEffects = savedEffects;
|
||||
this.savedPathConditions = savedPathConditions;
|
||||
this.pathConditionsAtCreation = [].concat(joinCondition.$Realm.pathConditions);
|
||||
}
|
||||
|
||||
joinCondition: AbstractValue;
|
||||
consequent: Completion;
|
||||
alternate: Completion;
|
||||
consequent: AbruptCompletion | NormalCompletion;
|
||||
alternate: AbruptCompletion | NormalCompletion;
|
||||
composedWith: void | JoinedNormalAndAbruptCompletions;
|
||||
pathConditionsAtCreation: Array<AbstractValue>;
|
||||
savedEffects: void | Effects;
|
||||
// The path conditions that applied at the time of the oldest fork that caused this completion to arise.
|
||||
savedPathConditions: Array<AbstractValue>;
|
||||
|
||||
shallowCloneWithoutEffects(): PossiblyNormalCompletion {
|
||||
let consequentEffects = this.consequentEffects;
|
||||
let alternateEffects = this.alternateEffects;
|
||||
invariant(this.consequent === consequentEffects.result);
|
||||
invariant(this.alternate === alternateEffects.result);
|
||||
return new PossiblyNormalCompletion(
|
||||
this.value,
|
||||
this.joinCondition,
|
||||
this.consequent,
|
||||
this.alternate,
|
||||
this.savedPathConditions,
|
||||
this.savedEffects
|
||||
);
|
||||
}
|
||||
|
||||
// For convenience, this.consequent.effects should always be defined, but accessing it directly requires
|
||||
// verifying that with an invariant.
|
||||
get consequentEffects(): Effects {
|
||||
invariant(this.consequent.effects);
|
||||
return this.consequent.effects;
|
||||
}
|
||||
|
||||
get alternateEffects(): Effects {
|
||||
invariant(this.alternate.effects);
|
||||
return this.alternate.effects;
|
||||
}
|
||||
|
||||
updateConsequentKeepingCurrentEffects(newConsequent: Completion): PossiblyNormalCompletion {
|
||||
if (newConsequent instanceof NormalCompletion) this.value = newConsequent.value;
|
||||
let e = this.consequentEffects;
|
||||
let effects = e.shallowCloneWithResult(newConsequent);
|
||||
this.consequent = effects.result;
|
||||
return this;
|
||||
}
|
||||
|
||||
updateAlternateKeepingCurrentEffects(newAlternate: Completion): PossiblyNormalCompletion {
|
||||
if (newAlternate instanceof NormalCompletion) this.value = newAlternate.value;
|
||||
let e = this.alternateEffects;
|
||||
let effects = e.shallowCloneWithResult(newAlternate);
|
||||
this.alternate = effects.result;
|
||||
return this;
|
||||
containsSelectedCompletion(selector: Completion => boolean): boolean {
|
||||
if (this.composedWith !== undefined && this.composedWith.containsSelectedCompletion(selector)) return true;
|
||||
if (selector(this.consequent)) return true;
|
||||
if (selector(this.alternate)) return true;
|
||||
if (
|
||||
this.consequent instanceof JoinedAbruptCompletions ||
|
||||
this.consequent instanceof JoinedNormalAndAbruptCompletions
|
||||
) {
|
||||
if (this.consequent.containsSelectedCompletion(selector)) return true;
|
||||
}
|
||||
if (
|
||||
this.alternate instanceof JoinedAbruptCompletions ||
|
||||
this.alternate instanceof JoinedNormalAndAbruptCompletions
|
||||
) {
|
||||
if (this.alternate.containsSelectedCompletion(selector)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toDisplayString(): string {
|
||||
@ -296,46 +224,4 @@ export class PossiblyNormalCompletion extends NormalCompletion {
|
||||
superString + " c: [" + this.consequent.toDisplayString() + "] a: [" + this.alternate.toDisplayString() + "]]"
|
||||
);
|
||||
}
|
||||
|
||||
getNormalCompletion(): SimpleNormalCompletion {
|
||||
let result;
|
||||
if (this.alternate instanceof SimpleNormalCompletion) {
|
||||
result = this.alternate;
|
||||
} else if (this.consequent instanceof SimpleNormalCompletion) {
|
||||
result = this.consequent;
|
||||
} else {
|
||||
if (this.alternate instanceof PossiblyNormalCompletion) {
|
||||
result = this.alternate.getNormalCompletion();
|
||||
} else {
|
||||
invariant(this.consequent instanceof PossiblyNormalCompletion);
|
||||
result = this.consequent.getNormalCompletion();
|
||||
}
|
||||
}
|
||||
invariant(result.value === this.value);
|
||||
return result;
|
||||
}
|
||||
|
||||
containsCompletion(CompletionType: typeof Completion): boolean {
|
||||
if (this.consequent instanceof CompletionType) return true;
|
||||
if (this.alternate instanceof CompletionType) return true;
|
||||
if (this.consequent instanceof ForkedAbruptCompletion || this.consequent instanceof PossiblyNormalCompletion) {
|
||||
if (this.consequent.containsCompletion(CompletionType)) return true;
|
||||
}
|
||||
if (this.alternate instanceof ForkedAbruptCompletion || this.alternate instanceof PossiblyNormalCompletion) {
|
||||
if (this.alternate.containsCompletion(CompletionType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
containsBreakOrContinue(): boolean {
|
||||
if (this.consequent instanceof BreakCompletion || this.consequent instanceof ContinueCompletion) return true;
|
||||
if (this.alternate instanceof BreakCompletion || this.alternate instanceof ContinueCompletion) return true;
|
||||
if (this.consequent instanceof ForkedAbruptCompletion || this.consequent instanceof PossiblyNormalCompletion) {
|
||||
if (this.consequent.containsBreakOrContinue()) return true;
|
||||
}
|
||||
if (this.alternate instanceof ForkedAbruptCompletion || this.alternate instanceof PossiblyNormalCompletion) {
|
||||
if (this.alternate.containsBreakOrContinue()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import initializeGlobal from "./intrinsics/ecma262/global.js";
|
||||
import type { RealmOptions } from "./options.js";
|
||||
import { RealmStatistics } from "./statistics.js";
|
||||
import * as evaluators from "./evaluators/index.js";
|
||||
import * as partialEvaluators from "./partial-evaluators/index.js";
|
||||
import { Environment, DebugReproManager } from "./singletons.js";
|
||||
import { ObjectValue } from "./values/index.js";
|
||||
import { DebugServer } from "./debugger/server/Debugger.js";
|
||||
@ -50,7 +49,6 @@ export default function(
|
||||
r.$GlobalObject = new ObjectValue(r, i.ObjectPrototype, "global");
|
||||
initializeGlobal(r);
|
||||
for (let name in evaluators) r.evaluators[name] = evaluators[name];
|
||||
for (let name in partialEvaluators) r.partialEvaluators[name] = partialEvaluators[name];
|
||||
r.simplifyAndRefineAbstractValue = simplifyAndRefineAbstractValue.bind(null, r, false);
|
||||
r.simplifyAndRefineAbstractCondition = simplifyAndRefineAbstractValue.bind(null, r, true);
|
||||
r.$GlobalEnv = Environment.NewGlobalEnvironment(r, r.$GlobalObject, r.$GlobalObject);
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
AbstractValue,
|
||||
BooleanValue,
|
||||
ConcreteValue,
|
||||
EmptyValue,
|
||||
FunctionValue,
|
||||
NumberValue,
|
||||
IntegralValue,
|
||||
@ -33,10 +34,15 @@ export default class TypesDomain {
|
||||
this._type = type === Value ? undefined : type;
|
||||
}
|
||||
|
||||
static topVal: TypesDomain = new TypesDomain(undefined);
|
||||
static topVal: TypesDomain;
|
||||
static bottomVal: TypesDomain;
|
||||
|
||||
_type: void | typeof Value;
|
||||
|
||||
isBottom(): boolean {
|
||||
return this._type instanceof EmptyValue;
|
||||
}
|
||||
|
||||
isTop(): boolean {
|
||||
return this._type === undefined;
|
||||
}
|
||||
@ -47,6 +53,7 @@ export default class TypesDomain {
|
||||
|
||||
// return the type of the result in the case where there is no exception
|
||||
static binaryOp(op: BabelBinaryOperator, left: TypesDomain, right: TypesDomain): TypesDomain {
|
||||
if (left.isBottom() || right.isBottom()) return TypesDomain.bottomVal;
|
||||
let lType = left._type;
|
||||
let rType = right._type;
|
||||
let resultType = Value;
|
||||
@ -105,8 +112,9 @@ export default class TypesDomain {
|
||||
}
|
||||
|
||||
joinWith(t: typeof Value): TypesDomain {
|
||||
if (this.isBottom()) return t === EmptyValue ? TypesDomain.bottomVal : new TypesDomain(t);
|
||||
let type = this.getType();
|
||||
if (type === t) return this;
|
||||
if (type === t || t instanceof EmptyValue) return this;
|
||||
if (Value.isTypeCompatibleWith(type, NumberValue) && Value.isTypeCompatibleWith(t, NumberValue)) {
|
||||
return new TypesDomain(NumberValue);
|
||||
}
|
||||
@ -129,6 +137,7 @@ export default class TypesDomain {
|
||||
// return the type of the result in the case where there is no exception
|
||||
// note that the type of the operand has no influence on the type of the non exceptional result
|
||||
static unaryOp(op: BabelUnaryOperator, operand: TypesDomain): TypesDomain {
|
||||
if (operand.isBottom()) return TypesDomain.bottomVal;
|
||||
const type = operand._type;
|
||||
let resultType = Value;
|
||||
switch (op) {
|
||||
|
@ -54,7 +54,8 @@ export default class ValuesDomain {
|
||||
this._elements = values;
|
||||
}
|
||||
|
||||
static topVal = new ValuesDomain(undefined);
|
||||
static topVal: ValuesDomain;
|
||||
static bottomVal: ValuesDomain;
|
||||
|
||||
_elements: void | Set<ConcreteValue>;
|
||||
|
||||
@ -79,6 +80,10 @@ export default class ValuesDomain {
|
||||
return elems.has(x);
|
||||
}
|
||||
|
||||
isBottom(): boolean {
|
||||
return this._elements !== undefined && this._elements.size === 0;
|
||||
}
|
||||
|
||||
isTop(): boolean {
|
||||
return this._elements === undefined;
|
||||
}
|
||||
@ -91,6 +96,7 @@ export default class ValuesDomain {
|
||||
// return a set of values that may be result of performing the given operation on each pair in the
|
||||
// Cartesian product of the value sets of the operands.
|
||||
static binaryOp(realm: Realm, op: BabelBinaryOperator, left: ValuesDomain, right: ValuesDomain): ValuesDomain {
|
||||
if (left.isBottom() || right.isBottom()) return ValuesDomain.bottomVal;
|
||||
let leftElements = left._elements;
|
||||
let rightElements = right._elements;
|
||||
// Return top if left and/or right are top or if the size of the value set would get to be quite large.
|
||||
@ -413,6 +419,7 @@ export default class ValuesDomain {
|
||||
}
|
||||
|
||||
static unaryOp(realm: Realm, op: BabelUnaryOperator, operandValues: ValuesDomain): ValuesDomain {
|
||||
if (operandValues.isBottom()) return ValuesDomain.bottomVal;
|
||||
let operandElements = operandValues._elements;
|
||||
if (operandElements === undefined) return ValuesDomain.topVal;
|
||||
let resultSet = new Set();
|
||||
@ -503,6 +510,7 @@ export default class ValuesDomain {
|
||||
invariant(y instanceof ConcreteValue);
|
||||
union.add(y);
|
||||
}
|
||||
if (union.size === 0) return ValuesDomain.bottomVal;
|
||||
return new ValuesDomain(union);
|
||||
}
|
||||
|
||||
@ -517,6 +525,7 @@ export default class ValuesDomain {
|
||||
invariant(v1 instanceof ConcreteValue);
|
||||
invariant(v2 instanceof ConcreteValue);
|
||||
if (v1 === v2) intersection.add(v1);
|
||||
if (intersection.size === 0) return ValuesDomain.bottomVal;
|
||||
return new ValuesDomain(intersection);
|
||||
}
|
||||
|
||||
@ -532,11 +541,12 @@ export default class ValuesDomain {
|
||||
invariant(y instanceof ConcreteValue);
|
||||
if (elements === undefined || elements.has(y)) intersection.add(y);
|
||||
}
|
||||
if (intersection.size === 0) return ValuesDomain.bottomVal;
|
||||
return new ValuesDomain(intersection);
|
||||
}
|
||||
|
||||
promoteEmptyToUndefined(): ValuesDomain {
|
||||
if (this.isTop()) return this;
|
||||
if (this.isTop() || this.isBottom()) return this;
|
||||
let newSet = new Set();
|
||||
for (let cval of this.getElements()) {
|
||||
if (cval instanceof EmptyValue) newSet.add(cval.$Realm.intrinsics.undefined);
|
||||
|
@ -15,17 +15,14 @@ import type {
|
||||
BabelNodeFile,
|
||||
BabelNodeLVal,
|
||||
BabelNodePosition,
|
||||
BabelNodeStatement,
|
||||
BabelNodeSourceLocation,
|
||||
} from "@babel/types";
|
||||
import type { Realm } from "./realm.js";
|
||||
import type { SourceFile, SourceMap, SourceType } from "./types.js";
|
||||
import type { SourceFile, SourceType } from "./types.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import { AbruptCompletion, Completion, ThrowCompletion } from "./completions.js";
|
||||
import { CompilerDiagnostic, FatalError } from "./errors.js";
|
||||
import { defaultOptions } from "./options.js";
|
||||
import type { PartialEvaluatorOptions } from "./options";
|
||||
import { ExecutionContext } from "./realm.js";
|
||||
import {
|
||||
AbstractValue,
|
||||
@ -41,7 +38,6 @@ import {
|
||||
UndefinedValue,
|
||||
Value,
|
||||
} from "./values/index.js";
|
||||
import generate from "@babel/generator";
|
||||
import parse from "./utils/parse.js";
|
||||
import invariant from "./invariant.js";
|
||||
import traverseFast from "./utils/traverse-fast.js";
|
||||
@ -1077,35 +1073,6 @@ export class LexicalEnvironment {
|
||||
Properties.PutValue(this.realm, globalValue, rvalue);
|
||||
}
|
||||
|
||||
partiallyEvaluateCompletionDeref(
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
metadata?: any
|
||||
): [Completion | Value, BabelNode, Array<BabelNodeStatement>] {
|
||||
let [result, partial_ast, partial_io] = this.partiallyEvaluateCompletion(ast, strictCode, metadata);
|
||||
if (result instanceof Reference) {
|
||||
result = Environment.GetValue(this.realm, result);
|
||||
}
|
||||
return [result, partial_ast, partial_io];
|
||||
}
|
||||
|
||||
partiallyEvaluateCompletion(
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
metadata?: any
|
||||
): [Completion | Reference | Value, BabelNode, Array<BabelNodeStatement>] {
|
||||
try {
|
||||
return this.partiallyEvaluate(ast, strictCode, metadata);
|
||||
} catch (err) {
|
||||
if (err instanceof Completion) return [err, ast, []];
|
||||
if (err instanceof Error)
|
||||
// rethrowing Error should preserve stack trace
|
||||
throw err;
|
||||
// let's wrap into a proper Error to create stack trace
|
||||
throw new FatalError(err);
|
||||
}
|
||||
}
|
||||
|
||||
evaluateCompletionDeref(ast: BabelNode, strictCode: boolean, metadata?: any): AbruptCompletion | Value {
|
||||
let result = this.evaluateCompletion(ast, strictCode, metadata);
|
||||
if (result instanceof Reference) result = Environment.GetValue(this.realm, result);
|
||||
@ -1216,37 +1183,6 @@ export class LexicalEnvironment {
|
||||
return [Environment.GetValue(this.realm, res), code];
|
||||
}
|
||||
|
||||
executePartialEvaluator(
|
||||
sources: Array<SourceFile>,
|
||||
options: PartialEvaluatorOptions = defaultOptions,
|
||||
sourceType: SourceType = "script"
|
||||
): AbruptCompletion | { code: string, map?: SourceMap } {
|
||||
let [ast, code] = this.concatenateAndParse(sources, sourceType);
|
||||
let context = new ExecutionContext();
|
||||
context.lexicalEnvironment = this;
|
||||
context.variableEnvironment = this;
|
||||
context.realm = this.realm;
|
||||
this.realm.pushContext(context);
|
||||
let partialAST;
|
||||
try {
|
||||
[, partialAST] = this.partiallyEvaluateCompletionDeref(ast, false);
|
||||
} finally {
|
||||
this.realm.popContext(context);
|
||||
this.realm.onDestroyScope(context.lexicalEnvironment);
|
||||
if (!this.destroyed) this.realm.onDestroyScope(this);
|
||||
invariant(
|
||||
this.realm.activeLexicalEnvironments.size === 0,
|
||||
`expected 0 active lexical environments, got ${this.realm.activeLexicalEnvironments.size}`
|
||||
);
|
||||
}
|
||||
invariant(partialAST.type === "File");
|
||||
let fileAst = ((partialAST: any): BabelNodeFile);
|
||||
let prog = t.program(fileAst.program.body, ast.program.directives);
|
||||
this.fixupFilenames(prog);
|
||||
// The type signature for generate is not complete, hence the any
|
||||
return generate(prog, { sourceMaps: options.sourceMaps }, (code: any));
|
||||
}
|
||||
|
||||
execute(
|
||||
code: string,
|
||||
filename: string,
|
||||
@ -1434,20 +1370,6 @@ export class LexicalEnvironment {
|
||||
if (result instanceof Reference) result = Environment.GetValue(this.realm, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
partiallyEvaluate(
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
metadata?: any
|
||||
): [Completion | Reference | Value, BabelNode, Array<BabelNodeStatement>] {
|
||||
let partialEvaluator = this.realm.partialEvaluators[(ast.type: string)];
|
||||
if (partialEvaluator) {
|
||||
return partialEvaluator(ast, strictCode, this, this.realm, metadata);
|
||||
}
|
||||
|
||||
let err = new TypeError(`Unsupported node type ${ast.type}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// ECMA262 6.2.3
|
||||
|
@ -26,7 +26,6 @@ import {
|
||||
UndefinedValue,
|
||||
Value,
|
||||
} from "../values/index.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { Environment, Havoc, To } from "../singletons.js";
|
||||
import type { BabelBinaryOperator, BabelNodeBinaryExpression, BabelNodeSourceLocation } from "@babel/types";
|
||||
import { createOperationDescriptor } from "../utils/generator.js";
|
||||
@ -284,23 +283,8 @@ export function computeBinary(
|
||||
}
|
||||
|
||||
if (isPure && effects) {
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in effects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
} else if (completion instanceof SimpleNormalCompletion) {
|
||||
completion = completion.value;
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(effects.result);
|
||||
}
|
||||
|
||||
// If this ended up reporting an error, it might not be pure, so we'll leave it in
|
||||
|
@ -21,5 +21,5 @@ export default function(
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): Value {
|
||||
throw new BreakCompletion(realm.intrinsics.empty, undefined, ast.loc, ast.label && ast.label.name);
|
||||
throw new BreakCompletion(realm.intrinsics.empty, ast.loc, ast.label && ast.label.name);
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import { AbruptCompletion, Completion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import { type LexicalEnvironment, type BaseValue, mightBecomeAnObject } from "../environment.js";
|
||||
import { EnvironmentRecord } from "../environment.js";
|
||||
@ -217,29 +216,9 @@ function callBothFunctionsAndJoinTheirEffects(
|
||||
"callBothFunctionsAndJoinTheirEffects/2"
|
||||
);
|
||||
|
||||
let r1 = e1.result;
|
||||
if (r1 instanceof Completion) r1 = r1.shallowCloneWithoutEffects();
|
||||
let r2 = e2.result;
|
||||
if (r2 instanceof Completion) r2 = r2.shallowCloneWithoutEffects();
|
||||
let joinedEffects = Join.joinForkOrChoose(realm, cond, e1.shallowCloneWithResult(r1), e2.shallowCloneWithResult(r2));
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
let joinedEffects = Join.joinEffects(cond, e1, e2);
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(joinedEffects.result);
|
||||
}
|
||||
|
||||
function generateRuntimeCall(
|
||||
@ -308,22 +287,8 @@ function tryToEvaluateCallOrLeaveAsAbstract(
|
||||
} finally {
|
||||
realm.suppressDiagnostics = savedSuppressDiagnostics;
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in effects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(effects.result);
|
||||
}
|
||||
|
||||
function EvaluateCall(
|
||||
|
@ -21,5 +21,5 @@ export default function(
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): Value {
|
||||
throw new ContinueCompletion(realm.intrinsics.empty, undefined, ast.loc, ast.label && ast.label.name);
|
||||
throw new ContinueCompletion(realm.intrinsics.empty, ast.loc, ast.label && ast.label.name);
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import { FatalError } from "../errors.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import { EmptyValue } from "../values/index.js";
|
||||
import { UpdateEmpty } from "../methods/index.js";
|
||||
import { LoopContinues, InternalGetResultValue, TryToApplyEffectsOfJoiningBranches } from "./ForOfStatement.js";
|
||||
import { AbruptCompletion, BreakCompletion, ForkedAbruptCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { LoopContinues, InternalGetResultValue } from "./ForOfStatement.js";
|
||||
import { AbruptCompletion, BreakCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { Environment, To } from "../singletons.js";
|
||||
import invariant from "../invariant.js";
|
||||
import type { BabelNodeDoWhileStatement } from "@babel/types";
|
||||
@ -38,9 +38,8 @@ export default function(
|
||||
while (true) {
|
||||
// a. Let stmt be the result of evaluating Statement.
|
||||
let stmt = env.evaluateCompletion(body, strictCode);
|
||||
//todo: check if stmt is a PossiblyNormalCompletion and defer to fixpoint computation below
|
||||
//todo: check if stmt is JoinedNormalAndAbruptCompletions and defer to fixpoint computation below
|
||||
invariant(stmt instanceof Value || stmt instanceof AbruptCompletion);
|
||||
if (stmt instanceof ForkedAbruptCompletion) stmt = TryToApplyEffectsOfJoiningBranches(realm, stmt);
|
||||
|
||||
// b. If LoopContinues(stmt, labelSet) is false, return Completion(UpdateEmpty(stmt, V)).
|
||||
if (LoopContinues(realm, stmt, labelSet) === false) {
|
||||
|
@ -14,7 +14,14 @@ import type { LexicalEnvironment } from "../environment.js";
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import { DeclarativeEnvironmentRecord } from "../environment.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { BreakCompletion, AbruptCompletion, ContinueCompletion, ForkedAbruptCompletion } from "../completions.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
BreakCompletion,
|
||||
Completion,
|
||||
ContinueCompletion,
|
||||
JoinedAbruptCompletions,
|
||||
NormalCompletion,
|
||||
} from "../completions.js";
|
||||
import {
|
||||
AbstractObjectValue,
|
||||
AbstractValue,
|
||||
@ -33,7 +40,7 @@ import {
|
||||
DestructuringAssignmentEvaluation,
|
||||
GetIterator,
|
||||
} from "../methods/index.js";
|
||||
import { Environment, Join, Properties, To } from "../singletons.js";
|
||||
import { Environment, Properties, To } from "../singletons.js";
|
||||
import type {
|
||||
BabelNode,
|
||||
BabelNodeForOfStatement,
|
||||
@ -45,37 +52,23 @@ import type {
|
||||
export type IterationKind = "iterate" | "enumerate";
|
||||
export type LhsKind = "lexicalBinding" | "varBinding" | "assignment";
|
||||
|
||||
export function InternalGetResultValue(realm: Realm, result: Value | AbruptCompletion): Value {
|
||||
if (result instanceof AbruptCompletion) {
|
||||
export function InternalGetResultValue(realm: Realm, result: Value | Completion): Value {
|
||||
if (result instanceof Completion) {
|
||||
return result.value;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function TryToApplyEffectsOfJoiningBranches(realm: Realm, c: ForkedAbruptCompletion): AbruptCompletion {
|
||||
let joinedEffects = Join.joinNestedEffects(realm, c);
|
||||
let jr = joinedEffects.result;
|
||||
invariant(jr instanceof AbruptCompletion);
|
||||
if (jr instanceof ContinueCompletion || jr instanceof BreakCompletion) {
|
||||
// The end of a loop body is join point for these.
|
||||
realm.applyEffects(joinedEffects, "end of loop body");
|
||||
} else if (jr instanceof ForkedAbruptCompletion) {
|
||||
if (jr.containsBreakOrContinue()) {
|
||||
// todo: extract the continue completions, apply those while stashing the other completions
|
||||
// in realm.savedCompletion. This may need customization depending on the caller.
|
||||
AbstractValue.reportIntrospectionError(jr.joinCondition);
|
||||
throw new FatalError();
|
||||
}
|
||||
}
|
||||
return jr;
|
||||
}
|
||||
|
||||
// ECMA262 13.7.1.2
|
||||
export function LoopContinues(realm: Realm, completion: Value | AbruptCompletion, labelSet: ?Array<string>): boolean {
|
||||
export function LoopContinues(realm: Realm, completion: Value | Completion, labelSet: ?Array<string>): boolean {
|
||||
// 1. If completion.[[Type]] is normal, return true.
|
||||
if (completion instanceof Value) return true;
|
||||
invariant(completion instanceof AbruptCompletion);
|
||||
if (completion instanceof Value || completion instanceof NormalCompletion) return true;
|
||||
if (completion instanceof JoinedAbruptCompletions) {
|
||||
return (
|
||||
LoopContinues(realm, completion.consequent, labelSet) || LoopContinues(realm, completion.alternate, labelSet)
|
||||
);
|
||||
}
|
||||
|
||||
// 2. If completion.[[Type]] is not continue, return false.
|
||||
if (!(completion instanceof ContinueCompletion)) return false;
|
||||
@ -167,7 +160,7 @@ export function ForInOfHeadEvaluation(
|
||||
// a. If exprValue.[[Value]] is null or undefined, then
|
||||
if (exprValue instanceof NullValue || exprValue instanceof UndefinedValue) {
|
||||
// i. Return Completion{[[Type]]: break, [[Value]]: empty, [[Target]]: empty}.
|
||||
throw new BreakCompletion(realm.intrinsics.empty, undefined, expr.loc, null);
|
||||
throw new BreakCompletion(realm.intrinsics.empty, expr.loc, null);
|
||||
}
|
||||
|
||||
// b. Let obj be ToObject(exprValue).
|
||||
@ -346,7 +339,6 @@ export function ForInOfBodyEvaluation(
|
||||
// i. Let result be the result of evaluating stmt.
|
||||
let result = env.evaluateCompletion(stmt, strictCode);
|
||||
invariant(result instanceof Value || result instanceof AbruptCompletion);
|
||||
if (result instanceof ForkedAbruptCompletion) result = TryToApplyEffectsOfJoiningBranches(realm, result);
|
||||
|
||||
// j. Set the running execution context's LexicalEnvironment to oldEnv.
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
/* @flow */
|
||||
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import { Realm } from "../realm.js";
|
||||
import {
|
||||
AbstractValue,
|
||||
Value,
|
||||
@ -23,19 +23,19 @@ import {
|
||||
BreakCompletion,
|
||||
Completion,
|
||||
ContinueCompletion,
|
||||
ForkedAbruptCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
ReturnCompletion,
|
||||
SimpleNormalCompletion,
|
||||
ThrowCompletion,
|
||||
SimpleNormalCompletion,
|
||||
} from "../completions.js";
|
||||
import traverse from "@babel/traverse";
|
||||
import type { BabelTraversePath } from "@babel/traverse";
|
||||
import { TypesDomain, ValuesDomain } from "../domains/index.js";
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import { UpdateEmpty } from "../methods/index.js";
|
||||
import { LoopContinues, InternalGetResultValue, TryToApplyEffectsOfJoiningBranches } from "./ForOfStatement.js";
|
||||
import { Environment, Functions, Havoc, Join, To } from "../singletons.js";
|
||||
import { LoopContinues, InternalGetResultValue } from "./ForOfStatement.js";
|
||||
import { Environment, Functions, Havoc, To } from "../singletons.js";
|
||||
import invariant from "../invariant.js";
|
||||
import * as t from "@babel/types";
|
||||
import type { BabelNodeExpression, BabelNodeForStatement, BabelNodeBlockStatement } from "@babel/types";
|
||||
@ -103,6 +103,7 @@ function ForBodyEvaluation(
|
||||
|
||||
// 3. Repeat
|
||||
while (true) {
|
||||
let result;
|
||||
// a. If test is not [empty], then
|
||||
if (test) {
|
||||
// i. Let testRef be the result of evaluating test.
|
||||
@ -113,37 +114,44 @@ function ForBodyEvaluation(
|
||||
|
||||
// iii. If ToBoolean(testValue) is false, return NormalCompletion(V).
|
||||
if (!To.ToBooleanPartial(realm, testValue)) {
|
||||
// joinAllLoopExits does not handle labeled break/continue, so only use it when doing AI
|
||||
if (realm.useAbstractInterpretation) return joinAllLoopExits(V);
|
||||
result = Functions.incorporateSavedCompletion(realm, V);
|
||||
if (result instanceof JoinedNormalAndAbruptCompletions) {
|
||||
let selector = c => c instanceof BreakCompletion && !c.target;
|
||||
result = Completion.normalizeSelectedCompletions(selector, result);
|
||||
result = realm.composeWithSavedCompletion(result);
|
||||
}
|
||||
return V;
|
||||
}
|
||||
}
|
||||
|
||||
// b. Let result be the result of evaluating stmt.
|
||||
let result = env.evaluateCompletion(stmt, strictCode);
|
||||
result = env.evaluateCompletion(stmt, strictCode);
|
||||
invariant(result instanceof Value || result instanceof AbruptCompletion);
|
||||
if (result instanceof ForkedAbruptCompletion) result = TryToApplyEffectsOfJoiningBranches(realm, result);
|
||||
|
||||
// this is a join point for break and continue completions
|
||||
result = Functions.incorporateSavedCompletion(realm, result);
|
||||
invariant(result !== undefined);
|
||||
if (result instanceof Value) result = new SimpleNormalCompletion(result);
|
||||
|
||||
// c. If LoopContinues(result, labelSet) is false, return Completion(UpdateEmpty(result, V)).
|
||||
if (!LoopContinues(realm, result, labelSet)) {
|
||||
invariant(result instanceof AbruptCompletion);
|
||||
// joinAllLoopExits does not handle labeled break/continue, so only use it when doing AI
|
||||
if (realm.useAbstractInterpretation) {
|
||||
result = UpdateEmpty(realm, result, V);
|
||||
invariant(result instanceof AbruptCompletion);
|
||||
return joinAllLoopExits(result);
|
||||
}
|
||||
// ECMA262 13.1.7
|
||||
if (result instanceof BreakCompletion) {
|
||||
if (!result.target) return (UpdateEmpty(realm, result, V): any).value;
|
||||
} else if (result instanceof JoinedAbruptCompletions) {
|
||||
let selector = c => c instanceof BreakCompletion && !c.target;
|
||||
if (result.containsSelectedCompletion(selector)) {
|
||||
result = Completion.normalizeSelectedCompletions(selector, result);
|
||||
}
|
||||
}
|
||||
throw UpdateEmpty(realm, result, V);
|
||||
} else if (realm.useAbstractInterpretation) {
|
||||
// This is a join point for conditional continue completions lurking in realm.savedCompletion
|
||||
if (containsContinueCompletion(realm.savedCompletion)) {
|
||||
result = joinAllLoopContinues(result);
|
||||
}
|
||||
return realm.returnOrThrowCompletion(result);
|
||||
}
|
||||
if (result instanceof JoinedNormalAndAbruptCompletions) {
|
||||
result = Completion.normalizeSelectedCompletions(c => c instanceof ContinueCompletion, result);
|
||||
}
|
||||
invariant(result instanceof Completion);
|
||||
result = realm.composeWithSavedCompletion(result);
|
||||
|
||||
// d. If result.[[Value]] is not empty, let V be result.[[Value]].
|
||||
let resultValue = InternalGetResultValue(realm, result);
|
||||
@ -161,14 +169,14 @@ function ForBodyEvaluation(
|
||||
// ii. Perform ? GetValue(incRef).
|
||||
Environment.GetValue(realm, incRef);
|
||||
} else if (realm.useAbstractInterpretation) {
|
||||
// If we have no increment and we've hit 100 iterations of trying to evaluate
|
||||
// If we have no increment and we've hit 12 iterations of trying to evaluate
|
||||
// this loop body, then see if we have a break, return or throw completion in a
|
||||
// guarded condition and fail if it does. We already have logic to guard
|
||||
// against loops that are actually infinite. However, because there may be so
|
||||
// many forked execution paths, and they're non linear, then it might
|
||||
// computationally lead to a something that seems like an infinite loop.
|
||||
possibleInfiniteLoopIterations++;
|
||||
if (possibleInfiniteLoopIterations > 100) {
|
||||
if (possibleInfiniteLoopIterations > 12) {
|
||||
failIfContainsBreakOrReturnOrThrowCompletion(realm.savedCompletion);
|
||||
}
|
||||
}
|
||||
@ -187,136 +195,11 @@ function ForBodyEvaluation(
|
||||
realm.handleError(diagnostic);
|
||||
throw new FatalError();
|
||||
}
|
||||
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion) {
|
||||
if (c instanceof JoinedAbruptCompletions || c instanceof JoinedNormalAndAbruptCompletions) {
|
||||
failIfContainsBreakOrReturnOrThrowCompletion(c.consequent);
|
||||
failIfContainsBreakOrReturnOrThrowCompletion(c.alternate);
|
||||
}
|
||||
}
|
||||
|
||||
function failIfContainsBreakOrContinueCompletionWithNonLocalTarget(c: void | Completion | Value) {
|
||||
if (c === undefined) return;
|
||||
if (c instanceof ContinueCompletion || c instanceof BreakCompletion) {
|
||||
if (!c.target) return;
|
||||
if (labelSet && labelSet.indexOf(c.target) >= 0) {
|
||||
c.target = null;
|
||||
return;
|
||||
}
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
"break or continue with target cannot be guarded by abstract condition",
|
||||
c.location,
|
||||
"PP0034",
|
||||
"FatalError"
|
||||
);
|
||||
realm.handleError(diagnostic);
|
||||
throw new FatalError();
|
||||
}
|
||||
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion) {
|
||||
failIfContainsBreakOrContinueCompletionWithNonLocalTarget(c.consequent);
|
||||
failIfContainsBreakOrContinueCompletionWithNonLocalTarget(c.alternate);
|
||||
}
|
||||
}
|
||||
|
||||
function containsContinueCompletion(c: void | Completion | Value) {
|
||||
if (c === undefined) return false;
|
||||
if (c instanceof ContinueCompletion) {
|
||||
if (!c.target) return true;
|
||||
if (labelSet && labelSet.indexOf(c.target) >= 0) {
|
||||
c.target = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion)
|
||||
return containsContinueCompletion(c.consequent) || containsContinueCompletion(c.alternate);
|
||||
return false;
|
||||
}
|
||||
|
||||
function joinAllLoopContinues(
|
||||
valueOrCompletionAtLoopContinuePoint: Value | AbruptCompletion
|
||||
): Value | AbruptCompletion {
|
||||
// We are about start the next loop iteration and this presents a join point where all non loop breaking abrupt
|
||||
// control flows converge into a single flow using their joined effects as the new state.
|
||||
failIfContainsBreakOrContinueCompletionWithNonLocalTarget(realm.savedCompletion);
|
||||
|
||||
// Incorporate the savedCompletion (we should only get called if there is one).
|
||||
invariant(realm.savedCompletion !== undefined);
|
||||
if (valueOrCompletionAtLoopContinuePoint instanceof Value)
|
||||
valueOrCompletionAtLoopContinuePoint = new ContinueCompletion(valueOrCompletionAtLoopContinuePoint, undefined);
|
||||
let abruptCompletion = Functions.incorporateSavedCompletion(realm, valueOrCompletionAtLoopContinuePoint);
|
||||
invariant(abruptCompletion instanceof AbruptCompletion);
|
||||
|
||||
// If there is now a single completion, we don't need to join
|
||||
if (!(abruptCompletion instanceof ForkedAbruptCompletion)) return abruptCompletion;
|
||||
invariant(containsContinueCompletion(abruptCompletion));
|
||||
|
||||
// Apply the joined effects of continue completions to the current state since these now join the normal path
|
||||
let joinedContinueEffects = Join.extractAndJoinCompletionsOfType(ContinueCompletion, realm, abruptCompletion);
|
||||
realm.applyEffects(joinedContinueEffects);
|
||||
let c = joinedContinueEffects.result;
|
||||
invariant(c instanceof ContinueCompletion);
|
||||
|
||||
// We now make a PossiblyNormalCompletion out of abruptCompletion.
|
||||
// extractAndJoinCompletionsOfType helped with this by cheating and turning all of its nested completions
|
||||
// that contain continue completions into PossiblyNormalCompletions.
|
||||
let remainingCompletions = abruptCompletion.transferChildrenToPossiblyNormalCompletion();
|
||||
|
||||
// At this stage there can still be other kinds of abrupt completions left inside abruptCompletion. If not just return.
|
||||
let stillAbrupt =
|
||||
remainingCompletions.containsCompletion(BreakCompletion) ||
|
||||
remainingCompletions.containsCompletion(ReturnCompletion) ||
|
||||
remainingCompletions.containsCompletion(ThrowCompletion);
|
||||
if (!stillAbrupt) return c;
|
||||
|
||||
// Stash the remaining completions in the realm start tracking the effects that need to be appended
|
||||
// to the normal branch at the next join point.
|
||||
realm.savedCompletion = remainingCompletions;
|
||||
realm.captureEffects(remainingCompletions); // so that we can join the normal path wtih them later on
|
||||
return c;
|
||||
}
|
||||
|
||||
function joinAllLoopExits(valueOrCompletionAtUnconditionalExit: Value | AbruptCompletion): Value {
|
||||
// We are about the leave this loop and this presents a join point where all loop breaking control flows
|
||||
// converge into a single flow using their joined effects as the new state.
|
||||
failIfContainsBreakOrContinueCompletionWithNonLocalTarget(realm.savedCompletion);
|
||||
|
||||
// Incorporate the savedCompletion if there is one.
|
||||
if (valueOrCompletionAtUnconditionalExit instanceof Value)
|
||||
valueOrCompletionAtUnconditionalExit = new BreakCompletion(valueOrCompletionAtUnconditionalExit, undefined);
|
||||
let abruptCompletion = Functions.incorporateSavedCompletion(realm, valueOrCompletionAtUnconditionalExit);
|
||||
invariant(abruptCompletion instanceof AbruptCompletion);
|
||||
|
||||
// If there is now a single completion, we don't need to join
|
||||
if (abruptCompletion instanceof BreakCompletion) return (UpdateEmpty(realm, abruptCompletion, V): any).value;
|
||||
if (!(abruptCompletion instanceof ForkedAbruptCompletion)) throw abruptCompletion;
|
||||
|
||||
// If there are no breaks, we don't need to join
|
||||
if (!abruptCompletion.containsCompletion(BreakCompletion)) throw abruptCompletion;
|
||||
|
||||
// Apply the joined effects of break completions to the current state since these now join the normal path
|
||||
let joinedBreakEffects = Join.extractAndJoinCompletionsOfType(BreakCompletion, realm, abruptCompletion);
|
||||
realm.applyEffects(joinedBreakEffects);
|
||||
let c = joinedBreakEffects.result;
|
||||
invariant(c instanceof BreakCompletion);
|
||||
|
||||
// We now make a PossiblyNormalCompletion out of abruptCompletion.
|
||||
// extractAndJoinCompletionsOfType helped with this by cheating and turning all of its nested completions
|
||||
// that contain continue completions into PossiblyNormalCompletions.
|
||||
let remainingCompletions = abruptCompletion.transferChildrenToPossiblyNormalCompletion();
|
||||
|
||||
// At this stage there can still be other kinds of abrupt completions left inside abruptCompletion. If not just return.
|
||||
let stillAbrupt =
|
||||
remainingCompletions.containsCompletion(ReturnCompletion) ||
|
||||
remainingCompletions.containsCompletion(ThrowCompletion);
|
||||
if (!stillAbrupt) return (UpdateEmpty(realm, c, V): any).value;
|
||||
|
||||
// Stash the remaining completions in the realm start tracking the effects that need to be appended
|
||||
// to the normal branch at the next join point.
|
||||
realm.savedCompletion = remainingCompletions;
|
||||
realm.captureEffects(remainingCompletions); // so that we can join the normal path wtih them later on
|
||||
|
||||
// ECMA262 13.1.7
|
||||
return (UpdateEmpty(realm, c, V): any).value;
|
||||
}
|
||||
}
|
||||
|
||||
let BailOutWrapperClosureRefVisitor = {
|
||||
@ -490,22 +373,8 @@ function tryToEvaluateForStatementOrLeaveAsAbstract(
|
||||
} finally {
|
||||
realm.suppressDiagnostics = savedSuppressDiagnostics;
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in effects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(effects.result);
|
||||
}
|
||||
|
||||
// ECMA262 13.7.4.7
|
||||
|
@ -13,7 +13,12 @@ import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import type { Reference } from "../environment.js";
|
||||
import { BreakCompletion } from "../completions.js";
|
||||
import {
|
||||
BreakCompletion,
|
||||
Completion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
} from "../completions.js";
|
||||
import type { BabelNode, BabelNodeLabeledStatement, BabelNodeVariableDeclaration } from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
@ -44,6 +49,15 @@ function LabelledEvaluation(
|
||||
if (stmtResult instanceof BreakCompletion && stmtResult.target === label) {
|
||||
// a. Let stmtResult be NormalCompletion(stmtResult.[[Value]]).
|
||||
normalCompletionStmtResult = stmtResult.value;
|
||||
} else if (
|
||||
stmtResult instanceof JoinedAbruptCompletions ||
|
||||
stmtResult instanceof JoinedNormalAndAbruptCompletions
|
||||
) {
|
||||
let nc = Completion.normalizeSelectedCompletions(
|
||||
c => c instanceof BreakCompletion && c.target === label,
|
||||
stmtResult
|
||||
);
|
||||
return realm.returnOrThrowCompletion(nc);
|
||||
} else {
|
||||
// 5. Return Completion(stmtResult).
|
||||
throw stmtResult;
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import type { Realm } from "../realm.js";
|
||||
import { Effects } from "../realm.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { SimpleNormalCompletion } from "../completions.js";
|
||||
import { InfeasiblePathError } from "../errors.js";
|
||||
import { construct_empty_effects } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
@ -89,22 +89,14 @@ export default function(
|
||||
// use lval as is for the join condition.
|
||||
let joinedEffects;
|
||||
if (ast.operator === "&&") {
|
||||
joinedEffects = Join.joinForkOrChoose(
|
||||
realm,
|
||||
lval,
|
||||
new Effects(
|
||||
result2.shallowCloneWithoutEffects(),
|
||||
generator2,
|
||||
modifiedBindings2,
|
||||
modifiedProperties2,
|
||||
createdObjects2
|
||||
),
|
||||
joinedEffects = Join.joinEffects(
|
||||
lcond,
|
||||
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2),
|
||||
new Effects(new SimpleNormalCompletion(lval), generator1, modifiedBindings1, modifiedProperties1, createdObjects1)
|
||||
);
|
||||
} else {
|
||||
joinedEffects = Join.joinForkOrChoose(
|
||||
realm,
|
||||
lval,
|
||||
joinedEffects = Join.joinEffects(
|
||||
lcond,
|
||||
new Effects(
|
||||
new SimpleNormalCompletion(lval),
|
||||
generator1,
|
||||
@ -112,38 +104,18 @@ export default function(
|
||||
modifiedProperties1,
|
||||
createdObjects1
|
||||
),
|
||||
new Effects(
|
||||
result2.shallowCloneWithoutEffects(),
|
||||
generator2,
|
||||
modifiedBindings2,
|
||||
modifiedProperties2,
|
||||
createdObjects2
|
||||
)
|
||||
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2)
|
||||
);
|
||||
}
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case the evaluation of ast.right may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
if (result2 instanceof SimpleNormalCompletion) result2 = result2.value;
|
||||
invariant(completion instanceof Value);
|
||||
if (lval instanceof Value && result2 instanceof Value) {
|
||||
// joinForkOrChoose does the right thing for the side effects of the second expression but for the result the join
|
||||
realm.applyEffects(joinedEffects);
|
||||
let completion = realm.returnOrThrowCompletion(joinedEffects.result);
|
||||
if (lval instanceof Value && result2.value instanceof Value) {
|
||||
// joinEffects does the right thing for the side effects of the second expression but for the result the join
|
||||
// produces a conditional expressions of the form (a ? b : a) for a && b and (a ? a : b) for a || b
|
||||
// Rather than look for this pattern everywhere, we override this behavior and replace the completion with
|
||||
// the actual logical operator. This helps with simplification and reasoning when dealing with path conditions.
|
||||
completion = AbstractValue.createFromLogicalOp(realm, ast.operator, lval, result2, ast.loc);
|
||||
completion = AbstractValue.createFromLogicalOp(realm, ast.operator, lval, result2.value, ast.loc);
|
||||
}
|
||||
return completion;
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { TypesDomain, ValuesDomain } from "../domains/index.js";
|
||||
import { ObjectValue, Value, AbstractObjectValue, AbstractValue } from "../values/index.js";
|
||||
import { Environment, Havoc } from "../singletons.js";
|
||||
@ -113,21 +112,8 @@ function tryToEvaluateConstructOrLeaveAsAbstract(
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
let completion = realm.returnOrThrowCompletion(effects.result);
|
||||
invariant(completion instanceof ObjectValue || completion instanceof AbstractObjectValue);
|
||||
return completion;
|
||||
}
|
||||
|
@ -9,13 +9,18 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import { AbruptCompletion, ForkedAbruptCompletion, PossiblyNormalCompletion, ThrowCompletion } from "../completions.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Completion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
ThrowCompletion,
|
||||
} from "../completions.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { Value, EmptyValue } from "../values/index.js";
|
||||
import { GlobalEnvironmentRecord } from "../environment.js";
|
||||
import { Environment, Functions, Join } from "../singletons.js";
|
||||
import { Generator } from "../utils/generator.js";
|
||||
import IsStrict from "../utils/strict.js";
|
||||
import invariant from "../invariant.js";
|
||||
import traverseFast from "../utils/traverse-fast.js";
|
||||
@ -223,30 +228,17 @@ export default function(ast: BabelNodeProgram, strictCode: boolean, env: Lexical
|
||||
|
||||
GlobalDeclarationInstantiation(realm, ast, env, strictCode);
|
||||
|
||||
let val;
|
||||
let val, res;
|
||||
|
||||
for (let node of ast.body) {
|
||||
if (node.type !== "FunctionDeclaration") {
|
||||
let res = env.evaluateCompletionDeref(node, strictCode);
|
||||
if (res instanceof AbruptCompletion) {
|
||||
if (!realm.useAbstractInterpretation) throw res;
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
// We are about the leave this program and this presents a join point where all control flows
|
||||
// converge into a single flow using the joined effects as the new state.
|
||||
res = Functions.incorporateSavedCompletion(realm, res);
|
||||
if (res instanceof ForkedAbruptCompletion && res.containsCompletion(ThrowCompletion)) {
|
||||
// The global state is now at the point where the first fork occurred.
|
||||
let joinedEffects = Join.joinNestedEffects(realm, res);
|
||||
realm.applyEffects(joinedEffects);
|
||||
res = joinedEffects.result;
|
||||
} else if (res instanceof ThrowCompletion) {
|
||||
generator.emitThrow(res.value);
|
||||
res = realm.intrinsics.undefined;
|
||||
} else {
|
||||
invariant(false); // other kinds of abrupt completions should not get this far
|
||||
}
|
||||
break;
|
||||
res = env.evaluateCompletionDeref(node, strictCode);
|
||||
if (res instanceof AbruptCompletion && !realm.useAbstractInterpretation) throw res;
|
||||
res = Functions.incorporateSavedCompletion(realm, res);
|
||||
if (res instanceof Completion) {
|
||||
emitThrowStatementsIfNeeded(res);
|
||||
if (res instanceof ThrowCompletion) return res.value; // Program ends here at runtime, so don't carry on
|
||||
res = res.value;
|
||||
}
|
||||
if (!(res instanceof EmptyValue)) {
|
||||
val = res;
|
||||
@ -262,35 +254,33 @@ export default function(ast: BabelNodeProgram, strictCode: boolean, env: Lexical
|
||||
|
||||
// We are about to leave this program and this presents a join point where all control flows
|
||||
// converge into a single flow and the joined effects become the final state.
|
||||
invariant(val === undefined || val instanceof Value);
|
||||
if (val instanceof Value) {
|
||||
let res = Functions.incorporateSavedCompletion(realm, val);
|
||||
if (res instanceof PossiblyNormalCompletion) {
|
||||
// Get state to be joined in
|
||||
let e = realm.getCapturedEffects();
|
||||
realm.stopEffectCaptureAndUndoEffects(res);
|
||||
// The global state is now at the point where the last fork occurred.
|
||||
if (res.containsCompletion(ThrowCompletion)) {
|
||||
// Join e with the remaining completions
|
||||
let normalGenerator = e.generator;
|
||||
e.generator = new Generator(realm, "dummy", normalGenerator.pathConditions); // This generator comes after everything else.
|
||||
let r = new ThrowCompletion(realm.intrinsics.empty, e);
|
||||
let fc = Join.replacePossiblyNormalCompletionWithForkedAbruptCompletion(realm, res, r, e);
|
||||
let allEffects = Join.extractAndJoinCompletionsOfType(ThrowCompletion, realm, fc);
|
||||
realm.applyEffects(allEffects, "all code", true);
|
||||
r = allEffects.result;
|
||||
invariant(r instanceof ThrowCompletion);
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
generator.emitConditionalThrow(r.value);
|
||||
realm.appendGenerator(normalGenerator);
|
||||
} else {
|
||||
realm.applyEffects(e, "all code", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// program was empty. Nothing to do.
|
||||
res = Functions.incorporateSavedCompletion(realm, val);
|
||||
if (res instanceof Completion) emitThrowStatementsIfNeeded(res);
|
||||
}
|
||||
|
||||
invariant(val === undefined || val instanceof Value);
|
||||
return val || realm.intrinsics.empty;
|
||||
|
||||
function emitThrowStatementsIfNeeded(completion: Completion): void {
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
if (
|
||||
res instanceof ThrowCompletion &&
|
||||
res.value !== realm.intrinsics.__bottomValue &&
|
||||
!(res.value instanceof EmptyValue)
|
||||
) {
|
||||
generator.emitThrow(res.value);
|
||||
} else if (
|
||||
(res instanceof JoinedAbruptCompletions || res instanceof JoinedNormalAndAbruptCompletions) &&
|
||||
res.containsSelectedCompletion(c => c instanceof ThrowCompletion)
|
||||
) {
|
||||
let selector = c =>
|
||||
c instanceof ThrowCompletion && c.value !== realm.intrinsics.__bottomValue && !(c.value instanceof EmptyValue);
|
||||
generator.emitConditionalThrow(Join.joinValuesOfSelectedCompletions(selector, res));
|
||||
res = realm.intrinsics.undefined;
|
||||
} else {
|
||||
invariant(res instanceof Value); // other kinds of abrupt completions should not get this far
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,5 +28,5 @@ export default function(
|
||||
} else {
|
||||
arg = realm.intrinsics.undefined;
|
||||
}
|
||||
throw new ReturnCompletion(arg, undefined, ast.loc);
|
||||
throw new ReturnCompletion(arg, ast.loc);
|
||||
}
|
||||
|
@ -11,21 +11,19 @@
|
||||
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { CompilerDiagnostic, InfeasiblePathError } from "../errors.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { InfeasiblePathError } from "../errors.js";
|
||||
import { computeBinary } from "./BinaryExpression.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
BreakCompletion,
|
||||
SimpleNormalCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
Completion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
} from "../completions.js";
|
||||
import { InternalGetResultValue } from "./ForOfStatement.js";
|
||||
import { EmptyValue, AbstractValue, Value } from "../values/index.js";
|
||||
import { StrictEqualityComparisonPartial, UpdateEmpty } from "../methods/index.js";
|
||||
import { Environment, Path, Join } from "../singletons.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import { Environment, Functions, Join, Path } from "../singletons.js";
|
||||
import type { BabelNodeSwitchStatement, BabelNodeSwitchCase, BabelNodeExpression } from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
@ -62,19 +60,10 @@ function AbstractCaseBlockEvaluation(
|
||||
let c = cases[caseIndex];
|
||||
for (let i = 0; i < c.consequent.length; i += 1) {
|
||||
let node = c.consequent[i];
|
||||
let r = env.evaluateCompletion(node, strictCode);
|
||||
invariant(!(r instanceof Reference));
|
||||
let r = env.evaluateCompletionDeref(node, strictCode);
|
||||
|
||||
if (r instanceof PossiblyNormalCompletion) {
|
||||
// TODO correct handling of PossiblyNormal and AbruptCompletion
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
"case block containing a throw, return or continue is not yet supported",
|
||||
r.location,
|
||||
"PP0027",
|
||||
"FatalError"
|
||||
);
|
||||
realm.handleError(diagnostic);
|
||||
throw new FatalError();
|
||||
if (r instanceof JoinedNormalAndAbruptCompletions) {
|
||||
r = realm.composeWithSavedCompletion(r);
|
||||
}
|
||||
|
||||
result = UpdateEmpty(realm, r, result);
|
||||
@ -84,19 +73,21 @@ function AbstractCaseBlockEvaluation(
|
||||
if (result instanceof Completion) break;
|
||||
caseIndex++;
|
||||
}
|
||||
let sc = Functions.incorporateSavedCompletion(realm, result);
|
||||
invariant(sc !== undefined);
|
||||
result = sc;
|
||||
|
||||
if (result instanceof BreakCompletion) {
|
||||
if (result instanceof JoinedAbruptCompletions || result instanceof JoinedNormalAndAbruptCompletions) {
|
||||
let selector = c => c instanceof BreakCompletion && !c.target;
|
||||
let jc = AbstractValue.createJoinConditionForSelectedCompletions(selector, result);
|
||||
let jv = AbstractValue.createFromConditionalOp(realm, jc, realm.intrinsics.empty, result.value);
|
||||
result = Completion.normalizeSelectedCompletions(selector, result);
|
||||
realm.composeWithSavedCompletion(result);
|
||||
return jv;
|
||||
} else if (result instanceof BreakCompletion) {
|
||||
return result.value;
|
||||
} else if (result instanceof AbruptCompletion) {
|
||||
// TODO correct handling of PossiblyNormal and AbruptCompletion
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
"case block containing a throw, return or continue is not yet supported",
|
||||
result.location,
|
||||
"PP0027",
|
||||
"FatalError"
|
||||
);
|
||||
realm.handleError(diagnostic);
|
||||
throw new FatalError();
|
||||
throw result;
|
||||
} else {
|
||||
invariant(result instanceof Value);
|
||||
return result;
|
||||
@ -177,26 +168,10 @@ function AbstractCaseBlockEvaluation(
|
||||
|
||||
invariant(trueEffects !== undefined);
|
||||
invariant(falseEffects !== undefined);
|
||||
let joinedEffects = Join.joinForkOrChoose(realm, selectionResult, trueEffects, falseEffects);
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
let joinedEffects = Join.joinEffects(selectionResult, trueEffects, falseEffects);
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) {
|
||||
completion = completion.value;
|
||||
}
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(joinedEffects.result);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -24,5 +24,5 @@ export default function(
|
||||
): Value {
|
||||
let exprRef = env.evaluate(ast.argument, strictCode);
|
||||
let exprValue = Environment.GetValue(realm, exprRef);
|
||||
throw new ThrowCompletion(exprValue, undefined, ast.loc);
|
||||
throw new ThrowCompletion(exprValue, ast.loc);
|
||||
}
|
||||
|
@ -9,172 +9,96 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { Effects, Realm } from "../realm.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import { type LexicalEnvironment } from "../environment.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
ForkedAbruptCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
Completion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
ThrowCompletion,
|
||||
SimpleNormalCompletion,
|
||||
NormalCompletion,
|
||||
} from "../completions.js";
|
||||
import { UpdateEmpty } from "../methods/index.js";
|
||||
import { Functions, Join } from "../singletons.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import { InfeasiblePathError } from "../errors.js";
|
||||
import { construct_empty_effects } from "../realm.js";
|
||||
import { Functions, Join, Path } from "../singletons.js";
|
||||
import { AbstractValue, Value } from "../values/index.js";
|
||||
import type { BabelNodeTryStatement } from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: LexicalEnvironment, realm: Realm): Value {
|
||||
let wasInPureTryStatement = realm.isInPureTryStatement;
|
||||
if (realm.useAbstractInterpretation) return joinTryBlockWithHandlers(ast, strictCode, env, realm);
|
||||
|
||||
let blockRes = env.evaluateCompletionDeref(ast.block, strictCode);
|
||||
let result = blockRes;
|
||||
|
||||
if (blockRes instanceof ThrowCompletion && ast.handler) {
|
||||
result = env.evaluateCompletionDeref(ast.handler, strictCode, blockRes);
|
||||
}
|
||||
|
||||
if (ast.finalizer) {
|
||||
result = composeResults(result, env.evaluateCompletionDeref(ast.finalizer, strictCode));
|
||||
}
|
||||
|
||||
return realm.returnOrThrowCompletion(UpdateEmpty(realm, result, realm.intrinsics.undefined));
|
||||
}
|
||||
|
||||
function composeResults(r1: Completion | Value, r2: Completion | Value): Completion | Value {
|
||||
if (r2 instanceof AbruptCompletion) return r2;
|
||||
return Join.composeCompletions(r2, r1);
|
||||
}
|
||||
|
||||
function joinTryBlockWithHandlers(
|
||||
ast: BabelNodeTryStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): Value {
|
||||
let savedIsInPureTryStatement = realm.isInPureTryStatement;
|
||||
if (realm.isInPureScope()) {
|
||||
// TODO(1264): This is used to issue a warning if we have abstract function calls in here.
|
||||
// We might not need it once we have full support for handling potential errors. Even
|
||||
// then we might need it to know whether we should bother tracking error handling.
|
||||
realm.isInPureTryStatement = true;
|
||||
}
|
||||
let blockRes;
|
||||
try {
|
||||
blockRes = env.evaluateCompletionDeref(ast.block, strictCode);
|
||||
} finally {
|
||||
realm.isInPureTryStatement = wasInPureTryStatement;
|
||||
}
|
||||
let blockRes = env.evaluateCompletionDeref(ast.block, strictCode);
|
||||
// this is a join point for break and continue completions
|
||||
blockRes = Functions.incorporateSavedCompletion(realm, blockRes);
|
||||
invariant(blockRes !== undefined);
|
||||
realm.isInPureTryStatement = savedIsInPureTryStatement;
|
||||
|
||||
let handlerRes = blockRes;
|
||||
let result = blockRes;
|
||||
let handler = ast.handler;
|
||||
if (handler) {
|
||||
// The start of the catch handler is a join point where all throw completions come together
|
||||
blockRes = Functions.incorporateSavedCompletion(realm, blockRes);
|
||||
let selector = c => c instanceof ThrowCompletion;
|
||||
if (handler && blockRes instanceof Completion && blockRes.containsSelectedCompletion(selector)) {
|
||||
if (blockRes instanceof ThrowCompletion) {
|
||||
handlerRes = env.evaluateCompletionDeref(handler, strictCode, blockRes);
|
||||
// Note: The handler may have introduced new forks
|
||||
} else if (blockRes instanceof ForkedAbruptCompletion || blockRes instanceof PossiblyNormalCompletion) {
|
||||
if (blockRes instanceof PossiblyNormalCompletion) {
|
||||
// The throw completions have not been joined and we are going to keep it that way.
|
||||
// The current state may have advanced since the time control forked into the various paths recorded in blockRes.
|
||||
// Update the normal path and restore the global state to what it was at the time of the fork.
|
||||
let subsequentEffects = realm.getCapturedEffects(blockRes.value);
|
||||
realm.stopEffectCaptureAndUndoEffects(blockRes);
|
||||
Join.updatePossiblyNormalCompletionWithSubsequentEffects(realm, blockRes, subsequentEffects);
|
||||
result = env.evaluateCompletionDeref(handler, strictCode, blockRes);
|
||||
} else {
|
||||
invariant(blockRes instanceof JoinedAbruptCompletions || blockRes instanceof JoinedNormalAndAbruptCompletions);
|
||||
// put the handler under a guard that excludes normal paths from entering it.
|
||||
let joinCondition = AbstractValue.createJoinConditionForSelectedCompletions(selector, blockRes);
|
||||
try {
|
||||
let handlerEffects = Path.withCondition(joinCondition, () => {
|
||||
invariant(blockRes instanceof Completion);
|
||||
let joinedThrow = new ThrowCompletion(Join.joinValuesOfSelectedCompletions(selector, blockRes));
|
||||
let handlerEval = () => env.evaluateCompletionDeref(handler, strictCode, joinedThrow);
|
||||
return realm.evaluateForEffects(handlerEval, undefined, "joinTryBlockWithHandlers");
|
||||
});
|
||||
Completion.makeSelectedCompletionsInfeasible(selector, blockRes);
|
||||
let emptyEffects = construct_empty_effects(realm, blockRes);
|
||||
handlerEffects = Join.joinEffects(joinCondition, handlerEffects, emptyEffects);
|
||||
realm.applyEffects(handlerEffects);
|
||||
result = handlerEffects.result;
|
||||
} catch (e) {
|
||||
if (!(e instanceof InfeasiblePathError)) throw e;
|
||||
// It turns out that the handler is not reachable after all so just do nothing and carry on
|
||||
}
|
||||
// Add effects of normal exits from handler to blockRes and apply to global state
|
||||
let handlerEffects = composeNestedThrowEffectsWithHandler(blockRes);
|
||||
realm.applyEffects(handlerEffects);
|
||||
handlerRes = handlerEffects.result;
|
||||
} else {
|
||||
// The handler is not invoked, so just carry on.
|
||||
}
|
||||
}
|
||||
|
||||
let finalizerRes = handlerRes;
|
||||
if (ast.finalizer) {
|
||||
// The start of the finalizer is a join point where all threads of control come together.
|
||||
// However, we choose to keep the threads unjoined and to apply the finalizer separately to each thread.
|
||||
if (blockRes instanceof PossiblyNormalCompletion || blockRes instanceof ForkedAbruptCompletion) {
|
||||
// The current global state is a the point of the fork that led to blockRes
|
||||
// All subsequent effects are kept inside the branches of blockRes.
|
||||
let finalizerEffects = composeNestedEffectsWithFinalizer(blockRes);
|
||||
finalizerRes = finalizerEffects.result;
|
||||
// The result may become abrupt because of the finalizer, but it cannot become normal.
|
||||
invariant(!(finalizerRes instanceof SimpleNormalCompletion));
|
||||
} else {
|
||||
// A single thread of control has produced a normal blockRes and the global state is up to date.
|
||||
finalizerRes = env.evaluateCompletion(ast.finalizer, strictCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (finalizerRes instanceof AbruptCompletion) throw finalizerRes;
|
||||
if (finalizerRes instanceof PossiblyNormalCompletion) realm.composeWithSavedCompletion(finalizerRes);
|
||||
if (handlerRes instanceof NormalCompletion) handlerRes = handlerRes.value;
|
||||
if (handlerRes instanceof Value) return (UpdateEmpty(realm, handlerRes, realm.intrinsics.undefined): any);
|
||||
throw handlerRes;
|
||||
|
||||
// The handler is a potential join point for all throw completions, but is easier to not do the join here because
|
||||
// it is tricky to join the joined and composed result of the throw completions with the non exceptional completions.
|
||||
// Unfortunately, things are still complicated because the handler may turn abrupt completions into normal
|
||||
// completions and the other way around. When this happens the container has to change its type.
|
||||
// We do this by call joinForkOrChoose to create a new container at every level of the recursion.
|
||||
function composeNestedThrowEffectsWithHandler(
|
||||
c: PossiblyNormalCompletion | ForkedAbruptCompletion,
|
||||
priorEffects: Array<Effects> = []
|
||||
): Effects {
|
||||
let consequent = c.consequent;
|
||||
let consequentEffects = c.consequentEffects;
|
||||
priorEffects.push(consequentEffects);
|
||||
if (consequent instanceof ForkedAbruptCompletion || consequent instanceof PossiblyNormalCompletion) {
|
||||
consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
|
||||
} else if (consequent instanceof ThrowCompletion) {
|
||||
consequentEffects = realm.evaluateForEffectsWithPriorEffects(
|
||||
priorEffects,
|
||||
() => {
|
||||
invariant(ast.handler);
|
||||
return env.evaluateCompletionDeref(ast.handler, strictCode, consequent);
|
||||
},
|
||||
"composeNestedThrowEffectsWithHandler/1"
|
||||
);
|
||||
}
|
||||
priorEffects.pop();
|
||||
let alternate = c.alternate;
|
||||
let alternateEffects = c.alternateEffects;
|
||||
priorEffects.push(alternateEffects);
|
||||
if (alternate instanceof PossiblyNormalCompletion || alternate instanceof ForkedAbruptCompletion) {
|
||||
alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
|
||||
} else if (alternate instanceof ThrowCompletion) {
|
||||
alternateEffects = realm.evaluateForEffectsWithPriorEffects(
|
||||
priorEffects,
|
||||
() => {
|
||||
invariant(ast.handler);
|
||||
return env.evaluateCompletionDeref(ast.handler, strictCode, alternate);
|
||||
},
|
||||
"composeNestedThrowEffectsWithHandler/2"
|
||||
);
|
||||
}
|
||||
priorEffects.pop();
|
||||
return Join.joinForkOrChoose(realm, c.joinCondition, consequentEffects, alternateEffects);
|
||||
}
|
||||
|
||||
// The finalizer is not a join point, so update each path in the completion separately.
|
||||
// Things are complicated because the finalizer may turn normal completions into abrupt completions.
|
||||
// When this happens the container has to change its type.
|
||||
// We do this by call joinForkOrChoose to create a new container at every level of the recursion.
|
||||
function composeNestedEffectsWithFinalizer(
|
||||
c: PossiblyNormalCompletion | ForkedAbruptCompletion,
|
||||
priorEffects: Array<Effects> = []
|
||||
): Effects {
|
||||
let consequent = c.consequent;
|
||||
let consequentEffects = c.consequentEffects;
|
||||
priorEffects.push(consequentEffects);
|
||||
if (consequent instanceof ForkedAbruptCompletion || consequent instanceof PossiblyNormalCompletion) {
|
||||
consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
|
||||
} else {
|
||||
consequentEffects = realm.evaluateForEffectsWithPriorEffects(
|
||||
priorEffects,
|
||||
() => {
|
||||
invariant(ast.finalizer);
|
||||
return env.evaluateCompletionDeref(ast.finalizer, strictCode);
|
||||
},
|
||||
"composeNestedEffectsWithFinalizer/1"
|
||||
);
|
||||
if (!(consequentEffects.result instanceof AbruptCompletion)) consequentEffects.result = consequent;
|
||||
}
|
||||
priorEffects.pop();
|
||||
let alternate = c.alternate;
|
||||
let alternateEffects = c.alternateEffects;
|
||||
priorEffects.push(alternateEffects);
|
||||
if (alternate instanceof PossiblyNormalCompletion || alternate instanceof ForkedAbruptCompletion) {
|
||||
alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
|
||||
} else {
|
||||
alternateEffects = realm.evaluateForEffectsWithPriorEffects(
|
||||
priorEffects,
|
||||
() => {
|
||||
invariant(ast.finalizer);
|
||||
return env.evaluateCompletionDeref(ast.finalizer, strictCode);
|
||||
},
|
||||
"composeNestedEffectsWithFinalizer/2"
|
||||
);
|
||||
if (!(alternateEffects.result instanceof AbruptCompletion)) alternateEffects.result = alternate;
|
||||
}
|
||||
priorEffects.pop();
|
||||
return Join.joinForkOrChoose(realm, c.joinCondition, consequentEffects, alternateEffects);
|
||||
let res = env.evaluateCompletionDeref(ast.finalizer, strictCode);
|
||||
result = composeResults(result, res);
|
||||
}
|
||||
return realm.returnOrThrowCompletion(UpdateEmpty(realm, result, realm.intrinsics.undefined));
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
//import { SimpleNormalCompletion } from "../completions.js";
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import { TypesDomain, ValuesDomain } from "../domains/index.js";
|
||||
import {
|
||||
@ -129,23 +129,8 @@ function tryToEvaluateOperationOrLeaveAsAbstract(
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(effects.result);
|
||||
}
|
||||
|
||||
function evaluateOperation(
|
||||
|
@ -30,7 +30,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
|
||||
let g = context;
|
||||
|
||||
// 2. Let C be Completion{[[Type]]: return, [[Value]]: value, [[Target]]: empty}.
|
||||
let C = new ReturnCompletion(value, undefined, realm.currentLocation);
|
||||
let C = new ReturnCompletion(value, realm.currentLocation);
|
||||
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C).
|
||||
return GeneratorResumeAbrupt(realm, g, C);
|
||||
@ -42,7 +42,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
|
||||
let g = context;
|
||||
|
||||
// 2. Let C be Completion{[[Type]]: throw, [[Value]]: exception, [[Target]]: empty}.
|
||||
let C = new ReturnCompletion(exception, undefined, realm.currentLocation);
|
||||
let C = new ReturnCompletion(exception, realm.currentLocation);
|
||||
|
||||
// 3. Return ? GeneratorResumeAbrupt(g, C).
|
||||
return GeneratorResumeAbrupt(realm, g, C);
|
||||
|
@ -13,7 +13,7 @@ import { TypesDomain, ValuesDomain } from "../../domains/index.js";
|
||||
import { FatalError } from "../../errors.js";
|
||||
import { Realm } from "../../realm.js";
|
||||
import { NativeFunctionValue } from "../../values/index.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion } from "../../completions.js";
|
||||
//import { AbruptCompletion } from "../../completions.js";
|
||||
import {
|
||||
AbstractValue,
|
||||
AbstractObjectValue,
|
||||
@ -190,19 +190,8 @@ function tryAndApplySourceOrRecover(
|
||||
} finally {
|
||||
realm.suppressDiagnostics = savedSuppressDiagnostics;
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in effects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
realm.returnOrThrowCompletion(effects.result);
|
||||
return to_must_be_partial;
|
||||
}
|
||||
|
||||
|
@ -503,7 +503,7 @@ export default function(realm: Realm, obj: ObjectValue): ObjectValue {
|
||||
|
||||
// 7. Search string for the first occurrence of searchString and
|
||||
// let pos be the index within string of the first code unit of the matched substring and
|
||||
let pos = string.search(searchString);
|
||||
let pos = string.indexOf(searchString);
|
||||
|
||||
// let matched be searchString.
|
||||
let matched = searchString;
|
||||
|
@ -9,18 +9,20 @@
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import { TypesDomain, ValuesDomain } from "../domains/index.js";
|
||||
import type { Intrinsics } from "../types.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import {
|
||||
NumberValue,
|
||||
StringValue,
|
||||
NullValue,
|
||||
UndefinedValue,
|
||||
EmptyValue,
|
||||
ObjectValue,
|
||||
SymbolValue,
|
||||
AbstractValue,
|
||||
BooleanValue,
|
||||
EmptyValue,
|
||||
NativeFunctionValue,
|
||||
NullValue,
|
||||
NumberValue,
|
||||
ObjectValue,
|
||||
StringValue,
|
||||
SymbolValue,
|
||||
UndefinedValue,
|
||||
} from "../values/index.js";
|
||||
import { Functions } from "../singletons.js";
|
||||
|
||||
@ -467,5 +469,21 @@ export function initialize(i: Intrinsics, realm: Realm): Intrinsics {
|
||||
// 8.2.2, step 12
|
||||
Functions.AddRestrictedFunctionProperties(i.FunctionPrototype, realm);
|
||||
|
||||
//
|
||||
if (realm.useAbstractInterpretation) {
|
||||
TypesDomain.topVal = new TypesDomain(undefined);
|
||||
ValuesDomain.topVal = new ValuesDomain(undefined);
|
||||
i.__topValue = new AbstractValue(realm, TypesDomain.topVal, ValuesDomain.topVal, Number.MAX_SAFE_INTEGER, []);
|
||||
TypesDomain.bottomVal = new TypesDomain(EmptyValue);
|
||||
ValuesDomain.bottomVal = new ValuesDomain(new Set());
|
||||
i.__bottomValue = new AbstractValue(
|
||||
realm,
|
||||
TypesDomain.bottomVal,
|
||||
ValuesDomain.bottomVal,
|
||||
Number.MIN_SAFE_INTEGER,
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
@ -22,20 +22,28 @@ import { FatalError } from "../errors.js";
|
||||
import { Realm, ExecutionContext } from "../realm.js";
|
||||
import Value from "../values/Value.js";
|
||||
import {
|
||||
FunctionValue,
|
||||
ECMAScriptSourceFunctionValue,
|
||||
ObjectValue,
|
||||
NullValue,
|
||||
UndefinedValue,
|
||||
NativeFunctionValue,
|
||||
AbstractObjectValue,
|
||||
AbstractValue,
|
||||
ECMAScriptSourceFunctionValue,
|
||||
FunctionValue,
|
||||
NativeFunctionValue,
|
||||
NullValue,
|
||||
ObjectValue,
|
||||
UndefinedValue,
|
||||
} from "../values/index.js";
|
||||
import { GetIterator, HasSomeCompatibleType, IsCallable, IsPropertyKey, IteratorStep, IteratorValue } from "./index.js";
|
||||
import { GeneratorStart } from "./generator.js";
|
||||
import { ReturnCompletion, AbruptCompletion, ThrowCompletion, ForkedAbruptCompletion } from "../completions.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Completion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
NormalCompletion,
|
||||
ReturnCompletion,
|
||||
ThrowCompletion,
|
||||
} from "../completions.js";
|
||||
import { GetTemplateObject, GetV, GetThisValue } from "./get.js";
|
||||
import { Create, Environment, Functions, Join, Havoc, To, Widen } from "../singletons.js";
|
||||
import { Create, Environment, Functions, Havoc, Join, To, Widen } from "../singletons.js";
|
||||
import invariant from "../invariant.js";
|
||||
import { createOperationDescriptor } from "../utils/generator.js";
|
||||
import type { BabelNodeExpression, BabelNodeSpreadElement, BabelNodeTemplateLiteral } from "@babel/types";
|
||||
@ -291,7 +299,7 @@ function callNativeFunctionValue(
|
||||
realm: Realm,
|
||||
f: NativeFunctionValue,
|
||||
argumentsList: Array<Value>
|
||||
): Value | AbruptCompletion {
|
||||
): void | AbruptCompletion {
|
||||
let env = realm.getRunningContext().lexicalEnvironment;
|
||||
let context = env.environmentRecord.GetThisBinding();
|
||||
|
||||
@ -306,7 +314,7 @@ function callNativeFunctionValue(
|
||||
mightBecomeAnObject(contextVal)
|
||||
);
|
||||
let completion = f.callCallback(
|
||||
// this is to get around Flow not understanding the above invariant
|
||||
// TODO: this is not right. Either fix the type signature of callCallback or wrap contextVal in a coercion
|
||||
((contextVal: any): AbstractObjectValue | ObjectValue | NullValue | UndefinedValue),
|
||||
argumentsList,
|
||||
env.environmentRecord.$NewTarget
|
||||
@ -323,7 +331,7 @@ function callNativeFunctionValue(
|
||||
}
|
||||
};
|
||||
|
||||
const wrapInReturnCompletion = contextVal => new ReturnCompletion(contextVal, undefined, realm.currentLocation);
|
||||
const wrapInReturnCompletion = contextVal => new ReturnCompletion(contextVal, realm.currentLocation);
|
||||
|
||||
if (context instanceof AbstractObjectValue && context.kind === "conditional") {
|
||||
let [condValue, consequentVal, alternateVal] = context.args;
|
||||
@ -349,7 +357,9 @@ function callNativeFunctionValue(
|
||||
)
|
||||
);
|
||||
}
|
||||
return functionCall(context, false);
|
||||
let c = functionCall(context, false);
|
||||
if (c instanceof AbruptCompletion) return c;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ECMA262 9.2.1.3
|
||||
@ -357,7 +367,7 @@ export function OrdinaryCallEvaluateBody(
|
||||
realm: Realm,
|
||||
f: ECMAScriptFunctionValue,
|
||||
argumentsList: Array<Value>
|
||||
): Reference | Value | AbruptCompletion {
|
||||
): void | AbruptCompletion {
|
||||
if (f instanceof NativeFunctionValue) {
|
||||
return callNativeFunctionValue(realm, f, argumentsList);
|
||||
} else {
|
||||
@ -379,7 +389,7 @@ export function OrdinaryCallEvaluateBody(
|
||||
GeneratorStart(realm, G, code);
|
||||
|
||||
// 4. Return Completion{[[Type]]: return, [[Value]]: G, [[Target]]: empty}.
|
||||
return new ReturnCompletion(G, undefined, realm.currentLocation);
|
||||
return new ReturnCompletion(G, realm.currentLocation);
|
||||
} else {
|
||||
// TODO #1586: abstractRecursionSummarization is disabled for now, as it is likely too limiting
|
||||
// (as observed in large internal tests).
|
||||
@ -398,15 +408,15 @@ export function OrdinaryCallEvaluateBody(
|
||||
realm.applyEffects(effects);
|
||||
let c = effects.result;
|
||||
return processResult(() => {
|
||||
invariant(c instanceof Value || c instanceof AbruptCompletion);
|
||||
return c;
|
||||
if (c instanceof AbruptCompletion || c instanceof JoinedNormalAndAbruptCompletions) return c;
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
F.isSelfRecursive = savedIsSelfRecursive;
|
||||
}
|
||||
|
||||
function guardedCall() {
|
||||
function guardedCall(): Value | Completion {
|
||||
let currentLocation = realm.currentLocation;
|
||||
if (F.activeArguments !== undefined && F.activeArguments.has(currentLocation)) {
|
||||
let [previousPathLength, previousArguments] = F.activeArguments.get(currentLocation);
|
||||
@ -418,7 +428,7 @@ export function OrdinaryCallEvaluateBody(
|
||||
if (Widen.containsArraysOfValue(realm, previousArguments, widenedArgumentsList)) {
|
||||
// Reached a fixed point. Executing this call will not add any knowledge
|
||||
// about the effects of the original call.
|
||||
return AbstractValue.createFromType(realm, Value, "widened return result");
|
||||
return realm.intrinsics.undefined;
|
||||
} else {
|
||||
argumentsList = widenedArgumentsList;
|
||||
}
|
||||
@ -427,13 +437,13 @@ export function OrdinaryCallEvaluateBody(
|
||||
try {
|
||||
if (F.activeArguments === undefined) F.activeArguments = new Map();
|
||||
F.activeArguments.set(currentLocation, [realm.pathConditions.length, argumentsList]);
|
||||
return normalCall();
|
||||
return normalCall() || realm.intrinsics.undefined;
|
||||
} finally {
|
||||
F.activeArguments.delete(currentLocation);
|
||||
}
|
||||
}
|
||||
|
||||
function normalCall() {
|
||||
function normalCall(): void | AbruptCompletion {
|
||||
// 1. Perform ? FunctionDeclarationInstantiation(F, argumentsList).
|
||||
Functions.FunctionDeclarationInstantiation(realm, F, argumentsList);
|
||||
|
||||
@ -442,55 +452,47 @@ export function OrdinaryCallEvaluateBody(
|
||||
let code = F.$ECMAScriptCode;
|
||||
invariant(code !== undefined);
|
||||
let context = realm.getRunningContext();
|
||||
return processResult(() => context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict));
|
||||
return processResult(() => {
|
||||
let c = context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict);
|
||||
if (c instanceof AbruptCompletion || c instanceof JoinedNormalAndAbruptCompletions) return c;
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function processResult(getCompletion: () => AbruptCompletion | Value): AbruptCompletion | Value {
|
||||
function processResult(
|
||||
getCompletion: () => void | AbruptCompletion | JoinedNormalAndAbruptCompletions
|
||||
): void | AbruptCompletion {
|
||||
// We don't want the callee to see abrupt completions from the caller.
|
||||
let priorSavedCompletion = realm.savedCompletion;
|
||||
realm.savedCompletion = undefined;
|
||||
|
||||
let c;
|
||||
try {
|
||||
realm.savedCompletion = undefined;
|
||||
let c = getCompletion();
|
||||
|
||||
// We are about the leave this function and this presents a join point where all non exceptional control flows
|
||||
// converge into a single flow using their joint effects to update the post join point state.
|
||||
if (!(c instanceof ReturnCompletion)) {
|
||||
if (!(c instanceof AbruptCompletion)) {
|
||||
c = new ReturnCompletion(realm.intrinsics.undefined, undefined, realm.currentLocation);
|
||||
}
|
||||
}
|
||||
invariant(c instanceof AbruptCompletion);
|
||||
|
||||
// If there is a saved completion (i.e. unjoined abruptly completing control flows) then combine them with c
|
||||
let abruptCompletion = Functions.incorporateSavedCompletion(realm, c);
|
||||
invariant(abruptCompletion instanceof AbruptCompletion);
|
||||
|
||||
// If there is single completion, we don't need to join
|
||||
if (!(abruptCompletion instanceof ForkedAbruptCompletion)) return abruptCompletion;
|
||||
|
||||
// If none of the completions are return completions, there is no need to join either
|
||||
if (!abruptCompletion.containsCompletion(ReturnCompletion)) return abruptCompletion;
|
||||
|
||||
// Apply the joined effects of return completions to the current state since these now join the normal path
|
||||
let joinedReturnEffects = Join.extractAndJoinCompletionsOfType(ReturnCompletion, realm, abruptCompletion);
|
||||
realm.applyEffects(joinedReturnEffects);
|
||||
c = joinedReturnEffects.result;
|
||||
invariant(c instanceof ReturnCompletion);
|
||||
|
||||
// We now make a PossiblyNormalCompletion out of abruptCompletion.
|
||||
// extractAndJoinCompletionsOfType helped with this by cheating and turning all of its nested completions
|
||||
// that contain return completions into PossiblyNormalCompletions.
|
||||
let remainingCompletions = abruptCompletion.transferChildrenToPossiblyNormalCompletion();
|
||||
|
||||
// If there are no throw completions left inside remainingCompletions, just return.
|
||||
if (!remainingCompletions.containsCompletion(ThrowCompletion)) return c;
|
||||
|
||||
// Stash the remaining completions in the realm start tracking the effects that need to be appended
|
||||
// to the normal branch at the next join point.
|
||||
realm.composeWithSavedCompletion(remainingCompletions);
|
||||
return c;
|
||||
} finally {
|
||||
realm.incorporatePriorSavedCompletion(priorSavedCompletion);
|
||||
c = getCompletion();
|
||||
} catch (e) {
|
||||
invariant(!(e instanceof AbruptCompletion));
|
||||
throw e;
|
||||
}
|
||||
c = Functions.incorporateSavedCompletion(realm, c); // in case the callee had conditional abrupt completions
|
||||
realm.savedCompletion = priorSavedCompletion;
|
||||
if (c === undefined) return undefined; // the callee had no returns or throws
|
||||
if (c instanceof ThrowCompletion || c instanceof ReturnCompletion) return c;
|
||||
// Non mixed completions will not be joined completions, but single completions with joined values.
|
||||
// At this point it must be true that
|
||||
// c contains return completions and possibly also normal completions (which are implicitly "return undefined;")
|
||||
// and c also contains throw completions. Hence we assert:
|
||||
invariant(c instanceof JoinedAbruptCompletions || c instanceof JoinedNormalAndAbruptCompletions);
|
||||
|
||||
// We want to add only the throw completions to priorSavedCompletion (but must keep their conditions in tact).
|
||||
// The (joined) return completions must be returned to our caller
|
||||
let rc = c;
|
||||
Completion.makeAllNormalCompletionsResultInUndefined(c);
|
||||
c = Completion.normalizeSelectedCompletions(r => r instanceof ReturnCompletion, c);
|
||||
invariant(c.containsSelectedCompletion(r => r instanceof NormalCompletion));
|
||||
let rv = Join.joinValuesOfSelectedCompletions(r => r instanceof NormalCompletion, c);
|
||||
rc = new ReturnCompletion(rv);
|
||||
if (c.containsSelectedCompletion(r => r instanceof ThrowCompletion)) realm.composeWithSavedCompletion(c);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,13 @@ import type { PropertyKeyValue } from "../types.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { ECMAScriptFunctionValue } from "../values/index.js";
|
||||
import { Completion, ReturnCompletion, AbruptCompletion, NormalCompletion } from "../completions.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Completion,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
ReturnCompletion,
|
||||
SimpleNormalCompletion,
|
||||
} from "../completions.js";
|
||||
import { GlobalEnvironmentRecord, ObjectEnvironmentRecord } from "../environment.js";
|
||||
import {
|
||||
AbstractValue,
|
||||
@ -121,8 +127,8 @@ function InternalCall(
|
||||
return result.value;
|
||||
}
|
||||
|
||||
// 10. ReturnIfAbrupt(result). or if possibly abrupt
|
||||
if (result instanceof Completion) {
|
||||
// 10. ReturnIfAbrupt(result).
|
||||
if (result instanceof AbruptCompletion) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
@ -1113,31 +1119,25 @@ export class FunctionImplementation {
|
||||
}
|
||||
}
|
||||
|
||||
// If c is an abrupt completion and realm.savedCompletion is defined, the result is an instance of
|
||||
// ForkedAbruptCompletion and the effects that have been captured since the PossiblyNormalCompletion instance
|
||||
// in realm.savedCompletion has been created, becomes the effects of the branch that terminates in c.
|
||||
// If c is a normal completion, the result is realm.savedCompletion, with its value updated to c.
|
||||
// If c is undefined, the result is just realm.savedCompletion.
|
||||
// Composes realm.savedCompletion with c, clears realm.savedCompletion and return the composition.
|
||||
// Call this only when a join point has been reached.
|
||||
incorporateSavedCompletion(realm: Realm, c: void | AbruptCompletion | Value): void | Completion | Value {
|
||||
incorporateSavedCompletion(realm: Realm, c: void | Completion | Value): void | Completion | Value {
|
||||
let savedCompletion = realm.savedCompletion;
|
||||
if (savedCompletion !== undefined) {
|
||||
if (savedCompletion.savedPathConditions) {
|
||||
// Since we are joining several control flow paths, we need the curent path conditions to reflect
|
||||
// only the refinements that applied at the corresponding fork point.
|
||||
realm.pathConditions = savedCompletion.savedPathConditions;
|
||||
savedCompletion.savedPathConditions = [];
|
||||
}
|
||||
realm.savedCompletion = undefined;
|
||||
if (c === undefined) return savedCompletion;
|
||||
if (c instanceof Value) {
|
||||
Join.updatePossiblyNormalCompletionWithValue(realm, savedCompletion, c);
|
||||
return savedCompletion;
|
||||
} else {
|
||||
let e = realm.getCapturedEffects();
|
||||
realm.pathConditions = [].concat(savedCompletion.pathConditionsAtCreation);
|
||||
if (c === undefined) c = realm.intrinsics.empty;
|
||||
if (c instanceof Value) c = new SimpleNormalCompletion(c);
|
||||
if (savedCompletion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
let subsequentEffects = realm.getCapturedEffects(c);
|
||||
realm.stopEffectCaptureAndUndoEffects(savedCompletion);
|
||||
return Join.replacePossiblyNormalCompletionWithForkedAbruptCompletion(realm, savedCompletion, c, e);
|
||||
let joinedEffects = Join.composeWithEffects(savedCompletion, subsequentEffects);
|
||||
realm.applyEffects(joinedEffects);
|
||||
realm.savedCompletion = savedCompletion.composedWith;
|
||||
if (realm.savedCompletion !== undefined) return this.incorporateSavedCompletion(realm, joinedEffects.result);
|
||||
return joinedEffects.result;
|
||||
}
|
||||
return Join.composeCompletions(savedCompletion, c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -1165,34 +1165,6 @@ export class FunctionImplementation {
|
||||
return blockValue || realm.intrinsics.empty;
|
||||
}
|
||||
|
||||
PartiallyEvaluateStatements(
|
||||
body: Array<BabelNodeStatement>,
|
||||
blockValue: void | NormalCompletion | Value,
|
||||
strictCode: boolean,
|
||||
blockEnv: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, Array<BabelNodeStatement>] {
|
||||
let statementAsts = [];
|
||||
for (let node of body) {
|
||||
if (node.type !== "FunctionDeclaration") {
|
||||
let [res, nast, nio] = blockEnv.partiallyEvaluateCompletionDeref(node, strictCode);
|
||||
for (let ioAst of nio) statementAsts.push(ioAst);
|
||||
statementAsts.push((nast: any));
|
||||
if (!(res instanceof EmptyValue)) {
|
||||
if (blockValue === undefined || blockValue instanceof Value) {
|
||||
if (res instanceof AbruptCompletion)
|
||||
return [UpdateEmpty(realm, res, blockValue || realm.intrinsics.empty), statementAsts];
|
||||
invariant(res instanceof NormalCompletion || res instanceof Value);
|
||||
blockValue = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Return blockValue.
|
||||
return [blockValue || realm.intrinsics.empty, statementAsts];
|
||||
}
|
||||
|
||||
// ECMA262 9.2.5
|
||||
FunctionCreate(
|
||||
realm: Realm,
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import { AbruptCompletion, Completion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { InfeasiblePathError } from "../errors.js";
|
||||
import { construct_empty_effects, type Realm, Effects } from "../realm.js";
|
||||
import type { PropertyKeyValue, CallableObjectValue } from "../types.js";
|
||||
@ -194,30 +193,13 @@ export function OrdinaryGet(
|
||||
}
|
||||
// Join the effects, creating an abstract view of what happened, regardless
|
||||
// of the actual value of ownDesc.joinCondition.
|
||||
if (result1 instanceof Completion) result1 = result1.shallowCloneWithoutEffects();
|
||||
if (result2 instanceof Completion) result2 = result2.shallowCloneWithoutEffects();
|
||||
let joinedEffects = Join.joinForkOrChoose(
|
||||
realm,
|
||||
let joinedEffects = Join.joinEffects(
|
||||
joinCondition,
|
||||
new Effects(result1, generator1, modifiedBindings1, modifiedProperties1, createdObjects1),
|
||||
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2)
|
||||
);
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(joinedEffects.result);
|
||||
|
||||
function OrdinaryGetHelper() {
|
||||
let descValue = !desc
|
||||
|
@ -10,9 +10,8 @@
|
||||
/* @flow */
|
||||
|
||||
import type { Binding } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import type { Bindings, BindingEntry, EvaluationResult, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
|
||||
import { Effects } from "../realm.js";
|
||||
import type { Bindings, BindingEntry, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
|
||||
import { construct_empty_effects, Effects } from "../realm.js";
|
||||
import type { Descriptor, PropertyBinding } from "../types.js";
|
||||
|
||||
import {
|
||||
@ -20,29 +19,22 @@ import {
|
||||
BreakCompletion,
|
||||
Completion,
|
||||
ContinueCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
ForkedAbruptCompletion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
SimpleNormalCompletion,
|
||||
NormalCompletion,
|
||||
ReturnCompletion,
|
||||
ThrowCompletion,
|
||||
} from "../completions.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
|
||||
import { construct_empty_effects } from "../realm.js";
|
||||
import { Path } from "../singletons.js";
|
||||
import { Generator } from "../utils/generator.js";
|
||||
import { AbstractValue, ConcreteValue, EmptyValue, Value } from "../values/index.js";
|
||||
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
function joinGenerators(
|
||||
realm: Realm,
|
||||
joinCondition: AbstractValue,
|
||||
generator1: Generator,
|
||||
generator2: Generator
|
||||
): Generator {
|
||||
// TODO #2222: Check if `realm.pathConditions` is correct here.
|
||||
function joinGenerators(joinCondition: AbstractValue, generator1: Generator, generator2: Generator): Generator {
|
||||
let realm = joinCondition.$Realm;
|
||||
let result = new Generator(realm, "joined", realm.pathConditions);
|
||||
if (!generator1.empty() || !generator2.empty()) {
|
||||
result.joinGenerators(joinCondition, generator1, generator2);
|
||||
@ -99,405 +91,128 @@ function joinArraysOfValues(
|
||||
}
|
||||
|
||||
export class JoinImplementation {
|
||||
stopEffectCaptureJoinApplyAndReturnCompletion(
|
||||
c1: PossiblyNormalCompletion,
|
||||
c2: AbruptCompletion,
|
||||
realm: Realm
|
||||
): ForkedAbruptCompletion {
|
||||
let e = realm.getCapturedEffects();
|
||||
realm.stopEffectCaptureAndUndoEffects(c1);
|
||||
return this.replacePossiblyNormalCompletionWithForkedAbruptCompletion(realm, c1, c2, e);
|
||||
composeCompletions(leftCompletion: void | Completion | Value, rightCompletion: Completion | Value): Completion {
|
||||
if (leftCompletion instanceof AbruptCompletion) return leftCompletion;
|
||||
if (leftCompletion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
if (rightCompletion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
rightCompletion.composedWith = leftCompletion;
|
||||
rightCompletion.pathConditionsAtCreation = leftCompletion.pathConditionsAtCreation;
|
||||
return rightCompletion;
|
||||
}
|
||||
let c = this.composeCompletions(leftCompletion.consequent, rightCompletion);
|
||||
if (c instanceof Value) c = new SimpleNormalCompletion(c);
|
||||
let a = this.composeCompletions(leftCompletion.alternate, rightCompletion);
|
||||
if (a instanceof Value) a = new SimpleNormalCompletion(a);
|
||||
let joinedCompletion = this.joinCompletions(leftCompletion.joinCondition, c, a);
|
||||
if (joinedCompletion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
joinedCompletion.composedWith = leftCompletion.composedWith;
|
||||
joinedCompletion.pathConditionsAtCreation = leftCompletion.pathConditionsAtCreation;
|
||||
joinedCompletion.savedEffects = leftCompletion.savedEffects;
|
||||
}
|
||||
return joinedCompletion;
|
||||
}
|
||||
if (rightCompletion instanceof Value) rightCompletion = new SimpleNormalCompletion(rightCompletion);
|
||||
return rightCompletion;
|
||||
}
|
||||
|
||||
unbundleNormalCompletion(
|
||||
completionOrValue: Completion | Value | Reference
|
||||
): [void | NormalCompletion, Value | Reference] {
|
||||
let completion, value;
|
||||
if (completionOrValue instanceof PossiblyNormalCompletion) {
|
||||
completion = completionOrValue;
|
||||
value = completionOrValue.value;
|
||||
} else {
|
||||
invariant(completionOrValue instanceof Value || completionOrValue instanceof Reference);
|
||||
value = completionOrValue;
|
||||
}
|
||||
return [completion, value];
|
||||
composeWithEffects(completion: Completion, effects: Effects): Effects {
|
||||
if (completion instanceof AbruptCompletion) return construct_empty_effects(completion.value.$Realm, completion);
|
||||
if (completion instanceof SimpleNormalCompletion) return effects.shallowCloneWithResult(effects.result);
|
||||
invariant(completion instanceof JoinedNormalAndAbruptCompletions);
|
||||
let e1 = this.composeWithEffects(completion.consequent, effects);
|
||||
let e2 = this.composeWithEffects(completion.alternate, effects);
|
||||
return this.joinEffects(completion.joinCondition, e1, e2);
|
||||
}
|
||||
|
||||
composeNormalCompletions(
|
||||
leftCompletion: void | NormalCompletion,
|
||||
rightCompletion: void | NormalCompletion,
|
||||
resultValue: Value,
|
||||
realm: Realm
|
||||
): PossiblyNormalCompletion | Value {
|
||||
if (leftCompletion instanceof PossiblyNormalCompletion) {
|
||||
if (rightCompletion instanceof PossiblyNormalCompletion) {
|
||||
this.updatePossiblyNormalCompletionWithValue(realm, rightCompletion, resultValue);
|
||||
return this.composePossiblyNormalCompletions(realm, leftCompletion, rightCompletion);
|
||||
}
|
||||
this.updatePossiblyNormalCompletionWithValue(realm, leftCompletion, resultValue);
|
||||
return leftCompletion;
|
||||
} else if (rightCompletion instanceof PossiblyNormalCompletion) {
|
||||
this.updatePossiblyNormalCompletionWithValue(realm, rightCompletion, resultValue);
|
||||
return rightCompletion;
|
||||
} else {
|
||||
invariant(leftCompletion === undefined && rightCompletion === undefined);
|
||||
return resultValue;
|
||||
}
|
||||
}
|
||||
|
||||
composePossiblyNormalCompletions(
|
||||
realm: Realm,
|
||||
pnc: PossiblyNormalCompletion,
|
||||
c: PossiblyNormalCompletion,
|
||||
priorEffects?: Effects
|
||||
): PossiblyNormalCompletion {
|
||||
invariant(c.savedEffects === undefined); // the caller should ensure this
|
||||
let savedPathConditions = pnc.savedPathConditions;
|
||||
if (pnc.consequent instanceof AbruptCompletion) {
|
||||
let ae = pnc.alternateEffects;
|
||||
let na;
|
||||
if (pnc.alternate instanceof SimpleNormalCompletion) {
|
||||
na = c.shallowCloneWithoutEffects();
|
||||
let newAlternateEffects = ae.shallowCloneWithResult(na);
|
||||
if (priorEffects) newAlternateEffects = realm.composeEffects(priorEffects, newAlternateEffects);
|
||||
return new PossiblyNormalCompletion(
|
||||
c.value,
|
||||
pnc.joinCondition,
|
||||
pnc.consequent,
|
||||
newAlternateEffects.result,
|
||||
savedPathConditions,
|
||||
pnc.savedEffects
|
||||
);
|
||||
}
|
||||
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
|
||||
na = this.composePossiblyNormalCompletions(realm, pnc.alternate, c, priorEffects);
|
||||
ae.shallowCloneWithResult(na);
|
||||
return new PossiblyNormalCompletion(
|
||||
c.value,
|
||||
pnc.joinCondition,
|
||||
pnc.consequent,
|
||||
na,
|
||||
savedPathConditions,
|
||||
pnc.savedEffects
|
||||
);
|
||||
} else {
|
||||
let ce = pnc.consequentEffects;
|
||||
let nc;
|
||||
if (pnc.consequent instanceof SimpleNormalCompletion) {
|
||||
nc = c.shallowCloneWithoutEffects();
|
||||
let newConsequentEffects = ce.shallowCloneWithResult(nc);
|
||||
if (priorEffects) newConsequentEffects = realm.composeEffects(priorEffects, newConsequentEffects);
|
||||
return new PossiblyNormalCompletion(
|
||||
c.value,
|
||||
pnc.joinCondition,
|
||||
newConsequentEffects.result,
|
||||
pnc.alternate,
|
||||
savedPathConditions,
|
||||
pnc.savedEffects
|
||||
);
|
||||
}
|
||||
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
|
||||
nc = this.composePossiblyNormalCompletions(realm, pnc.consequent, c);
|
||||
ce.shallowCloneWithResult(nc);
|
||||
return new PossiblyNormalCompletion(
|
||||
c.value,
|
||||
pnc.joinCondition,
|
||||
nc,
|
||||
pnc.alternate,
|
||||
savedPathConditions,
|
||||
pnc.savedEffects
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updatePossiblyNormalCompletionWithSubsequentEffects(
|
||||
realm: Realm,
|
||||
pnc: PossiblyNormalCompletion,
|
||||
subsequentEffects: Effects
|
||||
): void {
|
||||
let v = subsequentEffects.result;
|
||||
invariant(v instanceof SimpleNormalCompletion);
|
||||
pnc.value = v.value;
|
||||
if (pnc.consequent instanceof AbruptCompletion) {
|
||||
if (pnc.alternate instanceof SimpleNormalCompletion) {
|
||||
let ce = realm.composeEffects(pnc.alternateEffects, subsequentEffects);
|
||||
pnc.alternate = ce.result;
|
||||
} else {
|
||||
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
|
||||
this.updatePossiblyNormalCompletionWithSubsequentEffects(realm, pnc.alternate, subsequentEffects);
|
||||
}
|
||||
} else {
|
||||
if (pnc.consequent instanceof SimpleNormalCompletion) {
|
||||
let ce = realm.composeEffects(pnc.consequentEffects, subsequentEffects);
|
||||
pnc.consequent = ce.result;
|
||||
} else {
|
||||
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
|
||||
this.updatePossiblyNormalCompletionWithSubsequentEffects(realm, pnc.consequent, subsequentEffects);
|
||||
}
|
||||
if (pnc.alternate instanceof SimpleNormalCompletion) {
|
||||
let ce = realm.composeEffects(pnc.alternateEffects, subsequentEffects);
|
||||
pnc.alternate = ce.result;
|
||||
} else if (pnc.alternate instanceof PossiblyNormalCompletion) {
|
||||
this.updatePossiblyNormalCompletionWithSubsequentEffects(realm, pnc.alternate, subsequentEffects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePossiblyNormalCompletionWithValue(realm: Realm, pnc: PossiblyNormalCompletion, v: Value): void {
|
||||
let updateNonAbruptCompletionWithValue = (c: Completion, val: Value) => {
|
||||
if (c instanceof SimpleNormalCompletion) {
|
||||
c.value = v;
|
||||
} else if (c instanceof PossiblyNormalCompletion) {
|
||||
this.updatePossiblyNormalCompletionWithValue(realm, c, val);
|
||||
} else {
|
||||
invariant(false);
|
||||
}
|
||||
};
|
||||
pnc.value = v;
|
||||
let pncc = pnc.consequent;
|
||||
let pnca = pnc.alternate;
|
||||
if (pncc instanceof AbruptCompletion) {
|
||||
Path.withInverseCondition(pnc.joinCondition, () => {
|
||||
let sv = v instanceof AbstractValue ? realm.simplifyAndRefineAbstractValue(v) : v;
|
||||
updateNonAbruptCompletionWithValue(pnca, sv);
|
||||
});
|
||||
} else {
|
||||
Path.withCondition(pnc.joinCondition, () => {
|
||||
let sv = v instanceof AbstractValue ? realm.simplifyAndRefineAbstractValue(v) : v;
|
||||
updateNonAbruptCompletionWithValue(pncc, sv);
|
||||
});
|
||||
if (!(pnca instanceof AbruptCompletion)) {
|
||||
Path.withInverseCondition(pnc.joinCondition, () => {
|
||||
let sv = v instanceof AbstractValue ? realm.simplifyAndRefineAbstractValue(v) : v;
|
||||
updateNonAbruptCompletionWithValue(pnca, sv);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replacePossiblyNormalCompletionWithForkedAbruptCompletion(
|
||||
realm: Realm,
|
||||
// a forked path with a non abrupt (normal) component
|
||||
pnc: PossiblyNormalCompletion,
|
||||
// an abrupt completion that completes the normal path
|
||||
ac: AbruptCompletion,
|
||||
// effects collected after pnc was constructed
|
||||
e: Effects
|
||||
): ForkedAbruptCompletion {
|
||||
let recurse = (xpnc, xe, nac, ne): ForkedAbruptCompletion => {
|
||||
let nx = this.replacePossiblyNormalCompletionWithForkedAbruptCompletion(realm, xpnc, nac, ne);
|
||||
xe.shallowCloneWithResult(nx);
|
||||
return nx;
|
||||
};
|
||||
|
||||
let cloneEffects = () => {
|
||||
let nac = ac.shallowCloneWithoutEffects();
|
||||
let ne = e.shallowCloneWithResult(nac);
|
||||
return [nac, ne];
|
||||
};
|
||||
|
||||
ac = ac.shallowCloneWithoutEffects();
|
||||
e.result = ac;
|
||||
ac.effects = e;
|
||||
|
||||
// # match (pncc, pnca)
|
||||
let pncc = pnc.consequent;
|
||||
let pnca = pnc.alternate;
|
||||
|
||||
// * case (AbruptCompletion, SimpleNormalCompletion)
|
||||
// * case (AbruptCompletion, PossiblyNormalCompletion)
|
||||
if (pncc instanceof AbruptCompletion) {
|
||||
if (pnca instanceof SimpleNormalCompletion) {
|
||||
// todo: simplify with implied path condition
|
||||
e = realm.composeEffects(pnc.alternateEffects, e);
|
||||
invariant(e.result instanceof AbruptCompletion);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, pncc, e.result);
|
||||
}
|
||||
invariant(pnca instanceof PossiblyNormalCompletion);
|
||||
let na = recurse(pnca, pnc.alternateEffects, ac, e);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, pncc, na);
|
||||
}
|
||||
|
||||
// * case (SimpleNormalCompletion, AbruptCompletion)
|
||||
// * case (PossiblyNormalCompletion, AbruptCompletion)
|
||||
if (pnca instanceof AbruptCompletion) {
|
||||
if (pncc instanceof SimpleNormalCompletion) {
|
||||
// todo: simplify with implied path condition
|
||||
e = realm.composeEffects(pnc.consequentEffects, e);
|
||||
invariant(e.result instanceof AbruptCompletion);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, e.result, pnca);
|
||||
}
|
||||
invariant(pncc instanceof PossiblyNormalCompletion);
|
||||
let nc = recurse(pncc, pnc.consequentEffects, ac, e);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, nc, pnca);
|
||||
}
|
||||
|
||||
// * case (SimpleNormalCompletion, SimpleNormalCompletion)
|
||||
// * case (SimpleNormalCompletion, PossibleNormalCompletion)
|
||||
if (pncc instanceof SimpleNormalCompletion) {
|
||||
let nce = realm.composeEffects(pnc.consequentEffects, e);
|
||||
invariant(nce.result instanceof AbruptCompletion);
|
||||
let nc = nce.result;
|
||||
[ac, e] = cloneEffects();
|
||||
let na, nae;
|
||||
if (pnca instanceof SimpleNormalCompletion) {
|
||||
nae = realm.composeEffects(pnc.alternateEffects, e);
|
||||
invariant(nae.result instanceof AbruptCompletion);
|
||||
na = nae.result;
|
||||
} else {
|
||||
invariant(pnca instanceof PossiblyNormalCompletion);
|
||||
na = recurse(pnca, pnc.alternateEffects, ac, e);
|
||||
}
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, nc, na);
|
||||
}
|
||||
|
||||
// * case (PossibleNormalCompletion, SimpleNormalCompletion)
|
||||
if (pnca instanceof SimpleNormalCompletion) {
|
||||
let nae = realm.composeEffects(pnc.alternateEffects, e);
|
||||
invariant(nae.result instanceof AbruptCompletion);
|
||||
let na = nae.result;
|
||||
invariant(pncc instanceof PossiblyNormalCompletion);
|
||||
[ac, e] = cloneEffects();
|
||||
let nc = recurse(pncc, pnc.consequentEffects, ac, e);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, nc, na);
|
||||
}
|
||||
|
||||
// * case (PossibleNormalCompletion, PossibleNormalCompletion)
|
||||
invariant(pncc instanceof PossiblyNormalCompletion);
|
||||
invariant(pnca instanceof PossiblyNormalCompletion);
|
||||
let nc = recurse(pncc, pnc.consequentEffects, ac, e);
|
||||
[ac, e] = cloneEffects();
|
||||
let na = recurse(pnca, pnc.alternateEffects, ac, e);
|
||||
return new ForkedAbruptCompletion(realm, pnc.joinCondition, nc, na);
|
||||
|
||||
// Impossible cases:
|
||||
// * case (AbruptCompletion, AbruptCompletion)
|
||||
}
|
||||
|
||||
joinNormalCompletions(
|
||||
realm: Realm,
|
||||
joinCondition: AbstractValue,
|
||||
c: NormalCompletion,
|
||||
ce: Effects,
|
||||
a: NormalCompletion,
|
||||
ae: Effects
|
||||
): PossiblyNormalCompletion {
|
||||
_collapseSimilarCompletions(joinCondition: AbstractValue, c1: Completion, c2: Completion): void | Completion {
|
||||
let realm = joinCondition.$Realm;
|
||||
let getAbstractValue = (v1: void | Value, v2: void | Value): Value => {
|
||||
if (v1 instanceof EmptyValue) return v2 || realm.intrinsics.undefined;
|
||||
if (v2 instanceof EmptyValue) return v1 || realm.intrinsics.undefined;
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
};
|
||||
let rv = this.joinValues(realm, c.value, a.value, getAbstractValue);
|
||||
invariant(rv instanceof Value);
|
||||
a.value = rv;
|
||||
return new PossiblyNormalCompletion(rv, joinCondition, c, a, []);
|
||||
if (c1 instanceof BreakCompletion && c2 instanceof BreakCompletion && c1.target === c2.target) {
|
||||
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new BreakCompletion(val, joinCondition.expressionLocation, c1.target);
|
||||
}
|
||||
if (c1 instanceof ContinueCompletion && c2 instanceof ContinueCompletion && c1.target === c2.target) {
|
||||
return new ContinueCompletion(realm.intrinsics.empty, joinCondition.expressionLocation, c1.target);
|
||||
}
|
||||
if (c1 instanceof ReturnCompletion && c2 instanceof ReturnCompletion) {
|
||||
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new ReturnCompletion(val, joinCondition.expressionLocation);
|
||||
}
|
||||
if (c1 instanceof ThrowCompletion && c2 instanceof ThrowCompletion) {
|
||||
getAbstractValue = (v1: void | Value, v2: void | Value) => {
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
};
|
||||
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new ThrowCompletion(val, c1.location);
|
||||
}
|
||||
if (c1 instanceof SimpleNormalCompletion && c2 instanceof SimpleNormalCompletion) {
|
||||
return new SimpleNormalCompletion(getAbstractValue(c1.value, c2.value));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Join all effects that result in completions of type CompletionType.
|
||||
// Erase all completions of type Completion type from c, so that we never join them again.
|
||||
// Also erase any generators that appears in branches resulting in completions of type CompletionType.
|
||||
// Note that c is modified in place and should be replaced with a PossiblyNormalCompletion by the caller
|
||||
// if either of its branches cease to be an AbruptCompletion.
|
||||
extractAndJoinCompletionsOfType(CompletionType: typeof AbruptCompletion, realm: Realm, c: AbruptCompletion): Effects {
|
||||
let emptyEffects = construct_empty_effects(realm);
|
||||
if (c instanceof CompletionType) {
|
||||
emptyEffects.result = c.shallowCloneWithoutEffects();
|
||||
return emptyEffects;
|
||||
}
|
||||
if (!(c instanceof ForkedAbruptCompletion)) {
|
||||
return emptyEffects;
|
||||
}
|
||||
// Join up the consequent and alternate completions and compose them with their prefix effects
|
||||
let ce = this.extractAndJoinCompletionsOfType(CompletionType, realm, c.consequent);
|
||||
// ce will be applied to the global state before any non joining branches in c.consequent, so move
|
||||
// the generator from c.consequentEffects to ce.generator so that all branches will see its effects.
|
||||
ce = realm.composeEffects(c.consequentEffects, ce);
|
||||
// ce now incorporates c.consequentEffects.generator, so remove it from there.
|
||||
c.consequentEffects.generator = emptyEffects.generator;
|
||||
if (ce.result instanceof CompletionType) {
|
||||
// Erase completions of type CompletionType and prepare for transformation of c to a possibly normal completion
|
||||
if (c.consequent instanceof CompletionType) {
|
||||
c.updateConsequentKeepingCurrentEffects(new SimpleNormalCompletion(realm.intrinsics.empty, undefined));
|
||||
} else if (c.consequent instanceof ForkedAbruptCompletion && c.consequent.containsCompletion(NormalCompletion)) {
|
||||
c.updateConsequentKeepingCurrentEffects((c.consequent.transferChildrenToPossiblyNormalCompletion(): any));
|
||||
}
|
||||
} else {
|
||||
ce.result = new CompletionType(realm.intrinsics.empty);
|
||||
}
|
||||
let ae = this.extractAndJoinCompletionsOfType(CompletionType, realm, c.alternate);
|
||||
// ae will be applied to the global state before any non joining branches in c.alternate, so move
|
||||
// the generator from c.alternateEffects to ae.generator so that all branches will see its effects.
|
||||
ae = realm.composeEffects(c.alternateEffects, ae);
|
||||
// ae now incorporates c.alternateEffects.generator, so remove it from there.
|
||||
c.alternateEffects.generator = emptyEffects.generator;
|
||||
if (ae.result instanceof CompletionType) {
|
||||
// Erase completions of type CompletionType and prepare for transformation of c to a possibly normal completion
|
||||
if (c.alternate instanceof CompletionType) {
|
||||
c.updateAlternateKeepingCurrentEffects(new SimpleNormalCompletion(realm.intrinsics.empty, undefined));
|
||||
} else if (c.alternate instanceof ForkedAbruptCompletion && c.alternate.containsCompletion(NormalCompletion)) {
|
||||
c.updateAlternateKeepingCurrentEffects((c.alternate.transferChildrenToPossiblyNormalCompletion(): any));
|
||||
}
|
||||
} else {
|
||||
ae.result = new CompletionType(realm.intrinsics.empty);
|
||||
}
|
||||
joinCompletions(joinCondition: Value, c1: Completion, c2: Completion): Completion {
|
||||
if (!joinCondition.mightNotBeTrue()) return c1;
|
||||
if (!joinCondition.mightNotBeFalse()) return c2;
|
||||
invariant(joinCondition instanceof AbstractValue);
|
||||
|
||||
let e = this.joinForkOrChoose(realm, c.joinCondition, ce, ae);
|
||||
if (e.result instanceof ForkedAbruptCompletion) {
|
||||
if (e.result.consequent instanceof CompletionType && e.result.alternate instanceof CompletionType) {
|
||||
let result = this.collapseResults(realm, e.result.joinCondition, e, e.result.consequent, e.result.alternate);
|
||||
e = result.effects;
|
||||
invariant(e !== undefined);
|
||||
let c = this._collapseSimilarCompletions(joinCondition, c1, c2);
|
||||
if (c === undefined) {
|
||||
if (c1 instanceof AbruptCompletion && c2 instanceof AbruptCompletion)
|
||||
c = new JoinedAbruptCompletions(joinCondition, c1, c2);
|
||||
else {
|
||||
invariant(c1 instanceof AbruptCompletion || c1 instanceof NormalCompletion);
|
||||
invariant(c2 instanceof AbruptCompletion || c2 instanceof NormalCompletion);
|
||||
c = new JoinedNormalAndAbruptCompletions(joinCondition, c1, c2);
|
||||
}
|
||||
}
|
||||
return e;
|
||||
return c;
|
||||
}
|
||||
|
||||
joinForkOrChoose(realm: Realm, joinCondition: Value, e1: Effects, e2: Effects): Effects {
|
||||
joinEffects(joinCondition: Value, e1: Effects, e2: Effects): Effects {
|
||||
invariant(e1.canBeApplied);
|
||||
invariant(e2.canBeApplied);
|
||||
if (!joinCondition.mightNotBeTrue()) return e1;
|
||||
if (!joinCondition.mightNotBeFalse()) return e2;
|
||||
invariant(joinCondition instanceof AbstractValue);
|
||||
|
||||
let {
|
||||
result: result1,
|
||||
result: c1,
|
||||
generator: generator1,
|
||||
modifiedBindings: modifiedBindings1,
|
||||
modifiedProperties: modifiedProperties1,
|
||||
createdObjects: createdObjects1,
|
||||
} = e1;
|
||||
invariant(result1.effects === e1);
|
||||
|
||||
let {
|
||||
result: result2,
|
||||
result: c2,
|
||||
generator: generator2,
|
||||
modifiedBindings: modifiedBindings2,
|
||||
modifiedProperties: modifiedProperties2,
|
||||
createdObjects: createdObjects2,
|
||||
} = e2;
|
||||
invariant(result2.effects === e2);
|
||||
|
||||
let emptyEffects = construct_empty_effects(realm);
|
||||
let realm = joinCondition.$Realm;
|
||||
|
||||
let result = this.joinOrForkResults(realm, joinCondition, result1, result2, e1, e2);
|
||||
if (result1 instanceof AbruptCompletion) {
|
||||
if (!(result2 instanceof AbruptCompletion)) {
|
||||
invariant(result instanceof PossiblyNormalCompletion);
|
||||
e2.generator = emptyEffects.generator;
|
||||
return new Effects(result, generator2, modifiedBindings2, modifiedProperties2, createdObjects2);
|
||||
}
|
||||
} else if (result2 instanceof AbruptCompletion) {
|
||||
invariant(result instanceof PossiblyNormalCompletion);
|
||||
e1.generator = emptyEffects.generator;
|
||||
return new Effects(result, generator1, modifiedBindings1, modifiedProperties1, createdObjects1);
|
||||
}
|
||||
let c = this.joinCompletions(joinCondition, c1, c2);
|
||||
|
||||
let [modifiedGenerator1, modifiedGenerator2, bindings] = this.joinBindings(
|
||||
realm,
|
||||
let [modifiedGenerator1, modifiedGenerator2, bindings] = this._joinBindings(
|
||||
joinCondition,
|
||||
generator1,
|
||||
modifiedBindings1,
|
||||
generator2,
|
||||
modifiedBindings2
|
||||
);
|
||||
|
||||
let generator = joinGenerators(joinCondition, modifiedGenerator1, modifiedGenerator2);
|
||||
|
||||
let properties = this.joinPropertyBindings(
|
||||
realm,
|
||||
joinCondition,
|
||||
@ -514,156 +229,31 @@ export class JoinImplementation {
|
||||
createdObjects.add(o);
|
||||
});
|
||||
|
||||
let generator = joinGenerators(realm, joinCondition, modifiedGenerator1, modifiedGenerator2);
|
||||
|
||||
return new Effects(result, generator, bindings, properties, createdObjects);
|
||||
return new Effects(c, generator, bindings, properties, createdObjects);
|
||||
}
|
||||
|
||||
joinNestedEffects(realm: Realm, c: Completion, precedingEffects?: Effects): Effects {
|
||||
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion) {
|
||||
let e1 = this.joinNestedEffects(realm, c.consequent, c.consequentEffects);
|
||||
let e2 = this.joinNestedEffects(realm, c.alternate, c.alternateEffects);
|
||||
let e3 = this.joinForkOrChoose(realm, c.joinCondition, e1, e2);
|
||||
let r = this.collapseResults(realm, c.joinCondition, e3, e1.result, e2.result);
|
||||
let re = r.effects;
|
||||
invariant(re !== undefined);
|
||||
return re;
|
||||
}
|
||||
if (precedingEffects !== undefined) return precedingEffects;
|
||||
let result = construct_empty_effects(realm);
|
||||
result.result = c;
|
||||
return result;
|
||||
}
|
||||
|
||||
collapseResults(
|
||||
realm: Realm,
|
||||
joinCondition: AbstractValue,
|
||||
precedingEffects: Effects,
|
||||
result1: EvaluationResult,
|
||||
result2: EvaluationResult
|
||||
): Completion {
|
||||
let getAbstractValue = (v1: void | Value, v2: void | Value): Value => {
|
||||
if (v1 instanceof EmptyValue) return v2 || realm.intrinsics.undefined;
|
||||
if (v2 instanceof EmptyValue) return v1 || realm.intrinsics.undefined;
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
};
|
||||
if (result1 instanceof BreakCompletion && result2 instanceof BreakCompletion && result1.target === result2.target) {
|
||||
let val = this.joinValues(realm, result1.value, result2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new BreakCompletion(val, precedingEffects, joinCondition.expressionLocation, result1.target);
|
||||
}
|
||||
if (
|
||||
result1 instanceof ContinueCompletion &&
|
||||
result2 instanceof ContinueCompletion &&
|
||||
result1.target === result2.target
|
||||
) {
|
||||
return new ContinueCompletion(
|
||||
realm.intrinsics.empty,
|
||||
precedingEffects,
|
||||
joinCondition.expressionLocation,
|
||||
result1.target
|
||||
);
|
||||
}
|
||||
if (result1 instanceof ReturnCompletion && result2 instanceof ReturnCompletion) {
|
||||
let val = this.joinValues(realm, result1.value, result2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new ReturnCompletion(val, precedingEffects, joinCondition.expressionLocation);
|
||||
}
|
||||
if (result1 instanceof ThrowCompletion && result2 instanceof ThrowCompletion) {
|
||||
getAbstractValue = (v1: void | Value, v2: void | Value) => {
|
||||
joinValuesOfSelectedCompletions(selector: Completion => boolean, completion: Completion): Value {
|
||||
let realm = completion.value.$Realm;
|
||||
if (completion instanceof JoinedAbruptCompletions || completion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
let joinCondition = completion.joinCondition;
|
||||
let c = this.joinValuesOfSelectedCompletions(selector, completion.consequent);
|
||||
let a = this.joinValuesOfSelectedCompletions(selector, completion.alternate);
|
||||
let getAbstractValue = (v1: void | Value, v2: void | Value): Value => {
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
};
|
||||
let val = this.joinValues(realm, result1.value, result2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new ThrowCompletion(val, precedingEffects, result1.location);
|
||||
}
|
||||
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion) {
|
||||
return new SimpleNormalCompletion(getAbstractValue(result1.value, result2.value), precedingEffects);
|
||||
}
|
||||
AbstractValue.reportIntrospectionError(joinCondition);
|
||||
throw new FatalError();
|
||||
}
|
||||
|
||||
joinOrForkResults(
|
||||
realm: Realm,
|
||||
joinCondition: AbstractValue,
|
||||
result1: EvaluationResult,
|
||||
result2: EvaluationResult,
|
||||
e1: Effects,
|
||||
e2: Effects
|
||||
): Completion {
|
||||
invariant(result1.effects === e1);
|
||||
invariant(e1.result === result1);
|
||||
invariant(result2.effects === e2);
|
||||
invariant(e2.result === result2);
|
||||
let getAbstractValue = (v1: void | Value, v2: void | Value) => {
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
};
|
||||
if (result1 instanceof Reference || result2 instanceof Reference) {
|
||||
AbstractValue.reportIntrospectionError(joinCondition);
|
||||
throw new FatalError();
|
||||
}
|
||||
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion) {
|
||||
let val = this.joinValues(realm, result1.value, result2.value, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return new SimpleNormalCompletion(val);
|
||||
}
|
||||
if (result1 instanceof AbruptCompletion && result2 instanceof AbruptCompletion) {
|
||||
return new ForkedAbruptCompletion(realm, joinCondition, result1, result2);
|
||||
}
|
||||
if (result1 instanceof NormalCompletion && result2 instanceof NormalCompletion) {
|
||||
return this.joinNormalCompletions(realm, joinCondition, result1, e1, result2, e2);
|
||||
}
|
||||
if (result1 instanceof AbruptCompletion) {
|
||||
let completion = result2;
|
||||
let savedEffects;
|
||||
let savedPathConditions = [];
|
||||
if (result2 instanceof PossiblyNormalCompletion) {
|
||||
completion = result2.getNormalCompletion();
|
||||
savedEffects = result2.savedEffects;
|
||||
savedPathConditions = result2.savedPathConditions;
|
||||
let jv = this.joinValues(realm, c, a, getAbstractValue);
|
||||
invariant(jv instanceof Value);
|
||||
if (completion instanceof JoinedNormalAndAbruptCompletions && completion.composedWith !== undefined) {
|
||||
let composedWith = completion.composedWith;
|
||||
let cjv = this.joinValuesOfSelectedCompletions(selector, composedWith);
|
||||
joinCondition = AbstractValue.createJoinConditionForSelectedCompletions(selector, composedWith);
|
||||
jv = this.joinValues(realm, jv, cjv, getAbstractValue);
|
||||
invariant(jv instanceof Value);
|
||||
}
|
||||
invariant(completion instanceof SimpleNormalCompletion);
|
||||
return new PossiblyNormalCompletion(
|
||||
completion.value,
|
||||
joinCondition,
|
||||
result1,
|
||||
result2,
|
||||
savedPathConditions,
|
||||
savedEffects
|
||||
);
|
||||
return jv;
|
||||
}
|
||||
if (result2 instanceof AbruptCompletion) {
|
||||
let completion = result1;
|
||||
let savedEffects;
|
||||
let savedPathConditions = [];
|
||||
if (result1 instanceof PossiblyNormalCompletion) {
|
||||
completion = result1.getNormalCompletion();
|
||||
savedEffects = result1.savedEffects;
|
||||
savedPathConditions = result1.savedPathConditions;
|
||||
}
|
||||
invariant(completion instanceof SimpleNormalCompletion);
|
||||
return new PossiblyNormalCompletion(
|
||||
completion.value,
|
||||
joinCondition,
|
||||
result1,
|
||||
result2,
|
||||
savedPathConditions,
|
||||
savedEffects
|
||||
);
|
||||
}
|
||||
invariant(false);
|
||||
}
|
||||
|
||||
composeGenerators(realm: Realm, generator1: Generator, generator2: Generator): Generator {
|
||||
// TODO #2222: The path condition of the resulting generator should really just be generator2.pathConditions,
|
||||
// and we shouldn't have to bring in generator1.pathConditions. We have observed that this causes an issue
|
||||
// in InstantRender.
|
||||
let result = new Generator(realm, "composed", generator1.pathConditions.concat(generator2.pathConditions));
|
||||
// We copy the entries here because actually composing the generators breaks the serializer
|
||||
if (!generator1.empty()) result.appendGenerator(generator1, "");
|
||||
if (!generator2.empty()) result.appendGenerator(generator2, "");
|
||||
return result;
|
||||
if (selector(completion)) return completion.value;
|
||||
return realm.intrinsics.empty;
|
||||
}
|
||||
|
||||
// Creates a single map that joins together maps m1 and m2 using the given join
|
||||
@ -688,16 +278,16 @@ export class JoinImplementation {
|
||||
// sets of m1 and m2. The value of a pair is the join of m1[key] and m2[key]
|
||||
// where the join is defined to be just m1[key] if m1[key] === m2[key] and
|
||||
// and abstract value with expression "joinCondition ? m1[key] : m2[key]" if not.
|
||||
joinBindings(
|
||||
realm: Realm,
|
||||
_joinBindings(
|
||||
joinCondition: AbstractValue,
|
||||
g1: Generator,
|
||||
m1: Bindings,
|
||||
g2: Generator,
|
||||
m2: Bindings
|
||||
): [Generator, Generator, Bindings] {
|
||||
let realm = joinCondition.$Realm;
|
||||
let getAbstractValue = (v1: void | Value, v2: void | Value) => {
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
|
||||
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2, undefined, true, true);
|
||||
};
|
||||
let rewritten1 = false;
|
||||
let rewritten2 = false;
|
||||
@ -728,18 +318,7 @@ export class JoinImplementation {
|
||||
// In that case, we reset the value to undefined to prevent any use of the last known value.
|
||||
let value = hasLeaked ? undefined : this.joinValues(realm, v1, v2, getAbstractValue);
|
||||
invariant(value === undefined || value instanceof Value);
|
||||
let previousHasLeaked, previousValue;
|
||||
if (b1 !== undefined) {
|
||||
previousHasLeaked = b1.previousHasLeaked;
|
||||
previousValue = b1.previousValue;
|
||||
invariant(
|
||||
b2 === undefined || (previousHasLeaked === b2.previousHasLeaked && previousValue === b2.previousValue)
|
||||
);
|
||||
} else if (b2 !== undefined) {
|
||||
previousHasLeaked = b2.previousHasLeaked;
|
||||
previousValue = b2.previousValue;
|
||||
}
|
||||
return { hasLeaked, value, previousHasLeaked, previousValue };
|
||||
return { hasLeaked, value };
|
||||
};
|
||||
let joinedBindings = this.joinMaps(m1, m2, join);
|
||||
return [g1, g2, joinedBindings];
|
||||
@ -910,28 +489,10 @@ export class JoinImplementation {
|
||||
undefined,
|
||||
"mapAndJoin"
|
||||
);
|
||||
joinedEffects =
|
||||
joinedEffects === undefined ? effects : this.joinForkOrChoose(realm, condition, effects, joinedEffects);
|
||||
joinedEffects = joinedEffects === undefined ? effects : this.joinEffects(condition, effects, joinedEffects);
|
||||
}
|
||||
invariant(joinedEffects !== undefined);
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) {
|
||||
completion = completion.value;
|
||||
}
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(joinedEffects.result);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import { AbruptCompletion, Completion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { construct_empty_effects, type Realm, Effects } from "../realm.js";
|
||||
import type { Descriptor, PropertyBinding, PropertyKeyValue } from "../types.js";
|
||||
import {
|
||||
@ -314,57 +313,41 @@ export class PropertiesImplementation {
|
||||
if (joinCondition !== undefined) {
|
||||
let descriptor2 = ownDesc.descriptor2;
|
||||
ownDesc = ownDesc.descriptor1;
|
||||
let e1 = Path.withCondition(joinCondition, () => {
|
||||
return ownDesc !== undefined
|
||||
? realm.evaluateForEffects(() => new BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/1")
|
||||
: construct_empty_effects(realm);
|
||||
});
|
||||
let {
|
||||
result: result1,
|
||||
generator: generator1,
|
||||
modifiedBindings: modifiedBindings1,
|
||||
modifiedProperties: modifiedProperties1,
|
||||
createdObjects: createdObjects1,
|
||||
} = Path.withCondition(joinCondition, () => {
|
||||
} = e1;
|
||||
ownDesc = descriptor2;
|
||||
let e2 = Path.withInverseCondition(joinCondition, () => {
|
||||
return ownDesc !== undefined
|
||||
? realm.evaluateForEffects(() => new BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/1")
|
||||
? realm.evaluateForEffects(() => new BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/2")
|
||||
: construct_empty_effects(realm);
|
||||
});
|
||||
ownDesc = descriptor2;
|
||||
let {
|
||||
result: result2,
|
||||
generator: generator2,
|
||||
modifiedBindings: modifiedBindings2,
|
||||
modifiedProperties: modifiedProperties2,
|
||||
createdObjects: createdObjects2,
|
||||
} = Path.withInverseCondition(joinCondition, () => {
|
||||
return ownDesc !== undefined
|
||||
? realm.evaluateForEffects(() => new BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/2")
|
||||
: construct_empty_effects(realm);
|
||||
});
|
||||
} = e2;
|
||||
|
||||
// Join the effects, creating an abstract view of what happened, regardless
|
||||
// of the actual value of ownDesc.joinCondition.
|
||||
if (result1 instanceof Completion) result1 = result1.shallowCloneWithoutEffects();
|
||||
if (result2 instanceof Completion) result2 = result2.shallowCloneWithoutEffects();
|
||||
let joinedEffects = Join.joinForkOrChoose(
|
||||
realm,
|
||||
let joinedEffects = Join.joinEffects(
|
||||
joinCondition,
|
||||
new Effects(result1, generator1, modifiedBindings1, modifiedProperties1, createdObjects1),
|
||||
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2)
|
||||
);
|
||||
let completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return To.ToBooleanPartial(realm, completion);
|
||||
return To.ToBooleanPartial(realm, realm.returnOrThrowCompletion(joinedEffects.result));
|
||||
}
|
||||
|
||||
return OrdinarySetHelper();
|
||||
|
@ -15,7 +15,7 @@ import type { Bindings, BindingEntry, EvaluationResult, PropertyBindings, Create
|
||||
import { Effects } from "../realm.js";
|
||||
import type { Descriptor, PropertyBinding } from "../types.js";
|
||||
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { AbruptCompletion, JoinedNormalAndAbruptCompletions, SimpleNormalCompletion } from "../completions.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "./index.js";
|
||||
import { Generator, createOperationDescriptor } from "../utils/generator.js";
|
||||
@ -103,7 +103,7 @@ export class WidenImplementation {
|
||||
realm: Realm,
|
||||
result1: EvaluationResult,
|
||||
result2: EvaluationResult
|
||||
): PossiblyNormalCompletion | SimpleNormalCompletion {
|
||||
): JoinedNormalAndAbruptCompletions | SimpleNormalCompletion {
|
||||
invariant(!(result1 instanceof Reference || result2 instanceof Reference), "loop bodies should not result in refs");
|
||||
invariant(
|
||||
!(result1 instanceof AbruptCompletion || result2 instanceof AbruptCompletion),
|
||||
@ -114,7 +114,7 @@ export class WidenImplementation {
|
||||
invariant(val instanceof Value);
|
||||
return new SimpleNormalCompletion(val);
|
||||
}
|
||||
if (result1 instanceof PossiblyNormalCompletion || result2 instanceof PossiblyNormalCompletion) {
|
||||
if (result1 instanceof JoinedNormalAndAbruptCompletions || result2 instanceof JoinedNormalAndAbruptCompletions) {
|
||||
//todo: #1174 figure out how to deal with loops that have embedded conditional exits
|
||||
// widen join pathConditions
|
||||
// widen normal result and Effects
|
||||
@ -141,6 +141,9 @@ export class WidenImplementation {
|
||||
|
||||
widenBindings(realm: Realm, m1: Bindings, m2: Bindings): Bindings {
|
||||
let widen = (b: Binding, b1: void | BindingEntry, b2: void | BindingEntry) => {
|
||||
let l1 = b1 === undefined ? b.hasLeaked : b1.hasLeaked;
|
||||
let l2 = b2 === undefined ? b.hasLeaked : b2.hasLeaked;
|
||||
let hasLeaked = l1 || l2; // If either has leaked, then this binding has leaked.
|
||||
let v1 = b1 === undefined || b1.value === undefined ? b.value : b1.value;
|
||||
invariant(b2 !== undefined); // Local variables are not going to get deleted as a result of widening
|
||||
let v2 = b2.value;
|
||||
@ -168,14 +171,7 @@ export class WidenImplementation {
|
||||
result.operationDescriptor = createOperationDescriptor("WIDENED_IDENTIFIER", { id: phiName });
|
||||
}
|
||||
invariant(result instanceof Value);
|
||||
let previousHasLeaked = b2.previousHasLeaked;
|
||||
let previousValue = b2.previousValue;
|
||||
return {
|
||||
hasLeaked: previousHasLeaked,
|
||||
value: result,
|
||||
previousHasLeaked,
|
||||
previousValue,
|
||||
};
|
||||
return { hasLeaked, value: result };
|
||||
};
|
||||
return this.widenMaps(m1, m2, widen);
|
||||
}
|
||||
|
@ -65,7 +65,6 @@ export type RealmOptions = {
|
||||
export type SerializerOptions = {
|
||||
lazyObjectsRuntime?: string,
|
||||
delayInitializations?: boolean,
|
||||
delayUnsupportedRequires?: boolean,
|
||||
accelerateUnsupportedRequires?: boolean,
|
||||
initializeMoreModules?: boolean,
|
||||
internalDebug?: boolean,
|
||||
|
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeArrayExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import { GetIterator, GetMethod, IteratorStep, IteratorValue } from "../methods/index.js";
|
||||
import { AbstractValue, NumberValue, ObjectValue, StringValue, Value } from "../values/index.js";
|
||||
import { Create, Properties } from "../singletons.js";
|
||||
|
||||
import invariant from "../invariant.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
// ECMA262 2.2.5.3
|
||||
export default function(
|
||||
ast: BabelNodeArrayExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeArrayExpression, Array<BabelNodeStatement>] {
|
||||
// 1. Let array be ArrayCreate(0).
|
||||
let array = Create.ArrayCreate(realm, 0);
|
||||
|
||||
// 2. Let len be the result of performing ArrayAccumulation for ElementList with arguments array and 0.
|
||||
let elements = ast.elements || [];
|
||||
let partial_elements = [];
|
||||
let io = [];
|
||||
let len = elements.length;
|
||||
let nextIndex = 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let elem = elements[i];
|
||||
if (!elem) {
|
||||
nextIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
let elemValue, elemAst, elemIO;
|
||||
if (elem.type === "SpreadElement")
|
||||
[elemValue, elemAst, elemIO] = env.partiallyEvaluateCompletionDeref(elem.argument, strictCode);
|
||||
else [elemValue, elemAst, elemIO] = env.partiallyEvaluateCompletionDeref(elem, strictCode);
|
||||
io.concat(elemIO);
|
||||
if (elemValue instanceof AbruptCompletion) {
|
||||
return [elemValue, ast, io]; //todo: log an error message
|
||||
} else if (elemValue instanceof PossiblyNormalCompletion) {
|
||||
// TODO: there was a conditional abrupt completion while evaluating elem, so join states somehow
|
||||
AbstractValue.reportIntrospectionError(elemValue.value);
|
||||
throw new FatalError();
|
||||
}
|
||||
invariant(elemValue instanceof Value);
|
||||
partial_elements[nextIndex] = (elemAst: any);
|
||||
|
||||
// ECMA262 12.2.5.2
|
||||
if (elem.type === "SpreadElement") {
|
||||
let spreadObj = elemValue;
|
||||
partial_elements[nextIndex] = t.spreadElement((elemAst: any));
|
||||
|
||||
// update the abstract state with the contents of spreadObj, if known
|
||||
if (spreadObj instanceof ObjectValue && !spreadObj.isPartialObject()) {
|
||||
// 3. Let iterator be ? GetIterator(spreadObj).
|
||||
let iterator = GetIterator(realm, spreadObj);
|
||||
|
||||
// 4. Repeat
|
||||
while (true) {
|
||||
// a. Let next be ? IteratorStep(iterator).
|
||||
let next = IteratorStep(realm, iterator);
|
||||
|
||||
// b. If next is false, return nextIndex.
|
||||
if (next === false) break;
|
||||
|
||||
// c. Let nextValue be ? IteratorValue(next).
|
||||
let nextValue = IteratorValue(realm, next);
|
||||
|
||||
// d. Let status be CreateDataProperty(array, ToString(ToUint32(nextIndex)), nextValue).
|
||||
let status = Create.CreateDataProperty(realm, array, new StringValue(realm, nextIndex + ""), nextValue);
|
||||
|
||||
// e. Assert: status is true.
|
||||
invariant(status === true);
|
||||
|
||||
// f. Let nextIndex be nextIndex + 1.
|
||||
nextIndex++;
|
||||
}
|
||||
} else {
|
||||
// Update the abstract state to reflect our lack of complete knowledge
|
||||
// of all of the properties of the result of evaluating elem.
|
||||
array.makePartial();
|
||||
|
||||
// terminate the loop if all elements have been processed
|
||||
if (i === len - 1) break;
|
||||
|
||||
// If there are elements that come after this spread element, we need
|
||||
// to take their effects into account for the abstract state that results
|
||||
// from the array expression.
|
||||
|
||||
// First check if the runtime spread operation cannot fail
|
||||
if (spreadObj instanceof AbstractValue && spreadObj.getType() === "Array") {
|
||||
let method = GetMethod(realm, spreadObj, realm.intrinsics.SymbolIterator);
|
||||
if (method === realm.intrinsics.ArrayProto_values) continue;
|
||||
}
|
||||
|
||||
// At this point we have to be pessimistic and assume that iterating spreadObj may
|
||||
// throw an exception, in which case we can't assume that the remaining element
|
||||
// expressions will be evaluated at runtime. As a consequence their effects
|
||||
// have be provisional.
|
||||
// TODO: join states somehow
|
||||
AbstractValue.reportIntrospectionError(spreadObj);
|
||||
throw new FatalError();
|
||||
}
|
||||
} else if (array.isPartialObject()) {
|
||||
// Dealing with an array element that follows on a spread object that
|
||||
// could not be iterated at compile time, so the index that this element
|
||||
// will have at runtime is not known at this point.
|
||||
|
||||
let abstractIndex = AbstractValue.createFromType(realm, NumberValue);
|
||||
array.$SetPartial(abstractIndex, elemValue, array);
|
||||
} else {
|
||||
// Redundant steps.
|
||||
// 1. Let postIndex be the result of performing ArrayAccumulation for ElementList with arguments array and nextIndex.
|
||||
// 2. ReturnIfAbrupt(postIndex).
|
||||
// 3. Let padding be the ElisionWidth of Elision; if Elision is not present, use the numeric value zero.
|
||||
|
||||
// 4. Let initResult be the result of evaluating AssignmentExpression.
|
||||
// 5. Let initValue be ? GetValue(initResult).
|
||||
let initValue = elemValue;
|
||||
|
||||
// 6. Let created be CreateDataProperty(array, ToString(ToUint32(postIndex+padding)), initValue).
|
||||
let created = Create.CreateDataProperty(realm, array, new StringValue(realm, nextIndex++ + ""), initValue);
|
||||
|
||||
// 7. Assert: created is true.
|
||||
invariant(created === true, "expected data property creation");
|
||||
}
|
||||
}
|
||||
|
||||
// Not necessary since we propagate completions with exceptions.
|
||||
// 3. ReturnIfAbrupt(len).
|
||||
|
||||
// 4. Perform Set(array, "length", ToUint32(len), false).
|
||||
Properties.Set(realm, array, "length", new NumberValue(realm, nextIndex), false);
|
||||
|
||||
// 5. NOTE: The above Set cannot fail because of the nature of the object returned by ArrayCreate.
|
||||
|
||||
// 6. Return array.
|
||||
return [array, t.arrayExpression(partial_elements), io];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeArrowFunctionExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 14.2.16
|
||||
export default function(
|
||||
ast: BabelNodeArrowFunctionExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeArrowFunctionExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type {
|
||||
BabelBinaryOperator,
|
||||
BabelNodeAssignmentExpression,
|
||||
BabelNodeExpression,
|
||||
BabelNodeStatement,
|
||||
} from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { computeBinary } from "../evaluators/BinaryExpression.js";
|
||||
import { createAbstractValueForBinary } from "../partial-evaluators/BinaryExpression.js";
|
||||
import { AbruptCompletion, Completion } from "../completions.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import { BooleanValue, ConcreteValue, NullValue, ObjectValue, UndefinedValue, Value } from "../values/index.js";
|
||||
import { IsAnonymousFunctionDefinition, IsIdentifierRef, HasOwnProperty } from "../methods/index.js";
|
||||
import { Environment, Functions, Join, Properties } from "../singletons.js";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
// ECMA262 12.15 Assignment Operators
|
||||
export default function(
|
||||
ast: BabelNodeAssignmentExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeExpression, Array<BabelNodeStatement>] {
|
||||
let LeftHandSideExpression = ast.left;
|
||||
let AssignmentExpression = ast.right;
|
||||
let AssignmentOperator = ast.operator;
|
||||
|
||||
// AssignmentExpression : LeftHandSideExpression = AssignmentExpression
|
||||
if (AssignmentOperator === "=") {
|
||||
// 1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
|
||||
if (LeftHandSideExpression.type !== "ObjectLiteral" && LeftHandSideExpression.type !== "ArrayLiteral") {
|
||||
// a. Let lref be the result of evaluating LeftHandSideExpression.
|
||||
let [lref, last, lio] = env.partiallyEvaluateCompletion(LeftHandSideExpression, strictCode);
|
||||
|
||||
// b. ReturnIfAbrupt(lref).
|
||||
if (lref instanceof AbruptCompletion) return [lref, (last: any), lio];
|
||||
let leftCompletion;
|
||||
[leftCompletion, lref] = Join.unbundleNormalCompletion(lref);
|
||||
|
||||
// c. Let rref be the result of evaluating AssignmentExpression.
|
||||
// d. Let rval be ? GetValue(rref).
|
||||
let [rval, rast, rio] = env.partiallyEvaluateCompletionDeref(AssignmentExpression, strictCode);
|
||||
let io = lio.concat(rio);
|
||||
if (rval instanceof AbruptCompletion) {
|
||||
return [rval, t.assignmentExpression(ast.operator, (last: any), (rast: any)), io];
|
||||
}
|
||||
let rightCompletion;
|
||||
[rightCompletion, rval] = Join.unbundleNormalCompletion(rval);
|
||||
invariant(rval instanceof Value);
|
||||
|
||||
// e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
|
||||
if (
|
||||
IsAnonymousFunctionDefinition(realm, AssignmentExpression) &&
|
||||
IsIdentifierRef(realm, LeftHandSideExpression)
|
||||
) {
|
||||
invariant(rval instanceof ObjectValue);
|
||||
|
||||
// i. Let hasNameProperty be ? HasOwnProperty(rval, "name").
|
||||
let hasNameProperty = HasOwnProperty(realm, rval, "name");
|
||||
|
||||
// ii. If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)).
|
||||
if (!hasNameProperty) {
|
||||
invariant(lref instanceof Reference);
|
||||
Functions.SetFunctionName(realm, rval, Environment.GetReferencedName(realm, lref));
|
||||
}
|
||||
}
|
||||
|
||||
// f. Perform ? PutValue(lref, rval).
|
||||
Properties.PutValue(realm, lref, rval);
|
||||
|
||||
// g. Return rval.
|
||||
let resultAst = t.assignmentExpression(ast.operator, (last: any), (rast: any));
|
||||
rval = Join.composeNormalCompletions(leftCompletion, rightCompletion, rval, realm);
|
||||
return [rval, resultAst, io];
|
||||
}
|
||||
throw new FatalError("Patterns aren't supported yet");
|
||||
// 2. Let assignmentPattern be the parse of the source text corresponding to LeftHandSideExpression using AssignmentPattern[?Yield] as the goal symbol.
|
||||
// 3. Let rref be the result of evaluating AssignmentExpression.
|
||||
// 4. Let rval be ? GetValue(rref).
|
||||
// 5. Let status be the result of performing DestructuringAssignmentEvaluation of assignmentPattern using rval as the argument.
|
||||
// 6. ReturnIfAbrupt(status).
|
||||
// 7. Return rval.
|
||||
}
|
||||
|
||||
// AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression
|
||||
|
||||
// 1. Let lref be the result of evaluating LeftHandSideExpression.
|
||||
let [lref, last, lio] = env.partiallyEvaluateCompletion(LeftHandSideExpression, strictCode);
|
||||
if (lref instanceof AbruptCompletion) return [lref, (last: any), lio];
|
||||
let leftCompletion;
|
||||
[leftCompletion, lref] = Join.unbundleNormalCompletion(lref);
|
||||
|
||||
// 2. Let lval be ? GetValue(lref).
|
||||
let lval = Environment.GetValue(realm, lref);
|
||||
|
||||
// 3. Let rref be the result of evaluating AssignmentExpression.
|
||||
// 4. Let rval be ? GetValue(rref).
|
||||
let [rval, rast, rio] = env.partiallyEvaluateCompletionDeref(AssignmentExpression, strictCode);
|
||||
let io = lio.concat(rio);
|
||||
if (rval instanceof AbruptCompletion) {
|
||||
return [rval, t.assignmentExpression(ast.operator, (last: any), (rast: any)), io];
|
||||
}
|
||||
let rightCompletion;
|
||||
[rightCompletion, rval] = Join.unbundleNormalCompletion(rval);
|
||||
invariant(rval instanceof Value);
|
||||
|
||||
// 5. Let op be the @ where AssignmentOperator is @=.
|
||||
let op = ((AssignmentOperator.slice(0, -1): any): BabelBinaryOperator);
|
||||
|
||||
// 6. Let r be the result of applying op to lval and rval as if evaluating the expression lval op rval.
|
||||
let resultValue, resultAst;
|
||||
if (lval instanceof ConcreteValue) {
|
||||
if (rval instanceof ConcreteValue) {
|
||||
resultValue = computeBinary(realm, op, lval, rval);
|
||||
resultAst = t.assignmentExpression(ast.operator, (last: any), t.valueToNode(resultValue.serialize()));
|
||||
}
|
||||
}
|
||||
// if resultValue is undefined, one or both operands are abstract.
|
||||
if (resultValue === undefined && (op === "==" || op === "===" || op === "!=" || op === "!==")) {
|
||||
// When comparing to null or undefined, we can return a compile time value if we know the
|
||||
// other operand must be an object.
|
||||
if (
|
||||
(!lval.mightNotBeObject() && (rval instanceof NullValue || rval instanceof UndefinedValue)) ||
|
||||
(!rval.mightNotBeObject() && (lval instanceof NullValue || lval instanceof UndefinedValue))
|
||||
) {
|
||||
resultValue = new BooleanValue(realm, op[0] !== "=");
|
||||
resultAst = t.assignmentExpression(ast.operator, (last: any), t.valueToNode(resultValue.serialize()));
|
||||
}
|
||||
}
|
||||
// todo: special case if one result is known to be 0 or 1
|
||||
if (resultAst === undefined) {
|
||||
resultAst = t.assignmentExpression(ast.operator, (last: any), (rast: any));
|
||||
}
|
||||
return createAbstractValueForBinary(
|
||||
op,
|
||||
resultAst,
|
||||
lval,
|
||||
rval,
|
||||
last.loc,
|
||||
rast.loc,
|
||||
leftCompletion,
|
||||
rightCompletion,
|
||||
resultValue,
|
||||
io,
|
||||
realm
|
||||
);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeAwaitExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeAwaitExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeAwaitExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type {
|
||||
BabelBinaryOperator,
|
||||
BabelNodeBinaryExpression,
|
||||
BabelNodeExpression,
|
||||
BabelNodeStatement,
|
||||
BabelNodeSourceLocation,
|
||||
} from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { computeBinary, getPureBinaryOperationResultType } from "../evaluators/BinaryExpression.js";
|
||||
import { AbruptCompletion, Completion, NormalCompletion } from "../completions.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import { Join } from "../singletons.js";
|
||||
import { AbstractValue, BooleanValue, ConcreteValue, NullValue, UndefinedValue, Value } from "../values/index.js";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeBinaryExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeExpression, Array<BabelNodeStatement>] {
|
||||
let [lval, leftAst, leftIO] = env.partiallyEvaluateCompletionDeref(ast.left, strictCode);
|
||||
if (lval instanceof AbruptCompletion) return [lval, (leftAst: any), leftIO];
|
||||
let leftCompletion;
|
||||
[leftCompletion, lval] = Join.unbundleNormalCompletion(lval);
|
||||
invariant(lval instanceof Value);
|
||||
|
||||
let [rval, rightAst, rightIO] = env.partiallyEvaluateCompletionDeref(ast.right, strictCode);
|
||||
let io = leftIO.concat(rightIO);
|
||||
if (rval instanceof AbruptCompletion) {
|
||||
// todo: if leftCompletion is a PossiblyNormalCompletion, compose
|
||||
return [rval, t.binaryExpression(ast.operator, (leftAst: any), (rightAst: any)), io];
|
||||
}
|
||||
let rightCompletion;
|
||||
[rightCompletion, rval] = Join.unbundleNormalCompletion(rval);
|
||||
invariant(rval instanceof Value);
|
||||
|
||||
let op = ast.operator;
|
||||
let resultValue, resultAst;
|
||||
if (lval instanceof ConcreteValue) {
|
||||
if (rval instanceof ConcreteValue) {
|
||||
resultValue = computeBinary(realm, op, lval, rval);
|
||||
resultAst = t.valueToNode(resultValue.serialize());
|
||||
}
|
||||
}
|
||||
// if resultValue is undefined, one or both operands are abstract.
|
||||
if (resultValue === undefined && (op === "==" || op === "===" || op === "!=" || op === "!==")) {
|
||||
// When comparing to null or undefined, we can return a compile time value if we know the
|
||||
// other operand must be an object.
|
||||
if (
|
||||
(!lval.mightNotBeObject() && (rval instanceof NullValue || rval instanceof UndefinedValue)) ||
|
||||
(!rval.mightNotBeObject() && (lval instanceof NullValue || lval instanceof UndefinedValue))
|
||||
) {
|
||||
resultValue = new BooleanValue(realm, op[0] !== "=");
|
||||
resultAst = t.valueToNode(resultValue.serialize());
|
||||
}
|
||||
}
|
||||
// todo: special case if one result is known to be 0 or 1
|
||||
if (resultAst === undefined) {
|
||||
resultAst = t.binaryExpression(op, (leftAst: any), (rightAst: any));
|
||||
}
|
||||
return createAbstractValueForBinary(
|
||||
op,
|
||||
resultAst,
|
||||
lval,
|
||||
rval,
|
||||
leftAst.loc,
|
||||
rightAst.loc,
|
||||
leftCompletion,
|
||||
rightCompletion,
|
||||
resultValue,
|
||||
io,
|
||||
realm
|
||||
);
|
||||
}
|
||||
|
||||
export function createAbstractValueForBinary(
|
||||
op: BabelBinaryOperator,
|
||||
ast: BabelNodeExpression,
|
||||
lval: Value,
|
||||
rval: Value,
|
||||
lloc: ?BabelNodeSourceLocation,
|
||||
rloc: ?BabelNodeSourceLocation,
|
||||
leftCompletion: void | NormalCompletion,
|
||||
rightCompletion: void | NormalCompletion,
|
||||
resultValue: void | Value,
|
||||
io: Array<BabelNodeStatement>,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeExpression, Array<BabelNodeStatement>] {
|
||||
if (resultValue === undefined) {
|
||||
let resultType = getPureBinaryOperationResultType(realm, op, lval, rval, lloc, rloc);
|
||||
if (resultType === undefined) {
|
||||
// The operation may result in side effects that we cannot track.
|
||||
// Since we have no idea what those effects are, we can either forget
|
||||
// (havoc) everything we know at this stage, or we can fault the
|
||||
// program and/or native model and stop evaluating.
|
||||
// We choose to do the latter.
|
||||
// TODO: report the error and carry on assuming no side effects.
|
||||
let val = lval instanceof AbstractValue ? lval : rval;
|
||||
AbstractValue.reportIntrospectionError((val: any));
|
||||
throw new FatalError();
|
||||
}
|
||||
resultValue = AbstractValue.createFromBinaryOp(realm, op, lval, rval, ast.loc);
|
||||
}
|
||||
let r = Join.composeNormalCompletions(leftCompletion, rightCompletion, resultValue, realm);
|
||||
return [r, ast, io];
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeBlockStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
|
||||
import { Completion, NormalCompletion } from "../completions.js";
|
||||
import { EmptyValue, StringValue, Value } from "../values/index.js";
|
||||
import { Environment, Functions } from "../singletons.js";
|
||||
|
||||
import invariant from "../invariant.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
// ECMA262 13.2.13
|
||||
export default function(
|
||||
ast: BabelNodeBlockStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeStatement, Array<BabelNodeStatement>] {
|
||||
// 1. Let oldEnv be the running execution context's LexicalEnvironment.
|
||||
let oldEnv = realm.getRunningContext().lexicalEnvironment;
|
||||
|
||||
// 2. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
|
||||
let blockEnv = Environment.NewDeclarativeEnvironment(realm, oldEnv);
|
||||
|
||||
// 3. Perform BlockDeclarationInstantiation(StatementList, blockEnv).
|
||||
Environment.BlockDeclarationInstantiation(realm, strictCode, ast.body, blockEnv);
|
||||
|
||||
// 4. Set the running execution context's LexicalEnvironment to blockEnv.
|
||||
realm.getRunningContext().lexicalEnvironment = blockEnv;
|
||||
|
||||
try {
|
||||
// 5. Let blockValue be the result of evaluating StatementList.
|
||||
let blockValue: void | NormalCompletion | Value;
|
||||
|
||||
if (ast.directives) {
|
||||
for (let directive of ast.directives) {
|
||||
blockValue = new StringValue(realm, directive.value.value);
|
||||
}
|
||||
}
|
||||
|
||||
let [res, bAst] = Functions.PartiallyEvaluateStatements(ast.body, blockValue, strictCode, blockEnv, realm);
|
||||
invariant(bAst.length > 0 || res instanceof EmptyValue);
|
||||
if (bAst.length === 0) return [res, t.emptyStatement(), []];
|
||||
let rAst = t.blockStatement(bAst, ast.directives);
|
||||
return [res, rAst, []];
|
||||
} finally {
|
||||
// 6. Set the running execution context's LexicalEnvironment to oldEnv.
|
||||
realm.getRunningContext().lexicalEnvironment = oldEnv;
|
||||
realm.onDestroyScope(blockEnv);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeBooleanLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { BooleanValue } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeBooleanLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [BooleanValue, BabelNodeBooleanLiteral, Array<BabelNodeStatement>] {
|
||||
let result = new BooleanValue(realm, ast.value);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeBreakStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
|
||||
import { BreakCompletion } from "../completions.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeBreakStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [BreakCompletion, BabelNodeBreakStatement, Array<BabelNodeStatement>] {
|
||||
let result = new BreakCompletion(realm.intrinsics.empty, undefined, ast.loc, ast.label && ast.label.name);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeCallExpression, BabelNodeExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
|
||||
import { AbruptCompletion, Completion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { EnvironmentRecord, Reference } from "../environment.js";
|
||||
import { EvaluateDirectCallWithArgList, GetThisValue, IsInTailPosition, SameValue } from "../methods/index.js";
|
||||
import { Environment, Functions, Join } from "../singletons.js";
|
||||
import { AbstractValue, BooleanValue, FunctionValue, Value } from "../values/index.js";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
// ECMA262 12.3.4.1
|
||||
export default function(
|
||||
ast: BabelNodeCallExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeExpression, Array<BabelNodeStatement>] {
|
||||
// 1. Let ref be the result of evaluating MemberExpression.
|
||||
let [ref, calleeAst, calleeIO] = env.partiallyEvaluateCompletion(ast.callee, strictCode);
|
||||
if (ref instanceof AbruptCompletion) return [ref, (calleeAst: any), calleeIO];
|
||||
let completion;
|
||||
if (ref instanceof PossiblyNormalCompletion) {
|
||||
completion = ref;
|
||||
ref = completion.value;
|
||||
}
|
||||
invariant(ref instanceof Value || ref instanceof Reference);
|
||||
|
||||
// 2. Let func be ? GetValue(ref).
|
||||
let func = Environment.GetValue(realm, ref);
|
||||
|
||||
let io = calleeIO;
|
||||
let partialArgs = [];
|
||||
let argVals = [];
|
||||
for (let arg of ast.arguments) {
|
||||
let [argValue, argAst, argIO] = env.partiallyEvaluateCompletionDeref(arg, strictCode);
|
||||
io = io.concat(argIO);
|
||||
partialArgs.push((argAst: any));
|
||||
if (argValue instanceof AbruptCompletion) {
|
||||
if (completion instanceof PossiblyNormalCompletion)
|
||||
completion = Join.stopEffectCaptureJoinApplyAndReturnCompletion(completion, argValue, realm);
|
||||
else completion = argValue;
|
||||
let resultAst = t.callExpression((calleeAst: any), partialArgs);
|
||||
return [completion, resultAst, io];
|
||||
}
|
||||
if (argValue instanceof PossiblyNormalCompletion) {
|
||||
argVals.push(argValue.value);
|
||||
if (completion instanceof PossiblyNormalCompletion)
|
||||
completion = Join.composeNormalCompletions(completion, argValue, argValue.value, realm);
|
||||
else completion = argValue;
|
||||
} else {
|
||||
invariant(argValue instanceof Value);
|
||||
argVals.push(argValue);
|
||||
}
|
||||
}
|
||||
|
||||
let previousLoc = realm.setNextExecutionContextLocation(ast.loc);
|
||||
try {
|
||||
let callResult = EvaluateCall(ref, func, ast, argVals, strictCode, env, realm);
|
||||
if (callResult instanceof AbruptCompletion) {
|
||||
if (completion instanceof PossiblyNormalCompletion)
|
||||
completion = Join.stopEffectCaptureJoinApplyAndReturnCompletion(completion, callResult, realm);
|
||||
else completion = callResult;
|
||||
let resultAst = t.callExpression((calleeAst: any), partialArgs);
|
||||
return [completion, resultAst, io];
|
||||
}
|
||||
let callCompletion;
|
||||
[callCompletion, callResult] = Join.unbundleNormalCompletion(callResult);
|
||||
invariant(callResult instanceof Value);
|
||||
invariant(completion === undefined || completion instanceof PossiblyNormalCompletion);
|
||||
completion = Join.composeNormalCompletions(completion, callCompletion, callResult, realm);
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
realm.captureEffects(completion);
|
||||
}
|
||||
return [completion, t.callExpression((calleeAst: any), partialArgs), io];
|
||||
} finally {
|
||||
realm.setNextExecutionContextLocation(previousLoc);
|
||||
}
|
||||
}
|
||||
|
||||
function callBothFunctionsAndJoinTheirEffects(
|
||||
funcs: Array<Value>,
|
||||
ast: BabelNodeCallExpression,
|
||||
argVals: Array<Value>,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): AbruptCompletion | Value {
|
||||
let [cond, func1, func2] = funcs;
|
||||
invariant(cond instanceof AbstractValue && cond.getType() === BooleanValue);
|
||||
invariant(Value.isTypeCompatibleWith(func1.getType(), FunctionValue));
|
||||
invariant(Value.isTypeCompatibleWith(func2.getType(), FunctionValue));
|
||||
|
||||
const e1 = realm.evaluateForEffects(
|
||||
() => EvaluateCall(func1, func1, ast, argVals, strictCode, env, realm),
|
||||
undefined,
|
||||
"callBothFunctionsAndJoinTheirEffects/1"
|
||||
);
|
||||
let r1 = e1.result.shallowCloneWithoutEffects();
|
||||
|
||||
const e2 = realm.evaluateForEffects(
|
||||
() => EvaluateCall(func2, func2, ast, argVals, strictCode, env, realm),
|
||||
undefined,
|
||||
"callBothFunctionsAndJoinTheirEffects/2"
|
||||
);
|
||||
let r2 = e2.result.shallowCloneWithoutEffects();
|
||||
|
||||
let joinedEffects = Join.joinForkOrChoose(realm, cond, e1.shallowCloneWithResult(r1), e2.shallowCloneWithResult(r2));
|
||||
let joinedCompletion = joinedEffects.result;
|
||||
if (joinedCompletion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
joinedCompletion = realm.composeWithSavedCompletion(joinedCompletion);
|
||||
}
|
||||
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside joinedCompletion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
// return or throw completion
|
||||
if (joinedCompletion instanceof SimpleNormalCompletion) joinedCompletion = joinedCompletion.value;
|
||||
invariant(joinedCompletion instanceof AbruptCompletion || joinedCompletion instanceof Value);
|
||||
return joinedCompletion;
|
||||
}
|
||||
|
||||
function EvaluateCall(
|
||||
ref: Value | Reference,
|
||||
func: Value,
|
||||
ast: BabelNodeCallExpression,
|
||||
argList: Array<Value>,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): AbruptCompletion | Value {
|
||||
if (func instanceof AbstractValue && Value.isTypeCompatibleWith(func.getType(), FunctionValue)) {
|
||||
if (func.kind === "conditional")
|
||||
return callBothFunctionsAndJoinTheirEffects(func.args, ast, argList, strictCode, env, realm);
|
||||
|
||||
// The called function comes from the environmental model and we require that
|
||||
// such functions have no visible side-effects. Hence we can carry on
|
||||
// by returning a call node with the arguments updated with their partial counterparts.
|
||||
// TODO: obtain the type of the return value from the abstract function.
|
||||
return AbstractValue.createFromType(realm, Value);
|
||||
}
|
||||
// If func is abstract and not known to be a safe function, we can't safely continue.
|
||||
func = func.throwIfNotConcrete();
|
||||
|
||||
// 3. If Type(ref) is Reference and IsPropertyReference(ref) is false and GetReferencedName(ref) is "eval", then
|
||||
if (
|
||||
ref instanceof Reference &&
|
||||
!Environment.IsPropertyReference(realm, ref) &&
|
||||
Environment.GetReferencedName(realm, ref) === "eval"
|
||||
) {
|
||||
// a. If SameValue(func, %eval%) is true, then
|
||||
if (SameValue(realm, func, realm.intrinsics.eval)) {
|
||||
// i. Let argList be ? ArgumentListEvaluation(Arguments).
|
||||
|
||||
// ii. If argList has no elements, return undefined.
|
||||
if (argList.length === 0) return realm.intrinsics.undefined;
|
||||
|
||||
// iii. Let evalText be the first element of argList.
|
||||
let evalText = argList[0];
|
||||
|
||||
// iv. If the source code matching this CallExpression is strict code, let strictCaller be true. Otherwise let strictCaller be false.
|
||||
let strictCaller = strictCode;
|
||||
|
||||
// v. Let evalRealm be the current Realm Record.
|
||||
let evalRealm = realm;
|
||||
|
||||
// vi. Return ? PerformEval(evalText, evalRealm, strictCaller, true).
|
||||
return Functions.PerformEval(realm, evalText, evalRealm, strictCaller, true);
|
||||
}
|
||||
}
|
||||
|
||||
let thisValue;
|
||||
|
||||
// 4. If Type(ref) is Reference, then
|
||||
if (ref instanceof Reference) {
|
||||
// a. If IsPropertyReference(ref) is true, then
|
||||
if (Environment.IsPropertyReference(realm, ref)) {
|
||||
// i. Let thisValue be GetThisValue(ref).
|
||||
thisValue = GetThisValue(realm, ref);
|
||||
} else {
|
||||
// b. Else, the base of ref is an Environment Record
|
||||
// i. Let refEnv be GetBase(ref).
|
||||
let refEnv = Environment.GetBase(realm, ref);
|
||||
invariant(refEnv instanceof EnvironmentRecord);
|
||||
|
||||
// ii. Let thisValue be refEnv.WithBaseObject().
|
||||
thisValue = refEnv.WithBaseObject();
|
||||
}
|
||||
} else {
|
||||
// 5. Else Type(ref) is not Reference,
|
||||
// a. Let thisValue be undefined.
|
||||
thisValue = realm.intrinsics.undefined;
|
||||
}
|
||||
|
||||
// 6. Let thisCall be this CallExpression.
|
||||
let thisCall = ast;
|
||||
|
||||
// 7. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
|
||||
let tailCall = IsInTailPosition(realm, thisCall);
|
||||
|
||||
// 8. Return ? EvaluateDirectCall(func, thisValue, Arguments, tailCall).
|
||||
|
||||
try {
|
||||
realm.currentLocation = ast.loc; // this helps us to detect recursive calls
|
||||
return EvaluateDirectCallWithArgList(realm, strictCode, env, ref, func, thisValue, argList, tailCall);
|
||||
} catch (err) {
|
||||
if (err instanceof Completion) return err;
|
||||
throw err;
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeCatchClause, BabelNodeStatement } from "@babel/types";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
|
||||
import { AbruptCompletion, ThrowCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
// ECAM262 13.15.7
|
||||
export default function(
|
||||
ast: BabelNodeCatchClause,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
thrownValue: any
|
||||
): [AbruptCompletion | Value, BabelNodeCatchClause, Array<BabelNodeStatement>] {
|
||||
invariant(thrownValue instanceof ThrowCompletion, "Metadata isn't a throw completion");
|
||||
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeClassDeclaration, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeClassDeclaration,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeClassDeclaration, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeClassExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeClassExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeClassExpression, Array<BabelNodeStatement>] {
|
||||
throw new FatalError("TODO: ClassExpression");
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeConditionalExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeConditionalExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeConditionalExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeContinueStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { ContinueCompletion } from "../completions.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeContinueStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [ContinueCompletion, BabelNodeContinueStatement, Array<BabelNodeStatement>] {
|
||||
let result = new ContinueCompletion(realm.intrinsics.empty, undefined, ast.loc, ast.label && ast.label.name);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeDirective, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeDirective,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeDirective, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast.value, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
export { default } from "./StringLiteral";
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
export { default } from "./BlockStatement.js";
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeDoWhileStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeDoWhileStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeDoWhileStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeEmptyStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { EmptyValue } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeEmptyStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [EmptyValue, BabelNodeEmptyStatement, Array<BabelNodeStatement>] {
|
||||
return [realm.intrinsics.empty, ast, []];
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeExpressionStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { Completion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
// ECMA262 13.5.1
|
||||
export default function(
|
||||
ast: BabelNodeExpressionStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeExpressionStatement, Array<BabelNodeStatement>] {
|
||||
let [result, partial_expression_ast, io] = env.partiallyEvaluateCompletionDeref(ast.expression, strictCode);
|
||||
let partial_ast = t.expressionStatement((partial_expression_ast: any));
|
||||
return [result, partial_ast, io];
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeFile, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { Completion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeFile,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeFile, Array<BabelNodeStatement>] {
|
||||
let [result, partial_program, io] = env.partiallyEvaluateCompletionDeref(ast.program, strictCode);
|
||||
let partial_file = t.file((partial_program: any), ast.comments, ast.tokens);
|
||||
return [result, partial_file, io];
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeForInStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.7.5.11
|
||||
export default function(
|
||||
ast: BabelNodeForInStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeForInStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeForOfStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.7.5.11
|
||||
export default function(
|
||||
ast: BabelNodeForOfStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeForOfStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeForStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.7.4.7
|
||||
export default function(
|
||||
ast: BabelNodeForStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeForStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeFunctionDeclaration, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 14.1.20
|
||||
export default function(
|
||||
ast: BabelNodeFunctionDeclaration,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeFunctionDeclaration, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeFunctionExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeFunctionExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeFunctionExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeIdentifier, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment, Reference } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.1.6
|
||||
export default function(
|
||||
ast: BabelNodeIdentifier,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Reference | Value, BabelNodeIdentifier, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletion(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeIfStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion, Completion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { UpdateEmpty } from "../methods/index.js";
|
||||
import { AbstractValue, Value } from "../values/index.js";
|
||||
import { construct_empty_effects } from "../realm.js";
|
||||
import { Join } from "../singletons.js";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
import invariant from "../invariant.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeIfStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeStatement, Array<BabelNodeStatement>] {
|
||||
let [exprValue, exprAst, exprIO] = env.partiallyEvaluateCompletionDeref(ast.test, strictCode);
|
||||
if (exprValue instanceof AbruptCompletion) return [exprValue, t.expressionStatement((exprAst: any)), exprIO];
|
||||
let completion;
|
||||
if (exprValue instanceof PossiblyNormalCompletion) {
|
||||
completion = exprValue;
|
||||
exprValue = completion.value;
|
||||
}
|
||||
invariant(exprValue instanceof Value);
|
||||
|
||||
if (!exprValue.mightNotBeTrue()) {
|
||||
// 3.a. Let stmtCompletion be the result of evaluating the first Statement
|
||||
let [stmtCompletion, stmtAst, stmtIO] = env.partiallyEvaluateCompletionDeref(ast.consequent, strictCode);
|
||||
|
||||
// 5. Return Completion(UpdateEmpty(stmtCompletion, undefined)
|
||||
stmtCompletion = UpdateEmpty(realm, stmtCompletion, realm.intrinsics.undefined);
|
||||
return [stmtCompletion, (stmtAst: any), exprIO.concat(stmtIO)];
|
||||
} else if (!exprValue.mightNotBeFalse()) {
|
||||
let stmtCompletion, stmtAst, stmtIO;
|
||||
if (ast.alternate)
|
||||
// 4.a. Let stmtCompletion be the result of evaluating the second Statement
|
||||
[stmtCompletion, stmtAst, stmtIO] = env.partiallyEvaluateCompletionDeref(ast.alternate, strictCode);
|
||||
else {
|
||||
// 3 (of the if only statement). Return NormalCompletion(undefined)
|
||||
stmtCompletion = realm.intrinsics.undefined;
|
||||
stmtAst = t.emptyStatement();
|
||||
stmtIO = [];
|
||||
}
|
||||
// 5. Return Completion(UpdateEmpty(stmtCompletion, undefined)
|
||||
stmtCompletion = UpdateEmpty(realm, stmtCompletion, realm.intrinsics.undefined);
|
||||
return [stmtCompletion, (stmtAst: any), exprIO.concat(stmtIO)];
|
||||
}
|
||||
invariant(exprValue instanceof AbstractValue);
|
||||
|
||||
// Evaluate consequent and alternate in sandboxes and get their effects.
|
||||
let [consequentEffects, conAst, conIO] = realm.partiallyEvaluateNodeForEffects(ast.consequent, strictCode, env);
|
||||
let consequentAst = (conAst: any);
|
||||
if (conIO.length > 0) consequentAst = t.blockStatement(conIO.concat(consequentAst));
|
||||
|
||||
let [alternateEffects, altAst, altIO] = ast.alternate
|
||||
? realm.partiallyEvaluateNodeForEffects(ast.alternate, strictCode, env)
|
||||
: [construct_empty_effects(realm), undefined, []];
|
||||
let alternateAst = (altAst: any);
|
||||
if (altIO.length > 0) alternateAst = t.blockStatement(altIO.concat(alternateAst));
|
||||
|
||||
// Join the effects, creating an abstract view of what happened, regardless
|
||||
// of the actual value of exprValue.
|
||||
const ce = consequentEffects;
|
||||
let cr = ce.result.shallowCloneWithoutEffects();
|
||||
const ae = alternateEffects;
|
||||
let ar = ae.result.shallowCloneWithoutEffects();
|
||||
let joinedEffects = Join.joinForkOrChoose(
|
||||
realm,
|
||||
exprValue,
|
||||
ce.shallowCloneWithResult(cr),
|
||||
ae.shallowCloneWithResult(ar)
|
||||
);
|
||||
completion = joinedEffects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
realm.captureEffects(completion);
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in joinedEffects, but are tracked separately inside completion.
|
||||
realm.applyEffects(joinedEffects);
|
||||
|
||||
let resultAst = t.ifStatement((exprAst: any), (consequentAst: any), (alternateAst: any));
|
||||
invariant(!(completion instanceof Reference));
|
||||
return [completion, resultAst, exprIO];
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeLabeledStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.13.14
|
||||
|
||||
// ECMA262 13.13.15
|
||||
export default function(
|
||||
ast: BabelNodeLabeledStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeLabeledStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeLogicalExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeLogicalExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeLogicalExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeMemberExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment, Reference } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.3.2.1
|
||||
export default function(
|
||||
ast: BabelNodeMemberExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Reference | Value, BabelNodeMemberExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletion(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeMetaProperty, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA 12.3.8.1
|
||||
export default function(
|
||||
ast: BabelNodeMetaProperty,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeMetaProperty, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeNewExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeNewExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeNewExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeNullLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { NullValue } from "../values/index.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeNullLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [NullValue, BabelNodeNullLiteral, Array<BabelNodeStatement>] {
|
||||
let result = realm.intrinsics.null;
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeNumericLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { NumberValue } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeNumericLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [NumberValue, BabelNodeNumericLiteral, Array<BabelNodeStatement>] {
|
||||
let result = new NumberValue(realm, ast.value);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeObjectExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.2.6.8
|
||||
export default function(
|
||||
ast: BabelNodeObjectExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeObjectExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeProgram, BabelNodeStatement, BabelNodeModuleDeclaration } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { Completion } from "../completions.js";
|
||||
import { EmptyValue, Value } from "../values/index.js";
|
||||
import { GlobalDeclarationInstantiation } from "../evaluators/Program.js";
|
||||
|
||||
import IsStrict from "../utils/strict.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeProgram,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNodeProgram, Array<BabelNodeStatement>] {
|
||||
strictCode = IsStrict(ast);
|
||||
|
||||
GlobalDeclarationInstantiation(realm, ast, env, strictCode);
|
||||
|
||||
let partialBody: Array<BabelNodeStatement | BabelNodeModuleDeclaration> = [];
|
||||
let val;
|
||||
|
||||
for (let node of ast.body) {
|
||||
if (node.type !== "FunctionDeclaration") {
|
||||
let [potentialVal, partialAst, nio] = env.partiallyEvaluateCompletionDeref(node, strictCode);
|
||||
for (let ioAst of nio) partialBody.push(ioAst);
|
||||
partialBody.push((partialAst: any));
|
||||
if (!(potentialVal instanceof EmptyValue)) val = potentialVal;
|
||||
} else {
|
||||
// TODO: this goes away once residual functions are partially evaluated.
|
||||
partialBody.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: compute a global fixed point by invoking each escaped (i.e. call back)
|
||||
// function with dummy arguments and joining their effects with the
|
||||
// global state until there is no invocation that causes further changes to
|
||||
// the global state.
|
||||
|
||||
let result = val || realm.intrinsics.empty;
|
||||
return [result, t.program(partialBody, ast.directives), []];
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeRegExpLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { RegExpCreate } from "../methods/index.js";
|
||||
import { ObjectValue, StringValue } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeRegExpLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [ObjectValue, BabelNodeRegExpLiteral, Array<BabelNodeStatement>] {
|
||||
let result = RegExpCreate(
|
||||
realm,
|
||||
new StringValue(realm, ast.pattern),
|
||||
ast.flags ? new StringValue(realm, ast.flags) : undefined
|
||||
);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeReturnStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
|
||||
import { AbruptCompletion, ReturnCompletion } from "../completions.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeReturnStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion, BabelNodeReturnStatement, Array<BabelNodeStatement>] {
|
||||
let result;
|
||||
if (ast.argument) {
|
||||
result = env.evaluateCompletionDeref(ast.argument, strictCode);
|
||||
} else {
|
||||
result = realm.intrinsics.undefined;
|
||||
}
|
||||
if (!(result instanceof AbruptCompletion)) result = new ReturnCompletion(result, undefined, ast.loc);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeSequenceExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeSequenceExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeSequenceExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeStringLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { StringValue } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeStringLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [StringValue, BabelNodeStringLiteral, Array<BabelNodeStatement>] {
|
||||
let result = new StringValue(realm, ast.value);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeSwitchStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// 13.12.11
|
||||
export default function(
|
||||
ast: BabelNodeSwitchStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeSwitchStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeTaggedTemplateExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.3.7
|
||||
export default function(
|
||||
ast: BabelNodeTaggedTemplateExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeTaggedTemplateExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeTemplateLiteral, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.2.9
|
||||
export default function(
|
||||
ast: BabelNodeTemplateLiteral,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeTemplateLiteral, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeThisExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 12.2.2.1
|
||||
export default function(
|
||||
ast: BabelNodeThisExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeThisExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNode, BabelNodeStatement, BabelNodeThrowStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import { Completion, ThrowCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
import * as t from "@babel/types";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeThrowStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Completion | Value, BabelNode, Array<BabelNodeStatement>] {
|
||||
let [argValue, argAst, io] = env.partiallyEvaluateCompletionDeref(ast.argument, strictCode);
|
||||
if (argValue instanceof Value) {
|
||||
let c = new ThrowCompletion(argValue, undefined, ast.loc);
|
||||
let s = t.throwStatement((argAst: any)); // will be an expression because argValue is a Value
|
||||
return [c, s, io];
|
||||
}
|
||||
return [argValue, argAst, io];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeTryStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeTryStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeTryStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeUnaryExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeUnaryExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeUnaryExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeUpdateExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeUpdateExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeUpdateExpression, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeVariableDeclaration, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.3.2.4
|
||||
export default function(
|
||||
ast: BabelNodeVariableDeclaration,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeVariableDeclaration, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeWhileStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeWhileStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): [AbruptCompletion | Value, BabelNodeWhileStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeWithStatement, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { Value } from "../values/index.js";
|
||||
|
||||
// ECMA262 13.11.7
|
||||
export default function(
|
||||
ast: BabelNodeWithStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [AbruptCompletion | Value, BabelNodeWithStatement, Array<BabelNodeStatement>] {
|
||||
let result = env.evaluateCompletionDeref(ast, strictCode);
|
||||
return [result, ast, []];
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { BabelNodeYieldExpression, BabelNodeStatement } from "@babel/types";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { Value } from "../values/index.js";
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeYieldExpression,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): [Value, BabelNodeYieldExpression, Array<BabelNodeStatement>] {
|
||||
throw new FatalError("TODO: YieldExpression");
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
export { default as ArrayExpression } from "./ArrayExpression.js";
|
||||
export { default as ArrowFunctionExpression } from "./ArrowFunctionExpression.js";
|
||||
export { default as AssignmentExpression } from "./AssignmentExpression.js";
|
||||
export { default as AwaitExpression } from "./AwaitExpression.js";
|
||||
export { default as BinaryExpression } from "./BinaryExpression.js";
|
||||
export { default as BlockStatement } from "./BlockStatement.js";
|
||||
export { default as BooleanLiteral } from "./BooleanLiteral.js";
|
||||
export { default as BreakStatement } from "./BreakStatement.js";
|
||||
export { default as CallExpression } from "./CallExpression.js";
|
||||
export { default as CatchClause } from "./CatchClause.js";
|
||||
export { default as ClassExpression } from "./ClassExpression.js";
|
||||
export { default as ClassDeclaration } from "./ClassDeclaration.js";
|
||||
export { default as ConditionalExpression } from "./ConditionalExpression.js";
|
||||
export { default as ContinueStatement } from "./ContinueStatement.js";
|
||||
export { default as Directive } from "./Directive.js";
|
||||
export { default as DirectiveLiteral } from "./DirectiveLiteral.js";
|
||||
export { default as DoExpression } from "./DoExpression.js";
|
||||
export { default as DoWhileStatement } from "./DoWhileStatement.js";
|
||||
export { default as EmptyStatement } from "./EmptyStatement.js";
|
||||
export { default as ExpressionStatement } from "./ExpressionStatement.js";
|
||||
export { default as File } from "./File.js";
|
||||
export { default as ForInStatement } from "./ForInStatement.js";
|
||||
export { default as ForOfStatement } from "./ForOfStatement.js";
|
||||
export { default as ForStatement } from "./ForStatement.js";
|
||||
export { default as FunctionDeclaration } from "./FunctionDeclaration.js";
|
||||
export { default as FunctionExpression } from "./FunctionExpression.js";
|
||||
export { default as Identifier } from "./Identifier.js";
|
||||
export { default as IfStatement } from "./IfStatement.js";
|
||||
export { default as LabeledStatement } from "./LabeledStatement.js";
|
||||
export { default as LogicalExpression } from "./LogicalExpression.js";
|
||||
export { default as MemberExpression } from "./MemberExpression.js";
|
||||
export { default as MetaProperty } from "./MetaProperty.js";
|
||||
export { default as NewExpression } from "./NewExpression.js";
|
||||
export { default as NullLiteral } from "./NullLiteral.js";
|
||||
export { default as NumericLiteral } from "./NumericLiteral.js";
|
||||
export { default as ObjectExpression } from "./ObjectExpression.js";
|
||||
export { default as Program } from "./Program.js";
|
||||
export { default as RegExpLiteral } from "./RegExpLiteral.js";
|
||||
export { default as ReturnStatement } from "./ReturnStatement.js";
|
||||
export { default as SequenceExpression } from "./SequenceExpression.js";
|
||||
export { default as StringLiteral } from "./StringLiteral.js";
|
||||
export { default as SwitchStatement } from "./SwitchStatement.js";
|
||||
export { default as TaggedTemplateExpression } from "./TaggedTemplateExpression.js";
|
||||
export { default as TemplateLiteral } from "./TemplateLiteral.js";
|
||||
export { default as ThisExpression } from "./ThisExpression.js";
|
||||
export { default as ThrowStatement } from "./ThrowStatement.js";
|
||||
export { default as TryStatement } from "./TryStatement.js";
|
||||
export { default as UnaryExpression } from "./UnaryExpression.js";
|
||||
export { default as UpdateExpression } from "./UpdateExpression.js";
|
||||
export { default as VariableDeclaration } from "./VariableDeclaration.js";
|
||||
export { default as WhileStatement } from "./WhileStatement.js";
|
||||
export { default as WithStatement } from "./WithStatement.js";
|
||||
export { default as YieldExpression } from "./YieldExpression.js";
|
@ -118,7 +118,6 @@ function run(
|
||||
logStatistics: false,
|
||||
logModules: false,
|
||||
delayInitializations: false,
|
||||
delayUnsupportedRequires: false,
|
||||
accelerateUnsupportedRequires: true,
|
||||
internalDebug: false,
|
||||
debugScopes: false,
|
||||
|
@ -22,7 +22,6 @@ export type PrepackOptions = {|
|
||||
compatibility?: Compatibility,
|
||||
debugNames?: boolean,
|
||||
delayInitializations?: boolean,
|
||||
delayUnsupportedRequires?: boolean,
|
||||
accelerateUnsupportedRequires?: boolean,
|
||||
inputSourceMapFilenames?: Array<string>,
|
||||
internalDebug?: boolean,
|
||||
@ -122,7 +121,6 @@ export function getSerializerOptions({
|
||||
lazyObjectsRuntime,
|
||||
heapGraphFormat,
|
||||
delayInitializations = false,
|
||||
delayUnsupportedRequires = false,
|
||||
accelerateUnsupportedRequires = true,
|
||||
internalDebug = false,
|
||||
debugScopes = false,
|
||||
@ -136,7 +134,6 @@ export function getSerializerOptions({
|
||||
}: PrepackOptions): SerializerOptions {
|
||||
let result: SerializerOptions = {
|
||||
delayInitializations,
|
||||
delayUnsupportedRequires,
|
||||
accelerateUnsupportedRequires,
|
||||
initializeMoreModules,
|
||||
internalDebug,
|
||||
|
@ -54,19 +54,13 @@ export function prepackSources(
|
||||
if (options.check) {
|
||||
realm.generator = new Generator(realm, "main", realm.pathConditions);
|
||||
let logger = new Logger(realm, !!options.internalDebug);
|
||||
let modules = new Modules(
|
||||
realm,
|
||||
logger,
|
||||
!!options.logModules,
|
||||
!!options.delayUnsupportedRequires,
|
||||
!!options.accelerateUnsupportedRequires
|
||||
);
|
||||
let modules = new Modules(realm, logger, !!options.logModules, !!options.accelerateUnsupportedRequires);
|
||||
let [result] = realm.$GlobalEnv.executeSources(sourceFileCollection.toArray());
|
||||
if (result instanceof AbruptCompletion) throw result;
|
||||
invariant(options.check);
|
||||
checkResidualFunctions(modules, options.check[0], options.check[1]);
|
||||
return { code: "", map: undefined };
|
||||
} else if (options.serialize === true || options.residual !== true) {
|
||||
} else {
|
||||
let serializer = new Serializer(realm, getSerializerOptions(options));
|
||||
let serialized = serializer.init(sourceFileCollection, options.sourceMaps, options.onParse);
|
||||
|
||||
@ -88,30 +82,7 @@ export function prepackSources(
|
||||
serialized.sourceFilePaths = sourcePaths;
|
||||
}
|
||||
|
||||
if (!options.residual) return serialized;
|
||||
let residualSources = [
|
||||
{
|
||||
filePath: options.outputFilename || "unknown",
|
||||
fileContents: serialized.code,
|
||||
sourceMapContents: serialized.map && JSON.stringify(serialized.map),
|
||||
},
|
||||
];
|
||||
let debugChannel = options.debuggerConfigArgs ? options.debuggerConfigArgs.debugChannel : undefined;
|
||||
realm = construct_realm(realmOptions, debugChannel);
|
||||
initializeGlobals(realm);
|
||||
if (typeof options.additionalGlobals === "function") {
|
||||
options.additionalGlobals(realm);
|
||||
}
|
||||
realm.generator = new Generator(realm, "main", realm.pathConditions);
|
||||
let result = realm.$GlobalEnv.executePartialEvaluator(residualSources, options);
|
||||
if (result instanceof AbruptCompletion) throw result;
|
||||
return { ...result };
|
||||
} else {
|
||||
invariant(options.residual);
|
||||
realm.generator = new Generator(realm, "main", realm.pathConditions);
|
||||
let result = realm.$GlobalEnv.executePartialEvaluator(sourceFileCollection.toArray(), options);
|
||||
if (result instanceof AbruptCompletion) throw result;
|
||||
return { ...result };
|
||||
return serialized;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
/* @flow */
|
||||
|
||||
import { Realm } from "../realm.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import { AbruptCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import type { BabelNode, BabelNodeJSXIdentifier } from "@babel/types";
|
||||
import { parseExpression } from "@babel/parser";
|
||||
import {
|
||||
@ -795,12 +795,14 @@ export function getValueFromFunctionCall(
|
||||
let completion;
|
||||
let createdObjects = realm.createdObjects;
|
||||
try {
|
||||
let value;
|
||||
if (isConstructor) {
|
||||
invariant(newCall);
|
||||
completion = newCall(args, func);
|
||||
value = newCall(args, func);
|
||||
} else {
|
||||
completion = funcCall(funcThis, args);
|
||||
value = funcCall(funcThis, args);
|
||||
}
|
||||
completion = new SimpleNormalCompletion(value);
|
||||
} catch (error) {
|
||||
if (error instanceof AbruptCompletion) {
|
||||
completion = error;
|
||||
@ -810,18 +812,7 @@ export function getValueFromFunctionCall(
|
||||
} finally {
|
||||
invariant(createdObjects === realm.createdObjects, "realm.createdObjects was not correctly restored");
|
||||
}
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return realm.returnOrThrowCompletion(completion);
|
||||
}
|
||||
|
||||
function isEventProp(name: string): boolean {
|
||||
|
457
src/realm.js
457
src/realm.js
@ -44,7 +44,7 @@ import {
|
||||
UndefinedValue,
|
||||
Value,
|
||||
} from "./values/index.js";
|
||||
import type { TypesDomain, ValuesDomain } from "./domains/index.js";
|
||||
import { TypesDomain, ValuesDomain } from "./domains/index.js";
|
||||
import {
|
||||
LexicalEnvironment,
|
||||
Reference,
|
||||
@ -57,8 +57,9 @@ import { cloneDescriptor, Construct } from "./methods/index.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
Completion,
|
||||
ForkedAbruptCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
JoinedAbruptCompletions,
|
||||
JoinedNormalAndAbruptCompletions,
|
||||
NormalCompletion,
|
||||
SimpleNormalCompletion,
|
||||
ThrowCompletion,
|
||||
} from "./completions.js";
|
||||
@ -67,16 +68,10 @@ import invariant from "./invariant.js";
|
||||
import seedrandom from "seedrandom";
|
||||
import { createOperationDescriptor, Generator, type TemporalOperationEntry } from "./utils/generator.js";
|
||||
import { PreludeGenerator } from "./utils/PreludeGenerator.js";
|
||||
import { Environment, Functions, Join, Properties, To, Widen, Path } from "./singletons.js";
|
||||
import { Environment, Functions, Join, Path, Properties, To, Utils, Widen } from "./singletons.js";
|
||||
import type { ReactSymbolTypes } from "./react/utils.js";
|
||||
import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal, BabelNodeStatement } from "@babel/types";
|
||||
import { Utils } from "./singletons.js";
|
||||
export type BindingEntry = {
|
||||
hasLeaked: void | boolean,
|
||||
value: void | Value,
|
||||
previousHasLeaked: void | boolean,
|
||||
previousValue: void | Value,
|
||||
};
|
||||
import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal } from "@babel/types";
|
||||
export type BindingEntry = { hasLeaked: boolean, value: void | Value };
|
||||
export type Bindings = Map<Binding, BindingEntry>;
|
||||
export type EvaluationResult = Completion | Reference;
|
||||
export type PropertyBindings = Map<PropertyBinding, void | Descriptor>;
|
||||
@ -95,7 +90,7 @@ export class Effects {
|
||||
propertyBindings: PropertyBindings,
|
||||
createdObjects: CreatedObjects
|
||||
) {
|
||||
this._result = result;
|
||||
this.result = result;
|
||||
this.generator = generator;
|
||||
this.modifiedBindings = bindings;
|
||||
this.modifiedProperties = propertyBindings;
|
||||
@ -103,20 +98,9 @@ export class Effects {
|
||||
|
||||
this.canBeApplied = true;
|
||||
this._id = effects_uid++;
|
||||
invariant(result.effects === undefined);
|
||||
result.effects = this;
|
||||
}
|
||||
|
||||
_result: Completion;
|
||||
get result(): Completion {
|
||||
return this._result;
|
||||
}
|
||||
set result(completion: Completion): void {
|
||||
invariant(completion.effects === undefined);
|
||||
if (completion.effects === undefined) completion.effects = this; //todo: require callers to ensure this
|
||||
this._result = completion;
|
||||
}
|
||||
|
||||
result: Completion;
|
||||
generator: Generator;
|
||||
modifiedBindings: Bindings;
|
||||
modifiedProperties: PropertyBindings;
|
||||
@ -219,11 +203,8 @@ export class ExecutionContext {
|
||||
|
||||
export function construct_empty_effects(
|
||||
realm: Realm,
|
||||
c: Completion = new SimpleNormalCompletion(realm.intrinsics.empty, undefined)
|
||||
c: Completion = new SimpleNormalCompletion(realm.intrinsics.empty)
|
||||
): Effects {
|
||||
// TODO #2222: Check if `realm.pathConditions` is always correct here.
|
||||
// The path conditions here should probably be empty.
|
||||
// Picking up the current path conditions from the Realm might be the reason why composition does not work.
|
||||
return new Effects(
|
||||
c,
|
||||
new Generator(realm, "construct_empty_effects", realm.pathConditions),
|
||||
@ -281,7 +262,6 @@ export class Realm {
|
||||
this.intrinsics = ({}: any);
|
||||
this.$GlobalObject = (({}: any): ObjectValue);
|
||||
this.evaluators = (Object.create(null): any);
|
||||
this.partialEvaluators = (Object.create(null): any);
|
||||
this.$GlobalEnv = ((undefined: any): LexicalEnvironment);
|
||||
|
||||
this.derivedIds = new Map();
|
||||
@ -369,7 +349,7 @@ export class Realm {
|
||||
(sideEffectType: SideEffectType, binding: void | Binding | PropertyBinding, expressionLocation: any) => void
|
||||
>;
|
||||
reportPropertyAccess: void | (PropertyBinding => void);
|
||||
savedCompletion: void | PossiblyNormalCompletion;
|
||||
savedCompletion: void | JoinedNormalAndAbruptCompletions;
|
||||
|
||||
activeLexicalEnvironments: Set<LexicalEnvironment>;
|
||||
|
||||
@ -447,15 +427,6 @@ export class Realm {
|
||||
metadata?: any
|
||||
) => Value | Reference,
|
||||
};
|
||||
partialEvaluators: {
|
||||
[key: string]: (
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
metadata?: any
|
||||
) => [Completion | Reference | Value, BabelNode, Array<BabelNodeStatement>],
|
||||
};
|
||||
simplifyAndRefineAbstractValue: AbstractValue => Value;
|
||||
simplifyAndRefineAbstractCondition: AbstractValue => Value;
|
||||
|
||||
@ -560,26 +531,6 @@ export class Realm {
|
||||
}
|
||||
}
|
||||
|
||||
clearBlockBindingsFromCompletion(completion: Completion, environmentRecord: DeclarativeEnvironmentRecord): void {
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
this.clearBlockBindings(completion.alternateEffects.modifiedBindings, environmentRecord);
|
||||
this.clearBlockBindings(completion.consequentEffects.modifiedBindings, environmentRecord);
|
||||
if (completion.savedEffects !== undefined)
|
||||
this.clearBlockBindings(completion.savedEffects.modifiedBindings, environmentRecord);
|
||||
if (completion.alternate instanceof Completion)
|
||||
this.clearBlockBindingsFromCompletion(completion.alternate, environmentRecord);
|
||||
if (completion.consequent instanceof Completion)
|
||||
this.clearBlockBindingsFromCompletion(completion.consequent, environmentRecord);
|
||||
} else if (completion instanceof ForkedAbruptCompletion) {
|
||||
this.clearBlockBindings(completion.alternateEffects.modifiedBindings, environmentRecord);
|
||||
this.clearBlockBindings(completion.consequentEffects.modifiedBindings, environmentRecord);
|
||||
if (completion.alternate instanceof Completion)
|
||||
this.clearBlockBindingsFromCompletion(completion.alternate, environmentRecord);
|
||||
if (completion.consequent instanceof Completion)
|
||||
this.clearBlockBindingsFromCompletion(completion.consequent, environmentRecord);
|
||||
}
|
||||
}
|
||||
|
||||
// Call when a scope falls out of scope and should be destroyed.
|
||||
// Clears the Bindings corresponding to the disappearing Scope from ModifiedBindings
|
||||
onDestroyScope(lexicalEnvironment: LexicalEnvironment): void {
|
||||
@ -590,8 +541,6 @@ export class Realm {
|
||||
let environmentRecord = lexicalEnvironment.environmentRecord;
|
||||
if (environmentRecord instanceof DeclarativeEnvironmentRecord) {
|
||||
this.clearBlockBindings(modifiedBindings, environmentRecord);
|
||||
if (this.savedCompletion !== undefined)
|
||||
this.clearBlockBindingsFromCompletion(this.savedCompletion, environmentRecord);
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,31 +582,10 @@ export class Realm {
|
||||
}
|
||||
}
|
||||
|
||||
clearFunctionBindingsFromCompletion(completion: Completion, funcVal: FunctionValue): void {
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
this.clearFunctionBindings(completion.alternateEffects.modifiedBindings, funcVal);
|
||||
this.clearFunctionBindings(completion.consequentEffects.modifiedBindings, funcVal);
|
||||
if (completion.savedEffects !== undefined)
|
||||
this.clearFunctionBindings(completion.savedEffects.modifiedBindings, funcVal);
|
||||
if (completion.alternate instanceof Completion)
|
||||
this.clearFunctionBindingsFromCompletion(completion.alternate, funcVal);
|
||||
if (completion.consequent instanceof Completion)
|
||||
this.clearFunctionBindingsFromCompletion(completion.consequent, funcVal);
|
||||
} else if (completion instanceof ForkedAbruptCompletion) {
|
||||
this.clearFunctionBindings(completion.alternateEffects.modifiedBindings, funcVal);
|
||||
this.clearFunctionBindings(completion.consequentEffects.modifiedBindings, funcVal);
|
||||
if (completion.alternate instanceof Completion)
|
||||
this.clearFunctionBindingsFromCompletion(completion.alternate, funcVal);
|
||||
if (completion.consequent instanceof Completion)
|
||||
this.clearFunctionBindingsFromCompletion(completion.consequent, funcVal);
|
||||
}
|
||||
}
|
||||
|
||||
popContext(context: ExecutionContext): void {
|
||||
let funcVal = context.function;
|
||||
if (funcVal) {
|
||||
this.clearFunctionBindings(this.modifiedBindings, funcVal);
|
||||
if (this.savedCompletion !== undefined) this.clearFunctionBindingsFromCompletion(this.savedCompletion, funcVal);
|
||||
}
|
||||
let c = this.contextStack.pop();
|
||||
invariant(c === context);
|
||||
@ -733,7 +661,11 @@ export class Realm {
|
||||
bubbleSideEffectReports: boolean,
|
||||
reportSideEffectFunc:
|
||||
| null
|
||||
| ((sideEffectType: SideEffectType, binding: void | Binding | PropertyBinding, value: void | Value) => void)
|
||||
| ((
|
||||
sideEffectType: SideEffectType,
|
||||
binding: void | Binding | PropertyBinding,
|
||||
location: ?BabelNodeSourceLocation
|
||||
) => void)
|
||||
): T {
|
||||
let saved_createdObjectsTrackedForLeaks = this.createdObjectsTrackedForLeaks;
|
||||
let saved_reportSideEffectCallbacks;
|
||||
@ -787,7 +719,7 @@ export class Realm {
|
||||
invariant(this.isInPureScope(), "only abstract abrupt completion in pure functions");
|
||||
|
||||
// TODO(1264): We should create a new generator for this scope and wrap it in a try/catch.
|
||||
// We could use the outcome of that as the join condition for a PossiblyNormalCompletion.
|
||||
// We could use the outcome of that as the join condition for a JoinedNormalAndAbruptCompletions.
|
||||
// We should then compose that with the saved completion and move on to the normal route.
|
||||
// Currently we just issue a recoverable error instead if this might matter.
|
||||
let value = f();
|
||||
@ -834,7 +766,7 @@ export class Realm {
|
||||
result = func(effects);
|
||||
return this.intrinsics.undefined;
|
||||
} finally {
|
||||
this.undoBindings(effects.modifiedBindings);
|
||||
this.restoreBindings(effects.modifiedBindings);
|
||||
this.restoreProperties(effects.modifiedProperties);
|
||||
invariant(!effects.canBeApplied);
|
||||
effects.canBeApplied = true;
|
||||
@ -848,22 +780,6 @@ export class Realm {
|
||||
return this.wrapInGlobalEnv(() => this.evaluateNodeForEffects(node, false, this.$GlobalEnv, state, generatorName));
|
||||
}
|
||||
|
||||
partiallyEvaluateNodeForEffects(
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment
|
||||
): [Effects, BabelNode, Array<BabelNodeStatement>] {
|
||||
let nodeAst, nodeIO;
|
||||
function partialEval() {
|
||||
let result;
|
||||
[result, nodeAst, nodeIO] = env.partiallyEvaluateCompletionDeref(ast, strictCode);
|
||||
return result;
|
||||
}
|
||||
let effects = this.evaluateForEffects(partialEval, undefined, "partiallyEvaluateNodeForEffects");
|
||||
invariant(nodeAst !== undefined && nodeIO !== undefined);
|
||||
return [effects, nodeAst, nodeIO];
|
||||
}
|
||||
|
||||
// Use this to evaluate code for internal purposes, so that the tracked state does not get polluted
|
||||
evaluateWithoutEffects<T>(f: () => T): T {
|
||||
// Save old state and set up undefined state
|
||||
@ -912,21 +828,9 @@ export class Realm {
|
||||
if (e instanceof AbruptCompletion) c = e;
|
||||
else throw e;
|
||||
}
|
||||
// This is a join point for the normal branch of a PossiblyNormalCompletion.
|
||||
if (c instanceof Value || c instanceof AbruptCompletion) {
|
||||
c = Functions.incorporateSavedCompletion(this, c);
|
||||
if (c instanceof Completion && c.effects !== undefined) c = c.shallowCloneWithoutEffects();
|
||||
}
|
||||
// This is a join point for any normal completions inside realm.savedCompletion
|
||||
c = Functions.incorporateSavedCompletion(this, c);
|
||||
invariant(c !== undefined);
|
||||
if (c instanceof PossiblyNormalCompletion) {
|
||||
// The current state may have advanced since the time control forked into the various paths recorded in c.
|
||||
// Update the normal path and restore the global state to what it was at the time of the fork.
|
||||
let subsequentEffects = this.getCapturedEffects(c.value);
|
||||
this.stopEffectCaptureAndUndoEffects(c);
|
||||
Join.updatePossiblyNormalCompletionWithSubsequentEffects(this, c, subsequentEffects);
|
||||
this.savedCompletion = undefined;
|
||||
this.applyEffects(subsequentEffects, "subsequentEffects", true);
|
||||
}
|
||||
|
||||
invariant(this.generator !== undefined);
|
||||
invariant(this.modifiedBindings !== undefined);
|
||||
@ -952,12 +856,14 @@ export class Realm {
|
||||
return result;
|
||||
} finally {
|
||||
// Roll back the state changes
|
||||
if (this.savedCompletion !== undefined) this.stopEffectCaptureAndUndoEffects(this.savedCompletion);
|
||||
if (result !== undefined) {
|
||||
this.undoBindings(result.modifiedBindings);
|
||||
this.restoreBindings(result.modifiedBindings);
|
||||
this.restoreProperties(result.modifiedProperties);
|
||||
} else {
|
||||
this.undoBindings(this.modifiedBindings);
|
||||
if (this.savedCompletion !== undefined) {
|
||||
this.stopEffectCaptureAndUndoEffects(this.savedCompletion);
|
||||
}
|
||||
this.restoreBindings(this.modifiedBindings);
|
||||
this.restoreProperties(this.modifiedProperties);
|
||||
}
|
||||
this.generator = saved_generator;
|
||||
@ -1015,14 +921,6 @@ export class Realm {
|
||||
this.applyEffects(effects);
|
||||
let resultVal = effects.result;
|
||||
if (resultVal instanceof AbruptCompletion) throw resultVal;
|
||||
if (resultVal instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
resultVal = this.composeWithSavedCompletion(resultVal);
|
||||
}
|
||||
invariant(resultVal instanceof SimpleNormalCompletion);
|
||||
return resultVal.value;
|
||||
} catch (e) {
|
||||
if (diagnostic !== undefined) return diagnostic;
|
||||
@ -1044,10 +942,10 @@ export class Realm {
|
||||
};
|
||||
let effects1 = this.evaluateForEffects(f, undefined, "evaluateForFixpointEffects/1");
|
||||
while (true) {
|
||||
this.redoBindings(effects1.modifiedBindings);
|
||||
this.restoreBindings(effects1.modifiedBindings);
|
||||
this.restoreProperties(effects1.modifiedProperties);
|
||||
let effects2 = this.evaluateForEffects(f, undefined, "evaluateForFixpointEffects/2");
|
||||
this.undoBindings(effects1.modifiedBindings);
|
||||
this.restoreBindings(effects1.modifiedBindings);
|
||||
this.restoreProperties(effects1.modifiedProperties);
|
||||
if (Widen.containsEffects(effects1, effects2)) {
|
||||
// effects1 includes every value present in effects2, so doing another iteration using effects2 will not
|
||||
@ -1087,7 +985,6 @@ export class Realm {
|
||||
} catch (e) {
|
||||
if (!(e instanceof InfeasiblePathError)) throw e;
|
||||
}
|
||||
invariant(effects1 === undefined || effects1.result.effects === effects1);
|
||||
|
||||
let effects2;
|
||||
try {
|
||||
@ -1095,41 +992,20 @@ export class Realm {
|
||||
} catch (e) {
|
||||
if (!(e instanceof InfeasiblePathError)) throw e;
|
||||
}
|
||||
invariant(effects2 === undefined || effects2.result.effects === effects2);
|
||||
|
||||
let joinedEffects, completion;
|
||||
let effects;
|
||||
if (effects1 === undefined || effects2 === undefined) {
|
||||
if (effects1 === undefined && effects2 === undefined) throw new InfeasiblePathError();
|
||||
joinedEffects = effects1 || effects2;
|
||||
invariant(joinedEffects !== undefined);
|
||||
completion = joinedEffects.result;
|
||||
this.applyEffects(joinedEffects, "evaluateWithAbstractConditional");
|
||||
effects = effects1 || effects2;
|
||||
invariant(effects !== undefined);
|
||||
} else {
|
||||
// Join the effects, creating an abstract view of what happened, regardless
|
||||
// of the actual value of condValue.
|
||||
joinedEffects = Join.joinForkOrChoose(this, condValue, effects1, effects2);
|
||||
completion = joinedEffects.result;
|
||||
if (completion instanceof ForkedAbruptCompletion) {
|
||||
// Note that the effects are tracked separately inside completion and will be applied later.
|
||||
throw completion;
|
||||
}
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
this.applyEffects(joinedEffects, "evaluateWithAbstractConditional");
|
||||
completion = this.composeWithSavedCompletion(completion);
|
||||
} else {
|
||||
this.applyEffects(joinedEffects, "evaluateWithAbstractConditional");
|
||||
}
|
||||
effects = Join.joinEffects(condValue, effects1, effects2);
|
||||
}
|
||||
this.applyEffects(effects);
|
||||
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
return condValue.$Realm.returnOrThrowCompletion(effects.result);
|
||||
}
|
||||
|
||||
_applyPropertiesToNewlyCreatedObjects(
|
||||
@ -1225,187 +1101,126 @@ export class Realm {
|
||||
});
|
||||
}
|
||||
|
||||
composeEffects(priorEffects: Effects, subsequentEffects: Effects): Effects {
|
||||
let result = construct_empty_effects(this, subsequentEffects.result.shallowCloneWithoutEffects());
|
||||
|
||||
result.generator = Join.composeGenerators(
|
||||
this,
|
||||
priorEffects.generator || result.generator,
|
||||
subsequentEffects.generator
|
||||
);
|
||||
|
||||
if (priorEffects.modifiedBindings) {
|
||||
priorEffects.modifiedBindings.forEach((val, key, m) => result.modifiedBindings.set(key, val));
|
||||
returnOrThrowCompletion(completion: Completion | Value): Value {
|
||||
if (completion instanceof Value) completion = new SimpleNormalCompletion(completion);
|
||||
if (completion instanceof AbruptCompletion) {
|
||||
let c = Functions.incorporateSavedCompletion(this, completion);
|
||||
invariant(c instanceof Completion);
|
||||
completion = c;
|
||||
}
|
||||
subsequentEffects.modifiedBindings.forEach((val, key, m) => result.modifiedBindings.set(key, val));
|
||||
|
||||
if (priorEffects.modifiedProperties) {
|
||||
priorEffects.modifiedProperties.forEach((desc, propertyBinding, m) =>
|
||||
result.modifiedProperties.set(propertyBinding, desc)
|
||||
);
|
||||
}
|
||||
subsequentEffects.modifiedProperties.forEach((val, key, m) => result.modifiedProperties.set(key, val));
|
||||
|
||||
if (priorEffects.createdObjects) {
|
||||
priorEffects.createdObjects.forEach((ob, a) => result.createdObjects.add(ob));
|
||||
}
|
||||
subsequentEffects.createdObjects.forEach((ob, a) => result.createdObjects.add(ob));
|
||||
|
||||
return result;
|
||||
let cc = this.composeWithSavedCompletion(completion);
|
||||
if (cc instanceof AbruptCompletion) throw cc;
|
||||
return cc.value;
|
||||
}
|
||||
|
||||
updateAbruptCompletions(priorEffects: Effects, c: PossiblyNormalCompletion): void {
|
||||
if (c.consequent instanceof AbruptCompletion) {
|
||||
c.consequent.effects = this.composeEffects(priorEffects, c.consequentEffects);
|
||||
let alternate = c.alternate;
|
||||
if (alternate instanceof PossiblyNormalCompletion) this.updateAbruptCompletions(priorEffects, alternate);
|
||||
} else {
|
||||
invariant(c.alternate instanceof AbruptCompletion);
|
||||
c.alternate.effects = this.composeEffects(priorEffects, c.alternateEffects);
|
||||
let consequent = c.consequent;
|
||||
if (consequent instanceof PossiblyNormalCompletion) this.updateAbruptCompletions(priorEffects, consequent);
|
||||
}
|
||||
}
|
||||
|
||||
wrapSavedCompletion(completion: PossiblyNormalCompletion): void {
|
||||
if (this.savedCompletion !== undefined) {
|
||||
if (completion.consequent instanceof AbruptCompletion) {
|
||||
completion.alternate = this.savedCompletion;
|
||||
} else {
|
||||
completion.consequent = this.savedCompletion;
|
||||
}
|
||||
completion.savedEffects = this.savedCompletion.savedEffects;
|
||||
} else {
|
||||
this.captureEffects(completion);
|
||||
}
|
||||
this.savedCompletion = completion;
|
||||
}
|
||||
|
||||
composeWithSavedCompletion(completion: PossiblyNormalCompletion): Value {
|
||||
composeWithSavedCompletion(completion: Completion): Completion {
|
||||
if (this.savedCompletion === undefined) {
|
||||
this.savedCompletion = completion;
|
||||
this.savedCompletion.savedPathConditions = this.pathConditions;
|
||||
this.pathConditions = [].concat(this.pathConditions);
|
||||
this.captureEffects(completion);
|
||||
if (completion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
this.savedCompletion = completion;
|
||||
this.pushPathConditionsLeadingToCompletionOfType(NormalCompletion, completion);
|
||||
this.captureEffects(completion);
|
||||
}
|
||||
return completion;
|
||||
} else {
|
||||
let savedCompletion = this.savedCompletion;
|
||||
let e = this.getCapturedEffects();
|
||||
this.stopEffectCaptureAndUndoEffects(savedCompletion);
|
||||
savedCompletion = Join.composePossiblyNormalCompletions(this, savedCompletion, completion, e);
|
||||
this.applyEffects(e);
|
||||
this.captureEffects(savedCompletion);
|
||||
this.savedCompletion = savedCompletion;
|
||||
let cc = Join.composeCompletions(this.savedCompletion, completion);
|
||||
if (cc instanceof JoinedNormalAndAbruptCompletions) {
|
||||
this.savedCompletion = cc;
|
||||
this.pushPathConditionsLeadingToCompletionOfType(NormalCompletion, completion);
|
||||
if (cc.savedEffects === undefined) this.captureEffects(cc);
|
||||
} else {
|
||||
this.savedCompletion = undefined;
|
||||
}
|
||||
return cc;
|
||||
}
|
||||
let realm = this;
|
||||
pushPathConditionsLeadingToNormalCompletion(completion);
|
||||
return completion.value;
|
||||
}
|
||||
|
||||
function pushPathConditionsLeadingToNormalCompletion(c: ForkedAbruptCompletion | PossiblyNormalCompletion) {
|
||||
if (allPathsAreAbrupt(c.consequent)) {
|
||||
Path.pushInverseAndRefine(c.joinCondition);
|
||||
if (c.alternate instanceof PossiblyNormalCompletion || c.alternate instanceof ForkedAbruptCompletion)
|
||||
pushPathConditionsLeadingToNormalCompletion(c.alternate);
|
||||
} else if (allPathsAreAbrupt(c.alternate)) {
|
||||
Path.pushAndRefine(c.joinCondition);
|
||||
if (c.consequent instanceof PossiblyNormalCompletion || c.consequent instanceof ForkedAbruptCompletion)
|
||||
pushPathConditionsLeadingToNormalCompletion(c.consequent);
|
||||
} else if (allPathsAreNormal(c.consequent)) {
|
||||
if (!allPathsAreNormal(c.alternate)) {
|
||||
let alternatePC = getNormalPathConditionFor(c.alternate);
|
||||
let disjunct = AbstractValue.createFromLogicalOp(realm, "||", c.joinCondition, alternatePC);
|
||||
pushPathConditionsLeadingToCompletionOfType(CompletionType: typeof Completion, completion: Completion): void {
|
||||
let realm = this;
|
||||
let bottomValue = realm.intrinsics.__bottomValue;
|
||||
// Note that if a completion of type CompletionType has a value is that is bottom, that completion is unreachable
|
||||
// and pushing its corresponding path condition would cause an InfeasiblePathError to be thrown.
|
||||
if (completion instanceof JoinedAbruptCompletions || completion instanceof JoinedNormalAndAbruptCompletions) {
|
||||
if (completion.consequent.value === bottomValue || allPathsAreDifferent(completion.consequent)) {
|
||||
if (completion.alternate.value === bottomValue || allPathsAreDifferent(completion.alternate)) return;
|
||||
Path.pushInverseAndRefine(completion.joinCondition);
|
||||
this.pushPathConditionsLeadingToCompletionOfType(CompletionType, completion.alternate);
|
||||
} else if (completion.alternate.value === bottomValue || allPathsAreDifferent(completion.alternate)) {
|
||||
if (completion.consequent.value === bottomValue) return;
|
||||
Path.pushAndRefine(completion.joinCondition);
|
||||
this.pushPathConditionsLeadingToCompletionOfType(CompletionType, completion.consequent);
|
||||
} else if (allPathsAreTheSame(completion.consequent)) {
|
||||
if (!allPathsAreTheSame(completion.alternate)) {
|
||||
let alternatePC = getPathConditionForSame(completion.alternate);
|
||||
let disjunct = AbstractValue.createFromLogicalOp(realm, "||", completion.joinCondition, alternatePC);
|
||||
Path.pushAndRefine(disjunct);
|
||||
}
|
||||
} else if (allPathsAreNormal(c.alternate)) {
|
||||
let consequentPC = getNormalPathConditionFor(c.consequent);
|
||||
let inverse = AbstractValue.createFromUnaryOp(realm, "!", c.joinCondition);
|
||||
} else if (allPathsAreTheSame(completion.alternate)) {
|
||||
let consequentPC = getPathConditionForSame(completion.consequent);
|
||||
let inverse = AbstractValue.createFromUnaryOp(realm, "!", completion.joinCondition);
|
||||
let disjunct = AbstractValue.createFromLogicalOp(realm, "||", inverse, consequentPC);
|
||||
Path.pushAndRefine(disjunct);
|
||||
} else {
|
||||
let jc = c.joinCondition;
|
||||
let consequentPC = AbstractValue.createFromLogicalOp(realm, "&&", jc, getNormalPathConditionFor(c.consequent));
|
||||
let jc = completion.joinCondition;
|
||||
let cpc = AbstractValue.createFromLogicalOp(realm, "&&", jc, getPathConditionForSame(completion.consequent));
|
||||
let ijc = AbstractValue.createFromUnaryOp(realm, "!", jc);
|
||||
let alternatePC = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getNormalPathConditionFor(c.alternate));
|
||||
let disjunct = AbstractValue.createFromLogicalOp(realm, "||", consequentPC, alternatePC);
|
||||
let apc = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getPathConditionForSame(completion.alternate));
|
||||
let disjunct = AbstractValue.createFromLogicalOp(realm, "||", cpc, apc);
|
||||
Path.pushAndRefine(disjunct);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
function allPathsAreAbrupt(c: Completion): boolean {
|
||||
if (c instanceof ForkedAbruptCompletion) return allPathsAreAbrupt(c.consequent) && allPathsAreAbrupt(c.alternate);
|
||||
if (c instanceof AbruptCompletion) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function allPathsAreNormal(c: Completion): boolean {
|
||||
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion)
|
||||
return allPathsAreNormal(c.consequent) && allPathsAreNormal(c.alternate);
|
||||
if (c instanceof AbruptCompletion) return false;
|
||||
function allPathsAreDifferent(c: Completion): boolean {
|
||||
if (c instanceof JoinedAbruptCompletions || c instanceof JoinedNormalAndAbruptCompletions)
|
||||
return allPathsAreDifferent(c.consequent) && allPathsAreDifferent(c.alternate);
|
||||
if (c instanceof CompletionType) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNormalPathConditionFor(c: Completion): Value {
|
||||
invariant(c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion);
|
||||
if (allPathsAreAbrupt(c.consequent)) {
|
||||
invariant(!allPathsAreAbrupt(c.alternate));
|
||||
function allPathsAreTheSame(c: Completion): boolean {
|
||||
if (c instanceof JoinedAbruptCompletions || c instanceof JoinedNormalAndAbruptCompletions)
|
||||
return allPathsAreTheSame(c.consequent) && allPathsAreTheSame(c.alternate);
|
||||
if (c instanceof CompletionType) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPathConditionForSame(c: Completion): Value {
|
||||
invariant(c instanceof JoinedAbruptCompletions || c instanceof JoinedNormalAndAbruptCompletions);
|
||||
if (c.consequent.value === bottomValue || allPathsAreDifferent(c.consequent)) {
|
||||
invariant(!allPathsAreDifferent(c.alternate));
|
||||
let inverse = AbstractValue.createFromUnaryOp(realm, "!", c.joinCondition);
|
||||
if (allPathsAreNormal(c.alternate)) return inverse;
|
||||
return AbstractValue.createFromLogicalOp(realm, "&&", inverse, getNormalPathConditionFor(c.alternate));
|
||||
} else if (allPathsAreAbrupt(c.alternate)) {
|
||||
invariant(!allPathsAreAbrupt(c.consequent));
|
||||
if (allPathsAreNormal(c.consequent)) return c.joinCondition;
|
||||
return AbstractValue.createFromLogicalOp(realm, "&&", c.joinCondition, getNormalPathConditionFor(c.consequent));
|
||||
} else if (allPathsAreNormal(c.consequent)) {
|
||||
if (allPathsAreTheSame(c.alternate)) return inverse;
|
||||
return AbstractValue.createFromLogicalOp(realm, "&&", inverse, getPathConditionForSame(c.alternate));
|
||||
} else if (c.alternate.value === bottomValue || allPathsAreDifferent(c.alternate)) {
|
||||
invariant(!allPathsAreDifferent(c.consequent));
|
||||
if (allPathsAreTheSame(c.consequent)) return c.joinCondition;
|
||||
return AbstractValue.createFromLogicalOp(realm, "&&", c.joinCondition, getPathConditionForSame(c.consequent));
|
||||
} else if (allPathsAreTheSame(c.consequent)) {
|
||||
// In principle the simplifier shoud reduce the result of the else clause to this case. This does less work.
|
||||
invariant(!allPathsAreNormal(c.alternate));
|
||||
invariant(!allPathsAreAbrupt(c.alternate));
|
||||
invariant(!allPathsAreTheSame(c.alternate));
|
||||
invariant(!allPathsAreDifferent(c.alternate));
|
||||
let ijc = AbstractValue.createFromUnaryOp(realm, "!", c.joinCondition);
|
||||
let alternatePC = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getNormalPathConditionFor(c.alternate));
|
||||
let alternatePC = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getPathConditionForSame(c.alternate));
|
||||
return AbstractValue.createFromLogicalOp(realm, "||", c.joinCondition, alternatePC);
|
||||
} else if (allPathsAreNormal(c.alternate)) {
|
||||
} else if (allPathsAreTheSame(c.alternate)) {
|
||||
// In principle the simplifier shoud reduce the result of the else clause to this case. This does less work.
|
||||
invariant(!allPathsAreNormal(c.consequent));
|
||||
invariant(!allPathsAreAbrupt(c.consequent));
|
||||
invariant(!allPathsAreTheSame(c.consequent));
|
||||
invariant(!allPathsAreDifferent(c.consequent));
|
||||
let jc = c.joinCondition;
|
||||
let consequentPC = AbstractValue.createFromLogicalOp(realm, "&&", jc, getNormalPathConditionFor(c.consequent));
|
||||
let consequentPC = AbstractValue.createFromLogicalOp(realm, "&&", jc, getPathConditionForSame(c.consequent));
|
||||
let ijc = AbstractValue.createFromUnaryOp(realm, "!", jc);
|
||||
return AbstractValue.createFromLogicalOp(realm, "||", consequentPC, ijc);
|
||||
} else {
|
||||
let jc = c.joinCondition;
|
||||
let consequentPC = AbstractValue.createFromLogicalOp(realm, "&&", jc, getNormalPathConditionFor(c.consequent));
|
||||
let consequentPC = AbstractValue.createFromLogicalOp(realm, "&&", jc, getPathConditionForSame(c.consequent));
|
||||
let ijc = AbstractValue.createFromUnaryOp(realm, "!", jc);
|
||||
let alternatePC = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getNormalPathConditionFor(c.alternate));
|
||||
let alternatePC = AbstractValue.createFromLogicalOp(realm, "&&", ijc, getPathConditionForSame(c.alternate));
|
||||
return AbstractValue.createFromLogicalOp(realm, "||", consequentPC, alternatePC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
incorporatePriorSavedCompletion(priorCompletion: void | PossiblyNormalCompletion): void {
|
||||
if (priorCompletion === undefined) return;
|
||||
// A completion that has been saved and that is still active, will always have savedEffects.
|
||||
invariant(priorCompletion.savedEffects !== undefined);
|
||||
if (this.savedCompletion === undefined) {
|
||||
// priorCompletion must be a previous savedCompletion, so the corresponding tracking maps would have been
|
||||
// captured in priorCompletion.savedEffects and restored to the realm when clearing out this.savedCompletion.
|
||||
// Since there is curently no savedCompletion, all the forks subsequent to the last normal fork in
|
||||
// priorCompletion will have joined up again and their effects will have been applied to the current
|
||||
// tracking maps.
|
||||
invariant(this.modifiedBindings !== undefined);
|
||||
this.savedCompletion = priorCompletion;
|
||||
} else {
|
||||
let savedEffects = this.savedCompletion.savedEffects;
|
||||
invariant(savedEffects !== undefined);
|
||||
this.redoBindings(savedEffects.modifiedBindings);
|
||||
this.restoreProperties(savedEffects.modifiedProperties);
|
||||
Join.updatePossiblyNormalCompletionWithSubsequentEffects(this, priorCompletion, savedEffects);
|
||||
this.undoBindings(savedEffects.modifiedBindings);
|
||||
this.restoreProperties(savedEffects.modifiedProperties);
|
||||
invariant(this.savedCompletion !== undefined);
|
||||
this.savedCompletion.savedEffects = undefined;
|
||||
this.savedCompletion = Join.composePossiblyNormalCompletions(this, priorCompletion, this.savedCompletion);
|
||||
}
|
||||
}
|
||||
|
||||
captureEffects(completion: PossiblyNormalCompletion): void {
|
||||
captureEffects(completion: JoinedNormalAndAbruptCompletions): void {
|
||||
invariant(completion.savedEffects === undefined);
|
||||
completion.savedEffects = new Effects(
|
||||
new SimpleNormalCompletion(this.intrinsics.undefined),
|
||||
@ -1420,13 +1235,13 @@ export class Realm {
|
||||
this.createdObjects = new Set();
|
||||
}
|
||||
|
||||
getCapturedEffects(v?: Value = this.intrinsics.undefined): Effects {
|
||||
getCapturedEffects(v?: Completion | Value = this.intrinsics.undefined): Effects {
|
||||
invariant(this.generator !== undefined);
|
||||
invariant(this.modifiedBindings !== undefined);
|
||||
invariant(this.modifiedProperties !== undefined);
|
||||
invariant(this.createdObjects !== undefined);
|
||||
return new Effects(
|
||||
new SimpleNormalCompletion(v),
|
||||
v instanceof Completion ? v : new SimpleNormalCompletion(v),
|
||||
this.generator,
|
||||
this.modifiedBindings,
|
||||
this.modifiedProperties,
|
||||
@ -1434,9 +1249,9 @@ export class Realm {
|
||||
);
|
||||
}
|
||||
|
||||
stopEffectCaptureAndUndoEffects(completion: PossiblyNormalCompletion): void {
|
||||
stopEffectCaptureAndUndoEffects(completion: JoinedNormalAndAbruptCompletions): void {
|
||||
// Roll back the state changes
|
||||
this.undoBindings(this.modifiedBindings);
|
||||
this.restoreBindings(this.modifiedBindings);
|
||||
this.restoreProperties(this.modifiedProperties);
|
||||
|
||||
// Restore saved state
|
||||
@ -1465,7 +1280,7 @@ export class Realm {
|
||||
if (appendGenerator) this.appendGenerator(generator, leadingComment);
|
||||
|
||||
// Restore modifiedBindings
|
||||
this.redoBindings(modifiedBindings);
|
||||
this.restoreBindings(modifiedBindings);
|
||||
this.restoreProperties(modifiedProperties);
|
||||
|
||||
// track modifiedBindings
|
||||
@ -1567,10 +1382,8 @@ export class Realm {
|
||||
|
||||
if (this.modifiedBindings !== undefined && !this.modifiedBindings.has(binding)) {
|
||||
this.modifiedBindings.set(binding, {
|
||||
hasLeaked: undefined,
|
||||
value: undefined,
|
||||
previousHasLeaked: binding.hasLeaked,
|
||||
previousValue: binding.value,
|
||||
hasLeaked: binding.hasLeaked,
|
||||
value: binding.value,
|
||||
});
|
||||
}
|
||||
return binding;
|
||||
@ -1648,21 +1461,17 @@ export class Realm {
|
||||
return result;
|
||||
}
|
||||
|
||||
redoBindings(modifiedBindings: void | Bindings): void {
|
||||
// Restores each Binding in the given map to the value it
|
||||
// had when it was entered into the map and updates the map to record
|
||||
// the value the Binding had just before the call to this method.
|
||||
restoreBindings(modifiedBindings: void | Bindings) {
|
||||
if (modifiedBindings === undefined) return;
|
||||
modifiedBindings.forEach(({ hasLeaked, value }, binding, m) => {
|
||||
binding.hasLeaked = hasLeaked || false;
|
||||
let l = binding.hasLeaked;
|
||||
let v = binding.value;
|
||||
binding.hasLeaked = hasLeaked;
|
||||
binding.value = value;
|
||||
});
|
||||
}
|
||||
|
||||
undoBindings(modifiedBindings: void | Bindings): void {
|
||||
if (modifiedBindings === undefined) return;
|
||||
modifiedBindings.forEach((entry, binding, m) => {
|
||||
if (entry.hasLeaked === undefined) entry.hasLeaked = binding.hasLeaked;
|
||||
if (entry.value === undefined) entry.value = binding.value;
|
||||
binding.hasLeaked = entry.previousHasLeaked || false;
|
||||
binding.value = entry.previousValue;
|
||||
m.set(binding, { hasLeaked: l, value: v });
|
||||
});
|
||||
}
|
||||
|
||||
@ -1755,7 +1564,7 @@ export class Realm {
|
||||
if (typeof message === "string") message = new StringValue(this, message);
|
||||
invariant(message instanceof StringValue);
|
||||
this.nextContextLocation = this.currentLocation;
|
||||
return new ThrowCompletion(Construct(this, type, [message]), undefined, this.currentLocation);
|
||||
return new ThrowCompletion(Construct(this, type, [message]), this.currentLocation);
|
||||
}
|
||||
|
||||
appendGenerator(generator: Generator, leadingComment: string = ""): void {
|
||||
|
@ -10,7 +10,7 @@
|
||||
/* @flow */
|
||||
|
||||
import type { BabelNodeSourceLocation } from "@babel/types";
|
||||
import { Completion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { AbruptCompletion } from "../completions.js";
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import invariant from "../invariant.js";
|
||||
import { type Effects, type PropertyBindings, Realm } from "../realm.js";
|
||||
@ -228,8 +228,8 @@ export class Functions {
|
||||
let call = Utils.createModelledFunctionCall(this.realm, functionValue, argModel);
|
||||
let realm = this.realm;
|
||||
|
||||
let logCompilerDiagnostic = (msg: string) => {
|
||||
let error = new CompilerDiagnostic(msg, undefined, "PP1007", "Warning");
|
||||
let logCompilerDiagnostic = (msg: string, location: ?BabelNodeSourceLocation) => {
|
||||
let error = new CompilerDiagnostic(msg, location, "PP1007", "Warning");
|
||||
realm.handleError(error);
|
||||
};
|
||||
let effects: Effects = realm.evaluatePure(
|
||||
@ -293,7 +293,7 @@ export class Functions {
|
||||
invariant(additionalFunctionEffects !== undefined);
|
||||
let e1 = additionalFunctionEffects.effects;
|
||||
invariant(e1 !== undefined);
|
||||
if (e1.result instanceof Completion && !e1.result instanceof PossiblyNormalCompletion) {
|
||||
if (e1.result instanceof AbruptCompletion) {
|
||||
let error = new CompilerDiagnostic(
|
||||
`Additional function ${fun1Name} will terminate abruptly`,
|
||||
e1.result.location,
|
||||
|
@ -51,7 +51,6 @@ export class Serializer {
|
||||
this.realm,
|
||||
this.logger,
|
||||
!!serializerOptions.logModules,
|
||||
!!serializerOptions.delayUnsupportedRequires,
|
||||
!!serializerOptions.accelerateUnsupportedRequires
|
||||
);
|
||||
this.functions = new Functions(this.realm, this.modules.moduleTracer);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user