Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Fix havoced binding not in optimized function (#2420)
Browse files Browse the repository at this point in the history
Summary:
Fixes #2419 and #2386. I ran into this issue while testing an internal React Native bundle. This invariant assumes that all property bindings emitted in a pure scope are also in an optimized function. This is not true for `__evaluatePureFunction` which the React Compiler wraps around initialization code so that globals outside of the closure are not havoced.
Pull Request resolved: #2420

Differential Revision: D9363247

Pulled By: calebmer

fbshipit-source-id: 4f1634165b6fe15f95b5b7332432fccacc596821
  • Loading branch information
calebmer authored and facebook-github-bot committed Aug 16, 2018
1 parent c18bd7e commit 3eb1e8e
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 17 deletions.
34 changes: 17 additions & 17 deletions src/serializer/ResidualHeapVisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import { GeneratorDAG } from "./GeneratorDAG.js";

type BindingState = {|
capturedBindings: Set<ResidualFunctionBinding>,
capturingFunctions: Set<FunctionValue>,
capturingScopes: Set<Scope>,
|};

/* This class visits all values that are reachable in the residual heap.
Expand Down Expand Up @@ -648,10 +648,10 @@ export class ResidualHeapVisitor {
// function c() { return x.length + y.length; }
// Here we need to make sure that a and b both initialize x and y because x and y will be in the same
// captured scope because c captures both x and y.
visitBinding(val: FunctionValue, residualFunctionBinding: ResidualFunctionBinding): void {
visitBinding(scope: Scope, residualFunctionBinding: ResidualFunctionBinding): void {
let environment = residualFunctionBinding.declarativeEnvironmentRecord;
if (environment === null) return;
invariant(this.scope === val);
invariant(this.scope === scope);

let refScope = this._getAdditionalFunctionOfScope() || "GLOBAL";
residualFunctionBinding.potentialReferentializationScopes.add(refScope);
Expand All @@ -661,20 +661,20 @@ export class ResidualHeapVisitor {
invariant(envRec !== null);
let bindingState = getOrDefault(funcToScopes, envRec, () => ({
capturedBindings: new Set(),
capturingFunctions: new Set(),
capturingScopes: new Set(),
}));
// If the binding is new for this bindingState, have all functions capturing bindings from that scope visit it
if (!bindingState.capturedBindings.has(residualFunctionBinding)) {
for (let functionValue of bindingState.capturingFunctions) {
this._enqueueWithUnrelatedScope(functionValue, () => this._visitBindingHelper(residualFunctionBinding));
for (let capturingScope of bindingState.capturingScopes) {
this._enqueueWithUnrelatedScope(capturingScope, () => this._visitBindingHelper(residualFunctionBinding));
}
bindingState.capturedBindings.add(residualFunctionBinding);
}
// If the function is new for this bindingState, visit all existent bindings in this scope
if (!bindingState.capturingFunctions.has(val)) {
invariant(this.scope === val);
if (!bindingState.capturingScopes.has(scope)) {
invariant(this.scope === scope);
for (let residualBinding of bindingState.capturedBindings) this._visitBindingHelper(residualBinding);
bindingState.capturingFunctions.add(val);
bindingState.capturingScopes.add(scope);
}
}

Expand Down Expand Up @@ -1210,15 +1210,15 @@ export class ResidualHeapVisitor {
residualBinding.hasLeaked = true;
// This may not have been referentialized if the binding is a local of an optimized function.
// in that case, we need to figure out which optimized function it is, and referentialize it in that scope.
let optimizedFunctionScope = this._getAdditionalFunctionOfScope();
let commonScope = this._getCommonScope();
if (residualBinding.potentialReferentializationScopes.size === 0) {
invariant(optimizedFunctionScope !== undefined);
this._enqueueWithUnrelatedScope(optimizedFunctionScope, () => {
invariant(additionalFunctionInfo !== undefined);
let funcInstance = additionalFunctionInfo.instance;
invariant(funcInstance !== undefined);
funcInstance.residualFunctionBindings.set(residualBinding.name, residualBinding);
this.visitBinding(optimizedFunctionScope, residualBinding);
this._enqueueWithUnrelatedScope(commonScope, () => {
if (additionalFunctionInfo !== undefined) {
let funcInstance = additionalFunctionInfo.instance;
invariant(funcInstance !== undefined);
funcInstance.residualFunctionBindings.set(residualBinding.name, residualBinding);
}
this.visitBinding(commonScope, residualBinding);
});
}
return this.visitEquivalentValue(value);
Expand Down
15 changes: 15 additions & 0 deletions test/serializer/optimized-functions/HavocBindings11.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
if (!global.__evaluatePureFunction) global.__evaluatePureFunction = f => f();

const havoc = global.__abstract ? global.__abstract("function", "(() => {})") : () => {};

const result = global.__evaluatePureFunction(() => {
let x = 23;
function incrementX() {
x = x + 42;
}
if (global.__optimize) __optimize(incrementX);
havoc(incrementX);
return x;
});

global.inspect = () => result;
14 changes: 14 additions & 0 deletions test/serializer/pure-functions/HavocBinding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
if (!global.__evaluatePureFunction) global.__evaluatePureFunction = f => f();

const x = global.__evaluatePureFunction(() => {
const x = { p: 42 };

const havoc = global.__abstract ? __abstract("function", "(() => {})") : () => {};
havoc(() => x);

return x;
});

global.inspect = () => {
return JSON.stringify(x);
};

0 comments on commit 3eb1e8e

Please sign in to comment.