blob: d56cec52f916ec093f05fb2dbc121ee8d67aba48 [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function sloppyDefaultSet(o, p, v) { return o[p] = v }
function sloppyReflectSet(o, p, v) { return Reflect.set(o, p, v) }
function strictDefaultSet(o, p, v) { "use strict"; return o[p] = v }
function strictReflectSet(o, p, v) { "use strict"; return Reflect.set(o, p, v) }
sloppyDefaultSet.shouldThrow = false;
sloppyReflectSet.shouldThrow = false;
strictDefaultSet.shouldThrow = true;
strictReflectSet.shouldThrow = false;
sloppyDefaultSet.returnsBool = false;
sloppyReflectSet.returnsBool = true;
strictDefaultSet.returnsBool = false;
strictReflectSet.returnsBool = true;
function assertTrueIf(flag, x) { if (flag) assertTrue(x) }
function assertFalseIf(flag, x) { if (flag) assertFalse(x) }
function assertSetFails(mySet, o, p, v) {
if (mySet.shouldThrow) {
assertThrows(() => mySet(o, p, v), TypeError);
} else {
assertFalseIf(mySet.returnsBool, mySet(o, p, v));
}
}
function dataDescriptor(x) {
return {value: x, writable: true, enumerable: true, configurable: true};
}
function toKey(x) {
if (typeof x === "symbol") return x;
return String(x);
}
var properties =
["bla", "0", 1, Symbol(), {[Symbol.toPrimitive]() {return "a"}}];
function TestForwarding(handler, mySet) {
assertTrue(undefined == handler.set);
assertTrue(undefined == handler.getOwnPropertyDescriptor);
assertTrue(undefined == handler.defineProperty);
var target = {};
var proxy = new Proxy(target, handler);
// Property does not exist on target.
for (var p of properties) {
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 42));
assertSame(42, target[p]);
}
// Property exists as writable data on target.
for (var p of properties) {
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
assertSame(0, target[p]);
}
// Property exists as non-writable data on target.
for (var p of properties) {
Object.defineProperty(target, p,
{value: 42, configurable: true, writable: false});
assertSetFails(mySet, proxy, p, 42);
assertSetFails(mySet, proxy, p, 0);
assertEquals(42, target[p]);
}
};
(function () {
// No trap.
var handler = {};
TestForwarding(handler, sloppyDefaultSet);
TestForwarding(handler, sloppyReflectSet);
TestForwarding(handler, strictDefaultSet);
TestForwarding(handler, strictReflectSet);
})();
(function () {
// "Undefined" trap.
var handler = { set: null };
TestForwarding(handler, sloppyDefaultSet);
TestForwarding(handler, sloppyReflectSet);
TestForwarding(handler, strictDefaultSet);
TestForwarding(handler, strictReflectSet);
})();
function TestForwarding2(mySet) {
// Check that setting on a proxy without "set" trap correctly triggers its
// "getOwnProperty" trap and its "defineProperty" trap.
var target = {};
var handler = {};
var observations = [];
var proxy = new Proxy(target, handler);
handler.getOwnPropertyDescriptor = function() {
observations.push(arguments);
return Reflect.getOwnPropertyDescriptor(...arguments);
}
handler.defineProperty = function() {
observations.push(arguments);
return Reflect.defineProperty(...arguments);
}
for (var p of properties) {
mySet(proxy, p, 42);
assertEquals(2, observations.length)
assertArrayEquals([target, toKey(p)], observations[0]);
assertSame(target, observations[0][0]);
assertArrayEquals([target, toKey(p), dataDescriptor(42)], observations[1]);
assertSame(target, observations[1][0]);
observations = [];
mySet(proxy, p, 42);
assertEquals(2, observations.length)
assertArrayEquals([target, toKey(p)], observations[0]);
assertSame(target, observations[0][0]);
assertArrayEquals([target, toKey(p), {value: 42}], observations[1]);
assertSame(target, observations[1][0]);
observations = [];
}
}
TestForwarding2(sloppyDefaultSet);
TestForwarding2(sloppyReflectSet);
TestForwarding2(strictDefaultSet);
TestForwarding2(strictReflectSet);
function TestInvalidTrap(proxy, mySet) {
for (var p of properties) {
assertThrows(() => mySet(proxy, p, 42), TypeError);
}
}
(function () {
var target = {};
var handler = { set: true };
var proxy = new Proxy(target, handler);
TestInvalidTrap(proxy, sloppyDefaultSet);
TestInvalidTrap(proxy, sloppyReflectSet);
TestInvalidTrap(proxy, strictDefaultSet);
TestInvalidTrap(proxy, strictReflectSet);
})();
function TestTrappingFalsish(mySet) {
var target = {};
var handler = { set() {return ""} };
var proxy = new Proxy(target, handler);
for (var p of properties) {
assertSetFails(mySet, proxy, p, 42);
}
}
TestTrappingFalsish(sloppyDefaultSet);
TestTrappingFalsish(sloppyReflectSet);
TestTrappingFalsish(strictDefaultSet);
TestTrappingFalsish(strictReflectSet);
function TestTrappingTrueish(mySet) {
var target = {};
var handler = { set() {return 42} };
var proxy = new Proxy(target, handler);
// Trap returns trueish and property does not exist in target.
for (var p of properties) {
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
}
// Trap returns trueish and target property is configurable or writable data.
for (var p of properties) {
Object.defineProperty(target, p, {configurable: true, writable: true});
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
Object.defineProperty(target, p, {configurable: true, writable: false});
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
Object.defineProperty(target, p, {configurable: false, writable: true});
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
}
}
TestTrappingTrueish(sloppyDefaultSet);
TestTrappingTrueish(sloppyReflectSet);
TestTrappingTrueish(strictDefaultSet);
TestTrappingTrueish(strictReflectSet);
function TestTrappingTrueish2(mySet) {
var target = {};
var handler = { set() {return 42} };
var proxy = new Proxy(target, handler);
// Trap returns trueish but target property is frozen data.
for (var p of properties) {
Object.defineProperty(target, p, {
configurable: false, writable: false, value: 0
});
assertThrows(() => mySet(proxy, p, 666), TypeError); // New value.
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0)); // Old value.
}
};
TestTrappingTrueish2(sloppyDefaultSet);
TestTrappingTrueish2(sloppyReflectSet);
TestTrappingTrueish2(strictDefaultSet);
TestTrappingTrueish2(strictReflectSet);
function TestTrappingTrueish3(mySet) {
var target = {};
var handler = { set() {return 42} };
var proxy = new Proxy(target, handler);
// Trap returns trueish and target property is configurable accessor.
for (var p of properties) {
Object.defineProperty(target, p, { configurable: true, set: undefined });
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
}
// Trap returns trueish and target property is non-configurable accessor.
for (var p of properties) {
Object.defineProperty(target, p, { configurable: false, set: undefined });
assertThrows(() => mySet(proxy, p, 0));
}
};
TestTrappingTrueish3(sloppyDefaultSet);
TestTrappingTrueish3(sloppyReflectSet);
TestTrappingTrueish3(strictDefaultSet);
TestTrappingTrueish3(strictReflectSet);
function TestTrapReceiverArgument(mySet) {
var target = {};
var handler = {};
var observations = [];
var proxy = new Proxy(target, handler);
var object = Object.create(proxy);
handler.set = function() {
observations.push(arguments);
return Reflect.set(...arguments);
}
for (var p of properties) {
mySet(object, p, 42);
assertEquals(1, observations.length)
assertArrayEquals([target, toKey(p), 42, object], observations[0]);
assertSame(target, observations[0][0]);
assertSame(object, observations[0][3]);
observations = [];
}
};
TestTrapReceiverArgument(sloppyDefaultSet);
TestTrapReceiverArgument(sloppyReflectSet);
TestTrapReceiverArgument(strictDefaultSet);
TestTrapReceiverArgument(strictReflectSet);
(function TestTrapReceiverArgument2() {
// Check that non-object receiver is passed through as well.
var target = {};
var handler = {};
var observations = [];
var proxy = new Proxy(target, handler);
handler.set = function() {
observations.push(arguments);
return Reflect.set(...arguments);
}
for (var p of properties) {
for (var receiver of [null, undefined, 1]) {
Reflect.set(proxy, p, 42, receiver);
assertEquals(1, observations.length)
assertArrayEquals([target, toKey(p), 42, receiver], observations[0]);
assertSame(target, observations[0][0]);
assertSame(receiver, observations[0][3]);
observations = [];
}
}
var object = Object.create(proxy);
for (var p of properties) {
for (var receiver of [null, undefined, 1]) {
Reflect.set(object, p, 42, receiver);
assertEquals(1, observations.length);
assertArrayEquals([target, toKey(p), 42, receiver], observations[0]);
assertSame(target, observations[0][0]);
assertSame(receiver, observations[0][3]);
observations = [];
}
}
})();
function TestTargetProxy(mySet) {
var q = new Proxy({}, {});
var proxy = new Proxy(q, {
set: function(t, k, v) {
return Reflect.set(t, k, v);
}
});
for (var p of properties) {
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 42));
assertSame(42, q[p]);
}
};
TestTargetProxy(sloppyDefaultSet);
TestTargetProxy(sloppyReflectSet);
TestTargetProxy(strictDefaultSet);
TestTargetProxy(strictReflectSet);
(function TestAccessorNoSet() {
var target = {
};
Object.defineProperty(target, 'prop', {
get: function() {
return 42;
},
configurable: false
})
var handler = {
set: function() { return true; }
}
var proxy = new Proxy(target, handler);
assertThrows(function() { proxy.prop = 0; }, TypeError);
})();
(function TestProxyInPrototype() {
var handler = {
set: function(t, k, v) {
Reflect.set(t, k, v);
}
};
var obj = {};
var proxy = new Proxy(obj, handler);
var o = Object.create(proxy);
for (var i = 0; i < 3; ++i) {
o.prop = 42 + i;
assertEquals(42 + i, obj.prop);
}
})();
(function TestProxyInPrototypeNoTrap() {
var handler = {
};
var obj = {};
var proxy = new Proxy(obj, handler);
var o = Object.create(proxy);
for (var i = 0; i < 3; ++i) {
o.prop = 42 + i;
assertEquals(42 + i, o.prop);
assertEquals(undefined, obj.prop);
}
})();
// Note: this case is currently handled by runtime.
(function TestDifferentHolder() {
var obj = {
'1337': 100
};
var handler = {
set(target, name, value, receiver) {
if (name != '1337') return Reflect.set(target, name, value, receiver);
assertSame(target, obj);
assertSame(receiver, p);
return target[name] = value;
}
};
var p = new Proxy(obj, handler);
for (var i = 0; i < 3; ++i) {
assertEquals(42, p[1337] = 42);
}
})();
(function test32BitIndex() {
var index = (1 << 31) + 1;
var obj = {};
obj[index] = 42;
var p = new Proxy(obj, {});
for (var i = 0; i < 3; ++i) {
p[index] = 100;
assertEquals(100, obj[index]);
}
})();