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) {
"Reflect.isExtensible")));
}
// TODO(neis): For now, we ignore proxies. Once proxies are fully
// implemented, do something like the following:
/*
Maybe<bool> maybe = JSReceiver::IsExtensible(
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();
Maybe<bool> result =
JSReceiver::IsExtensible(Handle<JSReceiver>::cast(target));
MAYBE_RETURN(result, isolate->heap()->exception());
return *isolate->factory()->ToBoolean(result.FromJust());
}
......@@ -1676,8 +1666,8 @@ BUILTIN(ReflectPreventExtensions) {
Maybe<bool> result = JSReceiver::PreventExtensions(
Handle<JSReceiver>::cast(target), Object::DONT_THROW);
return result.IsJust() ? *isolate->factory()->ToBoolean(result.FromJust())
: isolate->heap()->exception();
MAYBE_RETURN(result, isolate->heap()->exception());
return *isolate->factory()->ToBoolean(result.FromJust());
}
......
......@@ -1175,9 +1175,6 @@ function ObjectIsFrozen(obj) {
// ES5 section 15.2.3.13
function ObjectIsExtensible(obj) {
if (!IS_SPEC_OBJECT(obj)) return false;
if (%_IsJSProxy(obj)) {
return true;
}
return %IsExtensible(obj);
}
......
......@@ -183,18 +183,24 @@ class CallSite {
"Class extends value does not have valid prototype property %") \
T(ProxyHandlerDeleteFailed, \
"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(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 " \
"extensible") \
T(ProxyPropNotConfigurable, \
"Proxy handler % returned non-configurable descriptor for property '%' " \
"from '%' trap") \
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(ProxyTargetNonObject, "Proxy.% called with non-object as target") \
T(ProxyTargetNonObject, "Proxy target is non-object") \
T(ProxyTargetPropNotConfigurable, \
"Proxy target property '%' is not configurable") \
T(ProxyTrapFunctionExpected, \
......
......@@ -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,
Handle<Object> receiver,
Handle<Name> name) {
......@@ -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,
const char* name,
Handle<Object> derived,
......@@ -4682,13 +4700,9 @@ MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy,
Handle<Object> argv[]) {
Isolate* isolate = proxy->GetIsolate();
Handle<Object> handler(proxy->handler(), isolate);
Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name);
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetPropertyOrElement(handler, trap_name),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, GetTrap(proxy, trap_name), Object);
if (trap->IsUndefined()) {
if (derived.is_null()) {
......@@ -7083,13 +7097,62 @@ bool JSObject::ReferencesObject(Object* obj) {
Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
ShouldThrow should_throw) {
if (!object->IsJSObject()) return Just(false);
// TODO(neis): Deal with proxies.
if (object->IsJSProxy()) {
return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object),
should_throw);
}
DCHECK(object->IsJSObject());
return JSObject::PreventExtensions(Handle<JSObject>::cast(object),
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,
ShouldThrow should_throw) {
Isolate* isolate = object->GetIsolate();
......@@ -7147,16 +7210,54 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object,
}
// static
Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) {
if (object->IsJSProxy()) {
// TODO(neis,cbruni): Redirect to the trap on JSProxy.
return Just(true);
return JSProxy::IsExtensible(Handle<JSProxy>::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) {
Isolate* isolate = object->GetIsolate();
if (object->IsAccessCheckNeeded() &&
......
......@@ -9531,9 +9531,18 @@ class JSProxy: public JSReceiver {
DECLARE_CAST(JSProxy)
static bool IsRevoked(Handle<JSProxy> proxy);
// ES6 9.5.1
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
static bool GetOwnPropertyDescriptor(LookupIterator* it,
PropertyDescriptor* desc);
......@@ -9586,6 +9595,9 @@ class JSProxy: public JSReceiver {
private:
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,
// but derived_trap is non-NULL, invoke that instead. May cause GC.
MUST_USE_RESULT static MaybeHandle<Object> CallTrap(
......
......@@ -296,8 +296,8 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
if (JSReceiver::PreventExtensions(obj, Object::THROW_ON_ERROR).IsNothing())
return isolate->heap()->exception();
MAYBE_RETURN(JSReceiver::PreventExtensions(obj, Object::THROW_ON_ERROR),
isolate->heap()->exception());
return *obj;
}
......@@ -305,8 +305,10 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) {
RUNTIME_FUNCTION(Runtime_IsExtensible) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
return isolate->heap()->ToBoolean(JSObject::IsExtensible(obj));
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
Maybe<bool> result = JSReceiver::IsExtensible(obj);
MAYBE_RETURN(result, isolate->heap()->exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
......
......@@ -4495,21 +4495,25 @@ TEST(IllegalRedeclaration) {
InitializedHandleScope handle_scope;
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; }",
3 * kPointerSize,
1,
14,
{
B(LdaSmi8), U8(MessageTemplate::kVarRedeclaration), //
B(Star), R(1), //
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kNewSyntaxError), R(1), U8(2), //
B(Throw), //
},
1,
{"a"}},
2,
{helper.factory()->NewNumberFromInt(MessageTemplate::kVarRedeclaration),
helper.factory()->NewStringFromAsciiChecked("a")}},
};
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