mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-11-09 00:15:21 +03:00
Analyze residual functions
Summary: Release note: Provide a way to analyze residual functions for Prepack errors Since the analysis takes a while, the --check option now takes start and count parameters so that a subrange of functions can be analyzed. Each function is analyzed only in the context of the heap as it was at the end of global code execution and the arguments supplied to the analysis are all simple partial abstract objects, so that the analysis of a function does not come to screeching halt every time a parameter is used as the object of a member expression or as the argument of an operator. A number of tweaks to the handling of simple objects will be in a follow up pull request. Closes https://github.com/facebook/prepack/pull/1379 Differential Revision: D6876045 Pulled By: hermanventer fbshipit-source-id: 4d192cc6cf222b3986114362c5642a949f292845
This commit is contained in:
parent
28668e57d9
commit
8834dd8d74
@ -26,7 +26,6 @@
|
||||
"build": "babel src --out-dir lib --source-maps && yarn build-bundle",
|
||||
"build-scripts": "babel scripts --out-dir lib --source-maps",
|
||||
"build-bundle": "webpack",
|
||||
"precheck": "node --stack_size=10000 --stack_trace_limit=200 --max_old_space_size=16384 lib/prepack-cli.js --accelerateUnsupportedRequires --check --compatibility jsc-600-1-4-17 --delayUnsupportedRequires --mathRandomSeed 0 ",
|
||||
"watch": "babel src scripts --out-dir lib --watch --source-maps",
|
||||
"lint": "eslint src scripts",
|
||||
"flow": "flow check",
|
||||
@ -48,7 +47,8 @@
|
||||
"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",
|
||||
"prepack": "node lib/prepack-cli.js",
|
||||
"precheck": "yarn prepack --check",
|
||||
"prepack": "node --stack_size=10000 --stack_trace_limit=200 --max_old_space_size=16384 lib/prepack-cli.js --accelerateUnsupportedRequires --compatibility jsc-600-1-4-17 --delayUnsupportedRequires --mathRandomSeed 0 ",
|
||||
"prepack-prepack": "node --stack_size=10000 --max_old_space_size=8096 ./bin/prepack.js ./lib/prepack-cli.js --out ./lib/prepack-cli.prepacked.js --compatibility node-cli --mathRandomSeed rnd",
|
||||
"validate": "yarn build && yarn build-scripts && yarn lint && yarn depcheck && yarn flow && yarn test",
|
||||
"prepublish": "yarn build",
|
||||
|
@ -18,7 +18,7 @@ export type ReactOutputTypes = "create-element" | "jsx" | "bytecode";
|
||||
export const ReactOutputValues = ["create-element", "jsx", "bytecode"];
|
||||
|
||||
export type RealmOptions = {
|
||||
check?: boolean,
|
||||
check?: Array<number>,
|
||||
compatibility?: Compatibility,
|
||||
debugNames?: boolean,
|
||||
errorHandler?: ErrorHandler,
|
||||
|
@ -56,7 +56,7 @@ function run(
|
||||
--trace Traces the order of module initialization.
|
||||
--serialize Serializes the partially evaluated global environment as a program that recreates it.
|
||||
(default = true)
|
||||
--check Check whole program for diagnostic messages. Do not serialize or produce residual code.
|
||||
--check [start[, count]] Check residual functions for diagnostic messages. Do not serialize or produce residual code.
|
||||
--residual Produces the residual program that results after constant folding.
|
||||
--profile Enables console logging of profile information of different phases of prepack.
|
||||
--statsFile The name of the output file where statistics will be written to.
|
||||
@ -71,6 +71,7 @@ function run(
|
||||
args.splice(0, 2);
|
||||
let inputFilenames = [];
|
||||
let outputFilename;
|
||||
let check: void | Array<number>;
|
||||
let compatibility: Compatibility;
|
||||
let mathRandomSeed;
|
||||
let inputSourceMap;
|
||||
@ -78,9 +79,9 @@ function run(
|
||||
let statsFileName;
|
||||
let maxStackDepth: number;
|
||||
let timeout: number;
|
||||
let additionalFunctions: Array<string>;
|
||||
let additionalFunctions: void | Array<string>;
|
||||
let lazyObjectsRuntime: string;
|
||||
let heapGraphFilePath: string;
|
||||
let heapGraphFilePath: void | string;
|
||||
let debugInFilePath: string;
|
||||
let debugOutFilePath: string;
|
||||
let reactOutput: ReactOutputTypes = "create-element";
|
||||
@ -101,7 +102,6 @@ function run(
|
||||
debugScopes: false,
|
||||
serialize: false,
|
||||
residual: false,
|
||||
check: false,
|
||||
profile: false,
|
||||
reactEnabled: false,
|
||||
};
|
||||
@ -157,6 +157,26 @@ function run(
|
||||
let line = args.shift();
|
||||
additionalFunctions = line.split(",");
|
||||
break;
|
||||
case "check":
|
||||
let range = args.shift();
|
||||
if (range.startsWith("--")) {
|
||||
args.unshift(range);
|
||||
range = "0";
|
||||
}
|
||||
let pair: Array<any> = range.split(",");
|
||||
if (pair.length === 1) pair.push(Number.MAX_SAFE_INTEGER);
|
||||
let start = +pair[0];
|
||||
if (start < 0 || !Number.isInteger(start)) {
|
||||
console.error("check start offset must be a number");
|
||||
process.exit(1);
|
||||
}
|
||||
let count = +pair[1];
|
||||
if (count < 0 || !Number.isInteger(count)) {
|
||||
console.error("check count must be a number");
|
||||
process.exit(1);
|
||||
}
|
||||
check = [start, count];
|
||||
break;
|
||||
case "debugInFilePath":
|
||||
debugInFilePath = args.shift();
|
||||
break;
|
||||
@ -188,6 +208,7 @@ function run(
|
||||
"--maxStackDepth depthValue",
|
||||
"--timeout seconds",
|
||||
"--additionalFunctions fnc1,fnc2,...",
|
||||
"--check [start[, number]]",
|
||||
"--lazyObjectsRuntime lazyObjectsRuntimeName",
|
||||
"--heapGraphFilePath heapGraphFilePath",
|
||||
"--reactOutput " + ReactOutputValues.join(" | "),
|
||||
@ -210,7 +231,7 @@ function run(
|
||||
}
|
||||
}
|
||||
if (!flags.serialize && !flags.residual) flags.serialize = true;
|
||||
if (flags.check) {
|
||||
if (check) {
|
||||
flags.serialize = false;
|
||||
flags.residual = false;
|
||||
}
|
||||
@ -226,8 +247,9 @@ function run(
|
||||
maxStackDepth: maxStackDepth,
|
||||
timeout: timeout,
|
||||
additionalFunctions: additionalFunctions,
|
||||
check: check,
|
||||
lazyObjectsRuntime: lazyObjectsRuntime,
|
||||
heapGraphFormat: "DotLanguage",
|
||||
heapGraphFormat: heapGraphFilePath ? "DotLanguage" : undefined,
|
||||
debugInFilePath: debugInFilePath,
|
||||
debugOutFilePath: debugOutFilePath,
|
||||
reactOutput: reactOutput,
|
||||
@ -276,7 +298,11 @@ function run(
|
||||
1}) ${error.severity} ${error.errorCode}: ${error.message}` +
|
||||
` (https://github.com/facebook/prepack/wiki/${error.errorCode})`
|
||||
);
|
||||
console.error(error.callStack || "");
|
||||
let callStack = error.callStack;
|
||||
if (callStack !== undefined) {
|
||||
let eolPos = callStack.indexOf("\n");
|
||||
if (eolPos > 0) console.error(callStack.substring(eolPos + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundFatal;
|
||||
|
@ -39,7 +39,7 @@ export type PrepackOptions = {|
|
||||
reactOutput?: ReactOutputTypes,
|
||||
residual?: boolean,
|
||||
serialize?: boolean,
|
||||
check?: boolean,
|
||||
check?: Array<number>,
|
||||
inlineExpressions?: boolean,
|
||||
simpleClosures?: boolean,
|
||||
sourceMaps?: boolean,
|
||||
|
@ -15,6 +15,7 @@ import Serializer from "./serializer/index.js";
|
||||
import construct_realm from "./construct_realm.js";
|
||||
import initializeGlobals from "./globals.js";
|
||||
import * as t from "babel-types";
|
||||
import { EvaluateDirectCallWithArgList } from "./methods/index.js";
|
||||
import { getRealmOptions, getSerializerOptions } from "./prepack-options";
|
||||
import { FatalError } from "./errors.js";
|
||||
import type { SourceFile } from "./types.js";
|
||||
@ -26,9 +27,11 @@ import invariant from "./invariant.js";
|
||||
import { version } from "../package.json";
|
||||
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
|
||||
import { type SerializedResult, SerializerStatistics } from "./serializer/types.js";
|
||||
import { ResidualHeapVisitor } from "./serializer/ResidualHeapVisitor.js";
|
||||
import { Modules } from "./utils/modules.js";
|
||||
import { Logger } from "./utils/logger.js";
|
||||
import { Generator } from "./utils/generator.js";
|
||||
import { AbstractObjectValue, AbstractValue, ObjectValue } from "./values/index.js";
|
||||
|
||||
// IMPORTANT: This function is now deprecated and will go away in a future release.
|
||||
// Please use FatalError instead.
|
||||
@ -67,7 +70,8 @@ export function prepackSources(
|
||||
);
|
||||
let [result] = realm.$GlobalEnv.executeSources(sources);
|
||||
if (result instanceof AbruptCompletion) throw result;
|
||||
checkResidualFunctions(modules);
|
||||
invariant(options.check);
|
||||
checkResidualFunctions(modules, options.check[0], options.check[1]);
|
||||
return { code: "", map: undefined };
|
||||
} else if (options.serialize || !options.residual) {
|
||||
let serializer = new Serializer(realm, getSerializerOptions(options));
|
||||
@ -159,10 +163,50 @@ export function prepackFromAst(
|
||||
return serialized;
|
||||
}
|
||||
|
||||
function checkResidualFunctions(modules: Modules) {
|
||||
function checkResidualFunctions(modules: Modules, startFunc: number, totalToAnalyze: number) {
|
||||
let realm = modules.realm;
|
||||
let env = realm.$GlobalEnv;
|
||||
realm.$GlobalObject.makeSimple();
|
||||
let errorHandler = realm.errorHandler;
|
||||
if (!errorHandler) errorHandler = diag => realm.handleError(diag);
|
||||
realm.errorHandler = diag => {
|
||||
invariant(errorHandler);
|
||||
if (diag.severity === "FatalError") return errorHandler(diag);
|
||||
else return "Recover";
|
||||
};
|
||||
modules.resolveInitializedModules();
|
||||
modules.initializeMoreModules();
|
||||
//todo: find residual functions and execute them for effects
|
||||
let residualHeapVisitor = new ResidualHeapVisitor(realm, modules.logger, modules, new Map());
|
||||
residualHeapVisitor.visitRoots();
|
||||
if (modules.logger.hasErrors()) return;
|
||||
let totalFunctions = 0;
|
||||
let nonFatalFunctions = 0;
|
||||
for (let fi of residualHeapVisitor.functionInstances.values()) {
|
||||
totalFunctions++;
|
||||
if (totalFunctions <= startFunc) continue;
|
||||
let fv = fi.functionValue;
|
||||
console.log("analyzing: " + totalFunctions);
|
||||
let thisValue = realm.intrinsics.null;
|
||||
let n = fv.getLength() || 0;
|
||||
let args = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
let name = "dummy parameter";
|
||||
let ob: AbstractObjectValue = (AbstractValue.createFromType(realm, ObjectValue, name): any);
|
||||
ob.makeSimple();
|
||||
ob.intrinsicName = name;
|
||||
args[i] = ob;
|
||||
}
|
||||
// todo: eventually join these effects, apply them to the global state and iterate to a fixed point
|
||||
try {
|
||||
realm.evaluateAndRevertInGlobalEnv(() =>
|
||||
EvaluateDirectCallWithArgList(modules.realm, true, env, fv, fv, thisValue, args)
|
||||
);
|
||||
nonFatalFunctions++;
|
||||
} catch (e) {}
|
||||
if (totalFunctions >= startFunc + totalToAnalyze) break;
|
||||
}
|
||||
console.log(
|
||||
`Analyzed ${totalToAnalyze} functions starting at ${startFunc} of which ${nonFatalFunctions} did not have fatal errors.`
|
||||
);
|
||||
}
|
||||
|
||||
export const prepackVersion = version;
|
||||
|
Loading…
Reference in New Issue
Block a user