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:
Nikolai Tillmann 2018-09-20 10:02:05 -07:00 committed by Facebook Github Bot
parent 0dced15809
commit 4b911f2138
6 changed files with 46 additions and 54 deletions

View File

@ -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);

View File

@ -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
);

View File

@ -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);

View File

@ -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");
}

View File

@ -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);

View File

@ -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()
);