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:
parent
ff5e1c9822
commit
42f0a73a96
38
src/ic.cc
38
src/ic.cc
@ -1351,7 +1351,7 @@ static bool StoreICableLookup(LookupResult* lookup) {
|
||||
}
|
||||
|
||||
|
||||
static bool LookupForWrite(JSReceiver* receiver,
|
||||
static bool LookupForWrite(JSObject* receiver,
|
||||
String* name,
|
||||
LookupResult* lookup) {
|
||||
receiver->LocalLookup(name, lookup);
|
||||
@ -1359,12 +1359,10 @@ static bool LookupForWrite(JSReceiver* receiver,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lookup->type() == INTERCEPTOR) {
|
||||
JSObject* object = JSObject::cast(receiver);
|
||||
if (object->GetNamedInterceptor()->setter()->IsUndefined()) {
|
||||
object->LocalLookupRealNamedProperty(name, lookup);
|
||||
return StoreICableLookup(lookup);
|
||||
}
|
||||
if (lookup->type() == INTERCEPTOR &&
|
||||
receiver->GetNamedInterceptor()->setter()->IsUndefined()) {
|
||||
receiver->LocalLookupRealNamedProperty(name, lookup);
|
||||
return StoreICableLookup(lookup);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1376,28 +1374,28 @@ MaybeObject* StoreIC::Store(State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name,
|
||||
Handle<Object> value) {
|
||||
// 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->IsJSObject()) {
|
||||
// Handle proxies.
|
||||
if (object->IsJSProxy()) {
|
||||
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.
|
||||
if (strict_mode == kStrictMode && object->IsString() &&
|
||||
name->Equals(isolate()->heap()->length_symbol())) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Handle proxies.
|
||||
if (object->IsJSProxy()) {
|
||||
return JSReceiver::cast(*object)->
|
||||
SetProperty(*name, *value, NONE, strict_mode);
|
||||
}
|
||||
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||
|
||||
// Check if the given name is an array index.
|
||||
|
327
src/objects.cc
327
src/objects.cc
@ -132,27 +132,20 @@ Object* Object::ToBoolean() {
|
||||
|
||||
void Object::Lookup(String* name, LookupResult* result) {
|
||||
Object* holder = NULL;
|
||||
if (IsSmi()) {
|
||||
Context* global_context = Isolate::Current()->context()->global_context();
|
||||
holder = global_context->number_function()->instance_prototype();
|
||||
if (IsJSReceiver()) {
|
||||
holder = this;
|
||||
} 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();
|
||||
if (heap_object->IsString()) {
|
||||
holder = global_context->string_function()->instance_prototype();
|
||||
} else if (heap_object->IsHeapNumber()) {
|
||||
if (IsNumber()) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
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,
|
||||
String* name_raw,
|
||||
Object* handler_raw) {
|
||||
Isolate* isolate = name_raw->GetIsolate();
|
||||
MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw,
|
||||
String* name_raw) {
|
||||
Isolate* isolate = GetIsolate();
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> receiver(receiver_raw);
|
||||
Handle<Object> name(name_raw);
|
||||
Handle<Object> handler(handler_raw);
|
||||
|
||||
// Extract trap function.
|
||||
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get");
|
||||
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
|
||||
Handle<Object> args[] = { receiver, name };
|
||||
Handle<Object> result = CallTrap(
|
||||
"get", isolate->derived_get_trap(), ARRAY_SIZE(args), args);
|
||||
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;
|
||||
}
|
||||
@ -566,14 +546,13 @@ MaybeObject* Object::GetProperty(Object* receiver,
|
||||
}
|
||||
*attributes = result->GetAttributes();
|
||||
Object* value;
|
||||
JSObject* holder = result->holder();
|
||||
switch (result->type()) {
|
||||
case NORMAL:
|
||||
value = holder->GetNormalizedProperty(result);
|
||||
value = result->holder()->GetNormalizedProperty(result);
|
||||
ASSERT(!value->IsTheHole() || result->IsReadOnly());
|
||||
return value->IsTheHole() ? heap->undefined_value() : value;
|
||||
case FIELD:
|
||||
value = holder->FastPropertyAt(result->GetFieldIndex());
|
||||
value = result->holder()->FastPropertyAt(result->GetFieldIndex());
|
||||
ASSERT(!value->IsTheHole() || result->IsReadOnly());
|
||||
return value->IsTheHole() ? heap->undefined_value() : value;
|
||||
case CONSTANT_FUNCTION:
|
||||
@ -582,14 +561,13 @@ MaybeObject* Object::GetProperty(Object* receiver,
|
||||
return GetPropertyWithCallback(receiver,
|
||||
result->GetCallbackObject(),
|
||||
name,
|
||||
holder);
|
||||
case HANDLER: {
|
||||
JSProxy* proxy = JSProxy::cast(this);
|
||||
return GetPropertyWithHandler(receiver, name, proxy->handler());
|
||||
}
|
||||
result->holder());
|
||||
case HANDLER:
|
||||
return result->proxy()->GetPropertyWithHandler(receiver, name);
|
||||
case INTERCEPTOR: {
|
||||
JSObject* recvr = JSObject::cast(receiver);
|
||||
return holder->GetPropertyWithInterceptor(recvr, name, attributes);
|
||||
return result->holder()->GetPropertyWithInterceptor(
|
||||
recvr, name, attributes);
|
||||
}
|
||||
case MAP_TRANSITION:
|
||||
case ELEMENTS_TRANSITION:
|
||||
@ -1900,12 +1878,12 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
|
||||
Object* value) {
|
||||
MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSFunction* setter,
|
||||
Object* value) {
|
||||
Isolate* isolate = GetIsolate();
|
||||
Handle<Object> value_handle(value, isolate);
|
||||
Handle<JSFunction> fun(JSFunction::cast(setter), isolate);
|
||||
Handle<JSObject> self(this, isolate);
|
||||
Handle<JSReceiver> self(this, isolate);
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
Debug* debug = isolate->debug();
|
||||
// Handle stepping into a setter if step into is active.
|
||||
@ -1928,6 +1906,9 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name,
|
||||
for (Object* pt = GetPrototype();
|
||||
pt != heap->null_value();
|
||||
pt = pt->GetPrototype()) {
|
||||
if (pt->IsJSProxy()) {
|
||||
return result->HandlerResult(JSProxy::cast(pt));
|
||||
}
|
||||
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
|
||||
if (result->IsProperty()) {
|
||||
if (result->type() == CALLBACKS && !result->IsReadOnly()) return;
|
||||
@ -2092,6 +2073,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
|
||||
Object* proto = GetPrototype();
|
||||
if (proto->IsNull()) return result->NotFound();
|
||||
ASSERT(proto->IsJSGlobalObject());
|
||||
// A GlobalProxy's prototype should always be a proper JSObject.
|
||||
return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
|
||||
}
|
||||
|
||||
@ -2218,7 +2200,7 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result,
|
||||
PropertyAttributes attributes,
|
||||
StrictModeFlag strict_mode) {
|
||||
if (result->IsFound() && result->type() == HANDLER) {
|
||||
return JSProxy::cast(this)->SetPropertyWithHandler(
|
||||
return result->proxy()->SetPropertyWithHandler(
|
||||
key, value, attributes, strict_mode);
|
||||
} else {
|
||||
return JSObject::cast(this)->SetPropertyForResult(
|
||||
@ -2232,22 +2214,11 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> receiver(this);
|
||||
Handle<Object> name(name_raw);
|
||||
Handle<Object> handler(this->handler());
|
||||
|
||||
// Extract trap function.
|
||||
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has");
|
||||
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
|
||||
Handle<Object> args[] = { name };
|
||||
Handle<Object> result = CallTrap(
|
||||
"has", isolate->derived_has_trap(), ARRAY_SIZE(args), args);
|
||||
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();
|
||||
}
|
||||
@ -2263,23 +2234,10 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
|
||||
Handle<Object> receiver(this);
|
||||
Handle<Object> name(name_raw);
|
||||
Handle<Object> value(value_raw);
|
||||
Handle<Object> handler(this->handler());
|
||||
|
||||
// Extract trap function.
|
||||
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set");
|
||||
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
|
||||
Handle<Object> args[] = { receiver, name, value };
|
||||
CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args);
|
||||
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;
|
||||
}
|
||||
@ -2291,31 +2249,16 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> receiver(this);
|
||||
Handle<Object> name(name_raw);
|
||||
Handle<Object> handler(this->handler());
|
||||
|
||||
// Extract trap function.
|
||||
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
|
||||
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
|
||||
Handle<Object> args[] = { name };
|
||||
Handle<Object> result = CallTrap(
|
||||
"delete", Handle<Object>(), ARRAY_SIZE(args), args);
|
||||
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();
|
||||
if (mode == STRICT_DELETION &&
|
||||
bool_result == isolate->heap()->false_value()) {
|
||||
Handle<Object> args[] = { handler, trap_name };
|
||||
if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) {
|
||||
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
|
||||
Handle<Object> args[] = { Handle<Object>(handler()), trap_name };
|
||||
Handle<Object> error = isolate->factory()->NewTypeError(
|
||||
"handler_failed", HandleVector(args, ARRAY_SIZE(args)));
|
||||
isolate->Throw(*error);
|
||||
@ -2327,36 +2270,20 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
|
||||
|
||||
MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
|
||||
JSReceiver* receiver_raw,
|
||||
String* name_raw,
|
||||
bool* has_exception) {
|
||||
String* name_raw) {
|
||||
Isolate* isolate = GetIsolate();
|
||||
HandleScope scope(isolate);
|
||||
Handle<JSReceiver> receiver(receiver_raw);
|
||||
Handle<Object> name(name_raw);
|
||||
Handle<Object> handler(this->handler());
|
||||
|
||||
// Extract trap function.
|
||||
Handle<String> trap_name =
|
||||
isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
|
||||
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
|
||||
Handle<Object> args[] = { name };
|
||||
Handle<Object> result = CallTrap(
|
||||
"getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
|
||||
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.
|
||||
Object** args[] = { name.location() };
|
||||
Handle<Object> result =
|
||||
Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception);
|
||||
if (has_exception) return NONE;
|
||||
if (result->IsUndefined()) return ABSENT;
|
||||
|
||||
// TODO(rossberg): convert result to PropertyAttributes
|
||||
USE(result);
|
||||
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,
|
||||
String* name,
|
||||
@ -2400,20 +2355,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||
}
|
||||
|
||||
// Check access rights if needed.
|
||||
if (IsAccessCheckNeeded()
|
||||
&& !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
||||
return SetPropertyWithFailedAccessCheck(result,
|
||||
name,
|
||||
value,
|
||||
true,
|
||||
strict_mode);
|
||||
if (IsAccessCheckNeeded()) {
|
||||
if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
||||
return SetPropertyWithFailedAccessCheck(
|
||||
result, name, value, true, strict_mode);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsJSGlobalProxy()) {
|
||||
Object* proto = GetPrototype();
|
||||
if (proto->IsNull()) return value;
|
||||
ASSERT(proto->IsJSGlobalObject());
|
||||
return JSObject::cast(proto)->SetProperty(
|
||||
return JSObject::cast(proto)->SetPropertyForResult(
|
||||
result, name, value, attributes, strict_mode);
|
||||
}
|
||||
|
||||
@ -2422,26 +2375,81 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||
// accessor that wants to handle the property.
|
||||
LookupResult accessor_result;
|
||||
LookupCallbackSetterInPrototypes(name, &accessor_result);
|
||||
if (accessor_result.IsProperty()) {
|
||||
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
|
||||
name,
|
||||
value,
|
||||
accessor_result.holder(),
|
||||
strict_mode);
|
||||
if (accessor_result.IsFound()) {
|
||||
if (accessor_result.type() == CALLBACKS) {
|
||||
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
|
||||
name,
|
||||
value,
|
||||
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()) {
|
||||
// Neither properties nor transitions found.
|
||||
return AddProperty(name, value, attributes, strict_mode);
|
||||
}
|
||||
if (result->IsReadOnly() && result->IsProperty()) {
|
||||
if (strict_mode == kStrictMode) {
|
||||
HandleScope scope(heap->isolate());
|
||||
Handle<String> key(name);
|
||||
Handle<Object> holder(this);
|
||||
Handle<Object> args[2] = { key, holder };
|
||||
Handle<JSObject> self(this);
|
||||
Handle<String> hname(name);
|
||||
Handle<Object> args[] = { hname, self };
|
||||
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 {
|
||||
return value;
|
||||
}
|
||||
@ -2702,10 +2710,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
|
||||
case CALLBACKS:
|
||||
return result->GetAttributes();
|
||||
case HANDLER: {
|
||||
// TODO(rossberg): propagate exceptions properly.
|
||||
bool has_exception = false;
|
||||
return JSProxy::cast(this)->GetPropertyAttributeWithHandler(
|
||||
receiver, name, &has_exception);
|
||||
return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler(
|
||||
receiver, name);
|
||||
}
|
||||
case INTERCEPTOR:
|
||||
return result->holder()->GetPropertyAttributeWithInterceptor(
|
||||
@ -3516,15 +3522,6 @@ AccessorDescriptor* Map::FindAccessor(String* name) {
|
||||
|
||||
|
||||
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());
|
||||
|
||||
Heap* heap = GetHeap();
|
||||
@ -3533,28 +3530,36 @@ void JSObject::LocalLookup(String* name, LookupResult* result) {
|
||||
Object* proto = GetPrototype();
|
||||
if (proto->IsNull()) return result->NotFound();
|
||||
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
|
||||
// that requires access checks.
|
||||
if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
|
||||
if (IsAccessCheckNeeded()) {
|
||||
result->DisallowCaching();
|
||||
}
|
||||
|
||||
JSObject* js_object = JSObject::cast(this);
|
||||
|
||||
// Check __proto__ before interceptor.
|
||||
if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
|
||||
result->ConstantResult(this);
|
||||
result->ConstantResult(js_object);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for lookup interceptor except when bootstrapping.
|
||||
if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) {
|
||||
result->InterceptorResult(this);
|
||||
if (js_object->HasNamedInterceptor() &&
|
||||
!heap->isolate()->bootstrapper()->IsActive()) {
|
||||
result->InterceptorResult(js_object);
|
||||
return;
|
||||
}
|
||||
|
||||
LocalLookupRealNamedProperty(name, result);
|
||||
js_object->LocalLookupRealNamedProperty(name, result);
|
||||
}
|
||||
|
||||
|
||||
@ -3564,7 +3569,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
|
||||
for (Object* current = this;
|
||||
current != heap->null_value();
|
||||
current = JSObject::cast(current)->GetPrototype()) {
|
||||
JSObject::cast(current)->LocalLookup(name, result);
|
||||
JSReceiver::cast(current)->LocalLookup(name, result);
|
||||
if (result->IsProperty()) return;
|
||||
}
|
||||
result->NotFound();
|
||||
@ -6217,10 +6222,10 @@ void Map::CreateBackPointers() {
|
||||
// Verify target.
|
||||
Object* source_prototype = prototype();
|
||||
Object* target_prototype = target->prototype();
|
||||
ASSERT(source_prototype->IsJSObject() ||
|
||||
ASSERT(source_prototype->IsJSReceiver() ||
|
||||
source_prototype->IsMap() ||
|
||||
source_prototype->IsNull());
|
||||
ASSERT(target_prototype->IsJSObject() ||
|
||||
ASSERT(target_prototype->IsJSReceiver() ||
|
||||
target_prototype->IsNull());
|
||||
ASSERT(source_prototype->IsMap() ||
|
||||
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
|
||||
// chain.
|
||||
for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) {
|
||||
if (JSObject::cast(pt) == this) {
|
||||
if (JSReceiver::cast(pt) == this) {
|
||||
// Cycle detected.
|
||||
HandleScope scope(heap->isolate());
|
||||
return heap->isolate()->Throw(
|
||||
@ -7769,8 +7774,8 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
|
||||
// hidden and set the new prototype on that object.
|
||||
Object* current_proto = real_receiver->GetPrototype();
|
||||
while (current_proto->IsJSObject() &&
|
||||
JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
|
||||
real_receiver = JSObject::cast(current_proto);
|
||||
JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) {
|
||||
real_receiver = JSReceiver::cast(current_proto);
|
||||
current_proto = current_proto->GetPrototype();
|
||||
}
|
||||
}
|
||||
|
@ -907,9 +907,6 @@ class Object : public MaybeObject {
|
||||
Object* structure,
|
||||
String* name,
|
||||
Object* holder);
|
||||
MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(Object* receiver,
|
||||
String* name,
|
||||
Object* handler);
|
||||
MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver,
|
||||
JSFunction* getter);
|
||||
|
||||
@ -1448,6 +1445,8 @@ class JSReceiver: public HeapObject {
|
||||
Object* value,
|
||||
PropertyAttributes attributes,
|
||||
StrictModeFlag strict_mode);
|
||||
MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSFunction* setter,
|
||||
Object* value);
|
||||
|
||||
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.
|
||||
MUST_USE_RESULT MaybeObject* PrepareSlowElementsForSort(uint32_t limit);
|
||||
|
||||
// Can cause GC.
|
||||
MUST_USE_RESULT MaybeObject* SetPropertyForResult(LookupResult* result,
|
||||
String* key,
|
||||
Object* value,
|
||||
@ -1571,8 +1571,6 @@ class JSObject: public JSReceiver {
|
||||
Object* value,
|
||||
JSObject* holder,
|
||||
StrictModeFlag strict_mode);
|
||||
MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSFunction* setter,
|
||||
Object* value);
|
||||
MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor(
|
||||
String* name,
|
||||
Object* value,
|
||||
@ -1801,10 +1799,6 @@ class JSObject: public JSReceiver {
|
||||
inline Object* GetInternalField(int index);
|
||||
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.
|
||||
void LocalLookupRealNamedProperty(String* name, LookupResult* result);
|
||||
void LookupRealNamedProperty(String* name, LookupResult* result);
|
||||
@ -6708,6 +6702,10 @@ class JSProxy: public JSReceiver {
|
||||
|
||||
bool HasPropertyWithHandler(String* name);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(
|
||||
Object* receiver,
|
||||
String* name);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* SetPropertyWithHandler(
|
||||
String* name,
|
||||
Object* value,
|
||||
@ -6720,8 +6718,7 @@ class JSProxy: public JSReceiver {
|
||||
|
||||
MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler(
|
||||
JSReceiver* receiver,
|
||||
String* name,
|
||||
bool* has_exception);
|
||||
String* name);
|
||||
|
||||
// Turn this into an (empty) JSObject.
|
||||
void Fix();
|
||||
@ -6729,6 +6726,13 @@ class JSProxy: public JSReceiver {
|
||||
// Initializes the body after the handler slot.
|
||||
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.
|
||||
#ifdef OBJECT_PRINT
|
||||
inline void JSProxyPrint() {
|
||||
|
@ -202,9 +202,9 @@ class LookupResult BASE_EMBEDDED {
|
||||
number_ = entry;
|
||||
}
|
||||
|
||||
void HandlerResult() {
|
||||
void HandlerResult(JSProxy* proxy) {
|
||||
lookup_type_ = HANDLER_TYPE;
|
||||
holder_ = NULL;
|
||||
holder_ = proxy;
|
||||
details_ = PropertyDetails(NONE, HANDLER);
|
||||
cacheable_ = false;
|
||||
}
|
||||
@ -221,7 +221,12 @@ class LookupResult BASE_EMBEDDED {
|
||||
|
||||
JSObject* holder() {
|
||||
ASSERT(IsFound());
|
||||
return holder_;
|
||||
return JSObject::cast(holder_);
|
||||
}
|
||||
|
||||
JSProxy* proxy() {
|
||||
ASSERT(IsFound());
|
||||
return JSProxy::cast(holder_);
|
||||
}
|
||||
|
||||
PropertyType type() {
|
||||
@ -354,7 +359,7 @@ class LookupResult BASE_EMBEDDED {
|
||||
CONSTANT_TYPE
|
||||
} lookup_type_;
|
||||
|
||||
JSObject* holder_;
|
||||
JSReceiver* holder_;
|
||||
int number_;
|
||||
bool cacheable_;
|
||||
PropertyDetails details_;
|
||||
|
@ -4645,7 +4645,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_HasProperty) {
|
||||
if (args[0]->IsJSReceiver()) {
|
||||
JSReceiver* receiver = JSReceiver::cast(args[0]);
|
||||
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();
|
||||
}
|
||||
|
@ -29,7 +29,8 @@
|
||||
|
||||
|
||||
// TODO(rossberg): for-in for proxies not implemented.
|
||||
// TODO(rossberg): inheritance from proxies not implemented.
|
||||
// TODO(rossberg): integer-index properties not implemented properly.
|
||||
|
||||
|
||||
// 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) {
|
||||
TestWithProxies(TestGet2, handler)
|
||||
@ -48,48 +131,52 @@ function TestGet(handler) {
|
||||
function TestGet2(handler, create) {
|
||||
var p = create(handler)
|
||||
assertEquals(42, p.a)
|
||||
assertEquals("a", key)
|
||||
assertEquals(42, p["b"])
|
||||
assertEquals("b", key)
|
||||
|
||||
// TODO(rossberg): inheritance from proxies not yet implemented.
|
||||
// var o = Object.create(p, {x: {value: 88}})
|
||||
// assertEquals(42, o.a)
|
||||
// assertEquals(42, o["b"])
|
||||
// assertEquals(88, o.x)
|
||||
// assertEquals(88, o["x"])
|
||||
var o = Object.create(p, {x: {value: 88}})
|
||||
assertEquals(42, o.a)
|
||||
assertEquals("a", key)
|
||||
assertEquals(42, o["b"])
|
||||
assertEquals("b", key)
|
||||
assertEquals(88, o.x)
|
||||
assertEquals(88, o["x"])
|
||||
}
|
||||
|
||||
TestGet({
|
||||
get: function(r, k) { return 42 }
|
||||
get: function(r, k) { key = k; return 42 }
|
||||
})
|
||||
|
||||
TestGet({
|
||||
get: function(r, k) { return this.get2(r, k) },
|
||||
get2: function(r, k) { return 42 }
|
||||
get2: function(r, k) { key = k; return 42 }
|
||||
})
|
||||
|
||||
TestGet({
|
||||
getPropertyDescriptor: function(k) { return {value: 42} }
|
||||
getPropertyDescriptor: function(k) { key = k; return {value: 42} }
|
||||
})
|
||||
|
||||
TestGet({
|
||||
getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
|
||||
getPropertyDescriptor2: function(k) { return {value: 42} }
|
||||
getPropertyDescriptor2: function(k) { key = k; return {value: 42} }
|
||||
})
|
||||
|
||||
TestGet({
|
||||
getPropertyDescriptor: function(k) {
|
||||
key = k;
|
||||
return {get value() { return 42 }}
|
||||
}
|
||||
})
|
||||
|
||||
TestGet({
|
||||
get: undefined,
|
||||
getPropertyDescriptor: function(k) { return {value: 42} }
|
||||
getPropertyDescriptor: function(k) { key = k; return {value: 42} }
|
||||
})
|
||||
|
||||
TestGet(Proxy.create({
|
||||
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) {
|
||||
var p = create(handler)
|
||||
assertEquals(55, p.f())
|
||||
assertEquals(55, p["f"]())
|
||||
assertEquals(55, p.f("unused", "arguments"))
|
||||
assertEquals(55, p.f.call(p))
|
||||
assertEquals(55, p["f"].call(p))
|
||||
assertEquals(55, p.withargs(45, 5))
|
||||
assertEquals(55, p.withargs.call(p, 11, 22))
|
||||
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({
|
||||
@ -170,6 +273,12 @@ function TestGetThrow2(handler, create) {
|
||||
var p = create(handler)
|
||||
assertThrows(function(){ p.a }, "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({
|
||||
@ -302,7 +411,6 @@ TestSet(Proxy.create({
|
||||
}))
|
||||
|
||||
|
||||
|
||||
function TestSetThrow(handler, create) {
|
||||
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).
|
||||
|
||||
@ -828,7 +1007,7 @@ TestIn({
|
||||
})
|
||||
|
||||
TestIn({
|
||||
get: undefined,
|
||||
has: undefined,
|
||||
getPropertyDescriptor: function(k) {
|
||||
key = k; return k < "z" ? {value: 42} : void 0
|
||||
}
|
||||
@ -874,7 +1053,7 @@ TestInThrow({
|
||||
})
|
||||
|
||||
TestInThrow({
|
||||
get: undefined,
|
||||
has: undefined,
|
||||
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).
|
||||
|
||||
@ -1004,34 +1278,46 @@ TestHasOwnThrow(Proxy.create({
|
||||
// Instanceof (instanceof)
|
||||
|
||||
function TestInstanceof() {
|
||||
var o = {}
|
||||
var o1 = {}
|
||||
var p1 = Proxy.create({})
|
||||
var p2 = Proxy.create({}, o)
|
||||
var p2 = Proxy.create({}, o1)
|
||||
var p3 = Proxy.create({}, p2)
|
||||
var o2 = Object.create(p2)
|
||||
|
||||
var f0 = function() {}
|
||||
f0.prototype = o
|
||||
f0.prototype = o1
|
||||
var f1 = function() {}
|
||||
f1.prototype = p1
|
||||
var f2 = function() {}
|
||||
f2.prototype = p2
|
||||
var f3 = function() {}
|
||||
f3.prototype = o2
|
||||
|
||||
assertTrue(o instanceof Object)
|
||||
assertFalse(o instanceof f0)
|
||||
assertFalse(o instanceof f1)
|
||||
assertFalse(o instanceof f2)
|
||||
assertTrue(o1 instanceof Object)
|
||||
assertFalse(o1 instanceof f0)
|
||||
assertFalse(o1 instanceof f1)
|
||||
assertFalse(o1 instanceof f2)
|
||||
assertFalse(o1 instanceof f3)
|
||||
assertFalse(p1 instanceof Object)
|
||||
assertFalse(p1 instanceof f0)
|
||||
assertFalse(p1 instanceof f1)
|
||||
assertFalse(p1 instanceof f2)
|
||||
assertFalse(p1 instanceof f3)
|
||||
assertTrue(p2 instanceof Object)
|
||||
assertTrue(p2 instanceof f0)
|
||||
assertFalse(p2 instanceof f1)
|
||||
assertFalse(p2 instanceof f2)
|
||||
assertFalse(p2 instanceof f3)
|
||||
assertTrue(p3 instanceof Object)
|
||||
assertTrue(p3 instanceof f0)
|
||||
assertFalse(p3 instanceof f1)
|
||||
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() {})
|
||||
assertTrue(f instanceof Function)
|
||||
@ -1044,43 +1330,57 @@ TestInstanceof()
|
||||
// Prototype (Object.getPrototypeOf, Object.prototype.isPrototypeOf).
|
||||
|
||||
function TestPrototype() {
|
||||
var o = {}
|
||||
var o1 = {}
|
||||
var p1 = Proxy.create({})
|
||||
var p2 = Proxy.create({}, o)
|
||||
var p2 = Proxy.create({}, o1)
|
||||
var p3 = Proxy.create({}, p2)
|
||||
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(p2), o)
|
||||
assertSame(Object.getPrototypeOf(p2), o1)
|
||||
assertSame(Object.getPrototypeOf(p3), p2)
|
||||
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))
|
||||
assertTrue(Object.prototype.isPrototypeOf(p2))
|
||||
assertTrue(Object.prototype.isPrototypeOf(p3))
|
||||
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))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p2))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p3))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p4))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(o, o))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(o, p1))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(o, p2))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(o, p3))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(o, p4))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o2))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(o1, o1))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(o1, p1))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(o1, p2))
|
||||
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, o))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(p1, o1))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(p1, p2))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(p1, p3))
|
||||
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, p2))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(p2, p3))
|
||||
assertFalse(Object.prototype.isPrototypeOf.call(p2, p4))
|
||||
assertTrue(Object.prototype.isPrototypeOf.call(p2, o2))
|
||||
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() {})
|
||||
assertSame(Object.getPrototypeOf(f), Function.prototype)
|
||||
@ -1265,7 +1565,7 @@ TestKeysThrow([], {
|
||||
// Fixing (Object.freeze, Object.seal, Object.preventExtensions,
|
||||
// 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) {
|
||||
var proto = {p: 77}
|
||||
var assertFixing = function(o, s, f, e) {
|
||||
@ -1312,6 +1612,14 @@ function TestFix(names, handler) {
|
||||
Object.keys(p3).sort())
|
||||
assertEquals(proto, Object.getPrototypeOf(p3))
|
||||
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([], {
|
||||
@ -1424,6 +1732,13 @@ function TestToString(handler) {
|
||||
assertEquals("my_proxy", Object.prototype.toLocaleString.call(f))
|
||||
assertEquals("toString", key)
|
||||
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({
|
||||
@ -1450,6 +1765,10 @@ function TestToStringThrow(handler) {
|
||||
var f = Proxy.createFunction(handler, function() {})
|
||||
assertEquals("[object Function]", Object.prototype.toString.call(f))
|
||||
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({
|
||||
@ -1508,6 +1827,11 @@ function TestIsEnumerable2(handler, create) {
|
||||
assertEquals("2", key)
|
||||
assertFalse(Object.prototype.propertyIsEnumerable.call(p, "z"))
|
||||
assertEquals("z", key)
|
||||
|
||||
var o = Object.create(p)
|
||||
key = ""
|
||||
assertFalse(Object.prototype.propertyIsEnumerable.call(o, "a"))
|
||||
assertEquals("", key) // trap not invoked
|
||||
}
|
||||
|
||||
TestIsEnumerable({
|
||||
|
Loading…
Reference in New Issue
Block a user