mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Get abstract computed properties (#464)
This commit is contained in:
parent
650c8a02a1
commit
cfbf667c21
@ -103,7 +103,7 @@ export function GetValue(realm: Realm, V: Reference | Value): Value {
|
||||
invariant(base instanceof ObjectValue || base instanceof AbstractObjectValue);
|
||||
|
||||
// b. Return ? base.[[Get]](GetReferencedName(V), GetThisValue(V)).
|
||||
return base.$Get(GetReferencedName(realm, V), GetThisValue(realm, V));
|
||||
return base.$GetPartial(GetReferencedNamePartial(realm, V), GetThisValue(realm, V));
|
||||
}
|
||||
|
||||
// 6. Else base must be an Environment Record,
|
||||
|
@ -65,6 +65,20 @@ export default class AbstractObjectValue extends AbstractValue {
|
||||
return result;
|
||||
}
|
||||
|
||||
isSimple(): boolean {
|
||||
let result;
|
||||
for (let element of this.values.getElements()) {
|
||||
invariant(element instanceof ObjectValue);
|
||||
if (result === undefined)
|
||||
result = element.isSimple();
|
||||
else if (result !== element.isSimple())
|
||||
throw AbstractValue.createIntrospectionErrorThrowCompletion(this);
|
||||
}
|
||||
if (result === undefined)
|
||||
throw AbstractValue.createIntrospectionErrorThrowCompletion(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
makeNotPartial(): void {
|
||||
for (let element of this.values.getElements()) {
|
||||
invariant(element instanceof ObjectValue);
|
||||
@ -252,6 +266,35 @@ export default class AbstractObjectValue extends AbstractValue {
|
||||
}
|
||||
}
|
||||
|
||||
$GetPartial(P: AbstractValue | PropertyKeyValue, Receiver: Value): Value {
|
||||
if (!(P instanceof AbstractValue)) return this.$Get(P, Receiver);
|
||||
invariant(this === Receiver, "TODO");
|
||||
|
||||
let elements = this.values.getElements();
|
||||
if (elements.size === 1) {
|
||||
for (let cv of elements) {
|
||||
return cv.$GetPartial(P, cv);
|
||||
}
|
||||
invariant(false);
|
||||
} else {
|
||||
let result;
|
||||
for (let cv of elements) {
|
||||
let cvVal = cv.$GetPartial(P, cv);
|
||||
if (result === undefined)
|
||||
result = cvVal;
|
||||
else {
|
||||
let cond = this.$Realm.createAbstract(new TypesDomain(BooleanValue),
|
||||
ValuesDomain.topVal,
|
||||
[this, cv],
|
||||
([x, y]) => t.binaryExpression("===", x, y));
|
||||
result = joinValuesAsConditional(this.$Realm, cond, cvVal, result);
|
||||
}
|
||||
}
|
||||
invariant(result !== undefined);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// ECMA262 9.1.9
|
||||
$Set(P: PropertyKeyValue, V: Value, Receiver: Value): boolean {
|
||||
if (P instanceof StringValue) P = P.value;
|
||||
@ -287,7 +330,27 @@ export default class AbstractObjectValue extends AbstractValue {
|
||||
|
||||
$SetPartial(P: AbstractValue | PropertyKeyValue, V: Value, Receiver: Value): boolean {
|
||||
if (!(P instanceof AbstractValue)) return this.$Set(P, V, Receiver);
|
||||
throw this.$Realm.createIntrospectionErrorThrowCompletion("TODO: $SetPartial");
|
||||
invariant(this === Receiver, "TODO");
|
||||
|
||||
let elements = this.values.getElements();
|
||||
if (elements.size === 1) {
|
||||
for (let cv of elements) {
|
||||
invariant(cv instanceof ObjectValue);
|
||||
return cv.$SetPartial(P, V, cv);
|
||||
}
|
||||
invariant(false);
|
||||
} else {
|
||||
for (let cv of elements) {
|
||||
invariant(cv instanceof ObjectValue);
|
||||
let oldVal = this.$GetPartial(P, Receiver);
|
||||
let cond = this.$Realm.createAbstract(new TypesDomain(BooleanValue), ValuesDomain.topVal,
|
||||
[this, cv],
|
||||
([x, y]) => t.binaryExpression("===", x, y));
|
||||
let v = joinValuesAsConditional(this.$Realm, cond, V, oldVal);
|
||||
cv.$SetPartial(P, v, cv);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ECMA262 9.1.10
|
||||
|
@ -372,25 +372,56 @@ export default class ObjectValue extends ConcreteValue {
|
||||
return OrdinaryGet(this.$Realm, this, P, Receiver);
|
||||
}
|
||||
|
||||
specializeJoin(absVal: AbstractValue, strVal: StringValue): AbstractValue {
|
||||
$GetPartial(P: AbstractValue | PropertyKeyValue, Receiver: Value): Value {
|
||||
if (!(P instanceof AbstractValue)) return this.$Get(P, Receiver);
|
||||
// We assume that simple objects have no getter/setter properties.
|
||||
if (this !== Receiver || !this.isSimple() || P.mightNotBeString())
|
||||
throw this.$Realm.createIntrospectionErrorThrowCompletion("TODO");
|
||||
// If all else fails, use this expression
|
||||
let result = this.$Realm.createAbstract(TypesDomain.topVal, ValuesDomain.topVal,
|
||||
[this, P],
|
||||
([o, x]) => t.memberExpression(o, x, true));
|
||||
// Get a specialization of the join of all values written to the object
|
||||
// with abstract property names.
|
||||
let prop = this.unknownProperty;
|
||||
if (prop !== undefined) {
|
||||
let desc = prop.descriptor; invariant(desc !== undefined);
|
||||
let val = desc.value; invariant(val instanceof AbstractValue);
|
||||
result = this.specializeJoin(val, P);
|
||||
}
|
||||
// Join in all of the other values that were written to the object with
|
||||
// concrete property names.
|
||||
for (let [key, propertyBinding] of this.properties) {
|
||||
let desc = propertyBinding.descriptor;
|
||||
if (desc === undefined || desc.value === undefined) continue; // deleted
|
||||
let val = desc.value;
|
||||
let cond = this.$Realm.createAbstract(new TypesDomain(BooleanValue), ValuesDomain.topVal,
|
||||
[P],
|
||||
([x]) => t.binaryExpression("===", x, t.stringLiteral(key)));
|
||||
result = joinValuesAsConditional(this.$Realm, cond, val, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
specializeJoin(absVal: AbstractValue, propName: Value): AbstractValue {
|
||||
invariant(absVal.args.length === 3);
|
||||
let generic_cond = absVal.args[0];
|
||||
invariant(generic_cond instanceof AbstractValue);
|
||||
let cond = this.specializeCond(generic_cond, strVal);
|
||||
let cond = this.specializeCond(generic_cond, propName);
|
||||
let arg1 = absVal.args[1];
|
||||
if (arg1 instanceof AbstractValue && arg1.args.length === 3)
|
||||
arg1 = this.specializeJoin(arg1, strVal);
|
||||
arg1 = this.specializeJoin(arg1, propName);
|
||||
let arg2 = absVal.args[2];
|
||||
if (arg2 instanceof AbstractValue && arg2.args.length === 3)
|
||||
arg2 = this.specializeJoin(arg2, strVal);
|
||||
arg2 = this.specializeJoin(arg2, propName);
|
||||
return this.$Realm.createAbstract(absVal.types, absVal.values,
|
||||
[cond, arg1, arg2], absVal._buildNode);
|
||||
}
|
||||
|
||||
specializeCond(absVal: AbstractValue, strVal: StringValue): AbstractValue {
|
||||
specializeCond(absVal: AbstractValue, propName: Value): AbstractValue {
|
||||
if (absVal.kind === "template for property name condition")
|
||||
return this.$Realm.createAbstract(absVal.types, absVal.values,
|
||||
[absVal.args[0], strVal], absVal._buildNode);
|
||||
[absVal.args[0], propName], absVal._buildNode);
|
||||
return absVal;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,18 @@
|
||||
// throws introspection error
|
||||
|
||||
var n = global.__abstract ? __abstract("string", '("x")') : "x";
|
||||
var m = global.__abstract ? __abstract("string", '("x")') : "x";
|
||||
var m = global.__abstract ? __abstract("string", '("z")') : "z";
|
||||
|
||||
a = {x: 123, y: 444};
|
||||
if (global.__makeSimple) global.__makeSimple(a);
|
||||
a[n] = 456;
|
||||
|
||||
z = a[n];
|
||||
|
||||
inspect = function() { return z; }
|
||||
a[n] = 456;
|
||||
z1 = a[n];
|
||||
|
||||
b = {};
|
||||
if (global.__makeSimple) global.__makeSimple(b);
|
||||
z2 = b[m];
|
||||
|
||||
b[m] = 789;
|
||||
z3 = b[m];
|
||||
|
||||
inspect = function() { return "" + a.x + a.y + a[n] + z + z1 + z2 + z3; }
|
||||
|
7
test/serializer/abstract/GetValue8.js
Normal file
7
test/serializer/abstract/GetValue8.js
Normal file
@ -0,0 +1,7 @@
|
||||
let ob = global.__abstract ? __abstract({}, "({a: 1})") : {a: 1};
|
||||
if (global.__makeSimple) __makeSimple(ob);
|
||||
var n = global.__abstract ? __abstract("string", '("a")') : "a";
|
||||
|
||||
z = ob[n];
|
||||
|
||||
inspect = function() { return z; }
|
Loading…
Reference in New Issue
Block a user