mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-08-17 10:30:32 +03:00
Refactoring of Generator DAG to Generator Tree. (#2434)
Summary: Release notes: None To avoid all kinds of serialization issues, generators should form a tree, and not a DAG. This is an attempt to establish that. Pull Request resolved: https://github.com/facebook/prepack/pull/2434 Differential Revision: D9887684 Pulled By: NTillmann fbshipit-source-id: c11c50c56dcd6eff02bd03448756bf79f6e2c820
This commit is contained in:
parent
0dced15809
commit
4b911f2138
@ -13,14 +13,13 @@ import invariant from "../invariant.js";
|
||||
import { FunctionValue, ObjectValue } from "../values/index.js";
|
||||
import { Generator } from "../utils/generator.js";
|
||||
|
||||
// This class maintains a DAG containing all generators known so far,
|
||||
// This class maintains a tree containing all generators known so far,
|
||||
// and information about the most specific generator that created any
|
||||
// particular object.
|
||||
// New sub-DAGs are added in chunks, at the beginning for the global generator,
|
||||
// New sub-trees are added in chunks, at the beginning for the global generator,
|
||||
// and every time the visitor handles another additional function.
|
||||
// NOTE: The serializer can only properly handle Generator trees, not actual DAGs.
|
||||
export class GeneratorDAG {
|
||||
parents: Map<Generator, Array<Generator | FunctionValue | "GLOBAL">>;
|
||||
export class GeneratorTree {
|
||||
parents: Map<Generator, Generator | FunctionValue | "GLOBAL">;
|
||||
createdObjects: Map<ObjectValue, Generator>;
|
||||
|
||||
constructor() {
|
||||
@ -28,16 +27,10 @@ export class GeneratorDAG {
|
||||
this.createdObjects = new Map();
|
||||
}
|
||||
|
||||
// DAG TODO: This function is dubious in the presence of actual dags.
|
||||
getParent(generator: Generator): Generator | FunctionValue | "GLOBAL" {
|
||||
let a = this.parents.get(generator);
|
||||
invariant(a !== undefined && a.length >= 1);
|
||||
return a[0];
|
||||
}
|
||||
|
||||
isParent(parent: Generator | FunctionValue | "GLOBAL", generator: Generator): boolean {
|
||||
let a = this.parents.get(generator);
|
||||
return a !== undefined && a.includes(parent);
|
||||
let parent = this.parents.get(generator);
|
||||
invariant(parent !== undefined);
|
||||
return parent;
|
||||
}
|
||||
|
||||
getCreator(value: ObjectValue): Generator | void {
|
||||
@ -49,9 +42,8 @@ export class GeneratorDAG {
|
||||
}
|
||||
|
||||
_add(parent: Generator | FunctionValue | "GLOBAL", generator: Generator): void {
|
||||
let a = this.parents.get(generator);
|
||||
if (a === undefined) this.parents.set(generator, (a = []));
|
||||
if (!a.includes(parent)) a.push(parent);
|
||||
invariant(!this.parents.has(generator));
|
||||
this.parents.set(generator, parent);
|
||||
let effects = generator.effectsToApply;
|
||||
if (effects !== undefined) {
|
||||
invariant(parent instanceof FunctionValue);
|
@ -24,7 +24,7 @@ import { ResidualHeapSerializer } from "./ResidualHeapSerializer.js";
|
||||
import { getOrDefault } from "./utils.js";
|
||||
import type { ResidualOptimizedFunctions } from "./ResidualOptimizedFunctions";
|
||||
import type { Referentializer } from "./Referentializer.js";
|
||||
import { GeneratorDAG } from "./GeneratorDAG.js";
|
||||
import { GeneratorTree } from "./GeneratorTree.js";
|
||||
|
||||
const LAZY_OBJECTS_SERIALIZER_BODY_TYPE = "LazyObjectInitializer";
|
||||
|
||||
@ -49,7 +49,7 @@ export class LazyObjectsSerializer extends ResidualHeapSerializer {
|
||||
options: SerializerOptions,
|
||||
additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
|
||||
referentializer: Referentializer,
|
||||
generatorDAG: GeneratorDAG,
|
||||
generatorTree: GeneratorTree,
|
||||
residualOptimizedFunctions: ResidualOptimizedFunctions
|
||||
) {
|
||||
super(
|
||||
@ -62,7 +62,7 @@ export class LazyObjectsSerializer extends ResidualHeapSerializer {
|
||||
options,
|
||||
additionalFunctionValuesAndEffects,
|
||||
referentializer,
|
||||
generatorDAG,
|
||||
generatorTree,
|
||||
residualOptimizedFunctions
|
||||
);
|
||||
|
||||
|
@ -81,7 +81,7 @@ import { ResidualReactElementSerializer } from "./ResidualReactElementSerializer
|
||||
import type { Binding } from "../environment.js";
|
||||
import { GlobalEnvironmentRecord, DeclarativeEnvironmentRecord } from "../environment.js";
|
||||
import type { Referentializer } from "./Referentializer.js";
|
||||
import { GeneratorDAG } from "./GeneratorDAG.js";
|
||||
import { GeneratorTree } from "./GeneratorTree.js";
|
||||
import { type Replacement, getReplacement } from "./ResidualFunctionInstantiator.js";
|
||||
import { describeValue } from "../utils.js";
|
||||
import { getAsPropertyNameExpression } from "../utils/babelhelpers.js";
|
||||
@ -123,7 +123,7 @@ export class ResidualHeapSerializer {
|
||||
options: SerializerOptions,
|
||||
additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
|
||||
referentializer: Referentializer,
|
||||
generatorDAG: GeneratorDAG,
|
||||
generatorTree: GeneratorTree,
|
||||
residualOptimizedFunctions: ResidualOptimizedFunctions
|
||||
) {
|
||||
this.realm = realm;
|
||||
@ -215,7 +215,7 @@ export class ResidualHeapSerializer {
|
||||
this.rewrittenAdditionalFunctions = new Map();
|
||||
this.declarativeEnvironmentRecordsBindings = residualHeapInfo.declarativeEnvironmentRecordsBindings;
|
||||
this.globalBindings = residualHeapInfo.globalBindings;
|
||||
this.generatorDAG = generatorDAG;
|
||||
this.generatorTree = generatorTree;
|
||||
this.conditionalFeasibility = residualHeapInfo.conditionalFeasibility;
|
||||
this.additionalFunctionGenerators = new Map();
|
||||
this.declaredGlobalLets = new Map();
|
||||
@ -274,7 +274,7 @@ export class ResidualHeapSerializer {
|
||||
// TODO: revisit this and fix additional functions to be capable of delaying initializations
|
||||
additionalFunctionValueNestedFunctions: Set<FunctionValue>;
|
||||
|
||||
generatorDAG: GeneratorDAG;
|
||||
generatorTree: GeneratorTree;
|
||||
conditionalFeasibility: Map<AbstractValue, { t: boolean, f: boolean }>;
|
||||
additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>;
|
||||
|
||||
@ -888,7 +888,7 @@ export class ResidualHeapSerializer {
|
||||
generators = generators.filter(generator => {
|
||||
let s = generator;
|
||||
while (s instanceof Generator) {
|
||||
s = this.generatorDAG.getParent(s);
|
||||
s = this.generatorTree.getParent(s);
|
||||
}
|
||||
return s === "GLOBAL";
|
||||
});
|
||||
@ -904,7 +904,7 @@ export class ResidualHeapSerializer {
|
||||
}
|
||||
|
||||
const getGeneratorParent = g => {
|
||||
let s = this.generatorDAG.getParent(g);
|
||||
let s = this.generatorTree.getParent(g);
|
||||
return s instanceof Generator ? s : undefined;
|
||||
};
|
||||
// This value is referenced from more than one generator.
|
||||
@ -2132,7 +2132,7 @@ export class ResidualHeapSerializer {
|
||||
|
||||
_annotateGeneratorStatements(generator: Generator, statements: Array<BabelNodeStatement>): void {
|
||||
let comment = `generator "${generator.getName()}"`;
|
||||
let parent = this.generatorDAG.getParent(generator);
|
||||
let parent = this.generatorTree.getParent(generator);
|
||||
if (parent instanceof Generator) {
|
||||
comment = `${comment} with parent "${parent.getName()}"`;
|
||||
} else if (parent instanceof FunctionValue) {
|
||||
@ -2574,7 +2574,7 @@ export class ResidualHeapSerializer {
|
||||
for (let s of scopes)
|
||||
if (s instanceof Generator) {
|
||||
let text = "";
|
||||
for (; s instanceof Generator; s = this.generatorDAG.getParent(s)) text += "=>" + s.getName();
|
||||
for (; s instanceof Generator; s = this.generatorTree.getParent(s)) text += "=>" + s.getName();
|
||||
console.log(` ${text}`);
|
||||
} else {
|
||||
invariant(s instanceof FunctionValue);
|
||||
|
@ -65,7 +65,7 @@ import {
|
||||
import { createPathConditions, Environment, To } from "../singletons.js";
|
||||
import { isReactElement, isReactPropsObject, valueIsReactLibraryObject } from "../react/utils.js";
|
||||
import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js";
|
||||
import { GeneratorDAG } from "./GeneratorDAG.js";
|
||||
import { GeneratorTree } from "./GeneratorTree.js";
|
||||
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
|
||||
|
||||
type BindingState = {|
|
||||
@ -113,7 +113,7 @@ export class ResidualHeapVisitor {
|
||||
this.globalEnvironmentRecord = environment;
|
||||
this.additionalGeneratorRoots = new Map();
|
||||
this.residualReactElementVisitor = new ResidualReactElementVisitor(this.realm, this);
|
||||
this.generatorDAG = new GeneratorDAG();
|
||||
this.generatorTree = new GeneratorTree();
|
||||
}
|
||||
|
||||
realm: Realm;
|
||||
@ -142,7 +142,7 @@ export class ResidualHeapVisitor {
|
||||
classMethodInstances: Map<FunctionValue, ClassMethodInstance>;
|
||||
// Parents will always be a generator, optimized function value or "GLOBAL"
|
||||
additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>;
|
||||
generatorDAG: GeneratorDAG;
|
||||
generatorTree: GeneratorTree;
|
||||
globalEnvironmentRecord: GlobalEnvironmentRecord;
|
||||
residualReactElementVisitor: ResidualReactElementVisitor;
|
||||
|
||||
@ -151,13 +151,13 @@ export class ResidualHeapVisitor {
|
||||
_getCommonScope(): FunctionValue | Generator {
|
||||
let s = this.scope;
|
||||
while (true) {
|
||||
if (s instanceof Generator) s = this.generatorDAG.getParent(s);
|
||||
if (s instanceof Generator) s = this.generatorTree.getParent(s);
|
||||
else if (s instanceof FunctionValue) {
|
||||
// Did we find an additional function?
|
||||
if (this.additionalFunctionValuesAndEffects.has(s)) return s;
|
||||
|
||||
// Did the function itself get created by a generator we can chase?
|
||||
s = this.generatorDAG.getCreator(s) || "GLOBAL";
|
||||
s = this.generatorTree.getCreator(s) || "GLOBAL";
|
||||
} else {
|
||||
invariant(s === "GLOBAL");
|
||||
let generator = this.globalGenerator;
|
||||
@ -179,7 +179,7 @@ export class ResidualHeapVisitor {
|
||||
// created --- this causes the value later to be serialized in its
|
||||
// creation scope, ensuring that the value has the right creation / life time.
|
||||
_registerAdditionalRoot(value: ObjectValue): void {
|
||||
let creationGenerator = this.generatorDAG.getCreator(value) || this.globalGenerator;
|
||||
let creationGenerator = this.generatorTree.getCreator(value) || this.globalGenerator;
|
||||
|
||||
let additionalFunction = this._getAdditionalFunctionOfScope() || "GLOBAL";
|
||||
let targetAdditionalFunction;
|
||||
@ -188,7 +188,7 @@ export class ResidualHeapVisitor {
|
||||
} else {
|
||||
let s = creationGenerator;
|
||||
while (s instanceof Generator) {
|
||||
s = this.generatorDAG.getParent(s);
|
||||
s = this.generatorTree.getParent(s);
|
||||
invariant(s !== undefined);
|
||||
}
|
||||
invariant(s === "GLOBAL" || s instanceof FunctionValue);
|
||||
@ -206,7 +206,7 @@ export class ResidualHeapVisitor {
|
||||
additionalFVEffects.additionalRoots.add(value);
|
||||
|
||||
this._visitInUnrelatedScope(creationGenerator, value);
|
||||
usageScope = this.generatorDAG.getCreator(value) || this.globalGenerator;
|
||||
usageScope = this.generatorTree.getCreator(value) || this.globalGenerator;
|
||||
}
|
||||
|
||||
usageScope = this.scope;
|
||||
@ -215,7 +215,7 @@ export class ResidualHeapVisitor {
|
||||
// applying effects; if so, store additional information that the serializer
|
||||
// can use to proactive serialize such objects from within the right generator
|
||||
let anyRelevantEffects = false;
|
||||
for (let g = usageScope; g instanceof Generator; g = this.generatorDAG.getParent(g)) {
|
||||
for (let g = usageScope; g instanceof Generator; g = this.generatorTree.getParent(g)) {
|
||||
if (g === creationGenerator) {
|
||||
if (anyRelevantEffects) {
|
||||
let s = this.additionalGeneratorRoots.get(g);
|
||||
@ -1098,7 +1098,7 @@ export class ResidualHeapVisitor {
|
||||
if (this.preProcessValue(val)) this.visitValueProxy(val);
|
||||
this.postProcessValue(val);
|
||||
} else if (val instanceof FunctionValue) {
|
||||
let creationGenerator = this.generatorDAG.getCreator(val) || this.globalGenerator;
|
||||
let creationGenerator = this.generatorTree.getCreator(val) || this.globalGenerator;
|
||||
|
||||
// 1. Visit function in its creation scope
|
||||
this._enqueueWithUnrelatedScope(creationGenerator, () => {
|
||||
@ -1132,7 +1132,7 @@ export class ResidualHeapVisitor {
|
||||
let callbacks = {
|
||||
visitEquivalentValue: this.visitEquivalentValue.bind(this),
|
||||
visitGenerator: (generator, parent) => {
|
||||
invariant(this.generatorDAG.isParent(parent, generator));
|
||||
invariant(this.generatorTree.getParent(generator) === parent);
|
||||
this.visitGenerator(generator, additionalFunctionInfo);
|
||||
},
|
||||
canOmit: (value: Value): boolean => {
|
||||
@ -1282,7 +1282,7 @@ export class ResidualHeapVisitor {
|
||||
this.additionalFunctionValueInfos.set(functionValue, additionalFunctionInfo);
|
||||
|
||||
let effectsGenerator = additionalEffects.generator;
|
||||
this.generatorDAG.add(functionValue, effectsGenerator);
|
||||
this.generatorTree.add(functionValue, effectsGenerator);
|
||||
this.visitGenerator(effectsGenerator, additionalFunctionInfo);
|
||||
};
|
||||
|
||||
@ -1294,7 +1294,7 @@ export class ResidualHeapVisitor {
|
||||
}
|
||||
|
||||
visitRoots(): void {
|
||||
this.generatorDAG.add("GLOBAL", this.globalGenerator);
|
||||
this.generatorTree.add("GLOBAL", this.globalGenerator);
|
||||
this.visitGenerator(this.globalGenerator);
|
||||
for (let moduleValue of this.modules.initializedModules.values()) this.visitValue(moduleValue);
|
||||
|
||||
@ -1316,7 +1316,7 @@ export class ResidualHeapVisitor {
|
||||
let expected = 0;
|
||||
for (let { scope, action } of this.delayedActions) {
|
||||
let generator;
|
||||
if (scope instanceof FunctionValue) generator = this.generatorDAG.getCreator(scope) || this.globalGenerator;
|
||||
if (scope instanceof FunctionValue) generator = this.generatorTree.getCreator(scope) || this.globalGenerator;
|
||||
else if (scope === "GLOBAL") generator = this.globalGenerator;
|
||||
else {
|
||||
invariant(scope instanceof Generator);
|
||||
@ -1370,10 +1370,10 @@ export class ResidualHeapVisitor {
|
||||
info.nestedEffectsRunners.push(runGeneratorAction);
|
||||
runGeneratorAction = undefined;
|
||||
}
|
||||
s = this.generatorDAG.getParent(s);
|
||||
s = this.generatorTree.getParent(s);
|
||||
} else if (s instanceof FunctionValue) {
|
||||
invariant(this.additionalFunctionValuesAndEffects.has(s));
|
||||
s = this.generatorDAG.getCreator(s) || "GLOBAL";
|
||||
s = this.generatorTree.getCreator(s) || "GLOBAL";
|
||||
}
|
||||
invariant(s instanceof Generator || s instanceof FunctionValue || s === "GLOBAL");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
import { FunctionValue } from "../values/index.js";
|
||||
import type { AdditionalFunctionEffects } from "./types";
|
||||
import invariant from "../invariant.js";
|
||||
import { GeneratorDAG } from "./GeneratorDAG";
|
||||
import { GeneratorTree } from "./GeneratorTree";
|
||||
import type { Scope } from "./types.js";
|
||||
import { FunctionEnvironmentRecord } from "../environment";
|
||||
import type { Value } from "../values/index";
|
||||
@ -20,16 +20,16 @@ import { Generator } from "../utils/generator";
|
||||
|
||||
export class ResidualOptimizedFunctions {
|
||||
constructor(
|
||||
generatorDAG: GeneratorDAG,
|
||||
generatorTree: GeneratorTree,
|
||||
optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
|
||||
residualValues: Map<Value, Set<Scope>>
|
||||
) {
|
||||
this._generatorDAG = generatorDAG;
|
||||
this._generatorTree = generatorTree;
|
||||
this._optimizedFunctionsAndEffects = optimizedFunctionsAndEffects;
|
||||
this._residualValues = residualValues;
|
||||
}
|
||||
|
||||
_generatorDAG: GeneratorDAG;
|
||||
_generatorTree: GeneratorTree;
|
||||
_optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>;
|
||||
_residualValues: Map<Value, Set<Scope>>;
|
||||
|
||||
@ -80,7 +80,7 @@ export class ResidualOptimizedFunctions {
|
||||
for (let scope of scopes) {
|
||||
let s = scope;
|
||||
while (s instanceof Generator) {
|
||||
s = this._generatorDAG.getParent(s);
|
||||
s = this._generatorTree.getParent(s);
|
||||
}
|
||||
if (s === "GLOBAL") return undefined;
|
||||
invariant(s instanceof FunctionValue);
|
||||
|
@ -193,7 +193,7 @@ export class Serializer {
|
||||
if (this.realm.react.verbose) {
|
||||
this.logger.logInformation(`Visiting evaluated nodes...`);
|
||||
}
|
||||
let [residualHeapInfo, generatorDAG, inspector] = (() => {
|
||||
let [residualHeapInfo, generatorTree, inspector] = (() => {
|
||||
let residualHeapVisitor = new ResidualHeapVisitor(
|
||||
this.realm,
|
||||
this.logger,
|
||||
@ -201,12 +201,12 @@ export class Serializer {
|
||||
additionalFunctionValuesAndEffects
|
||||
);
|
||||
statistics.deepTraversal.measure(() => residualHeapVisitor.visitRoots());
|
||||
return [residualHeapVisitor.toInfo(), residualHeapVisitor.generatorDAG, residualHeapVisitor.inspector];
|
||||
return [residualHeapVisitor.toInfo(), residualHeapVisitor.generatorTree, residualHeapVisitor.inspector];
|
||||
})();
|
||||
if (this.logger.hasErrors()) return undefined;
|
||||
|
||||
let residualOptimizedFunctions = new ResidualOptimizedFunctions(
|
||||
generatorDAG,
|
||||
generatorTree,
|
||||
additionalFunctionValuesAndEffects,
|
||||
residualHeapInfo.values
|
||||
);
|
||||
@ -270,7 +270,7 @@ export class Serializer {
|
||||
this.options,
|
||||
additionalFunctionValuesAndEffects,
|
||||
referentializer,
|
||||
generatorDAG,
|
||||
generatorTree,
|
||||
residualOptimizedFunctions
|
||||
).serialize();
|
||||
});
|
||||
@ -293,7 +293,7 @@ export class Serializer {
|
||||
this.options,
|
||||
additionalFunctionValuesAndEffects,
|
||||
referentializer,
|
||||
generatorDAG,
|
||||
generatorTree,
|
||||
residualOptimizedFunctions
|
||||
).serialize()
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user