Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index 7bd771be8640ba83e043ad2ec7d93c3e79c84905..701bc12967687091f6d0fa3b956dc1dedc632bb8 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -977,97 +977,109 @@ static void GetOwnPropertyImplementation(JSObject* obj, |
Object* proto = obj->GetPrototype(); |
if (proto->IsJSObject() && |
- JSObject::cast(proto)->map()->is_hidden_prototype()) |
+ JSObject::cast(proto)->map()->is_hidden_prototype()) { |
GetOwnPropertyImplementation(JSObject::cast(proto), |
name, result); |
+ } |
} |
-static bool CheckAccessException(LookupResult* result, |
+static bool CheckAccessException(Object* callback, |
v8::AccessType access_type) { |
- if (result->type() == CALLBACKS) { |
- Object* callback = result->GetCallbackObject(); |
- if (callback->IsAccessorInfo()) { |
- AccessorInfo* info = AccessorInfo::cast(callback); |
- bool can_access = |
- (access_type == v8::ACCESS_HAS && |
- (info->all_can_read() || info->all_can_write())) || |
- (access_type == v8::ACCESS_GET && info->all_can_read()) || |
- (access_type == v8::ACCESS_SET && info->all_can_write()); |
- return can_access; |
- } |
+ if (callback->IsAccessorInfo()) { |
+ AccessorInfo* info = AccessorInfo::cast(callback); |
+ return |
+ (access_type == v8::ACCESS_HAS && |
+ (info->all_can_read() || info->all_can_write())) || |
+ (access_type == v8::ACCESS_GET && info->all_can_read()) || |
+ (access_type == v8::ACCESS_SET && info->all_can_write()); |
} |
- |
return false; |
} |
-static bool CheckAccess(JSObject* obj, |
- String* name, |
- LookupResult* result, |
- v8::AccessType access_type) { |
- ASSERT(result->IsProperty()); |
- |
- JSObject* holder = result->holder(); |
- JSObject* current = obj; |
- Isolate* isolate = obj->GetIsolate(); |
- while (true) { |
+template<class Key> |
+static bool CheckGenericAccess( |
+ JSObject* receiver, |
+ JSObject* holder, |
+ Key key, |
+ v8::AccessType access_type, |
+ bool (Isolate::*mayAccess)(JSObject*, Key, v8::AccessType)) { |
+ Isolate* isolate = receiver->GetIsolate(); |
+ for (JSObject* current = receiver; |
+ true; |
+ current = JSObject::cast(current->GetPrototype())) { |
if (current->IsAccessCheckNeeded() && |
- !isolate->MayNamedAccess(current, name, access_type)) { |
- // Access check callback denied the access, but some properties |
- // can have a special permissions which override callbacks descision |
- // (currently see v8::AccessControl). |
- break; |
+ !(isolate->*mayAccess)(current, key, access_type)) { |
+ return false; |
} |
+ if (current == holder) break; |
+ } |
+ return true; |
+} |
- if (current == holder) { |
- return true; |
- } |
- current = JSObject::cast(current->GetPrototype()); |
+static bool CheckElementAccess( |
+ JSObject* obj, |
+ uint32_t index, |
+ v8::AccessType access_type) { |
+ // TODO(1095): we should traverse hidden prototype hierachy as well. |
+ if (CheckGenericAccess( |
+ obj, obj, index, access_type, &Isolate::MayIndexedAccess)) { |
+ return true; |
+ } |
+ |
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type); |
+ return false; |
+} |
+ |
+ |
+static bool CheckPropertyAccess( |
+ JSObject* obj, |
+ String* name, |
+ v8::AccessType access_type) { |
+ uint32_t index; |
+ if (name->AsArrayIndex(&index)) { |
+ return CheckElementAccess(obj, index, access_type); |
+ } |
+ |
+ LookupResult lookup(obj->GetIsolate()); |
+ obj->LocalLookup(name, &lookup); |
+ |
+ if (CheckGenericAccess<Object*>( |
+ obj, lookup.holder(), name, access_type, &Isolate::MayNamedAccess)) { |
+ return true; |
} |
+ // Access check callback denied the access, but some properties |
+ // can have a special permissions which override callbacks descision |
+ // (currently see v8::AccessControl). |
// API callbacks can have per callback access exceptions. |
- switch (result->type()) { |
- case CALLBACKS: { |
- if (CheckAccessException(result, access_type)) { |
+ switch (lookup.type()) { |
+ case CALLBACKS: |
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type)) { |
return true; |
} |
break; |
- } |
- case INTERCEPTOR: { |
+ case INTERCEPTOR: |
// If the object has an interceptor, try real named properties. |
// Overwrite the result to fetch the correct property later. |
- holder->LookupRealNamedProperty(name, result); |
- if (result->IsProperty()) { |
- if (CheckAccessException(result, access_type)) { |
+ lookup.holder()->LookupRealNamedProperty(name, &lookup); |
+ if (lookup.IsProperty() && lookup.IsPropertyCallbacks()) { |
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type)) { |
return true; |
} |
} |
break; |
- } |
default: |
break; |
} |
- isolate->ReportFailedAccessCheck(current, access_type); |
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type); |
return false; |
} |
-// TODO(1095): we should traverse hidden prototype hierachy as well. |
-static bool CheckElementAccess(JSObject* obj, |
- uint32_t index, |
- v8::AccessType access_type) { |
- if (obj->IsAccessCheckNeeded() && |
- !obj->GetIsolate()->MayIndexedAccess(obj, index, access_type)) { |
- return false; |
- } |
- |
- return true; |
-} |
- |
- |
// Enumerator used as indices into the array returned from GetOwnProperty |
enum PropertyDescriptorIndices { |
IS_ACCESSOR_INDEX, |
@@ -1085,141 +1097,33 @@ static MaybeObject* GetOwnProperty(Isolate* isolate, |
Handle<JSObject> obj, |
Handle<String> name) { |
Heap* heap = isolate->heap(); |
- Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); |
- Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms); |
- LookupResult result(isolate); |
- // This could be an element. |
- uint32_t index; |
- if (name->AsArrayIndex(&index)) { |
- switch (obj->GetLocalElementKind(index)) { |
- case JSObject::UNDEFINED_ELEMENT: |
- return heap->undefined_value(); |
- |
- case JSObject::STRING_CHARACTER_ELEMENT: { |
- // Special handling of string objects according to ECMAScript 5 |
- // 15.5.5.2. Note that this might be a string object with elements |
- // other than the actual string value. This is covered by the |
- // subsequent cases. |
- Handle<JSValue> js_value = Handle<JSValue>::cast(obj); |
- Handle<String> str(String::cast(js_value->value())); |
- Handle<String> substr = SubString(str, index, index + 1, NOT_TENURED); |
- |
- elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- elms->set(VALUE_INDEX, *substr); |
- elms->set(WRITABLE_INDEX, heap->false_value()); |
- elms->set(ENUMERABLE_INDEX, heap->true_value()); |
- elms->set(CONFIGURABLE_INDEX, heap->false_value()); |
- return *desc; |
- } |
+ PropertyAttributes attrs = obj->GetLocalPropertyAttribute(*name); |
+ if (attrs == ABSENT) return heap->undefined_value(); |
+ AccessorPair* accessors = obj->GetLocalPropertyAccessorPair(*name); |
- case JSObject::INTERCEPTED_ELEMENT: |
- case JSObject::FAST_ELEMENT: { |
- elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- Handle<Object> value = Object::GetElement(obj, index); |
- RETURN_IF_EMPTY_HANDLE(isolate, value); |
- elms->set(VALUE_INDEX, *value); |
- elms->set(WRITABLE_INDEX, heap->true_value()); |
- elms->set(ENUMERABLE_INDEX, heap->true_value()); |
- elms->set(CONFIGURABLE_INDEX, heap->true_value()); |
- return *desc; |
- } |
- |
- case JSObject::DICTIONARY_ELEMENT: { |
- Handle<JSObject> holder = obj; |
- if (obj->IsJSGlobalProxy()) { |
- Object* proto = obj->GetPrototype(); |
- if (proto->IsNull()) return heap->undefined_value(); |
- ASSERT(proto->IsJSGlobalObject()); |
- holder = Handle<JSObject>(JSObject::cast(proto)); |
- } |
- FixedArray* elements = FixedArray::cast(holder->elements()); |
- SeededNumberDictionary* dictionary = NULL; |
- if (elements->map() == heap->non_strict_arguments_elements_map()) { |
- dictionary = SeededNumberDictionary::cast(elements->get(1)); |
- } else { |
- dictionary = SeededNumberDictionary::cast(elements); |
- } |
- int entry = dictionary->FindEntry(index); |
- ASSERT(entry != SeededNumberDictionary::kNotFound); |
- PropertyDetails details = dictionary->DetailsAt(entry); |
- switch (details.type()) { |
- case CALLBACKS: { |
- // This is an accessor property with getter and/or setter. |
- AccessorPair* accessors = |
- AccessorPair::cast(dictionary->ValueAt(entry)); |
- elms->set(IS_ACCESSOR_INDEX, heap->true_value()); |
- if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) { |
- elms->set(GETTER_INDEX, accessors->GetComponent(ACCESSOR_GETTER)); |
- } |
- if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) { |
- elms->set(SETTER_INDEX, accessors->GetComponent(ACCESSOR_SETTER)); |
- } |
- break; |
- } |
- case NORMAL: { |
- // This is a data property. |
- elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- Handle<Object> value = Object::GetElement(obj, index); |
- ASSERT(!value.is_null()); |
- elms->set(VALUE_INDEX, *value); |
- elms->set(WRITABLE_INDEX, heap->ToBoolean(!details.IsReadOnly())); |
- break; |
- } |
- default: |
- UNREACHABLE(); |
- break; |
- } |
- elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!details.IsDontEnum())); |
- elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!details.IsDontDelete())); |
- return *desc; |
- } |
- } |
- } |
- |
- // Use recursive implementation to also traverse hidden prototypes |
- GetOwnPropertyImplementation(*obj, *name, &result); |
- |
- if (!result.IsProperty()) { |
- return heap->undefined_value(); |
- } |
- |
- if (!CheckAccess(*obj, *name, &result, v8::ACCESS_HAS)) { |
- return heap->false_value(); |
- } |
- |
- elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!result.IsDontEnum())); |
- elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete())); |
- |
- bool is_js_accessor = result.IsPropertyCallbacks() && |
- (result.GetCallbackObject()->IsAccessorPair()); |
- |
- if (is_js_accessor) { |
- // __defineGetter__/__defineSetter__ callback. |
- elms->set(IS_ACCESSOR_INDEX, heap->true_value()); |
- |
- AccessorPair* accessors = AccessorPair::cast(result.GetCallbackObject()); |
+ Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); |
+ elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0)); |
+ elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0)); |
+ elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(accessors != NULL)); |
+ |
+ if (accessors == NULL) { |
+ elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0)); |
+ // GetProperty does access check. |
+ elms->set(VALUE_INDEX, *GetProperty(obj, name)); |
+ } else { |
+ // Access checks are performed for both accessors separately. |
+ // When they fail, the respective field is not set in the descriptor. |
Object* getter = accessors->GetComponent(ACCESSOR_GETTER); |
- if (!getter->IsMap() && CheckAccess(*obj, *name, &result, v8::ACCESS_GET)) { |
+ Object* setter = accessors->GetComponent(ACCESSOR_SETTER); |
+ if (!getter->IsMap() && CheckPropertyAccess(*obj, *name, v8::ACCESS_GET)) { |
elms->set(GETTER_INDEX, getter); |
} |
- Object* setter = accessors->GetComponent(ACCESSOR_SETTER); |
- if (!setter->IsMap() && CheckAccess(*obj, *name, &result, v8::ACCESS_SET)) { |
+ if (!setter->IsMap() && CheckPropertyAccess(*obj, *name, v8::ACCESS_SET)) { |
elms->set(SETTER_INDEX, setter); |
} |
- } else { |
- elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- elms->set(WRITABLE_INDEX, heap->ToBoolean(!result.IsReadOnly())); |
- |
- PropertyAttributes attrs; |
- Object* value; |
- // GetProperty will check access and report any violations. |
- { MaybeObject* maybe_value = obj->GetProperty(*obj, &result, *name, &attrs); |
- if (!maybe_value->ToObject(&value)) return maybe_value; |
- } |
- elms->set(VALUE_INDEX, value); |
} |
- return *desc; |
+ return *isolate->factory()->NewJSArrayWithElements(elms); |
} |
@@ -4727,41 +4631,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) { |
CONVERT_ARG_CHECKED(JSObject, object, 0); |
CONVERT_ARG_CHECKED(String, key, 1); |
- uint32_t index; |
- if (key->AsArrayIndex(&index)) { |
- JSObject::LocalElementKind type = object->GetLocalElementKind(index); |
- switch (type) { |
- case JSObject::UNDEFINED_ELEMENT: |
- case JSObject::STRING_CHARACTER_ELEMENT: |
- return isolate->heap()->false_value(); |
- case JSObject::INTERCEPTED_ELEMENT: |
- case JSObject::FAST_ELEMENT: |
- return isolate->heap()->true_value(); |
- case JSObject::DICTIONARY_ELEMENT: { |
- if (object->IsJSGlobalProxy()) { |
- Object* proto = object->GetPrototype(); |
- if (proto->IsNull()) { |
- return isolate->heap()->false_value(); |
- } |
- ASSERT(proto->IsJSGlobalObject()); |
- object = JSObject::cast(proto); |
- } |
- FixedArray* elements = FixedArray::cast(object->elements()); |
- SeededNumberDictionary* dictionary = NULL; |
- if (elements->map() == |
- isolate->heap()->non_strict_arguments_elements_map()) { |
- dictionary = SeededNumberDictionary::cast(elements->get(1)); |
- } else { |
- dictionary = SeededNumberDictionary::cast(elements); |
- } |
- int entry = dictionary->FindEntry(index); |
- ASSERT(entry != SeededNumberDictionary::kNotFound); |
- PropertyDetails details = dictionary->DetailsAt(entry); |
- return isolate->heap()->ToBoolean(!details.IsDontEnum()); |
- } |
- } |
- } |
- |
PropertyAttributes att = object->GetLocalPropertyAttribute(key); |
return isolate->heap()->ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0); |
} |