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
26
src/ic.cc
26
src/ic.cc
@ -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,13 +1359,11 @@ 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 (!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
|
// If the object is undefined or null it's illegal to try to set any
|
||||||
// properties on it; throw a TypeError in that case.
|
// properties on it; throw a TypeError in that case.
|
||||||
if (object->IsUndefined() || object->IsNull()) {
|
if (object->IsUndefined() || object->IsNull()) {
|
||||||
return TypeError("non_object_property_store", object, name);
|
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.
|
||||||
|
315
src/objects.cc
315
src/objects.cc
@ -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()) {
|
||||||
|
if (accessor_result.type() == CALLBACKS) {
|
||||||
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
|
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
accessor_result.holder(),
|
accessor_result.holder(),
|
||||||
strict_mode);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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_;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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({
|
||||||
|
Loading…
Reference in New Issue
Block a user