blob: 623d87fbccbf33b2fbb7d0cd33e615481ff15d39 [file] [log] [blame]
function assert(a, message) {
if (!a)
throw new Error(message);
}
function shallowEquals(a, b) {
if (Array.isArray(a) !== Array.isArray(b))
return false;
if (Array.isArray(a)) {
let aLen = a.length | 0;
let bLen = a.length | 0;
if (aLen === bLen) {
for (let i = 0; i < aLen; ++i) {
if (a[i] !== b[i])
return false;
}
return true;
}
}
return a === b;
}
assert.same = function(a, b, message) {
let msg = message ? `. ${message}` : "";
assert(shallowEquals(a, b), `Expected ${a} to equal ${b}${msg}`);
}
assert.not = function(a, message) {
assert(!a, message);
}
function makeComputedKey(options = {}) {
let { toString, valueOf, toPrimitive } = options;
let hasToPrimitive = options.hasOwnProperty("toPrimitive");
function toPrimitiveFn() {
order.push(`call @@toPrimitive() -> ${toPrimitive}`);
return toPrimitive;
}
function toStringFn() {
order.push(`call toString() -> ${toString}`);
return toString;
}
function valueOfFn() {
order.push(`call valueOf() -> ${valueOf}`);
return valueOf;
}
return {
get [Symbol.toPrimitive]() {
order.push(`load @@toPrimitive`);
this.toPrimitiveLoaded++;
return hasToPrimitive ? toPrimitiveFn : void 0;
},
get toString() {
order.push(`load toString`);
return toStringFn;
},
get valueOf() {
order.push(`load valueOf`);
return valueOfFn;
},
};
}
let object = { toString() { return "object"; } };
let order = [];
class Base {
constructor() {
order.push("construct Base");
return new Proxy(this, {
defineProperty(target, p, desc) {
order.push(`defineProperty ${p}`);
return Reflect.defineProperty(target, p, desc);
}
});
}
}
let computedKey = makeComputedKey({ toString: "test" });
assert.same(order, []);
class BasicTPK extends Base {
[computedKey] = "basic";
}
assert.same(order, ["load @@toPrimitive", "load toString", "call toString() -> test"]);
order.length = 0;
let instance = new BasicTPK;
assert.same(order, ["construct Base", "defineProperty test"]);
assert.same(instance.test, "basic");
// The computed key is evaluated during class evaluation, not initialization
order.length = 0;
computedKey = makeComputedKey({ toString: object, valueOf: null });
instance = new BasicTPK;
assert.same(instance.test, "basic");
assert.not(instance.hasOwnProperty("null"));
assert.same(order, ["construct Base", "defineProperty test"]);
order.length = 0;
class BasicTPK2 extends Base {
[computedKey] = "valueOf";
}
instance = new BasicTPK2;
assert.not(instance.hasOwnProperty("test"));
assert.same(instance.null, "valueOf");
assert.same(order, [
"load @@toPrimitive",
"load toString",
"call toString() -> object",
"load valueOf",
"call valueOf() -> null",
"construct Base",
"defineProperty null"
]);
// exoticToPrimitive
order.length = 0;
computedKey = makeComputedKey({ toPrimitive: "bingo" });
class ExoticTPK {
[computedKey] = "exotic";
}
assert.same(order, ["load @@toPrimitive", "call @@toPrimitive() -> bingo"]);
order.length = 0;
instance = new ExoticTPK;
assert.same(order, ["construct Base", "defineProperty bingo"]);
assert.same(instance.bingo, "exotic");