Commit 031751d5 authored by neis's avatar neis Committed by Commit bot

[proxies] Implement [[Set]].

R=rossberg
BUG=v8:1543
LOG=N

Review URL: https://codereview.chromium.org/1481103002

Cr-Commit-Position: refs/heads/master@{#32457}
parent c83db2d0
......@@ -3473,7 +3473,8 @@ Maybe<bool> v8::Object::CreateDataProperty(v8::Local<v8::Context> context,
i::LookupIterator it = i::LookupIterator::PropertyOrElement(
isolate, self, key_obj, i::LookupIterator::OWN);
Maybe<bool> result = i::JSReceiver::CreateDataProperty(&it, value_obj);
Maybe<bool> result =
i::JSReceiver::CreateDataProperty(&it, value_obj, i::Object::DONT_THROW);
has_pending_exception = result.IsNothing();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return result;
......@@ -3489,7 +3490,8 @@ Maybe<bool> v8::Object::CreateDataProperty(v8::Local<v8::Context> context,
i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
i::LookupIterator it(isolate, self, index, i::LookupIterator::OWN);
Maybe<bool> result = i::JSReceiver::CreateDataProperty(&it, value_obj);
Maybe<bool> result =
i::JSReceiver::CreateDataProperty(&it, value_obj, i::Object::DONT_THROW);
has_pending_exception = result.IsNothing();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return result;
......
......@@ -109,7 +109,6 @@ enum BindingFlags {
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
V(CREATE_DATE_FUN_INDEX, JSFunction, create_date_fun) \
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
V(DERIVED_SET_TRAP_INDEX, JSFunction, derived_set_trap) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
......
......@@ -193,7 +193,6 @@ utils.Export(function(to) {
%InstallToContext([
"derived_get_trap", DerivedGetTrap,
"derived_set_trap", DerivedSetTrap,
"proxy_enumerate", ProxyEnumerate,
]);
......
......@@ -206,6 +206,10 @@ class LookupIterator final BASE_EMBEDDED {
bool HolderIsReceiverOrHiddenPrototype() const;
bool check_prototype_chain() const {
return (configuration_ & kPrototypeChain) != 0;
}
/* ACCESS_CHECK */
bool HasAccess() const;
......@@ -283,9 +287,6 @@ class LookupIterator final BASE_EMBEDDED {
bool check_interceptor() const {
return (configuration_ & kInterceptor) != 0;
}
bool check_prototype_chain() const {
return (configuration_ & kPrototypeChain) != 0;
}
int descriptor_number() const {
DCHECK(has_property_);
DCHECK(!holder_map_->is_dictionary_map());
......
......@@ -190,8 +190,6 @@ class CallSite {
T(ProxyHandlerTrapMissing, "Proxy handler % has no '%' trap") \
T(ProxyHandlerTrapMustBeCallable, \
"Proxy handler %0 has non-callable '%' trap") \
T(ProxyIsExtensibleViolatesInvariant, \
"Result of trap 'isExtensible' is inconsistent with proxy's target") \
T(ProxyNonObjectPropNames, "Trap '%' returned non-object %") \
T(ProxyPreventExtensionsViolatesInvariant, \
"Trap 'preventExtensions' returned true but the proxy's target is " \
......@@ -210,6 +208,8 @@ class CallSite {
T(ProxyTrapFunctionExpected, \
"Proxy.createFunction called with non-function for '%' trap") \
T(ProxyTrapResultMustInclude, "Trap result must include %.") \
T(ProxyTrapViolatesInvariant, \
"Result of trap '%' is inconsistent with proxy's target") \
T(RedefineDisallowed, "Cannot redefine property: %") \
T(RedefineExternalArray, \
"Cannot redefine a property of an object with external array elements") \
......
This diff is collapsed.
......@@ -1832,9 +1832,9 @@ class JSReceiver: public HeapObject {
Handle<Object> key, PropertyDescriptor* desc,
ShouldThrow should_throw);
// "virtual" dispatcher to the correct [[CreateDataProperty]] implementation.
MUST_USE_RESULT static Maybe<bool> CreateDataProperty(LookupIterator* it,
Handle<Object> value);
// ES6 7.3.4 (when passed DONT_THROW)
MUST_USE_RESULT static Maybe<bool> CreateDataProperty(
LookupIterator* it, Handle<Object> value, ShouldThrow should_throw);
// ES6 9.1.6.1
static bool OrdinaryDefineOwnProperty(Isolate* isolate,
......@@ -9540,22 +9540,14 @@ class JSProxy: public JSReceiver {
Handle<Object> receiver,
Handle<Name> name);
// If the handler defines an accessor property with a setter, invoke it.
// If it defines an accessor property without a setter, or a data property
// that is read-only, fail. In all these cases set '*done' to true.
// Otherwise set it to false, in which case the return value is not
// meaningful.
MUST_USE_RESULT
static Maybe<bool> SetPropertyViaPrototypesWithHandler(
Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, ShouldThrow should_throw, bool* done);
MUST_USE_RESULT static Maybe<PropertyAttributes> GetPropertyAttributes(
LookupIterator* it);
MUST_USE_RESULT static Maybe<bool> SetPropertyWithHandler(
Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, ShouldThrow should_throw);
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> value,
Handle<Object> receiver,
LanguageMode language_mode);
// Dispatched behavior.
DECLARE_PRINTER(JSProxy)
......
// 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.
// Flags: --harmony-proxies --harmony-reflect
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 = [];
}
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment