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 { FunctionValue, ObjectValue } from "../values/index.js";
import { Generator } from "../utils/generator.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 // and information about the most specific generator that created any
// particular object. // 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. // and every time the visitor handles another additional function.
// NOTE: The serializer can only properly handle Generator trees, not actual DAGs. export class GeneratorTree {
export class GeneratorDAG { parents: Map<Generator, Generator | FunctionValue | "GLOBAL">;
parents: Map<Generator, Array<Generator | FunctionValue | "GLOBAL">>;
createdObjects: Map<ObjectValue, Generator>; createdObjects: Map<ObjectValue, Generator>;
constructor() { constructor() {
@ -28,16 +27,10 @@ export class GeneratorDAG {
this.createdObjects = new Map(); this.createdObjects = new Map();
} }
// DAG TODO: This function is dubious in the presence of actual dags.
getParent(generator: Generator): Generator | FunctionValue | "GLOBAL" { getParent(generator: Generator): Generator | FunctionValue | "GLOBAL" {
let a = this.parents.get(generator); let parent = this.parents.get(generator);
invariant(a !== undefined && a.length >= 1); invariant(parent !== undefined);
return a[0]; return parent;
}
isParent(parent: Generator | FunctionValue | "GLOBAL", generator: Generator): boolean {
let a = this.parents.get(generator);
return a !== undefined && a.includes(parent);
} }
getCreator(value: ObjectValue): Generator | void { getCreator(value: ObjectValue): Generator | void {
@ -49,9 +42,8 @@ export class GeneratorDAG {
} }
_add(parent: Generator | FunctionValue | "GLOBAL", generator: Generator): void { _add(parent: Generator | FunctionValue | "GLOBAL", generator: Generator): void {
let a = this.parents.get(generator); invariant(!this.parents.has(generator));
if (a === undefined) this.parents.set(generator, (a = [])); this.parents.set(generator, parent);
if (!a.includes(parent)) a.push(parent);
let effects = generator.effectsToApply; let effects = generator.effectsToApply;
if (effects !== undefined) { if (effects !== undefined) {
invariant(parent instanceof FunctionValue); invariant(parent instanceof FunctionValue);

View File

@ -24,7 +24,7 @@ import { ResidualHeapSerializer } from "./ResidualHeapSerializer.js";
import { getOrDefault } from "./utils.js"; import { getOrDefault } from "./utils.js";
import type { ResidualOptimizedFunctions } from "./ResidualOptimizedFunctions"; import type { ResidualOptimizedFunctions } from "./ResidualOptimizedFunctions";
import type { Referentializer } from "./Referentializer.js"; import type { Referentializer } from "./Referentializer.js";
import { GeneratorDAG } from "./GeneratorDAG.js"; import { GeneratorTree } from "./GeneratorTree.js";
const LAZY_OBJECTS_SERIALIZER_BODY_TYPE = "LazyObjectInitializer"; const LAZY_OBJECTS_SERIALIZER_BODY_TYPE = "LazyObjectInitializer";
@ -49,7 +49,7 @@ export class LazyObjectsSerializer extends ResidualHeapSerializer {
options: SerializerOptions, options: SerializerOptions,
additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>, additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
referentializer: Referentializer, referentializer: Referentializer,
generatorDAG: GeneratorDAG, generatorTree: GeneratorTree,
residualOptimizedFunctions: ResidualOptimizedFunctions residualOptimizedFunctions: ResidualOptimizedFunctions
) { ) {
super( super(
@ -62,7 +62,7 @@ export class LazyObjectsSerializer extends ResidualHeapSerializer {
options, options,
additionalFunctionValuesAndEffects, additionalFunctionValuesAndEffects,
referentializer, referentializer,
generatorDAG, generatorTree,
residualOptimizedFunctions residualOptimizedFunctions
); );

View File

@ -81,7 +81,7 @@ import { ResidualReactElementSerializer } from "./ResidualReactElementSerializer
import type { Binding } from "../environment.js"; import type { Binding } from "../environment.js";
import { GlobalEnvironmentRecord, DeclarativeEnvironmentRecord } from "../environment.js"; import { GlobalEnvironmentRecord, DeclarativeEnvironmentRecord } from "../environment.js";
import type { Referentializer } from "./Referentializer.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 { type Replacement, getReplacement } from "./ResidualFunctionInstantiator.js";
import { describeValue } from "../utils.js"; import { describeValue } from "../utils.js";
import { getAsPropertyNameExpression } from "../utils/babelhelpers.js"; import { getAsPropertyNameExpression } from "../utils/babelhelpers.js";
@ -123,7 +123,7 @@ export class ResidualHeapSerializer {
options: SerializerOptions, options: SerializerOptions,
additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>, additionalFunctionValuesAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
referentializer: Referentializer, referentializer: Referentializer,
generatorDAG: GeneratorDAG, generatorTree: GeneratorTree,
residualOptimizedFunctions: ResidualOptimizedFunctions residualOptimizedFunctions: ResidualOptimizedFunctions
) { ) {
this.realm = realm; this.realm = realm;
@ -215,7 +215,7 @@ export class ResidualHeapSerializer {
this.rewrittenAdditionalFunctions = new Map(); this.rewrittenAdditionalFunctions = new Map();
this.declarativeEnvironmentRecordsBindings = residualHeapInfo.declarativeEnvironmentRecordsBindings; this.declarativeEnvironmentRecordsBindings = residualHeapInfo.declarativeEnvironmentRecordsBindings;
this.globalBindings = residualHeapInfo.globalBindings; this.globalBindings = residualHeapInfo.globalBindings;
this.generatorDAG = generatorDAG; this.generatorTree = generatorTree;
this.conditionalFeasibility = residualHeapInfo.conditionalFeasibility; this.conditionalFeasibility = residualHeapInfo.conditionalFeasibility;
this.additionalFunctionGenerators = new Map(); this.additionalFunctionGenerators = new Map();
this.declaredGlobalLets = 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 // TODO: revisit this and fix additional functions to be capable of delaying initializations
additionalFunctionValueNestedFunctions: Set<FunctionValue>; additionalFunctionValueNestedFunctions: Set<FunctionValue>;
generatorDAG: GeneratorDAG; generatorTree: GeneratorTree;
conditionalFeasibility: Map<AbstractValue, { t: boolean, f: boolean }>; conditionalFeasibility: Map<AbstractValue, { t: boolean, f: boolean }>;
additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>; additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>;
@ -888,7 +888,7 @@ export class ResidualHeapSerializer {
generators = generators.filter(generator => { generators = generators.filter(generator => {
let s = generator; let s = generator;
while (s instanceof Generator) { while (s instanceof Generator) {
s = this.generatorDAG.getParent(s); s = this.generatorTree.getParent(s);
} }
return s === "GLOBAL"; return s === "GLOBAL";
}); });
@ -904,7 +904,7 @@ export class ResidualHeapSerializer {
} }
const getGeneratorParent = g => { const getGeneratorParent = g => {
let s = this.generatorDAG.getParent(g); let s = this.generatorTree.getParent(g);
return s instanceof Generator ? s : undefined; return s instanceof Generator ? s : undefined;
}; };
// This value is referenced from more than one generator. // This value is referenced from more than one generator.
@ -2132,7 +2132,7 @@ export class ResidualHeapSerializer {
_annotateGeneratorStatements(generator: Generator, statements: Array<BabelNodeStatement>): void { _annotateGeneratorStatements(generator: Generator, statements: Array<BabelNodeStatement>): void {
let comment = `generator "${generator.getName()}"`; let comment = `generator "${generator.getName()}"`;
let parent = this.generatorDAG.getParent(generator); let parent = this.generatorTree.getParent(generator);
if (parent instanceof Generator) { if (parent instanceof Generator) {
comment = `${comment} with parent "${parent.getName()}"`; comment = `${comment} with parent "${parent.getName()}"`;
} else if (parent instanceof FunctionValue) { } else if (parent instanceof FunctionValue) {
@ -2574,7 +2574,7 @@ export class ResidualHeapSerializer {
for (let s of scopes) for (let s of scopes)
if (s instanceof Generator) { if (s instanceof Generator) {
let text = ""; 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}`); console.log(` ${text}`);
} else { } else {
invariant(s instanceof FunctionValue); invariant(s instanceof FunctionValue);

View File

@ -65,7 +65,7 @@ import {
import { createPathConditions, Environment, To } from "../singletons.js"; import { createPathConditions, Environment, To } from "../singletons.js";
import { isReactElement, isReactPropsObject, valueIsReactLibraryObject } from "../react/utils.js"; import { isReactElement, isReactPropsObject, valueIsReactLibraryObject } from "../react/utils.js";
import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js"; import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js";
import { GeneratorDAG } from "./GeneratorDAG.js"; import { GeneratorTree } from "./GeneratorTree.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js"; import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type BindingState = {| type BindingState = {|
@ -113,7 +113,7 @@ export class ResidualHeapVisitor {
this.globalEnvironmentRecord = environment; this.globalEnvironmentRecord = environment;
this.additionalGeneratorRoots = new Map(); this.additionalGeneratorRoots = new Map();
this.residualReactElementVisitor = new ResidualReactElementVisitor(this.realm, this); this.residualReactElementVisitor = new ResidualReactElementVisitor(this.realm, this);
this.generatorDAG = new GeneratorDAG(); this.generatorTree = new GeneratorTree();
} }
realm: Realm; realm: Realm;
@ -142,7 +142,7 @@ export class ResidualHeapVisitor {
classMethodInstances: Map<FunctionValue, ClassMethodInstance>; classMethodInstances: Map<FunctionValue, ClassMethodInstance>;
// Parents will always be a generator, optimized function value or "GLOBAL" // Parents will always be a generator, optimized function value or "GLOBAL"
additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>; additionalGeneratorRoots: Map<Generator, Set<ObjectValue>>;
generatorDAG: GeneratorDAG; generatorTree: GeneratorTree;
globalEnvironmentRecord: GlobalEnvironmentRecord; globalEnvironmentRecord: GlobalEnvironmentRecord;
residualReactElementVisitor: ResidualReactElementVisitor; residualReactElementVisitor: ResidualReactElementVisitor;
@ -151,13 +151,13 @@ export class ResidualHeapVisitor {
_getCommonScope(): FunctionValue | Generator { _getCommonScope(): FunctionValue | Generator {
let s = this.scope; let s = this.scope;
while (true) { 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) { else if (s instanceof FunctionValue) {
// Did we find an additional function? // Did we find an additional function?
if (this.additionalFunctionValuesAndEffects.has(s)) return s; if (this.additionalFunctionValuesAndEffects.has(s)) return s;
// Did the function itself get created by a generator we can chase? // 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 { } else {
invariant(s === "GLOBAL"); invariant(s === "GLOBAL");
let generator = this.globalGenerator; let generator = this.globalGenerator;
@ -179,7 +179,7 @@ export class ResidualHeapVisitor {
// created --- this causes the value later to be serialized in its // created --- this causes the value later to be serialized in its
// creation scope, ensuring that the value has the right creation / life time. // creation scope, ensuring that the value has the right creation / life time.
_registerAdditionalRoot(value: ObjectValue): void { _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 additionalFunction = this._getAdditionalFunctionOfScope() || "GLOBAL";
let targetAdditionalFunction; let targetAdditionalFunction;
@ -188,7 +188,7 @@ export class ResidualHeapVisitor {
} else { } else {
let s = creationGenerator; let s = creationGenerator;
while (s instanceof Generator) { while (s instanceof Generator) {
s = this.generatorDAG.getParent(s); s = this.generatorTree.getParent(s);
invariant(s !== undefined); invariant(s !== undefined);
} }
invariant(s === "GLOBAL" || s instanceof FunctionValue); invariant(s === "GLOBAL" || s instanceof FunctionValue);
@ -206,7 +206,7 @@ export class ResidualHeapVisitor {
additionalFVEffects.additionalRoots.add(value); additionalFVEffects.additionalRoots.add(value);
this._visitInUnrelatedScope(creationGenerator, value); this._visitInUnrelatedScope(creationGenerator, value);
usageScope = this.generatorDAG.getCreator(value) || this.globalGenerator; usageScope = this.generatorTree.getCreator(value) || this.globalGenerator;
} }
usageScope = this.scope; usageScope = this.scope;
@ -215,7 +215,7 @@ export class ResidualHeapVisitor {
// applying effects; if so, store additional information that the serializer // applying effects; if so, store additional information that the serializer
// can use to proactive serialize such objects from within the right generator // can use to proactive serialize such objects from within the right generator
let anyRelevantEffects = false; 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 (g === creationGenerator) {
if (anyRelevantEffects) { if (anyRelevantEffects) {
let s = this.additionalGeneratorRoots.get(g); let s = this.additionalGeneratorRoots.get(g);
@ -1098,7 +1098,7 @@ export class ResidualHeapVisitor {
if (this.preProcessValue(val)) this.visitValueProxy(val); if (this.preProcessValue(val)) this.visitValueProxy(val);
this.postProcessValue(val); this.postProcessValue(val);
} else if (val instanceof FunctionValue) { } 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 // 1. Visit function in its creation scope
this._enqueueWithUnrelatedScope(creationGenerator, () => { this._enqueueWithUnrelatedScope(creationGenerator, () => {
@ -1132,7 +1132,7 @@ export class ResidualHeapVisitor {
let callbacks = { let callbacks = {
visitEquivalentValue: this.visitEquivalentValue.bind(this), visitEquivalentValue: this.visitEquivalentValue.bind(this),
visitGenerator: (generator, parent) => { visitGenerator: (generator, parent) => {
invariant(this.generatorDAG.isParent(parent, generator)); invariant(this.generatorTree.getParent(generator) === parent);
this.visitGenerator(generator, additionalFunctionInfo); this.visitGenerator(generator, additionalFunctionInfo);
}, },
canOmit: (value: Value): boolean => { canOmit: (value: Value): boolean => {
@ -1282,7 +1282,7 @@ export class ResidualHeapVisitor {
this.additionalFunctionValueInfos.set(functionValue, additionalFunctionInfo); this.additionalFunctionValueInfos.set(functionValue, additionalFunctionInfo);
let effectsGenerator = additionalEffects.generator; let effectsGenerator = additionalEffects.generator;
this.generatorDAG.add(functionValue, effectsGenerator); this.generatorTree.add(functionValue, effectsGenerator);
this.visitGenerator(effectsGenerator, additionalFunctionInfo); this.visitGenerator(effectsGenerator, additionalFunctionInfo);
}; };
@ -1294,7 +1294,7 @@ export class ResidualHeapVisitor {
} }
visitRoots(): void { visitRoots(): void {
this.generatorDAG.add("GLOBAL", this.globalGenerator); this.generatorTree.add("GLOBAL", this.globalGenerator);
this.visitGenerator(this.globalGenerator); this.visitGenerator(this.globalGenerator);
for (let moduleValue of this.modules.initializedModules.values()) this.visitValue(moduleValue); for (let moduleValue of this.modules.initializedModules.values()) this.visitValue(moduleValue);
@ -1316,7 +1316,7 @@ export class ResidualHeapVisitor {
let expected = 0; let expected = 0;
for (let { scope, action } of this.delayedActions) { for (let { scope, action } of this.delayedActions) {
let generator; 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 if (scope === "GLOBAL") generator = this.globalGenerator;
else { else {
invariant(scope instanceof Generator); invariant(scope instanceof Generator);
@ -1370,10 +1370,10 @@ export class ResidualHeapVisitor {
info.nestedEffectsRunners.push(runGeneratorAction); info.nestedEffectsRunners.push(runGeneratorAction);
runGeneratorAction = undefined; runGeneratorAction = undefined;
} }
s = this.generatorDAG.getParent(s); s = this.generatorTree.getParent(s);
} else if (s instanceof FunctionValue) { } else if (s instanceof FunctionValue) {
invariant(this.additionalFunctionValuesAndEffects.has(s)); 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"); invariant(s instanceof Generator || s instanceof FunctionValue || s === "GLOBAL");
} }

View File

@ -12,7 +12,7 @@
import { FunctionValue } from "../values/index.js"; import { FunctionValue } from "../values/index.js";
import type { AdditionalFunctionEffects } from "./types"; import type { AdditionalFunctionEffects } from "./types";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { GeneratorDAG } from "./GeneratorDAG"; import { GeneratorTree } from "./GeneratorTree";
import type { Scope } from "./types.js"; import type { Scope } from "./types.js";
import { FunctionEnvironmentRecord } from "../environment"; import { FunctionEnvironmentRecord } from "../environment";
import type { Value } from "../values/index"; import type { Value } from "../values/index";
@ -20,16 +20,16 @@ import { Generator } from "../utils/generator";
export class ResidualOptimizedFunctions { export class ResidualOptimizedFunctions {
constructor( constructor(
generatorDAG: GeneratorDAG, generatorTree: GeneratorTree,
optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>, optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>,
residualValues: Map<Value, Set<Scope>> residualValues: Map<Value, Set<Scope>>
) { ) {
this._generatorDAG = generatorDAG; this._generatorTree = generatorTree;
this._optimizedFunctionsAndEffects = optimizedFunctionsAndEffects; this._optimizedFunctionsAndEffects = optimizedFunctionsAndEffects;
this._residualValues = residualValues; this._residualValues = residualValues;
} }
_generatorDAG: GeneratorDAG; _generatorTree: GeneratorTree;
_optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>; _optimizedFunctionsAndEffects: Map<FunctionValue, AdditionalFunctionEffects>;
_residualValues: Map<Value, Set<Scope>>; _residualValues: Map<Value, Set<Scope>>;
@ -80,7 +80,7 @@ export class ResidualOptimizedFunctions {
for (let scope of scopes) { for (let scope of scopes) {
let s = scope; let s = scope;
while (s instanceof Generator) { while (s instanceof Generator) {
s = this._generatorDAG.getParent(s); s = this._generatorTree.getParent(s);
} }
if (s === "GLOBAL") return undefined; if (s === "GLOBAL") return undefined;
invariant(s instanceof FunctionValue); invariant(s instanceof FunctionValue);

View File

@ -193,7 +193,7 @@ export class Serializer {
if (this.realm.react.verbose) { if (this.realm.react.verbose) {
this.logger.logInformation(`Visiting evaluated nodes...`); this.logger.logInformation(`Visiting evaluated nodes...`);
} }
let [residualHeapInfo, generatorDAG, inspector] = (() => { let [residualHeapInfo, generatorTree, inspector] = (() => {
let residualHeapVisitor = new ResidualHeapVisitor( let residualHeapVisitor = new ResidualHeapVisitor(
this.realm, this.realm,
this.logger, this.logger,
@ -201,12 +201,12 @@ export class Serializer {
additionalFunctionValuesAndEffects additionalFunctionValuesAndEffects
); );
statistics.deepTraversal.measure(() => residualHeapVisitor.visitRoots()); 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; if (this.logger.hasErrors()) return undefined;
let residualOptimizedFunctions = new ResidualOptimizedFunctions( let residualOptimizedFunctions = new ResidualOptimizedFunctions(
generatorDAG, generatorTree,
additionalFunctionValuesAndEffects, additionalFunctionValuesAndEffects,
residualHeapInfo.values residualHeapInfo.values
); );
@ -270,7 +270,7 @@ export class Serializer {
this.options, this.options,
additionalFunctionValuesAndEffects, additionalFunctionValuesAndEffects,
referentializer, referentializer,
generatorDAG, generatorTree,
residualOptimizedFunctions residualOptimizedFunctions
).serialize(); ).serialize();
}); });
@ -293,7 +293,7 @@ export class Serializer {
this.options, this.options,
additionalFunctionValuesAndEffects, additionalFunctionValuesAndEffects,
referentializer, referentializer,
generatorDAG, generatorTree,
residualOptimizedFunctions residualOptimizedFunctions
).serialize() ).serialize()
); );