Implement get trap for proxies.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 16 May 2011 16:33:58 +0000 (16:33 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 16 May 2011 16:33:58 +0000 (16:33 +0000)
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

16 files changed:
src/bootstrapper.cc
src/builtins.h
src/execution.cc
src/execution.h
src/handles.cc
src/handles.h
src/ic.cc
src/messages.js
src/mirror-debugger.js
src/objects.cc
src/objects.h
src/property.cc
src/property.h
src/runtime.js
src/v8globals.h
test/cctest/test-debug.cc

index b14fdb2..69608e9 100644 (file)
@@ -2006,8 +2006,9 @@ void Genesis::TransferNamedProperties(Handle<JSObject> 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;
       }
index bc0facb..a84eb31 100644 (file)
@@ -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;
index db74492..e84ab9e 100644 (file)
@@ -145,11 +145,16 @@ static Handle<Object> Invoke(bool construct,
 }
 
 
-Handle<Object> Execution::Call(Handle<JSFunction> func,
+Handle<Object> Execution::Call(Handle<Object> callable,
                                Handle<Object> receiver,
                                int argc,
                                Object*** args,
                                bool* pending_exception) {
+  if (!callable->IsJSFunction()) {
+    callable = TryGetFunctionDelegate(callable, pending_exception);
+    if (*pending_exception) return callable;
+  }
+  Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
   return Invoke(false, func, receiver, argc, args, pending_exception);
 }
 
index 7b6a48c..bb5f804 100644 (file)
@@ -53,7 +53,7 @@ class Execution : public AllStatic {
   // *pending_exception tells whether the invoke resulted in
   // a pending exception.
   //
-  static Handle<Object> Call(Handle<JSFunction> func,
+  static Handle<Object> Call(Handle<Object> callable,
                              Handle<Object> receiver,
                              int argc,
                              Object*** args,
index 326de86..3d5ac20 100644 (file)
@@ -362,6 +362,17 @@ Handle<Object> GetProperty(Handle<JSObject> obj,
 
 
 Handle<Object> GetProperty(Handle<Object> obj,
+                           const char* name,
+                           LookupResult* result) {
+  Isolate* isolate = Isolate::Current();
+  Handle<String> str = isolate->factory()->LookupAsciiSymbol(name);
+  PropertyAttributes attributes;
+  CALL_HEAP_FUNCTION(
+      isolate, obj->GetProperty(*obj, result, *str, &attributes), Object);
+}
+
+
+Handle<Object> GetProperty(Handle<Object> obj,
                            Handle<Object> key) {
   Isolate* isolate = Isolate::Current();
   CALL_HEAP_FUNCTION(isolate,
index ed8a824..3d930fd 100644 (file)
@@ -243,6 +243,10 @@ Handle<Object> GetProperty(Handle<JSObject> obj,
                            const char* name);
 
 Handle<Object> GetProperty(Handle<Object> obj,
+                           const char* name,
+                           LookupResult* result);
+
+Handle<Object> GetProperty(Handle<Object> obj,
                            Handle<Object> key);
 
 Handle<Object> GetProperty(Handle<JSObject> obj,
index e2089ff..3f5326b 100644 (file)
--- 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 =
index 7983350..de38821 100644 (file)
@@ -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"],
index 99e9819..3a03535 100644 (file)
@@ -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.
index 1821f50..8c7695d 100644 (file)
@@ -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<Object> receiver(receiver_raw);
+  Handle<Object> name(name_raw);
+  Handle<Object> handler(handler_raw);
+
+  // Extract trap function.
+  LookupResult lookup;
+  Handle<Object> 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>(JSFunction::cast(derived));
+  }
+
+  // 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;
+}
+
+
 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";
index cb4a420..e68ac53 100644 (file)
@@ -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);
 
index c35fb83..dd23209 100644 (file)
@@ -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;
index 5cd3c66..ad80677 100644 (file)
@@ -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_;
index 77b97ae..66e1661 100644 (file)
@@ -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...
+  }
+}
index 2a01dfd..1d50eb2 100644 (file)
@@ -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,
index 852c6d4..d8c1876 100644 (file)
@@ -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());