From 530ded6ea634bccb96652cd3e0cf67725449ed63 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Mon, 23 May 2011 16:21:02 +1000 Subject: [PATCH 03/16] Add a "fallback" mode for named property interceptors 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. --- include/v8.h | 8 ++++++++ src/api.cc | 29 +++++++++++++++++++++++++++++ src/factory.cc | 4 ++++ src/handles.cc | 6 ++++-- src/handles.h | 3 ++- src/objects-inl.h | 16 ++++++++++++++++ src/objects.cc | 22 ++++++++++++++++------ src/objects.h | 18 ++++++++++++++---- src/runtime.cc | 11 ++++++----- 9 files changed, 99 insertions(+), 18 deletions(-) diff --git a/include/v8.h b/include/v8.h index be1ee71..bb31ea0 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2170,6 +2170,7 @@ class V8EXPORT FunctionTemplate : public Template { NamedPropertyQuery query, NamedPropertyDeleter remover, NamedPropertyEnumerator enumerator, + bool is_fallback, Handle data); void SetIndexedInstancePropertyHandler(IndexedPropertyGetter getter, IndexedPropertySetter setter, @@ -2254,6 +2255,13 @@ class V8EXPORT ObjectTemplate : public Template { NamedPropertyEnumerator enumerator = 0, Handle data = Handle()); + void SetFallbackPropertyHandler(NamedPropertyGetter getter, + NamedPropertySetter setter = 0, + NamedPropertyQuery query = 0, + NamedPropertyDeleter deleter = 0, + NamedPropertyEnumerator enumerator = 0, + Handle data = Handle()); + /** * Sets an indexed property handler on the object template. * diff --git a/src/api.cc b/src/api.cc index 381935b..8b0b32a 100644 --- a/src/api.cc +++ b/src/api.cc @@ -981,6 +981,7 @@ void FunctionTemplate::SetNamedInstancePropertyHandler( NamedPropertyQuery query, NamedPropertyDeleter remover, NamedPropertyEnumerator enumerator, + bool is_fallback, Handle data) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); if (IsDeadCheck(isolate, @@ -999,6 +1000,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)); @@ -1143,6 +1145,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 data) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetFallbackPropertyHandler()")) { + return; + } + ENTER_V8(isolate); + i::HandleScope scope(isolate); + EnsureConstructor(this); + i::FunctionTemplateInfo* constructor = + i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); + i::Handle cons(constructor); + Utils::ToLocal(cons)->SetNamedInstancePropertyHandler(getter, + setter, + query, + remover, + enumerator, + true, data); } diff --git a/src/factory.cc b/src/factory.cc index 7dee66f..dcdc645 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1058,6 +1058,10 @@ Handle 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(); diff --git a/src/handles.cc b/src/handles.cc index 326de86..dd3a86c 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -262,9 +262,11 @@ Handle SetProperty(Handle object, Handle key, Handle 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); } diff --git a/src/handles.h b/src/handles.h index 3839f37..4b42506 100644 --- a/src/handles.h +++ b/src/handles.h @@ -188,7 +188,8 @@ Handle SetProperty(Handle object, Handle key, Handle value, PropertyAttributes attributes, - StrictModeFlag strict_mode); + StrictModeFlag strict_mode, + bool skip_fallback_interceptor = false); Handle SetProperty(Handle object, Handle key, diff --git a/src/objects-inl.h b/src/objects-inl.h index cce3edd..6aaca2f 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2521,6 +2521,21 @@ bool Map::is_shared() { } +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() { return reinterpret_cast(READ_FIELD(this, kConstructorOffset)); } @@ -2970,6 +2985,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) diff --git a/src/objects.cc b/src/objects.cc index 79d7240..15e2cdb 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1712,9 +1712,10 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( MaybeObject* JSObject::SetProperty(String* name, Object* value, PropertyAttributes attributes, - StrictModeFlag strict_mode) { + StrictModeFlag strict_mode, + bool skip_fallback_interceptor) { LookupResult result; - LocalLookup(name, &result); + LocalLookup(name, &result, skip_fallback_interceptor); return SetProperty(&result, name, value, attributes, strict_mode); } @@ -3148,7 +3149,8 @@ AccessorDescriptor* Map::FindAccessor(String* name) { } -void JSObject::LocalLookup(String* name, LookupResult* result) { +void JSObject::LocalLookup(String* name, LookupResult* result, + bool skip_fallback_interceptor) { ASSERT(name->IsString()); Heap* heap = GetHeap(); @@ -3174,22 +3176,30 @@ void JSObject::LocalLookup(String* name, LookupResult* result) { } // Check for lookup interceptor except when bootstrapping. - if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { + bool wouldIntercept = HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive(); + if (wouldIntercept && !map()->named_interceptor_is_fallback()) { result->InterceptorResult(this); return; } LocalLookupRealNamedProperty(name, result); + + if (wouldIntercept && !skip_fallback_interceptor && !result->IsProperty() && + map()->named_interceptor_is_fallback()) { + result->InterceptorResult(this); + return; + } } -void JSObject::Lookup(String* name, LookupResult* result) { +void JSObject::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()) { - JSObject::cast(current)->LocalLookup(name, result); + JSObject::cast(current)->LocalLookup(name, result, skip_fallback_interceptor); if (result->IsProperty()) return; } result->NotFound(); diff --git a/src/objects.h b/src/objects.h index 07e1089..a209cd0 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1405,7 +1405,8 @@ class JSObject: 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, @@ -1637,8 +1638,8 @@ class JSObject: 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); // The following lookup functions skip interceptors. void LocalLookupRealNamedProperty(String* name, LookupResult* result); @@ -3714,6 +3715,12 @@ 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) @@ -3904,6 +3911,7 @@ class Map: public HeapObject { static const int kHasExternalArrayElements = 6; // Bit positions for bit field 3 + static const int kNamedInterceptorIsFallback = 0; // Layout of the default cache. It holds alternating name and code objects. static const int kCodeCacheEntrySize = 2; @@ -6276,6 +6284,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); @@ -6295,7 +6304,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); diff --git a/src/runtime.cc b/src/runtime.cc index 7335da8..660352c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1097,7 +1097,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; - global->Lookup(*name, &lookup); + global->Lookup(*name, &lookup, true); if (lookup.IsProperty()) { // Determine if the property is local by comparing the holder // against the global object. The information will be used to @@ -1152,7 +1152,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { } LookupResult lookup; - global->LocalLookup(*name, &lookup); + global->LocalLookup(*name, &lookup, true); PropertyAttributes attributes = is_const_property ? static_cast(base | READ_ONLY) @@ -1196,7 +1196,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { name, value, attributes, - strict_mode)); + strict_mode, + true)); } } @@ -1343,7 +1344,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { JSObject* real_holder = global; LookupResult lookup; while (true) { - real_holder->LocalLookup(*name, &lookup); + real_holder->LocalLookup(*name, &lookup, true); if (lookup.IsProperty()) { // Determine if this is a redeclaration of something read-only. if (lookup.IsReadOnly()) { @@ -1400,7 +1401,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { 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(); } -- 1.7.4.4