Make proxies work as prototypes.

Fix a couple of other proxy bugs along the way.
Refactor trap invocation in native code.

R=kmillikin@chromium.org
BUG=v8:1543
TEST=

Review URL: http://codereview.chromium.org/7799026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9312 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2011-09-16 13:38:30 +00:00
parent ff5e1c9822
commit 42f0a73a96
6 changed files with 572 additions and 234 deletions

View File

@ -1351,7 +1351,7 @@ static bool StoreICableLookup(LookupResult* lookup) {
} }
static bool LookupForWrite(JSReceiver* receiver, static bool LookupForWrite(JSObject* receiver,
String* name, String* name,
LookupResult* lookup) { LookupResult* lookup) {
receiver->LocalLookup(name, lookup); receiver->LocalLookup(name, lookup);
@ -1359,12 +1359,10 @@ static bool LookupForWrite(JSReceiver* receiver,
return false; return false;
} }
if (lookup->type() == INTERCEPTOR) { if (lookup->type() == INTERCEPTOR &&
JSObject* object = JSObject::cast(receiver); receiver->GetNamedInterceptor()->setter()->IsUndefined()) {
if (object->GetNamedInterceptor()->setter()->IsUndefined()) { receiver->LocalLookupRealNamedProperty(name, lookup);
object->LocalLookupRealNamedProperty(name, lookup); return StoreICableLookup(lookup);
return StoreICableLookup(lookup);
}
} }
return true; return true;
@ -1376,28 +1374,28 @@ MaybeObject* StoreIC::Store(State state,
Handle<Object> object, Handle<Object> object,
Handle<String> name, Handle<String> name,
Handle<Object> value) { Handle<Object> value) {
// If the object is undefined or null it's illegal to try to set any if (!object->IsJSObject()) {
// properties on it; throw a TypeError in that case. // Handle proxies.
if (object->IsUndefined() || object->IsNull()) { if (object->IsJSProxy()) {
return TypeError("non_object_property_store", object, name); return JSProxy::cast(*object)->
} SetProperty(*name, *value, NONE, strict_mode);
}
// If the object is undefined or null it's illegal to try to set any
// properties on it; throw a TypeError in that case.
if (object->IsUndefined() || object->IsNull()) {
return TypeError("non_object_property_store", object, name);
}
if (!object->IsJSReceiver()) {
// The length property of string values is read-only. Throw in strict mode. // The length property of string values is read-only. Throw in strict mode.
if (strict_mode == kStrictMode && object->IsString() && if (strict_mode == kStrictMode && object->IsString() &&
name->Equals(isolate()->heap()->length_symbol())) { name->Equals(isolate()->heap()->length_symbol())) {
return TypeError("strict_read_only_property", object, name); return TypeError("strict_read_only_property", object, name);
} }
// Ignore stores where the receiver is not a JSObject. // Ignore other stores where the receiver is not a JSObject.
return *value; return *value;
} }
// Handle proxies.
if (object->IsJSProxy()) {
return JSReceiver::cast(*object)->
SetProperty(*name, *value, NONE, strict_mode);
}
Handle<JSObject> receiver = Handle<JSObject>::cast(object); Handle<JSObject> receiver = Handle<JSObject>::cast(object);
// Check if the given name is an array index. // Check if the given name is an array index.

View File

@ -132,27 +132,20 @@ Object* Object::ToBoolean() {
void Object::Lookup(String* name, LookupResult* result) { void Object::Lookup(String* name, LookupResult* result) {
Object* holder = NULL; Object* holder = NULL;
if (IsSmi()) { if (IsJSReceiver()) {
Context* global_context = Isolate::Current()->context()->global_context(); holder = this;
holder = global_context->number_function()->instance_prototype();
} else { } else {
HeapObject* heap_object = HeapObject::cast(this);
if (heap_object->IsJSObject()) {
return JSObject::cast(this)->Lookup(name, result);
} else if (heap_object->IsJSProxy()) {
return result->HandlerResult();
}
Context* global_context = Isolate::Current()->context()->global_context(); Context* global_context = Isolate::Current()->context()->global_context();
if (heap_object->IsString()) { if (IsNumber()) {
holder = global_context->string_function()->instance_prototype();
} else if (heap_object->IsHeapNumber()) {
holder = global_context->number_function()->instance_prototype(); holder = global_context->number_function()->instance_prototype();
} else if (heap_object->IsBoolean()) { } else if (IsString()) {
holder = global_context->string_function()->instance_prototype();
} else if (IsBoolean()) {
holder = global_context->boolean_function()->instance_prototype(); holder = global_context->boolean_function()->instance_prototype();
} }
} }
ASSERT(holder != NULL); // Cannot handle null or undefined. ASSERT(holder != NULL); // Cannot handle null or undefined.
JSObject::cast(holder)->Lookup(name, result); JSReceiver::cast(holder)->Lookup(name, result);
} }
@ -225,30 +218,17 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
} }
MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw, MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw,
String* name_raw, String* name_raw) {
Object* handler_raw) { Isolate* isolate = GetIsolate();
Isolate* isolate = name_raw->GetIsolate();
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> receiver(receiver_raw); Handle<Object> receiver(receiver_raw);
Handle<Object> name(name_raw); Handle<Object> name(name_raw);
Handle<Object> handler(handler_raw);
// Extract trap function. Handle<Object> args[] = { receiver, name };
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get"); Handle<Object> result = CallTrap(
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args);
if (isolate->has_pending_exception()) return Failure::Exception(); if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
// Get the derived `get' property.
trap = isolate->derived_get_trap();
}
// Call trap function.
Object** args[] = { receiver.location(), name.location() };
bool has_exception;
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
return *result; return *result;
} }
@ -566,14 +546,13 @@ MaybeObject* Object::GetProperty(Object* receiver,
} }
*attributes = result->GetAttributes(); *attributes = result->GetAttributes();
Object* value; Object* value;
JSObject* holder = result->holder();
switch (result->type()) { switch (result->type()) {
case NORMAL: case NORMAL:
value = holder->GetNormalizedProperty(result); value = result->holder()->GetNormalizedProperty(result);
ASSERT(!value->IsTheHole() || result->IsReadOnly()); ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? heap->undefined_value() : value; return value->IsTheHole() ? heap->undefined_value() : value;
case FIELD: case FIELD:
value = holder->FastPropertyAt(result->GetFieldIndex()); value = result->holder()->FastPropertyAt(result->GetFieldIndex());
ASSERT(!value->IsTheHole() || result->IsReadOnly()); ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? heap->undefined_value() : value; return value->IsTheHole() ? heap->undefined_value() : value;
case CONSTANT_FUNCTION: case CONSTANT_FUNCTION:
@ -582,14 +561,13 @@ MaybeObject* Object::GetProperty(Object* receiver,
return GetPropertyWithCallback(receiver, return GetPropertyWithCallback(receiver,
result->GetCallbackObject(), result->GetCallbackObject(),
name, name,
holder); result->holder());
case HANDLER: { case HANDLER:
JSProxy* proxy = JSProxy::cast(this); return result->proxy()->GetPropertyWithHandler(receiver, name);
return GetPropertyWithHandler(receiver, name, proxy->handler());
}
case INTERCEPTOR: { case INTERCEPTOR: {
JSObject* recvr = JSObject::cast(receiver); JSObject* recvr = JSObject::cast(receiver);
return holder->GetPropertyWithInterceptor(recvr, name, attributes); return result->holder()->GetPropertyWithInterceptor(
recvr, name, attributes);
} }
case MAP_TRANSITION: case MAP_TRANSITION:
case ELEMENTS_TRANSITION: case ELEMENTS_TRANSITION:
@ -1900,12 +1878,12 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
} }
MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter, MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSFunction* setter,
Object* value) { Object* value) {
Isolate* isolate = GetIsolate(); Isolate* isolate = GetIsolate();
Handle<Object> value_handle(value, isolate); Handle<Object> value_handle(value, isolate);
Handle<JSFunction> fun(JSFunction::cast(setter), isolate); Handle<JSFunction> fun(JSFunction::cast(setter), isolate);
Handle<JSObject> self(this, isolate); Handle<JSReceiver> self(this, isolate);
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
Debug* debug = isolate->debug(); Debug* debug = isolate->debug();
// Handle stepping into a setter if step into is active. // Handle stepping into a setter if step into is active.
@ -1928,6 +1906,9 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name,
for (Object* pt = GetPrototype(); for (Object* pt = GetPrototype();
pt != heap->null_value(); pt != heap->null_value();
pt = pt->GetPrototype()) { pt = pt->GetPrototype()) {
if (pt->IsJSProxy()) {
return result->HandlerResult(JSProxy::cast(pt));
}
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
if (result->IsProperty()) { if (result->IsProperty()) {
if (result->type() == CALLBACKS && !result->IsReadOnly()) return; if (result->type() == CALLBACKS && !result->IsReadOnly()) return;
@ -2092,6 +2073,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
Object* proto = GetPrototype(); Object* proto = GetPrototype();
if (proto->IsNull()) return result->NotFound(); if (proto->IsNull()) return result->NotFound();
ASSERT(proto->IsJSGlobalObject()); ASSERT(proto->IsJSGlobalObject());
// A GlobalProxy's prototype should always be a proper JSObject.
return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
} }
@ -2218,7 +2200,7 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result,
PropertyAttributes attributes, PropertyAttributes attributes,
StrictModeFlag strict_mode) { StrictModeFlag strict_mode) {
if (result->IsFound() && result->type() == HANDLER) { if (result->IsFound() && result->type() == HANDLER) {
return JSProxy::cast(this)->SetPropertyWithHandler( return result->proxy()->SetPropertyWithHandler(
key, value, attributes, strict_mode); key, value, attributes, strict_mode);
} else { } else {
return JSObject::cast(this)->SetPropertyForResult( return JSObject::cast(this)->SetPropertyForResult(
@ -2232,22 +2214,11 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) {
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> receiver(this); Handle<Object> receiver(this);
Handle<Object> name(name_raw); Handle<Object> name(name_raw);
Handle<Object> handler(this->handler());
// Extract trap function. Handle<Object> args[] = { name };
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has"); Handle<Object> result = CallTrap(
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args);
if (isolate->has_pending_exception()) return Failure::Exception(); if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
trap = isolate->derived_has_trap();
}
// Call trap function.
Object** args[] = { name.location() };
bool has_exception;
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
return result->ToBoolean()->IsTrue(); return result->ToBoolean()->IsTrue();
} }
@ -2263,23 +2234,10 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
Handle<Object> receiver(this); Handle<Object> receiver(this);
Handle<Object> name(name_raw); Handle<Object> name(name_raw);
Handle<Object> value(value_raw); Handle<Object> value(value_raw);
Handle<Object> handler(this->handler());
// Extract trap function. Handle<Object> args[] = { receiver, name, value };
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set"); CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args);
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
if (isolate->has_pending_exception()) return Failure::Exception(); if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
trap = isolate->derived_set_trap();
}
// Call trap function.
Object** args[] = {
receiver.location(), name.location(), value.location()
};
bool has_exception;
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
return *value; return *value;
} }
@ -2291,31 +2249,16 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> receiver(this); Handle<Object> receiver(this);
Handle<Object> name(name_raw); Handle<Object> name(name_raw);
Handle<Object> handler(this->handler());
// Extract trap function. Handle<Object> args[] = { name };
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); Handle<Object> result = CallTrap(
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); "delete", Handle<Object>(), ARRAY_SIZE(args), args);
if (isolate->has_pending_exception()) return Failure::Exception(); if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
return Failure::Exception();
}
// Call trap function.
Object** args[] = { name.location() };
bool has_exception;
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
Object* bool_result = result->ToBoolean(); Object* bool_result = result->ToBoolean();
if (mode == STRICT_DELETION && if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) {
bool_result == isolate->heap()->false_value()) { Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
Handle<Object> args[] = { handler, trap_name }; Handle<Object> args[] = { Handle<Object>(handler()), trap_name };
Handle<Object> error = isolate->factory()->NewTypeError( Handle<Object> error = isolate->factory()->NewTypeError(
"handler_failed", HandleVector(args, ARRAY_SIZE(args))); "handler_failed", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error); isolate->Throw(*error);
@ -2327,36 +2270,20 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
JSReceiver* receiver_raw, JSReceiver* receiver_raw,
String* name_raw, String* name_raw) {
bool* has_exception) {
Isolate* isolate = GetIsolate(); Isolate* isolate = GetIsolate();
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<JSReceiver> receiver(receiver_raw); Handle<JSReceiver> receiver(receiver_raw);
Handle<Object> name(name_raw); Handle<Object> name(name_raw);
Handle<Object> handler(this->handler());
// Extract trap function. Handle<Object> args[] = { name };
Handle<String> trap_name = Handle<Object> result = CallTrap(
isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
if (isolate->has_pending_exception()) return NONE; if (isolate->has_pending_exception()) return NONE;
if (trap->IsUndefined()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
*has_exception = true;
return NONE;
}
// Call trap function. if (result->IsUndefined()) return ABSENT;
Object** args[] = { name.location() };
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception);
if (has_exception) return NONE;
// TODO(rossberg): convert result to PropertyAttributes // TODO(rossberg): convert result to PropertyAttributes
USE(result);
return NONE; return NONE;
} }
@ -2376,6 +2303,34 @@ void JSProxy::Fix() {
} }
MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(
const char* name,
Handle<Object> derived,
int argc,
Handle<Object> args[]) {
Isolate* isolate = GetIsolate();
Handle<Object> handler(this->handler());
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol(name);
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
if (isolate->has_pending_exception()) return trap;
if (trap->IsUndefined()) {
if (*derived == NULL) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
return Handle<Object>();
}
trap = Handle<Object>(derived);
}
Object*** argv = reinterpret_cast<Object***>(args);
bool threw = false;
return Execution::Call(trap, handler, argc, argv, &threw);
}
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
String* name, String* name,
@ -2400,20 +2355,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
} }
// Check access rights if needed. // Check access rights if needed.
if (IsAccessCheckNeeded() if (IsAccessCheckNeeded()) {
&& !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(result, return SetPropertyWithFailedAccessCheck(
name, result, name, value, true, strict_mode);
value, }
true,
strict_mode);
} }
if (IsJSGlobalProxy()) { if (IsJSGlobalProxy()) {
Object* proto = GetPrototype(); Object* proto = GetPrototype();
if (proto->IsNull()) return value; if (proto->IsNull()) return value;
ASSERT(proto->IsJSGlobalObject()); ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetProperty( return JSObject::cast(proto)->SetPropertyForResult(
result, name, value, attributes, strict_mode); result, name, value, attributes, strict_mode);
} }
@ -2422,26 +2375,81 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
// accessor that wants to handle the property. // accessor that wants to handle the property.
LookupResult accessor_result; LookupResult accessor_result;
LookupCallbackSetterInPrototypes(name, &accessor_result); LookupCallbackSetterInPrototypes(name, &accessor_result);
if (accessor_result.IsProperty()) { if (accessor_result.IsFound()) {
return SetPropertyWithCallback(accessor_result.GetCallbackObject(), if (accessor_result.type() == CALLBACKS) {
name, return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
value, name,
accessor_result.holder(), value,
strict_mode); accessor_result.holder(),
strict_mode);
} else if (accessor_result.type() == HANDLER) {
// There is a proxy in the prototype chain. Invoke its
// getOwnPropertyDescriptor trap.
Isolate* isolate = heap->isolate();
Handle<JSObject> self(this);
Handle<String> hname(name);
Handle<Object> hvalue(value);
Handle<JSProxy> proxy(accessor_result.proxy());
Handle<Object> args[] = { hname };
Handle<Object> result = proxy->CallTrap(
"getOwnPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
if (isolate->has_pending_exception()) return Failure::Exception();
if (!result->IsUndefined()) {
// The proxy handler cares about this property.
// Check whether it is virtualized as an accessor.
Handle<String> getter_name =
isolate->factory()->LookupAsciiSymbol("get");
Handle<Object> getter(
v8::internal::GetProperty(result, getter_name));
if (isolate->has_pending_exception()) return Failure::Exception();
Handle<String> setter_name =
isolate->factory()->LookupAsciiSymbol("set");
Handle<Object> setter(
v8::internal::GetProperty(result, setter_name));
if (isolate->has_pending_exception()) return Failure::Exception();
if (!setter->IsUndefined()) {
// We have a setter -- invoke it.
if (setter->IsJSFunction()) {
return proxy->SetPropertyWithDefinedSetter(
JSFunction::cast(*setter), *hvalue);
}
Handle<Object> args[] = { setter };
Handle<Object> error = isolate->factory()->NewTypeError(
"setter_must_be_callable", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw(*error);
} else if (!getter->IsUndefined()) {
// We have a getter but no setter -- the property may not be
// written. In strict mode, throw an error.
if (strict_mode == kNonStrictMode) return *hvalue;
Handle<Object> args[] = { hname, proxy };
Handle<Object> error = isolate->factory()->NewTypeError(
"no_setter_in_callback", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw(*error);
}
// The proxy does not define the property as an accessor.
// Consequently, it has no effect on setting the receiver.
return self->AddProperty(*hname, *hvalue, attributes, strict_mode);
}
}
} }
} }
// At this point, no GC should have happened, as this would invalidate
// 'result', which we cannot handlify!
if (!result->IsFound()) { if (!result->IsFound()) {
// Neither properties nor transitions found. // Neither properties nor transitions found.
return AddProperty(name, value, attributes, strict_mode); return AddProperty(name, value, attributes, strict_mode);
} }
if (result->IsReadOnly() && result->IsProperty()) { if (result->IsReadOnly() && result->IsProperty()) {
if (strict_mode == kStrictMode) { if (strict_mode == kStrictMode) {
HandleScope scope(heap->isolate()); Handle<JSObject> self(this);
Handle<String> key(name); Handle<String> hname(name);
Handle<Object> holder(this); Handle<Object> args[] = { hname, self };
Handle<Object> args[2] = { key, holder };
return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, 2))); "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
} else { } else {
return value; return value;
} }
@ -2702,10 +2710,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
case CALLBACKS: case CALLBACKS:
return result->GetAttributes(); return result->GetAttributes();
case HANDLER: { case HANDLER: {
// TODO(rossberg): propagate exceptions properly. return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler(
bool has_exception = false; receiver, name);
return JSProxy::cast(this)->GetPropertyAttributeWithHandler(
receiver, name, &has_exception);
} }
case INTERCEPTOR: case INTERCEPTOR:
return result->holder()->GetPropertyAttributeWithInterceptor( return result->holder()->GetPropertyAttributeWithInterceptor(
@ -3516,15 +3522,6 @@ AccessorDescriptor* Map::FindAccessor(String* name) {
void JSReceiver::LocalLookup(String* name, LookupResult* result) { void JSReceiver::LocalLookup(String* name, LookupResult* result) {
if (IsJSProxy()) {
result->HandlerResult();
} else {
JSObject::cast(this)->LocalLookup(name, result);
}
}
void JSObject::LocalLookup(String* name, LookupResult* result) {
ASSERT(name->IsString()); ASSERT(name->IsString());
Heap* heap = GetHeap(); Heap* heap = GetHeap();
@ -3533,28 +3530,36 @@ void JSObject::LocalLookup(String* name, LookupResult* result) {
Object* proto = GetPrototype(); Object* proto = GetPrototype();
if (proto->IsNull()) return result->NotFound(); if (proto->IsNull()) return result->NotFound();
ASSERT(proto->IsJSGlobalObject()); ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->LocalLookup(name, result); return JSReceiver::cast(proto)->LocalLookup(name, result);
}
if (IsJSProxy()) {
result->HandlerResult(JSProxy::cast(this));
return;
} }
// Do not use inline caching if the object is a non-global object // Do not use inline caching if the object is a non-global object
// that requires access checks. // that requires access checks.
if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) { if (IsAccessCheckNeeded()) {
result->DisallowCaching(); result->DisallowCaching();
} }
JSObject* js_object = JSObject::cast(this);
// Check __proto__ before interceptor. // Check __proto__ before interceptor.
if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) { if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
result->ConstantResult(this); result->ConstantResult(js_object);
return; return;
} }
// Check for lookup interceptor except when bootstrapping. // Check for lookup interceptor except when bootstrapping.
if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { if (js_object->HasNamedInterceptor() &&
result->InterceptorResult(this); !heap->isolate()->bootstrapper()->IsActive()) {
result->InterceptorResult(js_object);
return; return;
} }
LocalLookupRealNamedProperty(name, result); js_object->LocalLookupRealNamedProperty(name, result);
} }
@ -3564,7 +3569,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
for (Object* current = this; for (Object* current = this;
current != heap->null_value(); current != heap->null_value();
current = JSObject::cast(current)->GetPrototype()) { current = JSObject::cast(current)->GetPrototype()) {
JSObject::cast(current)->LocalLookup(name, result); JSReceiver::cast(current)->LocalLookup(name, result);
if (result->IsProperty()) return; if (result->IsProperty()) return;
} }
result->NotFound(); result->NotFound();
@ -6217,10 +6222,10 @@ void Map::CreateBackPointers() {
// Verify target. // Verify target.
Object* source_prototype = prototype(); Object* source_prototype = prototype();
Object* target_prototype = target->prototype(); Object* target_prototype = target->prototype();
ASSERT(source_prototype->IsJSObject() || ASSERT(source_prototype->IsJSReceiver() ||
source_prototype->IsMap() || source_prototype->IsMap() ||
source_prototype->IsNull()); source_prototype->IsNull());
ASSERT(target_prototype->IsJSObject() || ASSERT(target_prototype->IsJSReceiver() ||
target_prototype->IsNull()); target_prototype->IsNull());
ASSERT(source_prototype->IsMap() || ASSERT(source_prototype->IsMap() ||
source_prototype == target_prototype); source_prototype == target_prototype);
@ -7754,7 +7759,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
// It is sufficient to validate that the receiver is not in the new prototype // It is sufficient to validate that the receiver is not in the new prototype
// chain. // chain.
for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) { for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) {
if (JSObject::cast(pt) == this) { if (JSReceiver::cast(pt) == this) {
// Cycle detected. // Cycle detected.
HandleScope scope(heap->isolate()); HandleScope scope(heap->isolate());
return heap->isolate()->Throw( return heap->isolate()->Throw(
@ -7769,8 +7774,8 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
// hidden and set the new prototype on that object. // hidden and set the new prototype on that object.
Object* current_proto = real_receiver->GetPrototype(); Object* current_proto = real_receiver->GetPrototype();
while (current_proto->IsJSObject() && while (current_proto->IsJSObject() &&
JSObject::cast(current_proto)->map()->is_hidden_prototype()) { JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) {
real_receiver = JSObject::cast(current_proto); real_receiver = JSReceiver::cast(current_proto);
current_proto = current_proto->GetPrototype(); current_proto = current_proto->GetPrototype();
} }
} }

View File

@ -907,9 +907,6 @@ class Object : public MaybeObject {
Object* structure, Object* structure,
String* name, String* name,
Object* holder); Object* holder);
MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(Object* receiver,
String* name,
Object* handler);
MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver, MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver,
JSFunction* getter); JSFunction* getter);
@ -1448,6 +1445,8 @@ class JSReceiver: public HeapObject {
Object* value, Object* value,
PropertyAttributes attributes, PropertyAttributes attributes,
StrictModeFlag strict_mode); StrictModeFlag strict_mode);
MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSFunction* setter,
Object* value);
MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode); MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode);
@ -1554,6 +1553,7 @@ class JSObject: public JSReceiver {
// a dictionary, and it will stay a dictionary. // a dictionary, and it will stay a dictionary.
MUST_USE_RESULT MaybeObject* PrepareSlowElementsForSort(uint32_t limit); MUST_USE_RESULT MaybeObject* PrepareSlowElementsForSort(uint32_t limit);
// Can cause GC.
MUST_USE_RESULT MaybeObject* SetPropertyForResult(LookupResult* result, MUST_USE_RESULT MaybeObject* SetPropertyForResult(LookupResult* result,
String* key, String* key,
Object* value, Object* value,
@ -1571,8 +1571,6 @@ class JSObject: public JSReceiver {
Object* value, Object* value,
JSObject* holder, JSObject* holder,
StrictModeFlag strict_mode); StrictModeFlag strict_mode);
MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSFunction* setter,
Object* value);
MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor( MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor(
String* name, String* name,
Object* value, Object* value,
@ -1801,10 +1799,6 @@ class JSObject: public JSReceiver {
inline Object* GetInternalField(int index); inline Object* GetInternalField(int index);
inline void SetInternalField(int index, Object* value); inline void SetInternalField(int index, Object* value);
// Lookup a property. If found, the result is valid and has
// detailed information.
void LocalLookup(String* name, LookupResult* result);
// The following lookup functions skip interceptors. // The following lookup functions skip interceptors.
void LocalLookupRealNamedProperty(String* name, LookupResult* result); void LocalLookupRealNamedProperty(String* name, LookupResult* result);
void LookupRealNamedProperty(String* name, LookupResult* result); void LookupRealNamedProperty(String* name, LookupResult* result);
@ -6708,6 +6702,10 @@ class JSProxy: public JSReceiver {
bool HasPropertyWithHandler(String* name); bool HasPropertyWithHandler(String* name);
MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(
Object* receiver,
String* name);
MUST_USE_RESULT MaybeObject* SetPropertyWithHandler( MUST_USE_RESULT MaybeObject* SetPropertyWithHandler(
String* name, String* name,
Object* value, Object* value,
@ -6720,8 +6718,7 @@ class JSProxy: public JSReceiver {
MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler( MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler(
JSReceiver* receiver, JSReceiver* receiver,
String* name, String* name);
bool* has_exception);
// Turn this into an (empty) JSObject. // Turn this into an (empty) JSObject.
void Fix(); void Fix();
@ -6729,6 +6726,13 @@ class JSProxy: public JSReceiver {
// Initializes the body after the handler slot. // Initializes the body after the handler slot.
inline void InitializeBody(int object_size, Object* value); inline void InitializeBody(int object_size, Object* value);
// 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.
Handle<Object> CallTrap(const char* name,
Handle<Object> derived_trap,
int argc,
Handle<Object> args[]);
// Dispatched behavior. // Dispatched behavior.
#ifdef OBJECT_PRINT #ifdef OBJECT_PRINT
inline void JSProxyPrint() { inline void JSProxyPrint() {

View File

@ -202,9 +202,9 @@ class LookupResult BASE_EMBEDDED {
number_ = entry; number_ = entry;
} }
void HandlerResult() { void HandlerResult(JSProxy* proxy) {
lookup_type_ = HANDLER_TYPE; lookup_type_ = HANDLER_TYPE;
holder_ = NULL; holder_ = proxy;
details_ = PropertyDetails(NONE, HANDLER); details_ = PropertyDetails(NONE, HANDLER);
cacheable_ = false; cacheable_ = false;
} }
@ -221,7 +221,12 @@ class LookupResult BASE_EMBEDDED {
JSObject* holder() { JSObject* holder() {
ASSERT(IsFound()); ASSERT(IsFound());
return holder_; return JSObject::cast(holder_);
}
JSProxy* proxy() {
ASSERT(IsFound());
return JSProxy::cast(holder_);
} }
PropertyType type() { PropertyType type() {
@ -354,7 +359,7 @@ class LookupResult BASE_EMBEDDED {
CONSTANT_TYPE CONSTANT_TYPE
} lookup_type_; } lookup_type_;
JSObject* holder_; JSReceiver* holder_;
int number_; int number_;
bool cacheable_; bool cacheable_;
PropertyDetails details_; PropertyDetails details_;

View File

@ -4645,7 +4645,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_HasProperty) {
if (args[0]->IsJSReceiver()) { if (args[0]->IsJSReceiver()) {
JSReceiver* receiver = JSReceiver::cast(args[0]); JSReceiver* receiver = JSReceiver::cast(args[0]);
CONVERT_CHECKED(String, key, args[1]); CONVERT_CHECKED(String, key, args[1]);
if (receiver->HasProperty(key)) return isolate->heap()->true_value(); bool result = receiver->HasProperty(key);
if (isolate->has_pending_exception()) return Failure::Exception();
return isolate->heap()->ToBoolean(result);
} }
return isolate->heap()->false_value(); return isolate->heap()->false_value();
} }

View File

@ -29,7 +29,8 @@
// TODO(rossberg): for-in for proxies not implemented. // TODO(rossberg): for-in for proxies not implemented.
// TODO(rossberg): inheritance from proxies not implemented. // TODO(rossberg): integer-index properties not implemented properly.
// Helper. // Helper.
@ -39,7 +40,89 @@ function TestWithProxies(test, handler) {
} }
// Getters.
// Getting property descriptors (Object.getOwnPropertyDescriptor).
var key
function TestGetOwnProperty(handler) {
TestWithProxies(TestGetOwnProperty2, handler)
}
function TestGetOwnProperty2(handler, create) {
var p = create(handler)
assertEquals(42, Object.getOwnPropertyDescriptor(p, "a").value)
assertEquals("a", key)
}
TestGetOwnProperty({
getOwnPropertyDescriptor: function(k) {
key = k
return {value: 42, configurable: true}
}
})
TestGetOwnProperty({
getOwnPropertyDescriptor: function(k) {
return this.getOwnPropertyDescriptor2(k)
},
getOwnPropertyDescriptor2: function(k) {
key = k
return {value: 42, configurable: true}
}
})
TestGetOwnProperty({
getOwnPropertyDescriptor: function(k) {
key = k
return {get value() { return 42 }, get configurable() { return true }}
}
})
TestGetOwnProperty(Proxy.create({
get: function(pr, pk) {
return function(k) { key = k; return {value: 42, configurable: true} }
}
}))
function TestGetOwnPropertyThrow(handler) {
TestWithProxies(TestGetOwnPropertyThrow2, handler)
}
function TestGetOwnPropertyThrow2(handler, create) {
var p = create(handler)
assertThrows(function(){ Object.getOwnPropertyDescriptor(p, "a") }, "myexn")
}
TestGetOwnPropertyThrow({
getOwnPropertyDescriptor: function(k) { throw "myexn" }
})
TestGetOwnPropertyThrow({
getOwnPropertyDescriptor: function(k) {
return this.getPropertyDescriptor2(k)
},
getOwnPropertyDescriptor2: function(k) { throw "myexn" }
})
TestGetOwnPropertyThrow({
getOwnPropertyDescriptor: function(k) {
return {get value() { throw "myexn" }}
}
})
TestGetOwnPropertyThrow(Proxy.create({
get: function(pr, pk) {
return function(k) { throw "myexn" }
}
}))
// Getters (dot, brackets).
var key
function TestGet(handler) { function TestGet(handler) {
TestWithProxies(TestGet2, handler) TestWithProxies(TestGet2, handler)
@ -48,48 +131,52 @@ function TestGet(handler) {
function TestGet2(handler, create) { function TestGet2(handler, create) {
var p = create(handler) var p = create(handler)
assertEquals(42, p.a) assertEquals(42, p.a)
assertEquals("a", key)
assertEquals(42, p["b"]) assertEquals(42, p["b"])
assertEquals("b", key)
// TODO(rossberg): inheritance from proxies not yet implemented. var o = Object.create(p, {x: {value: 88}})
// var o = Object.create(p, {x: {value: 88}}) assertEquals(42, o.a)
// assertEquals(42, o.a) assertEquals("a", key)
// assertEquals(42, o["b"]) assertEquals(42, o["b"])
// assertEquals(88, o.x) assertEquals("b", key)
// assertEquals(88, o["x"]) assertEquals(88, o.x)
assertEquals(88, o["x"])
} }
TestGet({ TestGet({
get: function(r, k) { return 42 } get: function(r, k) { key = k; return 42 }
}) })
TestGet({ TestGet({
get: function(r, k) { return this.get2(r, k) }, get: function(r, k) { return this.get2(r, k) },
get2: function(r, k) { return 42 } get2: function(r, k) { key = k; return 42 }
}) })
TestGet({ TestGet({
getPropertyDescriptor: function(k) { return {value: 42} } getPropertyDescriptor: function(k) { key = k; return {value: 42} }
}) })
TestGet({ TestGet({
getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) }, getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
getPropertyDescriptor2: function(k) { return {value: 42} } getPropertyDescriptor2: function(k) { key = k; return {value: 42} }
}) })
TestGet({ TestGet({
getPropertyDescriptor: function(k) { getPropertyDescriptor: function(k) {
key = k;
return {get value() { return 42 }} return {get value() { return 42 }}
} }
}) })
TestGet({ TestGet({
get: undefined, get: undefined,
getPropertyDescriptor: function(k) { return {value: 42} } getPropertyDescriptor: function(k) { key = k; return {value: 42} }
}) })
TestGet(Proxy.create({ TestGet(Proxy.create({
get: function(pr, pk) { get: function(pr, pk) {
return function(r, k) { return 42 } return function(r, k) { key = k; return 42 }
} }
})) }))
@ -101,11 +188,27 @@ function TestGetCall(handler) {
function TestGetCall2(handler, create) { function TestGetCall2(handler, create) {
var p = create(handler) var p = create(handler)
assertEquals(55, p.f()) assertEquals(55, p.f())
assertEquals(55, p["f"]())
assertEquals(55, p.f("unused", "arguments")) assertEquals(55, p.f("unused", "arguments"))
assertEquals(55, p.f.call(p)) assertEquals(55, p.f.call(p))
assertEquals(55, p["f"].call(p))
assertEquals(55, p.withargs(45, 5)) assertEquals(55, p.withargs(45, 5))
assertEquals(55, p.withargs.call(p, 11, 22)) assertEquals(55, p.withargs.call(p, 11, 22))
assertEquals("6655", "66" + p) // calls p.toString assertEquals("6655", "66" + p) // calls p.toString
var o = Object.create(p, {g: {value: function(x) { return x + 88 }}})
assertEquals(55, o.f())
assertEquals(55, o["f"]())
assertEquals(55, o.f("unused", "arguments"))
assertEquals(55, o.f.call(o))
assertEquals(55, o.f.call(p))
assertEquals(55, o["f"].call(p))
assertEquals(55, o.withargs(45, 5))
assertEquals(55, o.withargs.call(p, 11, 22))
assertEquals(90, o.g(2))
assertEquals(91, o.g.call(o, 3))
assertEquals(92, o.g.call(p, 4))
assertEquals("6655", "66" + o) // calls o.toString
} }
TestGetCall({ TestGetCall({
@ -170,6 +273,12 @@ function TestGetThrow2(handler, create) {
var p = create(handler) var p = create(handler)
assertThrows(function(){ p.a }, "myexn") assertThrows(function(){ p.a }, "myexn")
assertThrows(function(){ p["b"] }, "myexn") assertThrows(function(){ p["b"] }, "myexn")
var o = Object.create(p, {x: {value: 88}})
assertThrows(function(){ o.a }, "myexn")
assertThrows(function(){ o["b"] }, "myexn")
assertEquals(88, o.x)
assertEquals(88, o["x"])
} }
TestGetThrow({ TestGetThrow({
@ -302,7 +411,6 @@ TestSet(Proxy.create({
})) }))
function TestSetThrow(handler, create) { function TestSetThrow(handler, create) {
TestWithProxies(TestSetThrow2, handler) TestWithProxies(TestSetThrow2, handler)
} }
@ -422,6 +530,77 @@ TestSetThrow(Proxy.create({
})) }))
var key
var val
function TestSetForDerived(handler, create) {
TestWithProxies(TestSetForDerived2, handler)
}
function TestSetForDerived2(handler, create) {
var p = create(handler)
var o = Object.create(p, {x: {value: 88, writable: true}})
key = ""
assertEquals(48, o.x = 48)
assertEquals("", key) // trap not invoked
assertEquals(48, o.x)
assertEquals(49, o.y = 49)
assertEquals("y", key)
assertEquals(49, o.y)
assertEquals(44, o.p_writable = 44)
assertEquals("p_writable", key)
assertEquals(44, o.p_writable)
assertEquals(45, o.p_nonwritable = 45)
assertEquals("p_nonwritable", key)
assertEquals(45, o.p_nonwritable)
assertEquals(46, o.p_setter = 46)
assertEquals("p_setter", key)
assertEquals(46, val) // written to parent
assertFalse(Object.prototype.hasOwnProperty.call(o, "p_setter"))
val = ""
assertEquals(47, o.p_nosetter = 47)
assertEquals("p_nosetter", key)
assertEquals("", val) // not written at all
assertFalse(Object.prototype.hasOwnProperty.call(o, "p_nosetter"));
key = ""
assertThrows(function(){ "use strict"; o.p_nosetter = 50 }, TypeError)
assertEquals("p_nosetter", key)
assertEquals("", val) // not written at all
assertThrows(function(){ o.p_throw = 51 }, "myexn")
assertEquals("p_throw", key)
assertThrows(function(){ o.p_setterthrow = 52 }, "myexn")
assertEquals("p_setterthrow", key)
}
TestSetForDerived({
getOwnPropertyDescriptor: function(k) {
key = k;
switch (k) {
case "p_writable": return {writable: true}
case "p_nonwritable": return {writable: false}
case "p_setter":return {set: function(x) { val = x }}
case "p_nosetter": return {get: function() { return 1 }}
case "p_throw": throw "myexn"
case "p_setterthrow": return {set: function(x) { throw "myexn" }}
default: return undefined
}
}
})
// TODO(rossberg): TestSetReject, returning false
// TODO(rossberg): TestGetProperty, TestSetProperty
// Property definition (Object.defineProperty and Object.defineProperties). // Property definition (Object.defineProperty and Object.defineProperties).
@ -828,7 +1007,7 @@ TestIn({
}) })
TestIn({ TestIn({
get: undefined, has: undefined,
getPropertyDescriptor: function(k) { getPropertyDescriptor: function(k) {
key = k; return k < "z" ? {value: 42} : void 0 key = k; return k < "z" ? {value: 42} : void 0
} }
@ -874,7 +1053,7 @@ TestInThrow({
}) })
TestInThrow({ TestInThrow({
get: undefined, has: undefined,
getPropertyDescriptor: function(k) { throw "myexn" } getPropertyDescriptor: function(k) { throw "myexn" }
}) })
@ -889,6 +1068,101 @@ TestInThrow(Proxy.create({
})) }))
/* TODO(rossberg): does not work yet, JSProxy::GetPropertyAttributeWithHandler
* is not fully implemented.*/
function TestInForDerived(handler) {
TestWithProxies(TestInForDerived2, handler)
}
function TestInForDerived2(handler, create) {
var p = create(handler)
var o = Object.create(p)
assertTrue("a" in o)
assertEquals("a", key)
// TODO(rossberg): integer indexes not correctly imlemeted yet
// assertTrue(99 in o)
// assertEquals("99", key)
assertFalse("z" in o)
assertEquals("z", key)
assertEquals(2, ("a" in o) ? 2 : 0)
assertEquals(0, !("a" in o) ? 2 : 0)
assertEquals(0, ("zzz" in o) ? 2 : 0)
assertEquals(2, !("zzz" in o) ? 2 : 0)
if ("b" in o) {
} else {
assertTrue(false)
}
assertEquals("b", key)
if ("zz" in o) {
assertTrue(false)
}
assertEquals("zz", key)
if (!("c" in o)) {
assertTrue(false)
}
assertEquals("c", key)
if (!("zzz" in o)) {
} else {
assertTrue(false)
}
assertEquals("zzz", key)
}
TestInForDerived({
getPropertyDescriptor: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
TestInForDerived({
getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
getPropertyDescriptor2: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
TestInForDerived({
getPropertyDescriptor: function(k) {
key = k; return k < "z" ? {get value() { return 42 }} : void 0
}
})
/* TODO(rossberg): this will work once we implement the newest proposal
* regarding default traps for getPropertyDescriptor.
TestInForDerived({
getOwnPropertyDescriptor: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
TestInForDerived({
getOwnPropertyDescriptor: function(k) {
return this.getOwnPropertyDescriptor2(k)
},
getOwnPropertyDescriptor2: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
TestInForDerived({
getOwnPropertyDescriptor: function(k) {
key = k; return k < "z" ? {get value() { return 42 }} : void 0
}
})
*/
TestInForDerived(Proxy.create({
get: function(pr, pk) {
return function(k) { key = k; return k < "z" ? {value: 42} : void 0 }
}
}))
// Own Properties (Object.prototype.hasOwnProperty). // Own Properties (Object.prototype.hasOwnProperty).
@ -1004,34 +1278,46 @@ TestHasOwnThrow(Proxy.create({
// Instanceof (instanceof) // Instanceof (instanceof)
function TestInstanceof() { function TestInstanceof() {
var o = {} var o1 = {}
var p1 = Proxy.create({}) var p1 = Proxy.create({})
var p2 = Proxy.create({}, o) var p2 = Proxy.create({}, o1)
var p3 = Proxy.create({}, p2) var p3 = Proxy.create({}, p2)
var o2 = Object.create(p2)
var f0 = function() {} var f0 = function() {}
f0.prototype = o f0.prototype = o1
var f1 = function() {} var f1 = function() {}
f1.prototype = p1 f1.prototype = p1
var f2 = function() {} var f2 = function() {}
f2.prototype = p2 f2.prototype = p2
var f3 = function() {}
f3.prototype = o2
assertTrue(o instanceof Object) assertTrue(o1 instanceof Object)
assertFalse(o instanceof f0) assertFalse(o1 instanceof f0)
assertFalse(o instanceof f1) assertFalse(o1 instanceof f1)
assertFalse(o instanceof f2) assertFalse(o1 instanceof f2)
assertFalse(o1 instanceof f3)
assertFalse(p1 instanceof Object) assertFalse(p1 instanceof Object)
assertFalse(p1 instanceof f0) assertFalse(p1 instanceof f0)
assertFalse(p1 instanceof f1) assertFalse(p1 instanceof f1)
assertFalse(p1 instanceof f2) assertFalse(p1 instanceof f2)
assertFalse(p1 instanceof f3)
assertTrue(p2 instanceof Object) assertTrue(p2 instanceof Object)
assertTrue(p2 instanceof f0) assertTrue(p2 instanceof f0)
assertFalse(p2 instanceof f1) assertFalse(p2 instanceof f1)
assertFalse(p2 instanceof f2) assertFalse(p2 instanceof f2)
assertFalse(p2 instanceof f3)
assertTrue(p3 instanceof Object) assertTrue(p3 instanceof Object)
assertTrue(p3 instanceof f0) assertTrue(p3 instanceof f0)
assertFalse(p3 instanceof f1) assertFalse(p3 instanceof f1)
assertTrue(p3 instanceof f2) assertTrue(p3 instanceof f2)
assertFalse(p3 instanceof f3)
assertTrue(o2 instanceof Object)
assertTrue(o2 instanceof f0)
assertFalse(o2 instanceof f1)
assertTrue(o2 instanceof f2)
assertFalse(o2 instanceof f3)
var f = Proxy.createFunction({}, function() {}) var f = Proxy.createFunction({}, function() {})
assertTrue(f instanceof Function) assertTrue(f instanceof Function)
@ -1044,43 +1330,57 @@ TestInstanceof()
// Prototype (Object.getPrototypeOf, Object.prototype.isPrototypeOf). // Prototype (Object.getPrototypeOf, Object.prototype.isPrototypeOf).
function TestPrototype() { function TestPrototype() {
var o = {} var o1 = {}
var p1 = Proxy.create({}) var p1 = Proxy.create({})
var p2 = Proxy.create({}, o) var p2 = Proxy.create({}, o1)
var p3 = Proxy.create({}, p2) var p3 = Proxy.create({}, p2)
var p4 = Proxy.create({}, 666) var p4 = Proxy.create({}, 666)
var o2 = Object.create(p3)
assertSame(Object.getPrototypeOf(o), Object.prototype) assertSame(Object.getPrototypeOf(o1), Object.prototype)
assertSame(Object.getPrototypeOf(p1), null) assertSame(Object.getPrototypeOf(p1), null)
assertSame(Object.getPrototypeOf(p2), o) assertSame(Object.getPrototypeOf(p2), o1)
assertSame(Object.getPrototypeOf(p3), p2) assertSame(Object.getPrototypeOf(p3), p2)
assertSame(Object.getPrototypeOf(p4), null) assertSame(Object.getPrototypeOf(p4), null)
assertSame(Object.getPrototypeOf(o2), p3)
assertTrue(Object.prototype.isPrototypeOf(o)) assertTrue(Object.prototype.isPrototypeOf(o1))
assertFalse(Object.prototype.isPrototypeOf(p1)) assertFalse(Object.prototype.isPrototypeOf(p1))
assertTrue(Object.prototype.isPrototypeOf(p2)) assertTrue(Object.prototype.isPrototypeOf(p2))
assertTrue(Object.prototype.isPrototypeOf(p3)) assertTrue(Object.prototype.isPrototypeOf(p3))
assertFalse(Object.prototype.isPrototypeOf(p4)) assertFalse(Object.prototype.isPrototypeOf(p4))
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o)) assertTrue(Object.prototype.isPrototypeOf(o2))
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o1))
assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p1)) assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p1))
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p2)) assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p2))
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p3)) assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p3))
assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p4)) assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p4))
assertFalse(Object.prototype.isPrototypeOf.call(o, o)) assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o2))
assertFalse(Object.prototype.isPrototypeOf.call(o, p1)) assertFalse(Object.prototype.isPrototypeOf.call(o1, o1))
assertTrue(Object.prototype.isPrototypeOf.call(o, p2)) assertFalse(Object.prototype.isPrototypeOf.call(o1, p1))
assertTrue(Object.prototype.isPrototypeOf.call(o, p3)) assertTrue(Object.prototype.isPrototypeOf.call(o1, p2))
assertFalse(Object.prototype.isPrototypeOf.call(o, p4)) assertTrue(Object.prototype.isPrototypeOf.call(o1, p3))
assertFalse(Object.prototype.isPrototypeOf.call(o1, p4))
assertTrue(Object.prototype.isPrototypeOf.call(o1, o2))
assertFalse(Object.prototype.isPrototypeOf.call(p1, p1)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p1))
assertFalse(Object.prototype.isPrototypeOf.call(p1, o)) assertFalse(Object.prototype.isPrototypeOf.call(p1, o1))
assertFalse(Object.prototype.isPrototypeOf.call(p1, p2)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p2))
assertFalse(Object.prototype.isPrototypeOf.call(p1, p3)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p3))
assertFalse(Object.prototype.isPrototypeOf.call(p1, p4)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p4))
assertFalse(Object.prototype.isPrototypeOf.call(p1, o2))
assertFalse(Object.prototype.isPrototypeOf.call(p2, p1)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p1))
assertFalse(Object.prototype.isPrototypeOf.call(p2, p2)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p2))
assertTrue(Object.prototype.isPrototypeOf.call(p2, p3)) assertTrue(Object.prototype.isPrototypeOf.call(p2, p3))
assertFalse(Object.prototype.isPrototypeOf.call(p2, p4)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p4))
assertTrue(Object.prototype.isPrototypeOf.call(p2, o2))
assertFalse(Object.prototype.isPrototypeOf.call(p3, p2)) assertFalse(Object.prototype.isPrototypeOf.call(p3, p2))
assertTrue(Object.prototype.isPrototypeOf.call(p3, o2))
assertFalse(Object.prototype.isPrototypeOf.call(o2, o1))
assertFalse(Object.prototype.isPrototypeOf.call(o2, p1))
assertFalse(Object.prototype.isPrototypeOf.call(o2, p2))
assertFalse(Object.prototype.isPrototypeOf.call(o2, p3))
assertFalse(Object.prototype.isPrototypeOf.call(o2, p4))
assertFalse(Object.prototype.isPrototypeOf.call(o2, o2))
var f = Proxy.createFunction({}, function() {}) var f = Proxy.createFunction({}, function() {})
assertSame(Object.getPrototypeOf(f), Function.prototype) assertSame(Object.getPrototypeOf(f), Function.prototype)
@ -1265,7 +1565,7 @@ TestKeysThrow([], {
// Fixing (Object.freeze, Object.seal, Object.preventExtensions, // Fixing (Object.freeze, Object.seal, Object.preventExtensions,
// Object.isFrozen, Object.isSealed, Object.isExtensible) // Object.isFrozen, Object.isSealed, Object.isExtensible)
// TODO(rossberg): use TestWithProxies to include funciton proxies // TODO(rossberg): use TestWithProxies to include function proxies
function TestFix(names, handler) { function TestFix(names, handler) {
var proto = {p: 77} var proto = {p: 77}
var assertFixing = function(o, s, f, e) { var assertFixing = function(o, s, f, e) {
@ -1312,6 +1612,14 @@ function TestFix(names, handler) {
Object.keys(p3).sort()) Object.keys(p3).sort())
assertEquals(proto, Object.getPrototypeOf(p3)) assertEquals(proto, Object.getPrototypeOf(p3))
assertEquals(77, p3.p) assertEquals(77, p3.p)
var p = Proxy.create(handler, proto)
var o = Object.create(p)
assertFixing(p, false, false, true)
assertFixing(o, false, false, true)
Object.freeze(o)
assertFixing(p, false, false, true)
assertFixing(o, true, true, false)
} }
TestFix([], { TestFix([], {
@ -1424,6 +1732,13 @@ function TestToString(handler) {
assertEquals("my_proxy", Object.prototype.toLocaleString.call(f)) assertEquals("my_proxy", Object.prototype.toLocaleString.call(f))
assertEquals("toString", key) assertEquals("toString", key)
assertDoesNotThrow(function(){ Function.prototype.toString.call(f) }) assertDoesNotThrow(function(){ Function.prototype.toString.call(f) })
var o = Object.create(p)
key = ""
assertEquals("[object Object]", Object.prototype.toString.call(o))
assertEquals("", key)
assertEquals("my_proxy", Object.prototype.toLocaleString.call(o))
assertEquals("toString", key)
} }
TestToString({ TestToString({
@ -1450,6 +1765,10 @@ function TestToStringThrow(handler) {
var f = Proxy.createFunction(handler, function() {}) var f = Proxy.createFunction(handler, function() {})
assertEquals("[object Function]", Object.prototype.toString.call(f)) assertEquals("[object Function]", Object.prototype.toString.call(f))
assertThrows(function(){ Object.prototype.toLocaleString.call(f) }, "myexn") assertThrows(function(){ Object.prototype.toLocaleString.call(f) }, "myexn")
var o = Object.create(p)
assertEquals("[object Object]", Object.prototype.toString.call(o))
assertThrows(function(){ Object.prototype.toLocaleString.call(o) }, "myexn")
} }
TestToStringThrow({ TestToStringThrow({
@ -1508,6 +1827,11 @@ function TestIsEnumerable2(handler, create) {
assertEquals("2", key) assertEquals("2", key)
assertFalse(Object.prototype.propertyIsEnumerable.call(p, "z")) assertFalse(Object.prototype.propertyIsEnumerable.call(p, "z"))
assertEquals("z", key) assertEquals("z", key)
var o = Object.create(p)
key = ""
assertFalse(Object.prototype.propertyIsEnumerable.call(o, "a"))
assertEquals("", key) // trap not invoked
} }
TestIsEnumerable({ TestIsEnumerable({