[V8] Add a "fallback" mode for named property interceptors
authorAaron Kennedy <aaron.kennedy@nokia.com>
Thu, 27 Oct 2011 10:31:56 +0000 (11:31 +0100)
committerQt by Nokia <qt-info@nokia.com>
Thu, 1 Mar 2012 11:03:38 +0000 (12:03 +0100)
By default interceptors are called before the normal property
resolution on objects.  When an interceptor is installed as a
"fallback" interceptor, it is only called if the object doesn't
already have the property.

In the case of a global object having an fallback interceptor,
the interceptor is not invoked at all for var or function
declarations.

Change-Id: Ibbd0a6b3ad4bd68a8d15ce99520e71c46ad14f84
Reviewed-by: Simon Hausmann <simon.hausmann@nokia.com>
src/3rdparty/v8/include/v8.h
src/3rdparty/v8/src/api.cc
src/3rdparty/v8/src/factory.cc
src/3rdparty/v8/src/handles.cc
src/3rdparty/v8/src/handles.h
src/3rdparty/v8/src/objects-inl.h
src/3rdparty/v8/src/objects.cc
src/3rdparty/v8/src/objects.h
src/3rdparty/v8/src/runtime.cc

index 86ea70f..d2e6c32 100644 (file)
@@ -2305,6 +2305,7 @@ class V8EXPORT FunctionTemplate : public Template {
                                        NamedPropertyQuery query,
                                        NamedPropertyDeleter remover,
                                        NamedPropertyEnumerator enumerator,
+                                       bool is_fallback,
                                        Handle<Value> data);
   void SetIndexedInstancePropertyHandler(IndexedPropertyGetter getter,
                                          IndexedPropertySetter setter,
@@ -2388,6 +2389,12 @@ class V8EXPORT ObjectTemplate : public Template {
                                NamedPropertyDeleter deleter = 0,
                                NamedPropertyEnumerator enumerator = 0,
                                Handle<Value> data = Handle<Value>());
+  void SetFallbackPropertyHandler(NamedPropertyGetter getter,
+                                  NamedPropertySetter setter = 0,
+                                  NamedPropertyQuery query = 0,
+                                  NamedPropertyDeleter deleter = 0,
+                                  NamedPropertyEnumerator enumerator = 0,
+                                  Handle<Value> data = Handle<Value>());
 
   /**
    * Sets an indexed property handler on the object template.
index 4292238..06dc0ec 100644 (file)
@@ -1123,6 +1123,7 @@ void FunctionTemplate::SetNamedInstancePropertyHandler(
       NamedPropertyQuery query,
       NamedPropertyDeleter remover,
       NamedPropertyEnumerator enumerator,
+      bool is_fallback,
       Handle<Value> data) {
   i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
   if (IsDeadCheck(isolate,
@@ -1141,6 +1142,7 @@ void FunctionTemplate::SetNamedInstancePropertyHandler(
   if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query);
   if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover);
   if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator);
+  obj->set_is_fallback(i::Smi::FromInt(is_fallback));
 
   if (data.IsEmpty()) data = v8::Undefined();
   obj->set_data(*Utils::OpenHandle(*data));
@@ -1285,6 +1287,33 @@ void ObjectTemplate::SetNamedPropertyHandler(NamedPropertyGetter getter,
                                                         query,
                                                         remover,
                                                         enumerator,
+                                                        false,
+                                                        data);
+}
+
+
+void ObjectTemplate::SetFallbackPropertyHandler(NamedPropertyGetter getter,
+                                                NamedPropertySetter setter,
+                                                NamedPropertyQuery query,
+                                                NamedPropertyDeleter remover,
+                                                NamedPropertyEnumerator enumerator,
+                                                Handle<Value> data) {
+  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+  if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetNamedPropertyHandler()")) {
+    return;
+  }
+  ENTER_V8(isolate);
+  i::HandleScope scope(isolate);
+  EnsureConstructor(this);
+  i::FunctionTemplateInfo* constructor =
+      i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
+  i::Handle<i::FunctionTemplateInfo> cons(constructor);
+  Utils::ToLocal(cons)->SetNamedInstancePropertyHandler(getter,
+                                                        setter,
+                                                        query,
+                                                        remover,
+                                                        enumerator,
+                                                        true,
                                                         data);
 }
 
index 15f640e..1b95ed1 100644 (file)
@@ -1213,6 +1213,9 @@ Handle<JSFunction> Factory::CreateApiFunction(
   // Set interceptor information in the map.
   if (!obj->named_property_handler()->IsUndefined()) {
     map->set_has_named_interceptor();
+    InterceptorInfo *nph = InterceptorInfo::cast(obj->named_property_handler());
+    bool is_fallback = nph->is_fallback()->IsUndefined()?false:nph->is_fallback()->value();
+    map->set_named_interceptor_is_fallback(is_fallback);
   }
   if (!obj->indexed_property_handler()->IsUndefined()) {
     map->set_has_indexed_interceptor();
index db8ce4d..c2a173d 100644 (file)
@@ -269,9 +269,11 @@ Handle<Object> SetProperty(Handle<JSReceiver> object,
                            Handle<String> key,
                            Handle<Object> value,
                            PropertyAttributes attributes,
-                           StrictModeFlag strict_mode) {
+                           StrictModeFlag strict_mode,
+                           bool skip_fallback_interceptor) {
   CALL_HEAP_FUNCTION(object->GetIsolate(),
-                     object->SetProperty(*key, *value, attributes, strict_mode),
+                     object->SetProperty(*key, *value, attributes, strict_mode, 
+                                         skip_fallback_interceptor),
                      Object);
 }
 
index 06e47fc..c359cb3 100644 (file)
@@ -190,7 +190,8 @@ Handle<Object> SetProperty(Handle<JSReceiver> object,
                            Handle<String> key,
                            Handle<Object> value,
                            PropertyAttributes attributes,
-                           StrictModeFlag strict_mode);
+                           StrictModeFlag strict_mode,
+                           bool skip_fallback_interceptor = false);
 
 Handle<Object> SetProperty(Handle<Object> object,
                            Handle<Object> key,
index b5b3377..753a6aa 100644 (file)
@@ -2754,6 +2754,20 @@ void Map::set_is_shared(bool value) {
 bool Map::is_shared() {
   return ((1 << kIsShared) & bit_field3()) != 0;
 }
+void Map::set_named_interceptor_is_fallback(bool value)
+{
+  if (value) {
+    set_bit_field3(bit_field3() | (1 << kNamedInterceptorIsFallback));
+  } else {
+    set_bit_field3(bit_field3() & ~(1 << kNamedInterceptorIsFallback));
+  }
+}
+
+bool Map::named_interceptor_is_fallback()
+{
+  return ((1 << kNamedInterceptorIsFallback) & bit_field3()) != 0;
+}
 
 
 JSFunction* Map::unchecked_constructor() {
@@ -3255,6 +3269,7 @@ ACCESSORS(InterceptorInfo, query, Object, kQueryOffset)
 ACCESSORS(InterceptorInfo, deleter, Object, kDeleterOffset)
 ACCESSORS(InterceptorInfo, enumerator, Object, kEnumeratorOffset)
 ACCESSORS(InterceptorInfo, data, Object, kDataOffset)
+ACCESSORS(InterceptorInfo, is_fallback, Smi, kFallbackOffset)
 
 ACCESSORS(CallHandlerInfo, callback, Object, kCallbackOffset)
 ACCESSORS(CallHandlerInfo, data, Object, kDataOffset)
index 5641844..acfc357 100644 (file)
@@ -1980,9 +1980,10 @@ MaybeObject* JSObject::SetPropertyWithInterceptor(
 MaybeObject* JSReceiver::SetProperty(String* name,
                                      Object* value,
                                      PropertyAttributes attributes,
-                                     StrictModeFlag strict_mode) {
+                                     StrictModeFlag strict_mode,
+                                     bool skip_fallback_interceptor) {
   LookupResult result(GetIsolate());
-  LocalLookup(name, &result);
+  LocalLookup(name, &result, skip_fallback_interceptor);
   return SetProperty(&result, name, value, attributes, strict_mode);
 }
 
@@ -4201,7 +4202,8 @@ AccessorDescriptor* Map::FindAccessor(String* name) {
 }
 
 
-void JSReceiver::LocalLookup(String* name, LookupResult* result) {
+void JSReceiver::LocalLookup(String* name, LookupResult* result, 
+                             bool skip_fallback_interceptor) {
   ASSERT(name->IsString());
 
   Heap* heap = GetHeap();
@@ -4233,23 +4235,31 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) {
   }
 
   // Check for lookup interceptor except when bootstrapping.
-  if (js_object->HasNamedInterceptor() &&
-      !heap->isolate()->bootstrapper()->IsActive()) {
+  bool wouldIntercept = js_object->HasNamedInterceptor() && 
+                        !heap->isolate()->bootstrapper()->IsActive();
+  if (wouldIntercept && !map()->named_interceptor_is_fallback()) {
     result->InterceptorResult(js_object);
     return;
   }
 
   js_object->LocalLookupRealNamedProperty(name, result);
+
+  if (wouldIntercept && !skip_fallback_interceptor && !result->IsProperty() &&
+      map()->named_interceptor_is_fallback()) {
+    result->InterceptorResult(js_object);
+    return;
+  }
 }
 
 
-void JSReceiver::Lookup(String* name, LookupResult* result) {
+void JSReceiver::Lookup(String* name, LookupResult* result, 
+                        bool skip_fallback_interceptor) {
   // Ecma-262 3rd 8.6.2.4
   Heap* heap = GetHeap();
   for (Object* current = this;
        current != heap->null_value();
        current = JSObject::cast(current)->GetPrototype()) {
-    JSReceiver::cast(current)->LocalLookup(name, result);
+    JSReceiver::cast(current)->LocalLookup(name, result, skip_fallback_interceptor);
     if (result->IsProperty()) return;
   }
   result->NotFound();
index a204401..59ee3be 100644 (file)
@@ -1362,7 +1362,8 @@ class JSReceiver: public HeapObject {
   MUST_USE_RESULT MaybeObject* SetProperty(String* key,
                                            Object* value,
                                            PropertyAttributes attributes,
-                                           StrictModeFlag strict_mode);
+                                           StrictModeFlag strict_mode,
+                                           bool skip_fallback_interceptor = false);
   MUST_USE_RESULT MaybeObject* SetProperty(LookupResult* result,
                                            String* key,
                                            Object* value,
@@ -1414,8 +1415,8 @@ class JSReceiver: public HeapObject {
 
   // Lookup a property.  If found, the result is valid and has
   // detailed information.
-  void LocalLookup(String* name, LookupResult* result);
-  void Lookup(String* name, LookupResult* result);
+  void LocalLookup(String* name, LookupResult* result, bool skip_fallback_interceptor = false);
+  void Lookup(String* name, LookupResult* result, bool skip_fallback_interceptor = false);
 
  protected:
   Smi* GenerateIdentityHash();
@@ -4242,6 +4243,10 @@ class Map: public HeapObject {
   inline void set_is_access_check_needed(bool access_check_needed);
   inline bool is_access_check_needed();
 
+  // Whether the named interceptor is a fallback interceptor or not
+  inline void set_named_interceptor_is_fallback(bool value);
+  inline bool named_interceptor_is_fallback();
+
   // [prototype]: implicit prototype object.
   DECL_ACCESSORS(prototype, Object)
 
@@ -4506,6 +4511,7 @@ class Map: public HeapObject {
 
   // Bit positions for bit field 3
   static const int kIsShared = 0;
+  static const int kNamedInterceptorIsFallback = 1;
 
   // Layout of the default cache. It holds alternating name and code objects.
   static const int kCodeCacheEntrySize = 2;
@@ -7390,6 +7396,7 @@ class InterceptorInfo: public Struct {
   DECL_ACCESSORS(deleter, Object)
   DECL_ACCESSORS(enumerator, Object)
   DECL_ACCESSORS(data, Object)
+  DECL_ACCESSORS(is_fallback, Smi)
 
   static inline InterceptorInfo* cast(Object* obj);
 
@@ -7409,7 +7416,8 @@ class InterceptorInfo: public Struct {
   static const int kDeleterOffset = kQueryOffset + kPointerSize;
   static const int kEnumeratorOffset = kDeleterOffset + kPointerSize;
   static const int kDataOffset = kEnumeratorOffset + kPointerSize;
-  static const int kSize = kDataOffset + kPointerSize;
+  static const int kFallbackOffset = kDataOffset + kPointerSize;
+  static const int kSize = kFallbackOffset + kPointerSize;
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptorInfo);
index f2e23f4..6112f41 100644 (file)
@@ -1340,7 +1340,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
       // Lookup the property in the global object, and don't set the
       // value of the variable if the property is already there.
       LookupResult lookup(isolate);
-      global->Lookup(*name, &lookup);
+      global->Lookup(*name, &lookup, true);
       if (lookup.IsProperty()) {
         // We found an existing property. Unless it was an interceptor
         // that claims the property is absent, skip this declaration.
@@ -1367,7 +1367,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
     }
 
     LookupResult lookup(isolate);
-    global->LocalLookup(*name, &lookup);
+    global->LocalLookup(*name, &lookup, true);
 
     // Compute the property attributes. According to ECMA-262, section
     // 13, page 71, the property must be read-only and
@@ -1408,7 +1408,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
                                          name,
                                          value,
                                          static_cast<PropertyAttributes>(attr),
-                                         strict_mode));
+                                         strict_mode,
+                                         true));
     }
   }
 
@@ -1543,7 +1544,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) {
   while (object->IsJSObject() &&
          JSObject::cast(object)->map()->is_hidden_prototype()) {
     JSObject* raw_holder = JSObject::cast(object);
-    raw_holder->LocalLookup(*name, &lookup);
+    raw_holder->LocalLookup(*name, &lookup, true);
     if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
       HandleScope handle_scope(isolate);
       Handle<JSObject> holder(raw_holder);
@@ -1566,7 +1567,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) {
   // Reload global in case the loop above performed a GC.
   global = isolate->context()->global();
   if (assign) {
-    return global->SetProperty(*name, args[2], attributes, strict_mode);
+    return global->SetProperty(*name, args[2], attributes, strict_mode, true);
   }
   return isolate->heap()->undefined_value();
 }