From: rossberg@chromium.org Date: Mon, 16 May 2011 16:33:58 +0000 (+0000) Subject: Implement get trap for proxies. X-Git-Tag: upstream/4.7.83~19405 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=05fd779dd3cf0e1917c12e57e0c670edfaed130f;p=platform%2Fupstream%2Fv8.git Implement get trap for proxies. TODO: reflective Object methods not handled yet. BUG= TEST= Review URL: http://codereview.chromium.org/7035007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index b14fdb22f..69608e92d 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2006,8 +2006,9 @@ void Genesis::TransferNamedProperties(Handle from, break; case NORMAL: // Do not occur since the from object has fast properties. + case HANDLER: case INTERCEPTOR: - // No element in instance descriptors have interceptor type. + // No element in instance descriptors have proxy or interceptor type. UNREACHABLE(); break; } diff --git a/src/builtins.h b/src/builtins.h index bc0facb44..a84eb311f 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -237,7 +237,8 @@ enum BuiltinExtraArguments { V(STRING_ADD_LEFT, 1) \ V(STRING_ADD_RIGHT, 1) \ V(APPLY_PREPARE, 1) \ - V(APPLY_OVERFLOW, 1) + V(APPLY_OVERFLOW, 1) \ + V(DERIVED_GET_TRAP, 2) class BuiltinFunctionTable; diff --git a/src/execution.cc b/src/execution.cc index db7449231..e84ab9e8e 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -145,11 +145,16 @@ static Handle Invoke(bool construct, } -Handle Execution::Call(Handle func, +Handle Execution::Call(Handle callable, Handle receiver, int argc, Object*** args, bool* pending_exception) { + if (!callable->IsJSFunction()) { + callable = TryGetFunctionDelegate(callable, pending_exception); + if (*pending_exception) return callable; + } + Handle func = Handle::cast(callable); return Invoke(false, func, receiver, argc, args, pending_exception); } diff --git a/src/execution.h b/src/execution.h index 7b6a48c15..bb5f80450 100644 --- a/src/execution.h +++ b/src/execution.h @@ -53,7 +53,7 @@ class Execution : public AllStatic { // *pending_exception tells whether the invoke resulted in // a pending exception. // - static Handle Call(Handle func, + static Handle Call(Handle callable, Handle receiver, int argc, Object*** args, diff --git a/src/handles.cc b/src/handles.cc index 326de8637..3d5ac2061 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -361,6 +361,17 @@ Handle GetProperty(Handle obj, } +Handle GetProperty(Handle obj, + const char* name, + LookupResult* result) { + Isolate* isolate = Isolate::Current(); + Handle str = isolate->factory()->LookupAsciiSymbol(name); + PropertyAttributes attributes; + CALL_HEAP_FUNCTION( + isolate, obj->GetProperty(*obj, result, *str, &attributes), Object); +} + + Handle GetProperty(Handle obj, Handle key) { Isolate* isolate = Isolate::Current(); diff --git a/src/handles.h b/src/handles.h index ed8a824cd..3d930fd74 100644 --- a/src/handles.h +++ b/src/handles.h @@ -242,6 +242,10 @@ Handle SetOwnElement(Handle object, Handle GetProperty(Handle obj, const char* name); +Handle GetProperty(Handle obj, + const char* name, + LookupResult* result); + Handle GetProperty(Handle obj, Handle key); diff --git a/src/ic.cc b/src/ic.cc index e2089ff15..3f5326b00 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -903,7 +903,8 @@ MaybeObject* LoadIC::Load(State state, } PropertyAttributes attr; - if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { + if (lookup.IsProperty() && + (lookup.type() == INTERCEPTOR || lookup.type() == HANDLER)) { // Get the property. Object* result; { MaybeObject* maybe_result = diff --git a/src/messages.js b/src/messages.js index 798335022..de388214c 100644 --- a/src/messages.js +++ b/src/messages.js @@ -192,6 +192,7 @@ function FormatMessage(message) { redefine_disallowed: ["Cannot redefine property: ", "%0"], define_disallowed: ["Cannot define property, object is not extensible: ", "%0"], non_extensible_proto: ["%0", " is not extensible"], + handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"], // RangeError invalid_array_length: ["Invalid array length"], stack_overflow: ["Maximum call stack size exceeded"], diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index 99e981973..3a0353515 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -174,11 +174,12 @@ PropertyType.Normal = 0; PropertyType.Field = 1; PropertyType.ConstantFunction = 2; PropertyType.Callbacks = 3; -PropertyType.Interceptor = 4; -PropertyType.MapTransition = 5; -PropertyType.ExternalArrayTransition = 6; -PropertyType.ConstantTransition = 7; -PropertyType.NullDescriptor = 8; +PropertyType.Handler = 4; +PropertyType.Interceptor = 5; +PropertyType.MapTransition = 6; +PropertyType.ExternalArrayTransition = 7; +PropertyType.ConstantTransition = 8; +PropertyType.NullDescriptor = 9; // Different attributes for a property. diff --git a/src/objects.cc b/src/objects.cc index 1821f5000..8c7695db5 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -149,7 +149,7 @@ void Object::Lookup(String* name, LookupResult* result) { } else if (heap_object->IsBoolean()) { holder = global_context->boolean_function()->instance_prototype(); } else if (heap_object->IsJSProxy()) { - return result->NotFound(); // For now... + return result->HandlerResult(); } } ASSERT(holder != NULL); // Cannot handle null or undefined. @@ -225,6 +225,36 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver, } +MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw, + String* name_raw, + Object* handler_raw) { + Isolate* isolate = name_raw->GetIsolate(); + HandleScope scope; + Handle receiver(receiver_raw); + Handle name(name_raw); + Handle handler(handler_raw); + + // Extract trap function. + LookupResult lookup; + Handle trap(v8::internal::GetProperty(handler, "get", &lookup)); + if (!lookup.IsFound()) { + // Get the derived `get' property. + Object* derived = isolate->global_context()->builtins()->javascript_builtin( + Builtins::DERIVED_GET_TRAP); + trap = Handle(JSFunction::cast(derived)); + } + + // Call trap function. + Object** args[] = { receiver.location(), name.location() }; + bool has_exception; + Handle result = + Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); + if (has_exception) return Failure::Exception(); + + return *result; +} + + MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, JSFunction* getter) { HandleScope scope; @@ -497,26 +527,29 @@ MaybeObject* Object::GetProperty(Object* receiver, // holder will always be the interceptor holder and the search may // only continue with a current object just after the interceptor // holder in the prototype chain. - Object* last = result->IsProperty() ? result->holder() : heap->null_value(); - ASSERT(this != this->GetPrototype()); - for (Object* current = this; true; current = current->GetPrototype()) { - if (current->IsAccessCheckNeeded()) { - // Check if we're allowed to read from the current object. Note - // that even though we may not actually end up loading the named - // property from the current object, we still check that we have - // access to it. - JSObject* checked = JSObject::cast(current); - if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) { - return checked->GetPropertyWithFailedAccessCheck(receiver, - result, - name, - attributes); - } - } - // Stop traversing the chain once we reach the last object in the - // chain; either the holder of the result or null in case of an - // absent property. - if (current == last) break; + // Proxy handlers do not use the proxy's prototype, so we can skip this. + if (!result->IsHandler()) { + Object* last = result->IsProperty() ? result->holder() : heap->null_value(); + ASSERT(this != this->GetPrototype()); + for (Object* current = this; true; current = current->GetPrototype()) { + if (current->IsAccessCheckNeeded()) { + // Check if we're allowed to read from the current object. Note + // that even though we may not actually end up loading the named + // property from the current object, we still check that we have + // access to it. + JSObject* checked = JSObject::cast(current); + if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) { + return checked->GetPropertyWithFailedAccessCheck(receiver, + result, + name, + attributes); + } + } + // Stop traversing the chain once we reach the last object in the + // chain; either the holder of the result or null in case of an + // absent property. + if (current == last) break; + } } if (!result->IsProperty()) { @@ -542,14 +575,22 @@ MaybeObject* Object::GetProperty(Object* receiver, result->GetCallbackObject(), name, holder); + case HANDLER: { + JSProxy* proxy = JSProxy::cast(this); + return GetPropertyWithHandler(receiver, name, proxy->handler()); + } case INTERCEPTOR: { JSObject* recvr = JSObject::cast(receiver); return holder->GetPropertyWithInterceptor(recvr, name, attributes); } - default: - UNREACHABLE(); - return NULL; + case MAP_TRANSITION: + case EXTERNAL_ARRAY_TRANSITION: + case CONSTANT_TRANSITION: + case NULL_DESCRIPTOR: + break; } + UNREACHABLE(); + return NULL; } @@ -6550,6 +6591,7 @@ const char* Code::PropertyType2String(PropertyType type) { case FIELD: return "FIELD"; case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION"; case CALLBACKS: return "CALLBACKS"; + case HANDLER: return "HANDLER"; case INTERCEPTOR: return "INTERCEPTOR"; case MAP_TRANSITION: return "MAP_TRANSITION"; case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION"; diff --git a/src/objects.h b/src/objects.h index cb4a420ba..e68ac531f 100644 --- a/src/objects.h +++ b/src/objects.h @@ -808,6 +808,9 @@ 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); diff --git a/src/property.cc b/src/property.cc index c35fb8347..dd232093b 100644 --- a/src/property.cc +++ b/src/property.cc @@ -74,6 +74,9 @@ void LookupResult::Print(FILE* out) { PrintF(out, " -callback object:\n"); GetCallbackObject()->Print(out); break; + case HANDLER: + PrintF(out, " -type = lookup proxy\n"); + break; case INTERCEPTOR: PrintF(out, " -type = lookup interceptor\n"); break; diff --git a/src/property.h b/src/property.h index 5cd3c6696..ad806776b 100644 --- a/src/property.h +++ b/src/property.h @@ -166,15 +166,6 @@ class CallbacksDescriptor: public Descriptor { class LookupResult BASE_EMBEDDED { public: - // Where did we find the result; - enum { - NOT_FOUND, - DESCRIPTOR_TYPE, - DICTIONARY_TYPE, - INTERCEPTOR_TYPE, - CONSTANT_TYPE - } lookup_type_; - LookupResult() : lookup_type_(NOT_FOUND), cacheable_(true), @@ -211,6 +202,12 @@ class LookupResult BASE_EMBEDDED { number_ = entry; } + void HandlerResult() { + lookup_type_ = HANDLER_TYPE; + holder_ = NULL; + details_ = PropertyDetails(NONE, HANDLER); + } + void InterceptorResult(JSObject* holder) { lookup_type_ = INTERCEPTOR_TYPE; holder_ = holder; @@ -245,6 +242,7 @@ class LookupResult BASE_EMBEDDED { bool IsDontEnum() { return details_.IsDontEnum(); } bool IsDeleted() { return details_.IsDeleted(); } bool IsFound() { return lookup_type_ != NOT_FOUND; } + bool IsHandler() { return lookup_type_ == HANDLER_TYPE; } // Is the result is a property excluding transitions and the null // descriptor? @@ -345,6 +343,16 @@ class LookupResult BASE_EMBEDDED { } private: + // Where did we find the result; + enum { + NOT_FOUND, + DESCRIPTOR_TYPE, + DICTIONARY_TYPE, + HANDLER_TYPE, + INTERCEPTOR_TYPE, + CONSTANT_TYPE + } lookup_type_; + JSObject* holder_; int number_; bool cacheable_; diff --git a/src/runtime.js b/src/runtime.js index 77b97aed8..66e1661b7 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -647,3 +647,20 @@ function DefaultString(x) { // that is cloned when running the code. It is essential that the // boilerplate gets the right prototype. %FunctionSetPrototype($Array, new $Array(0)); + + +/* ------------------------------------------ +- - - H a r m o n y P r o x i e s - - - +--------------------------------------------- +*/ + +function DERIVED_GET_TRAP(receiver, name) { + var desc = this.getPropertyDescriptor(name); + if (IS_UNDEFINED(desc)) { return desc; } + if ('value' in desc) { + return desc.value; + } else { + if (IS_UNDEFINED(desc.get)) { return desc.get; } + return desc.get().call(receiver); // The proposal says so... + } +} diff --git a/src/v8globals.h b/src/v8globals.h index 2a01dfd1b..1d50eb2f0 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -322,11 +322,12 @@ enum PropertyType { FIELD = 1, // only in fast mode CONSTANT_FUNCTION = 2, // only in fast mode CALLBACKS = 3, - INTERCEPTOR = 4, // only in lookup results, not in descriptors. - MAP_TRANSITION = 5, // only in fast mode - EXTERNAL_ARRAY_TRANSITION = 6, - CONSTANT_TRANSITION = 7, // only in fast mode - NULL_DESCRIPTOR = 8, // only in fast mode + HANDLER = 4, // only in lookup results, not in descriptors + INTERCEPTOR = 5, // only in lookup results, not in descriptors + MAP_TRANSITION = 6, // only in fast mode + EXTERNAL_ARRAY_TRANSITION = 7, + CONSTANT_TRANSITION = 8, // only in fast mode + NULL_DESCRIPTOR = 9, // only in fast mode // All properties before MAP_TRANSITION are real. FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, // There are no IC stubs for NULL_DESCRIPTORS. Therefore, diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 852c6d4db..d8c1876a9 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -4264,9 +4264,9 @@ TEST(InterceptorPropertyMirror) { "named_values[%d] instanceof debug.PropertyMirror", i); CHECK(CompileRun(buffer.start())->BooleanValue()); - // 4 is PropertyType.Interceptor + // 5 is PropertyType.Interceptor OS::SNPrintF(buffer, "named_values[%d].propertyType()", i); - CHECK_EQ(4, CompileRun(buffer.start())->Int32Value()); + CHECK_EQ(5, CompileRun(buffer.start())->Int32Value()); OS::SNPrintF(buffer, "named_values[%d].isNative()", i); CHECK(CompileRun(buffer.start())->BooleanValue());