mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Purge some local bindings that are not captured in closures. (#2152)
Summary: Release note: none This reverses PR #2136, which Dan has noted to lead to a 3x slowdown for an internal bundle. In order to fix the problem that Nikolai's PR addressed, the original code has been modified to not purge any local bindings that could possibly have been captured in a closure. I expect that most effects will not be in a context that contains a closure, so this should help. It would be nice, however, to get some feedback on whether this is actually the case. Closes https://github.com/facebook/prepack/pull/2152 Differential Revision: D8617877 Pulled By: hermanventer fbshipit-source-id: 8e907ba1dded0428388fd42132658555e3b8e82e
This commit is contained in:
parent
5a5a100483
commit
f9e102c0d2
@ -155,6 +155,7 @@ export type Binding = {
|
||||
environment: EnvironmentRecord,
|
||||
name: string,
|
||||
isGlobal: boolean,
|
||||
mightHaveBeenCaptured: boolean,
|
||||
// bindings that are assigned to inside loops with abstract termination conditions need temporal locations
|
||||
phiNode?: AbstractValue,
|
||||
hasLeaked: boolean,
|
||||
@ -203,6 +204,7 @@ export class DeclarativeEnvironmentRecord extends EnvironmentRecord {
|
||||
environment: envRec,
|
||||
name: N,
|
||||
isGlobal: isGlobal,
|
||||
mightHaveBeenCaptured: false,
|
||||
hasLeaked: false,
|
||||
});
|
||||
|
||||
@ -229,6 +231,7 @@ export class DeclarativeEnvironmentRecord extends EnvironmentRecord {
|
||||
environment: envRec,
|
||||
name: N,
|
||||
isGlobal: isGlobal,
|
||||
mightHaveBeenCaptured: false,
|
||||
hasLeaked: false,
|
||||
};
|
||||
this.bindings[N] = skipRecord ? binding : realm.recordModifiedBinding(binding);
|
||||
|
@ -746,6 +746,9 @@ export class FunctionImplementation {
|
||||
Body: BabelNodeBlockStatement,
|
||||
Scope: LexicalEnvironment
|
||||
): ECMAScriptSourceFunctionValue {
|
||||
// Tell the realm to mark any local bindings that are visible to this function as being potentially captured by it.
|
||||
realm.markVisibleLocalBindingsAsPotentiallyCaptured();
|
||||
|
||||
// Note that F is a new object, and we can thus write to internal slots
|
||||
invariant(realm.isNewObject(F));
|
||||
|
||||
|
89
src/realm.js
89
src/realm.js
@ -499,10 +499,48 @@ export class Realm {
|
||||
return context;
|
||||
}
|
||||
|
||||
clearBlockBindings(modifiedBindings: void | Bindings, environmentRecord: DeclarativeEnvironmentRecord) {
|
||||
if (modifiedBindings === undefined) return;
|
||||
for (let b of modifiedBindings.keys()) {
|
||||
if (b.mightHaveBeenCaptured) continue;
|
||||
if (environmentRecord.bindings[b.name] && environmentRecord.bindings[b.name] === b) modifiedBindings.delete(b);
|
||||
}
|
||||
}
|
||||
|
||||
clearBlockBindingsFromCompletion(completion: Completion, environmentRecord: DeclarativeEnvironmentRecord) {
|
||||
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) {
|
||||
invariant(this.activeLexicalEnvironments.has(lexicalEnvironment));
|
||||
let modifiedBindings = this.modifiedBindings;
|
||||
if (modifiedBindings) {
|
||||
// Don't undo things to global scope because it's needed past its destruction point (for serialization)
|
||||
let environmentRecord = lexicalEnvironment.environmentRecord;
|
||||
if (environmentRecord instanceof DeclarativeEnvironmentRecord) {
|
||||
this.clearBlockBindings(modifiedBindings, environmentRecord);
|
||||
if (this.savedCompletion !== undefined)
|
||||
this.clearBlockBindingsFromCompletion(this.savedCompletion, environmentRecord);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures if we call onDestroyScope too early, there will be a failure.
|
||||
this.activeLexicalEnvironments.delete(lexicalEnvironment);
|
||||
@ -516,7 +554,58 @@ export class Realm {
|
||||
this.contextStack.push(context);
|
||||
}
|
||||
|
||||
markVisibleLocalBindingsAsPotentiallyCaptured() {
|
||||
let context = this.getRunningContext();
|
||||
if (context.function === undefined) return;
|
||||
let lexEnv = context.lexicalEnvironment;
|
||||
while (lexEnv != null) {
|
||||
let envRec = lexEnv.environmentRecord;
|
||||
if (envRec instanceof DeclarativeEnvironmentRecord) {
|
||||
let bindings = envRec.bindings;
|
||||
for (let name in bindings) {
|
||||
let binding = bindings[name];
|
||||
binding.mightHaveBeenCaptured = true;
|
||||
}
|
||||
}
|
||||
lexEnv = lexEnv.parent;
|
||||
}
|
||||
}
|
||||
|
||||
clearFunctionBindings(modifiedBindings: void | Bindings, funcVal: FunctionValue) {
|
||||
if (modifiedBindings === undefined) return;
|
||||
for (let b of modifiedBindings.keys()) {
|
||||
if (b.mightHaveBeenCaptured) continue;
|
||||
if (b.environment instanceof FunctionEnvironmentRecord && b.environment.$FunctionObject === funcVal)
|
||||
modifiedBindings.delete(b);
|
||||
}
|
||||
}
|
||||
|
||||
clearFunctionBindingsFromCompletion(completion: Completion, funcVal: FunctionValue) {
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user