mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-11-09 21:20:06 +03:00
Deal with loops where early iterations do not assign to a property
Summary: Release note: none This is the first among a number of step to deal with the gnarly edge cases that arise because a property can be absent (empty) rather than just undefined. Closes https://github.com/facebook/prepack/pull/1278 Differential Revision: D6596190 Pulled By: hermanventer fbshipit-source-id: f18f181e617a50e1bb39015378330dee14d71815
This commit is contained in:
parent
104fd40103
commit
92979e2c91
@ -338,10 +338,6 @@ export class PropertiesImplementation {
|
||||
|
||||
// iii. Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
|
||||
let valueDesc = { value: V };
|
||||
if (weakDeletion) {
|
||||
valueDesc = existingDescriptor;
|
||||
valueDesc.value = V;
|
||||
}
|
||||
|
||||
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
|
||||
if (weakDeletion || existingDescValue.mightHaveBeenDeleted()) {
|
||||
@ -350,6 +346,8 @@ export class PropertiesImplementation {
|
||||
// and that redefining the property with valueDesc will not change the
|
||||
// attributes of the property, so we delete it to make things nice for $DefineOwnProperty.
|
||||
Receiver.$Delete(P);
|
||||
valueDesc = existingDescriptor;
|
||||
valueDesc.value = V;
|
||||
}
|
||||
return Receiver.$DefineOwnProperty(P, valueDesc);
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@ import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { Reference } from "../environment.js";
|
||||
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
|
||||
import { Generator } from "../utils/generator.js";
|
||||
import { AbstractValue, ObjectValue, Value } from "../values/index.js";
|
||||
import { AbstractValue, EmptyValue, ObjectValue, Value } from "../values/index.js";
|
||||
|
||||
import invariant from "../invariant.js";
|
||||
import * as t from "babel-types";
|
||||
@ -128,7 +128,7 @@ export class WidenImplementation {
|
||||
// Create a temporal location for binding
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
phiNode = generator.derive(result.types, result.values, [v1 || realm.intrinsics.undefined], ([n]) => n, {
|
||||
phiNode = generator.derive(result.types, result.values, [b.value || realm.intrinsics.undefined], ([n]) => n, {
|
||||
skipInvariant: true,
|
||||
});
|
||||
b.phiNode = phiNode;
|
||||
@ -167,11 +167,7 @@ export class WidenImplementation {
|
||||
) {
|
||||
return v1; // no need to widen a loop invariant value
|
||||
} else {
|
||||
return AbstractValue.createFromWidening(
|
||||
realm,
|
||||
v1 || realm.intrinsics.undefined,
|
||||
v2 || realm.intrinsics.undefined
|
||||
);
|
||||
return AbstractValue.createFromWidening(realm, v1 || realm.intrinsics.empty, v2 || realm.intrinsics.undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +191,11 @@ export class WidenImplementation {
|
||||
} else {
|
||||
// no write to property in nth iteration, use the value from the (n-1)th iteration
|
||||
d1 = b.descriptor;
|
||||
if (d1 === undefined) {
|
||||
d1 = cloneDescriptor(d2);
|
||||
invariant(d1 !== undefined);
|
||||
d1.value = realm.intrinsics.empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d2 === undefined) {
|
||||
@ -227,6 +228,17 @@ export class WidenImplementation {
|
||||
pathNode = AbstractValue.createFromWidenedProperty(realm, rval, [b.object], ([o]) =>
|
||||
t.memberExpression(o, t.identifier(key))
|
||||
);
|
||||
// The value of the property at the start of the loop needs to be written to the property
|
||||
// before the loop commences, otherwise the memberExpression will result in an undefined value.
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
let initVal = (b.descriptor && b.descriptor.value) || realm.intrinsics.empty;
|
||||
if (!(initVal instanceof Value)) throw new FatalError("todo: handle internal properties");
|
||||
if (!(initVal instanceof EmptyValue)) {
|
||||
generator.emitVoidExpression(rval.types, rval.values, [b.object, initVal], ([o, v]) =>
|
||||
t.assignmentExpression("=", t.memberExpression(o, t.identifier(key)), v)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new FatalError("todo: handle the case where key is an abstract value");
|
||||
}
|
||||
|
21
src/realm.js
21
src/realm.js
@ -283,7 +283,12 @@ export class Realm {
|
||||
invariant(globrec instanceof GlobalEnvironmentRecord);
|
||||
let dclrec = globrec.$DeclarativeRecord;
|
||||
|
||||
return dclrec.HasBinding(key) ? dclrec.GetBindingValue(key, false) : undefined;
|
||||
try {
|
||||
return dclrec.HasBinding(key) ? dclrec.GetBindingValue(key, false) : undefined;
|
||||
} catch (e) {
|
||||
if (e instanceof FatalError) return undefined;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -564,6 +569,7 @@ export class Realm {
|
||||
let tval = gen.derive(value.types, value.values, [value], ([n]) => n, {
|
||||
skipInvariant: true,
|
||||
});
|
||||
tval.mightBeEmpty = value.mightBeEmpty;
|
||||
tvalFor.set(key, tval);
|
||||
}
|
||||
});
|
||||
@ -571,10 +577,21 @@ export class Realm {
|
||||
let path = key.pathNode;
|
||||
let tval = tvalFor.get(key);
|
||||
invariant(tval !== undefined);
|
||||
let mightBeEmpty = tval.mightBeEmpty;
|
||||
gen.emitStatement([key.object, tval], ([o, v]) => {
|
||||
invariant(path !== undefined);
|
||||
let lh = path.buildNode([o]);
|
||||
return t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
|
||||
let r = t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
|
||||
if (mightBeEmpty) {
|
||||
// If o does not have property key.key and v === undefined, omit the assignment (at runtime)
|
||||
// if (v !== undefined || key.key in o) r
|
||||
invariant(typeof key.key === "string"); // for now
|
||||
let inTest = t.binaryExpression("in", t.stringLiteral(key.key), o);
|
||||
let vDefined = t.binaryExpression("!==", v, t.unaryExpression("void", t.numericLiteral(0)));
|
||||
let guard = t.logicalExpression("||", vDefined, inTest);
|
||||
return t.ifStatement(guard, r);
|
||||
}
|
||||
return r;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1073,7 +1073,11 @@ export class ResidualHeapSerializer {
|
||||
if (prop.joinCondition !== undefined) return false;
|
||||
if ((obj instanceof FunctionValue && key === "prototype") || (obj.getKind() === "RegExp" && key === "lastIndex"))
|
||||
return !!prop.writable && !prop.configurable && !prop.enumerable && !prop.set && !prop.get;
|
||||
else return !!prop.writable && !!prop.configurable && !!prop.enumerable && !prop.set && !prop.get;
|
||||
else if (!!prop.writable && !!prop.configurable && !!prop.enumerable && !prop.set && !prop.get) {
|
||||
return !(prop.value instanceof AbstractValue && prop.value.kind === "widened property");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_findLastObjectPrototype(obj: ObjectValue): ObjectValue {
|
||||
@ -1097,6 +1101,7 @@ export class ResidualHeapSerializer {
|
||||
const dummyProperties = new Set();
|
||||
let props = [];
|
||||
for (let [key, propertyBinding] of val.properties) {
|
||||
if (propertyBinding.pathNode !== undefined) continue; // written to inside loop
|
||||
let descriptor = propertyBinding.descriptor;
|
||||
if (descriptor === undefined || descriptor.value === undefined) continue; // deleted
|
||||
if (this._canEmbedProperty(val, key, descriptor)) {
|
||||
|
@ -616,6 +616,7 @@ export default class AbstractValue extends Value {
|
||||
let Constructor = Value.isTypeCompatibleWith(types.getType(), ObjectValue) ? AbstractObjectValue : AbstractValue;
|
||||
let result = new Constructor(realm, types, values, hash, args, buildFunction);
|
||||
result.kind = "widened property";
|
||||
result.mightBeEmpty = resultTemplate.mightBeEmpty;
|
||||
result.expressionLocation = resultTemplate.expressionLocation;
|
||||
return result;
|
||||
}
|
||||
@ -628,6 +629,7 @@ export default class AbstractValue extends Value {
|
||||
let Constructor = Value.isTypeCompatibleWith(types.getType(), ObjectValue) ? AbstractObjectValue : AbstractValue;
|
||||
let result = new Constructor(realm, types, values, hash, []);
|
||||
result.kind = "widened";
|
||||
result.mightBeEmpty = value1.mightHaveBeenDeleted() || value2.mightHaveBeenDeleted();
|
||||
result.expressionLocation = value1.expressionLocation;
|
||||
return result;
|
||||
}
|
||||
|
9
test/serializer/abstract/DoWhile6a.js
Normal file
9
test/serializer/abstract/DoWhile6a.js
Normal file
@ -0,0 +1,9 @@
|
||||
let n = global.__abstract ? __abstract("number", "1") : 1;
|
||||
let i = 0;
|
||||
let ob = { j: 0 };
|
||||
do {
|
||||
i++;
|
||||
if (i > 1) ob.j = i;
|
||||
} while (i < n);
|
||||
|
||||
inspect = function() { return i + " " + ob.j; }
|
10
test/serializer/abstract/DoWhile6b.js
Normal file
10
test/serializer/abstract/DoWhile6b.js
Normal file
@ -0,0 +1,10 @@
|
||||
let n = global.__abstract ? __abstract("number", "1") : 1;
|
||||
let i = 0;
|
||||
let ob = { };
|
||||
do {
|
||||
i++;
|
||||
if (i > 1) ob.j = i;
|
||||
} while (i < n);
|
||||
let k = ob.j;
|
||||
|
||||
inspect = function() { return k + " " + JSON.stringify(Reflect.ownKeys(ob)); }
|
Loading…
Reference in New Issue
Block a user