Ensure temporal assignments are not lost when the value does not change. (#2197)

Summary:
Release note: Fix temporal assignments to intrinsics that happen inside non deterministic loops

Tweak the property set logic to not short circuit when the target object is intrinsic and the RH value is the same as the current (tracked) property value.

Also fix the code generator for loop bodies to not get upset when the lh side is temporal, but rh side is not widened (because it is a constant).
Closes https://github.com/facebook/prepack/pull/2197

Differential Revision: D8721001

Pulled By: hermanventer

fbshipit-source-id: d38565a40fa7ab8b4e30596f0e1dcfc655a4fb5e
This commit is contained in:
Herman Venter 2018-07-02 17:46:35 -07:00 committed by Facebook Github Bot
parent 466d19ddfd
commit b6ce555f37
3 changed files with 32 additions and 17 deletions

View File

@ -731,7 +731,8 @@ export class PropertiesImplementation {
}
if (!identical) break;
}
if (identical) {
// Only return here if the assigment is not temporal.
if (identical && (O === realm.$GlobalObject || (O !== undefined && !O.isIntrinsic()))) {
return true;
}

View File

@ -1156,22 +1156,28 @@ export class Realm {
let mightBeUndefined = value.mightBeUndefined();
let keyKey = key.key;
if (typeof keyKey === "string") {
gen.emitStatement([key.object, tval || value, this.intrinsics.empty], ([o, v, e]) => {
invariant(path !== undefined);
invariant(typeof keyKey === "string");
let lh = path.buildNode([o, t.identifier(keyKey)]);
let r = t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
if (mightHaveBeenDeleted) {
// If v === __empty || (v === undefined && !(key.key in o)) then delete it
let emptyTest = t.binaryExpression("===", v, e);
let undefinedTest = t.binaryExpression("===", v, voidExpression);
let inTest = t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(keyKey), o));
let guard = t.logicalExpression("||", emptyTest, t.logicalExpression("&&", undefinedTest, inTest));
let deleteIt = t.expressionStatement(t.unaryExpression("delete", (lh: any)));
return t.ifStatement(mightBeUndefined ? emptyTest : guard, deleteIt, r);
}
return r;
});
if (path !== undefined) {
gen.emitStatement([key.object, tval || value, this.intrinsics.empty], ([o, v, e]) => {
invariant(path !== undefined);
invariant(typeof keyKey === "string");
let lh = path.buildNode([o, t.identifier(keyKey)]);
let r = t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
if (mightHaveBeenDeleted) {
// If v === __empty || (v === undefined && !(key.key in o)) then delete it
let emptyTest = t.binaryExpression("===", v, e);
let undefinedTest = t.binaryExpression("===", v, voidExpression);
let inTest = t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(keyKey), o));
let guard = t.logicalExpression("||", emptyTest, t.logicalExpression("&&", undefinedTest, inTest));
let deleteIt = t.expressionStatement(t.unaryExpression("delete", (lh: any)));
return t.ifStatement(mightBeUndefined ? emptyTest : guard, deleteIt, r);
}
return r;
});
} else {
// RH value was not widened, so it must have been a constant. We don't need to assign that inside the loop.
// Note, however, that if the LH side is a property of an intrinsic object, then an assignment will
// have been emitted to the generator.
}
} else {
// TODO: What if keyKey is undefined?
invariant(keyKey instanceof Value);

View File

@ -0,0 +1,8 @@
let template = global.__abstract ? __abstract({x: __abstract("number")}, "({ set x(v) { console.log(v); } })") : { set x(v) { console.log(v); } };
let n = global.__abstract ? __abstract('number', '(2)') : 2;
let i = 0;
do {
template.x = 3;
} while (++i < n);
inspect = function() { return template.x; }