Commit a2501890 authored by neis's avatar neis Committed by Commit bot

[proxies] Implement [[PreventExtensions]] and [[IsExtensible]].

BUG=

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

Cr-Commit-Position: refs/heads/master@{#32212}
parent 1a90af55
...@@ -1621,20 +1621,10 @@ BUILTIN(ReflectIsExtensible) { ...@@ -1621,20 +1621,10 @@ BUILTIN(ReflectIsExtensible) {
"Reflect.isExtensible"))); "Reflect.isExtensible")));
} }
// TODO(neis): For now, we ignore proxies. Once proxies are fully Maybe<bool> result =
// implemented, do something like the following: JSReceiver::IsExtensible(Handle<JSReceiver>::cast(target));
/* MAYBE_RETURN(result, isolate->heap()->exception());
Maybe<bool> maybe = JSReceiver::IsExtensible( return *isolate->factory()->ToBoolean(result.FromJust());
Handle<JSReceiver>::cast(target));
if (!maybe.IsJust()) return isolate->heap()->exception();
return *isolate->factory()->ToBoolean(maybe.FromJust());
*/
if (target->IsJSObject()) {
return *isolate->factory()->ToBoolean(
JSObject::IsExtensible(Handle<JSObject>::cast(target)));
}
return *isolate->factory()->false_value();
} }
...@@ -1676,8 +1666,8 @@ BUILTIN(ReflectPreventExtensions) { ...@@ -1676,8 +1666,8 @@ BUILTIN(ReflectPreventExtensions) {
Maybe<bool> result = JSReceiver::PreventExtensions( Maybe<bool> result = JSReceiver::PreventExtensions(
Handle<JSReceiver>::cast(target), Object::DONT_THROW); Handle<JSReceiver>::cast(target), Object::DONT_THROW);
return result.IsJust() ? *isolate->factory()->ToBoolean(result.FromJust()) MAYBE_RETURN(result, isolate->heap()->exception());
: isolate->heap()->exception(); return *isolate->factory()->ToBoolean(result.FromJust());
} }
......
...@@ -1175,9 +1175,6 @@ function ObjectIsFrozen(obj) { ...@@ -1175,9 +1175,6 @@ function ObjectIsFrozen(obj) {
// ES5 section 15.2.3.13 // ES5 section 15.2.3.13
function ObjectIsExtensible(obj) { function ObjectIsExtensible(obj) {
if (!IS_SPEC_OBJECT(obj)) return false; if (!IS_SPEC_OBJECT(obj)) return false;
if (%_IsJSProxy(obj)) {
return true;
}
return %IsExtensible(obj); return %IsExtensible(obj);
} }
......
...@@ -183,18 +183,24 @@ class CallSite { ...@@ -183,18 +183,24 @@ class CallSite {
"Class extends value does not have valid prototype property %") \ "Class extends value does not have valid prototype property %") \
T(ProxyHandlerDeleteFailed, \ T(ProxyHandlerDeleteFailed, \
"Proxy handler % did not return a boolean value from 'delete' trap") \ "Proxy handler % did not return a boolean value from 'delete' trap") \
T(ProxyHandlerNonObject, "Proxy.create called with non-object as handler") \ T(ProxyHandlerNonObject, "Cannot create proxy with non-object as handler") \
T(ProxyHandlerReturned, "Proxy handler % returned % from '%' trap") \ T(ProxyHandlerReturned, "Proxy handler % returned % from '%' trap") \
T(ProxyHandlerTrapMissing, "Proxy handler % has no '%' trap") \ T(ProxyHandlerTrapMissing, "Proxy handler % has no '%' trap") \
T(ProxyHandlerTrapMustBeCallable, \ T(ProxyHandlerTrapMustBeCallable, \
"Proxy handler %0 has non-callable '%' trap") \ "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(ProxyNonObjectPropNames, "Trap '%' returned non-object %") \
T(ProxyPreventExtensionsViolatesInvariant, \
"Trap 'preventExtensions' returned true but the proxy's target is " \
"extensible") \
T(ProxyPropNotConfigurable, \ T(ProxyPropNotConfigurable, \
"Proxy handler % returned non-configurable descriptor for property '%' " \ "Proxy handler % returned non-configurable descriptor for property '%' " \
"from '%' trap") \ "from '%' trap") \
T(ProxyRepeatedPropName, "Trap '%' returned repeated property name '%'") \ T(ProxyRepeatedPropName, "Trap '%' returned repeated property name '%'") \
T(ProxyRevoked, "Cannot perform '%' on a proxy that has been revoked") \
T(ProxyTargetNotExtensible, "Proxy target is not extensible") \ T(ProxyTargetNotExtensible, "Proxy target is not extensible") \
T(ProxyTargetNonObject, "Proxy.% called with non-object as target") \ T(ProxyTargetNonObject, "Proxy target is non-object") \
T(ProxyTargetPropNotConfigurable, \ T(ProxyTargetPropNotConfigurable, \
"Proxy target property '%' is not configurable") \ "Proxy target property '%' is not configurable") \
T(ProxyTrapFunctionExpected, \ T(ProxyTrapFunctionExpected, \
......
...@@ -906,6 +906,15 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { ...@@ -906,6 +906,15 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
} }
bool JSProxy::IsRevoked(Handle<JSProxy> proxy) {
// TODO(neis): Decide on how to represent revocation. For now, revocation is
// unsupported.
DCHECK(proxy->target()->IsJSReceiver());
DCHECK(proxy->handler()->IsJSReceiver());
return false;
}
MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy, MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy,
Handle<Object> receiver, Handle<Object> receiver,
Handle<Name> name) { Handle<Name> name) {
...@@ -4675,6 +4684,15 @@ Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) { ...@@ -4675,6 +4684,15 @@ Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) {
} }
MaybeHandle<Object> JSProxy::GetTrap(Handle<JSProxy> proxy,
Handle<String> trap) {
DCHECK(!IsRevoked(proxy));
Isolate* isolate = proxy->GetIsolate();
Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
return Object::GetMethod(handler, trap);
}
MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy, MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy,
const char* name, const char* name,
Handle<Object> derived, Handle<Object> derived,
...@@ -4682,13 +4700,9 @@ MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy, ...@@ -4682,13 +4700,9 @@ MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy,
Handle<Object> argv[]) { Handle<Object> argv[]) {
Isolate* isolate = proxy->GetIsolate(); Isolate* isolate = proxy->GetIsolate();
Handle<Object> handler(proxy->handler(), isolate); Handle<Object> handler(proxy->handler(), isolate);
Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name); Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name);
Handle<Object> trap; Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, GetTrap(proxy, trap_name), Object);
isolate, trap,
Object::GetPropertyOrElement(handler, trap_name),
Object);
if (trap->IsUndefined()) { if (trap->IsUndefined()) {
if (derived.is_null()) { if (derived.is_null()) {
...@@ -7083,13 +7097,62 @@ bool JSObject::ReferencesObject(Object* obj) { ...@@ -7083,13 +7097,62 @@ bool JSObject::ReferencesObject(Object* obj) {
Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object, Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
ShouldThrow should_throw) { ShouldThrow should_throw) {
if (!object->IsJSObject()) return Just(false); if (object->IsJSProxy()) {
// TODO(neis): Deal with proxies. return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object),
should_throw);
}
DCHECK(object->IsJSObject());
return JSObject::PreventExtensions(Handle<JSObject>::cast(object), return JSObject::PreventExtensions(Handle<JSObject>::cast(object),
should_throw); should_throw);
} }
Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy,
ShouldThrow should_throw) {
Isolate* isolate = proxy->GetIsolate();
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->preventExtensions_string();
if (IsRevoked(proxy)) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, trap, GetTrap(proxy, trap_name),
Nothing<bool>());
if (trap->IsUndefined()) {
return JSReceiver::PreventExtensions(target, should_throw);
}
Handle<Object> trap_result;
Handle<Object> args[] = {target};
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(args), args),
Nothing<bool>());
if (!trap_result->BooleanValue()) {
RETURN_FAILURE(isolate, should_throw,
NewTypeError(MessageTemplate::kProxyHandlerReturned, handler,
factory->false_string(), trap_name));
}
// Enforce the invariant.
Maybe<bool> target_result = JSReceiver::IsExtensible(target);
MAYBE_RETURN(target_result, Nothing<bool>());
if (target_result.FromJust()) {
isolate->Throw(*factory->NewTypeError(
MessageTemplate::kProxyPreventExtensionsViolatesInvariant));
return Nothing<bool>();
}
return Just(true);
}
Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
ShouldThrow should_throw) { ShouldThrow should_throw) {
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
...@@ -7147,16 +7210,54 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, ...@@ -7147,16 +7210,54 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
} }
// static
Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) { Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) {
if (object->IsJSProxy()) { if (object->IsJSProxy()) {
// TODO(neis,cbruni): Redirect to the trap on JSProxy. return JSProxy::IsExtensible(Handle<JSProxy>::cast(object));
return Just(true);
} }
return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object))); return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object)));
} }
Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->isExtensible_string();
if (IsRevoked(proxy)) {
isolate->Throw(
*factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
return Nothing<bool>();
}
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate);
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, trap, GetTrap(proxy, trap_name),
Nothing<bool>());
if (trap->IsUndefined()) {
return JSReceiver::IsExtensible(target);
}
Handle<Object> trap_result;
Handle<Object> args[] = {target};
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(args), args),
Nothing<bool>());
// Enforce the invariant.
Maybe<bool> target_result = JSReceiver::IsExtensible(target);
MAYBE_RETURN(target_result, Nothing<bool>());
if (target_result.FromJust() != trap_result->BooleanValue()) {
isolate->Throw(*factory->NewTypeError(
MessageTemplate::kProxyIsExtensibleViolatesInvariant));
return Nothing<bool>();
}
return target_result;
}
bool JSObject::IsExtensible(Handle<JSObject> object) { bool JSObject::IsExtensible(Handle<JSObject> object) {
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
if (object->IsAccessCheckNeeded() && if (object->IsAccessCheckNeeded() &&
......
...@@ -9531,9 +9531,18 @@ class JSProxy: public JSReceiver { ...@@ -9531,9 +9531,18 @@ class JSProxy: public JSReceiver {
DECLARE_CAST(JSProxy) DECLARE_CAST(JSProxy)
static bool IsRevoked(Handle<JSProxy> proxy);
// ES6 9.5.1 // ES6 9.5.1
static MaybeHandle<Object> GetPrototype(Handle<JSProxy> receiver); static MaybeHandle<Object> GetPrototype(Handle<JSProxy> receiver);
// ES6 9.5.3
MUST_USE_RESULT static Maybe<bool> IsExtensible(Handle<JSProxy> proxy);
// ES6 9.5.4 (when passed DONT_THROW)
MUST_USE_RESULT static Maybe<bool> PreventExtensions(
Handle<JSProxy> proxy, ShouldThrow should_throw);
// ES6 9.5.5 // ES6 9.5.5
static bool GetOwnPropertyDescriptor(LookupIterator* it, static bool GetOwnPropertyDescriptor(LookupIterator* it,
PropertyDescriptor* desc); PropertyDescriptor* desc);
...@@ -9586,6 +9595,9 @@ class JSProxy: public JSReceiver { ...@@ -9586,6 +9595,9 @@ class JSProxy: public JSReceiver {
private: private:
friend class JSReceiver; friend class JSReceiver;
MUST_USE_RESULT static MaybeHandle<Object> GetTrap(Handle<JSProxy> proxy,
Handle<String> trap);
// Invoke a trap by name. If the trap does not exist on this's handler, // Invoke a trap by name. If the trap does not exist on this's handler,
// but derived_trap is non-NULL, invoke that instead. May cause GC. // but derived_trap is non-NULL, invoke that instead. May cause GC.
MUST_USE_RESULT static MaybeHandle<Object> CallTrap( MUST_USE_RESULT static MaybeHandle<Object> CallTrap(
......
...@@ -296,8 +296,8 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) { ...@@ -296,8 +296,8 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
if (JSReceiver::PreventExtensions(obj, Object::THROW_ON_ERROR).IsNothing()) MAYBE_RETURN(JSReceiver::PreventExtensions(obj, Object::THROW_ON_ERROR),
return isolate->heap()->exception(); isolate->heap()->exception());
return *obj; return *obj;
} }
...@@ -305,8 +305,10 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) { ...@@ -305,8 +305,10 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) {
RUNTIME_FUNCTION(Runtime_IsExtensible) { RUNTIME_FUNCTION(Runtime_IsExtensible) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
return isolate->heap()->ToBoolean(JSObject::IsExtensible(obj)); Maybe<bool> result = JSReceiver::IsExtensible(obj);
MAYBE_RETURN(result, isolate->heap()->exception());
return isolate->heap()->ToBoolean(result.FromJust());
} }
......
...@@ -4495,21 +4495,25 @@ TEST(IllegalRedeclaration) { ...@@ -4495,21 +4495,25 @@ TEST(IllegalRedeclaration) {
InitializedHandleScope handle_scope; InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper; BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = { CHECK_GE(MessageTemplate::kVarRedeclaration, 128);
// Must adapt bytecode if this changes.
ExpectedSnippet<Handle<Object>, 2> snippets[] = {
{"const a = 1; { var a = 2; }", {"const a = 1; { var a = 2; }",
3 * kPointerSize, 3 * kPointerSize,
1, 1,
14, 14,
{ {
B(LdaSmi8), U8(MessageTemplate::kVarRedeclaration), //
B(Star), R(1), //
B(LdaConstant), U8(0), // B(LdaConstant), U8(0), //
B(Star), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), // B(Star), R(2), //
B(CallRuntime), U16(Runtime::kNewSyntaxError), R(1), U8(2), // B(CallRuntime), U16(Runtime::kNewSyntaxError), R(1), U8(2), //
B(Throw), // B(Throw), //
}, },
1, 2,
{"a"}}, {helper.factory()->NewNumberFromInt(MessageTemplate::kVarRedeclaration),
helper.factory()->NewStringFromAsciiChecked("a")}},
}; };
for (size_t i = 0; i < arraysize(snippets); i++) { for (size_t i = 0; i < arraysize(snippets); i++) {
......
// 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 () {
// No trap.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
assertTrue(Reflect.isExtensible(target));
assertTrue(Reflect.isExtensible(proxy));
assertTrue(Reflect.preventExtensions(proxy));
assertFalse(Reflect.isExtensible(target));
assertFalse(Reflect.isExtensible(proxy));
})();
(function () {
// "Undefined" trap.
var target = {};
var handler = { isExtensible: null };
var proxy = new Proxy(target, handler);
assertTrue(Reflect.isExtensible(target));
assertTrue(Reflect.isExtensible(proxy));
assertTrue(Reflect.preventExtensions(proxy));
assertFalse(Reflect.isExtensible(target));
assertFalse(Reflect.isExtensible(proxy));
})();
(function () {
// Invalid trap.
var target = {};
var handler = { isExtensible: true };
var proxy = new Proxy(target, handler);
assertThrows(() => {Reflect.isExtensible(proxy)}, TypeError);
})();
(function () {
var target = {};
var handler = { isExtensible() {return "bla"} };
var proxy = new Proxy(target, handler);
// Trap returns trueish and target is extensible.
assertTrue(Reflect.isExtensible(proxy));
// Trap returns trueish but target is not extensible.
Reflect.preventExtensions(target);
assertThrows(() => {Reflect.isExtensible(proxy)}, TypeError);
})();
(function () {
var target = {};
var handler = { isExtensible() {return 0} };
var proxy = new Proxy(target, handler);
// Trap returns falsish but target is extensible.
assertThrows(() => {Reflect.isExtensible(proxy)}, TypeError);
// Trap returns falsish and target is not extensible.
Reflect.preventExtensions(target);
assertFalse(Reflect.isExtensible(proxy));
})();
// 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 () {
// No trap.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
assertTrue(Reflect.isExtensible(target));
assertTrue(Reflect.isExtensible(proxy));
assertTrue(Reflect.preventExtensions(proxy));
assertFalse(Reflect.isExtensible(target));
assertFalse(Reflect.isExtensible(proxy));
})();
(function () {
// "Undefined" trap.
var target = {};
var handler = { preventExtensions: null };
var proxy = new Proxy(target, handler);
assertTrue(Reflect.isExtensible(target));
assertTrue(Reflect.isExtensible(proxy));
assertTrue(Reflect.preventExtensions(proxy));
assertFalse(Reflect.isExtensible(target));
assertFalse(Reflect.isExtensible(proxy));
})();
(function () {
// Invalid trap.
var target = {};
var handler = { preventExtensions: 42 };
var proxy = new Proxy(target, handler);
assertThrows(() => {Reflect.preventExtensions(proxy)}, TypeError);
})();
(function () {
var target = {};
var handler = { isExtensible() {return "bla"} };
var proxy = new Proxy(target, handler);
// Trap returns trueish and target is extensible.
assertTrue(Reflect.isExtensible(proxy));
// Trap returns trueish but target is not extensible.
Reflect.preventExtensions(target);
assertThrows(() => {Reflect.isExtensible(proxy)}, TypeError);
})();
(function () {
// Trap returns falsish.
var target = {};
var handler = { preventExtensions() {return 0} };
var proxy = new Proxy(target, handler);
assertFalse(Reflect.preventExtensions(proxy));
Reflect.preventExtensions(target);
assertFalse(Reflect.preventExtensions(proxy));
})();
(function () {
var target = {};
var handler = { preventExtensions() {return Symbol()} };
var proxy = new Proxy(target, handler);
// Trap returns trueish but target is extensible.
assertThrows(() => {Reflect.preventExtensions(proxy)}, TypeError);
// Trap returns trueish and target is not extensible.
Reflect.preventExtensions(target);
assertTrue(Reflect.preventExtensions(proxy));
})();
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