Get abstract computed properties (#464)

This commit is contained in:
Herman Venter 2017-04-26 21:46:03 -07:00 committed by GitHub
parent 650c8a02a1
commit cfbf667c21
5 changed files with 122 additions and 14 deletions

View File

@ -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,

View File

@ -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

View File

@ -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;
}

View File

@ -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; }

View 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; }