From 168742b81a213fbfa960344c60cb36f1232aa750 Mon Sep 17 00:00:00 2001 From: "jochen@chromium.org" Date: Mon, 14 Jul 2014 07:19:49 +0000 Subject: [PATCH] Introduce a PrototypeIterator class and use it for prototype access The new pattern is that we first get the map of the root of the prototype chain using Object::GetMapRoot() and then walk up the prototype chain using Map::prototype(). BUG=??? R=verwaest@chromium.org LOG=n Review URL: https://codereview.chromium.org/376233002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22365 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- BUILD.gn | 1 + src/accessors.cc | 7 ++- src/api.cc | 5 +- src/builtins.cc | 12 +++-- src/ic-inl.h | 9 ++-- src/ic.cc | 4 +- src/isolate.cc | 9 ++-- src/objects.cc | 138 ++++++++++++++++++++++++--------------------------- src/objects.h | 9 ++-- src/prototype.h | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ src/runtime.cc | 78 ++++++++++++++++------------- src/string-stream.cc | 11 ++-- tools/gyp/v8.gyp | 1 + 13 files changed, 279 insertions(+), 135 deletions(-) create mode 100644 src/prototype.h diff --git a/BUILD.gn b/BUILD.gn index 43e17c8..f8dd39c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -686,6 +686,7 @@ source_set("v8_base") { "src/property-details.h", "src/property.cc", "src/property.h", + "src/prototype.h", "src/regexp-macro-assembler-irregexp-inl.h", "src/regexp-macro-assembler-irregexp.cc", "src/regexp-macro-assembler-irregexp.h", diff --git a/src/accessors.cc b/src/accessors.cc index cfa2049..13c2803 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -15,6 +15,7 @@ #include "src/isolate.h" #include "src/list-inl.h" #include "src/property-details.h" +#include "src/prototype.h" namespace v8 { namespace internal { @@ -67,8 +68,10 @@ Handle Accessors::CloneAccessor( template static C* FindInstanceOf(Isolate* isolate, Object* obj) { - for (Object* cur = obj; !cur->IsNull(); cur = cur->GetPrototype(isolate)) { - if (Is(cur)) return C::cast(cur); + for (PrototypeIterator iter(isolate, obj, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (Is(iter.GetCurrent())) return C::cast(iter.GetCurrent()); } return NULL; } diff --git a/src/api.cc b/src/api.cc index 106c1fd..2c50d75 100644 --- a/src/api.cc +++ b/src/api.cc @@ -36,6 +36,7 @@ #include "src/profile-generator-inl.h" #include "src/property.h" #include "src/property-details.h" +#include "src/prototype.h" #include "src/runtime.h" #include "src/runtime-profiler.h" #include "src/scanner-character-streams.h" @@ -3191,8 +3192,8 @@ Local v8::Object::GetPrototype() { ON_BAILOUT(isolate, "v8::Object::GetPrototype()", return Local()); ENTER_V8(isolate); i::Handle self = Utils::OpenHandle(this); - i::Handle result(self->GetPrototype(isolate), isolate); - return Utils::ToLocal(result); + i::PrototypeIterator iter(isolate, self); + return Utils::ToLocal(i::PrototypeIterator::GetCurrent(iter)); } diff --git a/src/builtins.cc b/src/builtins.cc index eb5513e..925e8a9 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -14,6 +14,7 @@ #include "src/heap-profiler.h" #include "src/ic-inl.h" #include "src/mark-compact.h" +#include "src/prototype.h" #include "src/stub-cache.h" #include "src/vm-state-inl.h" @@ -1091,11 +1092,12 @@ BUILTIN(GeneratorPoisonPill) { static inline Object* FindHidden(Heap* heap, Object* object, FunctionTemplateInfo* type) { - if (type->IsTemplateFor(object)) return object; - Object* proto = object->GetPrototype(heap->isolate()); - if (proto->IsJSObject() && - JSObject::cast(proto)->map()->is_hidden_prototype()) { - return FindHidden(heap, proto, type); + for (PrototypeIterator iter(heap->isolate(), object, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN); iter.Advance()) { + if (type->IsTemplateFor(iter.GetCurrent())) { + return iter.GetCurrent(); + } } return heap->null_value(); } diff --git a/src/ic-inl.h b/src/ic-inl.h index a26d31b..78d42ea 100644 --- a/src/ic-inl.h +++ b/src/ic-inl.h @@ -10,6 +10,7 @@ #include "src/compiler.h" #include "src/debug.h" #include "src/macro-assembler.h" +#include "src/prototype.h" namespace v8 { namespace internal { @@ -132,9 +133,11 @@ HeapObject* IC::GetCodeCacheHolder(Isolate* isolate, Object* object, InlineCacheHolderFlag holder) { if (object->IsSmi()) holder = PROTOTYPE_MAP; - Object* map_owner = holder == OWN_MAP - ? object : object->GetPrototype(isolate); - return HeapObject::cast(map_owner); + PrototypeIterator iter(isolate, object, + holder == OWN_MAP + ? PrototypeIterator::START_AT_RECEIVER + : PrototypeIterator::START_AT_PROTOTYPE); + return HeapObject::cast(iter.GetCurrent()); } diff --git a/src/ic.cc b/src/ic.cc index a9c53ba..e2123d0 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -11,6 +11,7 @@ #include "src/conversions.h" #include "src/execution.h" #include "src/ic-inl.h" +#include "src/prototype.h" #include "src/runtime.h" #include "src/stub-cache.h" @@ -248,7 +249,8 @@ bool IC::TryRemoveInvalidPrototypeDependentStub(Handle receiver, break; case PROTOTYPE_MAP: // IC::GetCodeCacheHolder is not applicable. - if (receiver->GetPrototype(isolate())->IsNull()) return false; + PrototypeIterator iter(isolate(), receiver); + if (iter.IsAtEnd()) return false; break; } diff --git a/src/isolate.cc b/src/isolate.cc index 0dbce1f..5e7fc71 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -21,6 +21,7 @@ #include "src/lithium-allocator.h" #include "src/log.h" #include "src/messages.h" +#include "src/prototype.h" #include "src/regexp-stack.h" #include "src/runtime-profiler.h" #include "src/sampler.h" @@ -982,10 +983,10 @@ bool Isolate::IsErrorObject(Handle obj) { js_builtins_object(), error_key).ToHandleChecked(); DisallowHeapAllocation no_gc; - for (Object* prototype = *obj; !prototype->IsNull(); - prototype = prototype->GetPrototype(this)) { - if (!prototype->IsJSObject()) return false; - if (JSObject::cast(prototype)->map()->constructor() == + for (PrototypeIterator iter(this, *obj, PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (iter.GetCurrent()->IsJSProxy()) return false; + if (JSObject::cast(iter.GetCurrent())->map()->constructor() == *error_constructor) { return true; } diff --git a/src/objects.cc b/src/objects.cc index ad066af..2763658 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -28,6 +28,7 @@ #include "src/mark-compact.h" #include "src/objects-inl.h" #include "src/objects-visiting-inl.h" +#include "src/prototype.h" #include "src/safepoint-table.h" #include "src/string-search.h" #include "src/string-stream.h" @@ -801,30 +802,30 @@ MaybeHandle Object::GetElementWithReceiver(Isolate* isolate, Handle object, Handle receiver, uint32_t index) { - Handle holder; + if (object->IsUndefined()) { + // TODO(verwaest): Why is this check here? + UNREACHABLE(); + return isolate->factory()->undefined_value(); + } // Iterate up the prototype chain until an element is found or the null // prototype is encountered. - for (holder = object; - !holder->IsNull(); - holder = Handle(holder->GetPrototype(isolate), isolate)) { - if (!holder->IsJSObject()) { - if (holder->IsJSProxy()) { - return JSProxy::GetElementWithHandler( - Handle::cast(holder), receiver, index); - } else if (holder->IsUndefined()) { - // Undefined has no indexed properties. - return isolate->factory()->undefined_value(); - } else { - holder = Handle(holder->GetPrototype(isolate), isolate); - ASSERT(holder->IsJSObject()); - } + for (PrototypeIterator iter(isolate, object, + object->IsJSProxy() || object->IsJSObject() + ? PrototypeIterator::START_AT_RECEIVER + : PrototypeIterator::START_AT_PROTOTYPE); + !iter.IsAtEnd(); iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + return JSProxy::GetElementWithHandler( + Handle::cast(PrototypeIterator::GetCurrent(iter)), receiver, + index); } // Inline the case for JSObjects. Doing so significantly improves the // performance of fetching elements where checking the prototype chain is // necessary. - Handle js_object = Handle::cast(holder); + Handle js_object = + Handle::cast(PrototypeIterator::GetCurrent(iter)); // Check access rights if needed. if (js_object->IsAccessCheckNeeded()) { @@ -853,11 +854,11 @@ MaybeHandle Object::GetElementWithReceiver(Isolate* isolate, } -Object* Object::GetPrototype(Isolate* isolate) { +Map* Object::GetRootMap(Isolate* isolate) { DisallowHeapAllocation no_alloc; if (IsSmi()) { Context* context = isolate->context()->native_context(); - return context->number_function()->instance_prototype(); + return context->number_function()->initial_map(); } HeapObject* heap_object = HeapObject::cast(this); @@ -865,30 +866,23 @@ Object* Object::GetPrototype(Isolate* isolate) { // The object is either a number, a string, a boolean, // a real JS object, or a Harmony proxy. if (heap_object->IsJSReceiver()) { - return heap_object->map()->prototype(); + return heap_object->map(); } Context* context = isolate->context()->native_context(); if (heap_object->IsHeapNumber()) { - return context->number_function()->instance_prototype(); + return context->number_function()->initial_map(); } if (heap_object->IsString()) { - return context->string_function()->instance_prototype(); + return context->string_function()->initial_map(); } if (heap_object->IsSymbol()) { - return context->symbol_function()->instance_prototype(); + return context->symbol_function()->initial_map(); } if (heap_object->IsBoolean()) { - return context->boolean_function()->instance_prototype(); - } else { - return isolate->heap()->null_value(); + return context->boolean_function()->initial_map(); } -} - - -Handle Object::GetPrototype(Isolate* isolate, - Handle object) { - return handle(object->GetPrototype(isolate), isolate); + return isolate->heap()->null_value()->map(); } @@ -3033,20 +3027,16 @@ MaybeHandle JSObject::SetElementWithCallbackSetterInPrototypes( bool* found, StrictMode strict_mode) { Isolate *isolate = object->GetIsolate(); - for (Handle proto = handle(object->GetPrototype(), isolate); - !proto->IsNull(); - proto = handle(proto->GetPrototype(isolate), isolate)) { - if (proto->IsJSProxy()) { + for (PrototypeIterator iter(isolate, object); !iter.IsAtEnd(); + iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { return JSProxy::SetPropertyViaPrototypesWithHandler( - Handle::cast(proto), - object, + Handle::cast(PrototypeIterator::GetCurrent(iter)), object, isolate->factory()->Uint32ToString(index), // name - value, - NONE, - strict_mode, - found); + value, NONE, strict_mode, found); } - Handle js_proto = Handle::cast(proto); + Handle js_proto = + Handle::cast(PrototypeIterator::GetCurrent(iter)); if (!js_proto->HasDictionaryElements()) { continue; } @@ -3524,14 +3514,11 @@ void JSObject::LookupRealNamedPropertyInPrototypes(Handle name, LookupResult* result) { DisallowHeapAllocation no_gc; Isolate* isolate = GetIsolate(); - Heap* heap = isolate->heap(); - for (Object* pt = GetPrototype(); - pt != heap->null_value(); - pt = pt->GetPrototype(isolate)) { - if (pt->IsJSProxy()) { - return result->HandlerResult(JSProxy::cast(pt)); + for (PrototypeIterator iter(isolate, this); !iter.IsAtEnd(); iter.Advance()) { + if (iter.GetCurrent()->IsJSProxy()) { + return result->HandlerResult(JSProxy::cast(iter.GetCurrent())); } - JSObject::cast(pt)->LookupOwnRealNamedProperty(name, result); + JSObject::cast(iter.GetCurrent())->LookupOwnRealNamedProperty(name, result); ASSERT(!(result->IsFound() && result->type() == INTERCEPTOR)); if (result->IsFound()) return; } @@ -6374,11 +6361,12 @@ MaybeHandle JSReceiver::GetKeys(Handle object, JSFunction::cast(isolate->sloppy_arguments_map()->constructor())); // Only collect keys if access is permitted. - for (Handle p = object; - *p != isolate->heap()->null_value(); - p = Handle(p->GetPrototype(isolate), isolate)) { - if (p->IsJSProxy()) { - Handle proxy(JSProxy::cast(*p), isolate); + for (PrototypeIterator iter(isolate, object, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + Handle proxy(JSProxy::cast(*PrototypeIterator::GetCurrent(iter)), + isolate); Handle args[] = { proxy }; Handle names; ASSIGN_RETURN_ON_EXCEPTION( @@ -6397,7 +6385,8 @@ MaybeHandle JSReceiver::GetKeys(Handle object, break; } - Handle current(JSObject::cast(*p), isolate); + Handle current = + Handle::cast(PrototypeIterator::GetCurrent(iter)); // Check access rights if required. if (current->IsAccessCheckNeeded() && @@ -6615,22 +6604,18 @@ void JSObject::DefinePropertyAccessor(Handle object, bool Map::DictionaryElementsInPrototypeChainOnly() { - Heap* heap = GetHeap(); - if (IsDictionaryElementsKind(elements_kind())) { return false; } - for (Object* prototype = this->prototype(); - prototype != heap->null_value(); - prototype = prototype->GetPrototype(GetIsolate())) { - if (prototype->IsJSProxy()) { + for (PrototypeIterator iter(this); !iter.IsAtEnd(); iter.Advance()) { + if (iter.GetCurrent()->IsJSProxy()) { // Be conservative, don't walk into proxies. return true; } if (IsDictionaryElementsKind( - JSObject::cast(prototype)->map()->elements_kind())) { + JSObject::cast(iter.GetCurrent())->map()->elements_kind())) { return true; } } @@ -10131,9 +10116,14 @@ void JSFunction::EnsureHasInitialMap(Handle function) { Handle prototype; if (function->has_instance_prototype()) { prototype = handle(function->instance_prototype(), isolate); - for (Handle p = prototype; !p->IsNull() && !p->IsJSProxy(); - p = Object::GetPrototype(isolate, p)) { - JSObject::OptimizeAsPrototype(Handle::cast(p)); + for (PrototypeIterator iter(isolate, prototype, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + break; + } + JSObject::OptimizeAsPrototype( + Handle::cast(PrototypeIterator::GetCurrent(iter))); } } else { prototype = isolate->factory()->NewFunctionPrototype(function); @@ -12203,10 +12193,10 @@ MaybeHandle JSObject::SetPrototype(Handle object, // prototype cycles are prevented. // It is sufficient to validate that the receiver is not in the new prototype // chain. - for (Object* pt = *value; - pt != heap->null_value(); - pt = pt->GetPrototype(isolate)) { - if (JSReceiver::cast(pt) == *object) { + for (PrototypeIterator iter(isolate, *value, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (JSReceiver::cast(iter.GetCurrent()) == *object) { // Cycle detected. Handle error = isolate->factory()->NewError( "cyclic_proto", HandleVector(NULL, 0)); @@ -12221,11 +12211,11 @@ MaybeHandle JSObject::SetPrototype(Handle object, if (skip_hidden_prototypes) { // Find the first object in the chain whose prototype object is not // hidden and set the new prototype on that object. - Object* current_proto = real_receiver->GetPrototype(); - while (current_proto->IsJSObject() && - JSObject::cast(current_proto)->map()->is_hidden_prototype()) { - real_receiver = handle(JSObject::cast(current_proto), isolate); - current_proto = current_proto->GetPrototype(isolate); + PrototypeIterator iter(isolate, real_receiver); + while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { + real_receiver = + Handle::cast(PrototypeIterator::GetCurrent(iter)); + iter.Advance(); } } diff --git a/src/objects.h b/src/objects.h index af8cc28..d742b23 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1514,10 +1514,6 @@ class Object { Handle receiver, uint32_t index); - // Return the object's prototype (might be Heap::null_value()). - Object* GetPrototype(Isolate* isolate); - static Handle GetPrototype(Isolate* isolate, Handle object); - // Returns the permanent hash code associated with this object. May return // undefined if not yet created. Object* GetHash(); @@ -1574,6 +1570,11 @@ class Object { #endif private: + friend class PrototypeIterator; + + // Return the map of the root of object's prototype chain. + Map* GetRootMap(Isolate* isolate); + DISALLOW_IMPLICIT_CONSTRUCTORS(Object); }; diff --git a/src/prototype.h b/src/prototype.h new file mode 100644 index 0000000..aebdcbc --- /dev/null +++ b/src/prototype.h @@ -0,0 +1,130 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PROTOTYPE_H_ +#define V8_PROTOTYPE_H_ + +#include "src/isolate.h" +#include "src/objects.h" + +namespace v8 { +namespace internal { + +/** + * A class to uniformly access the prototype of any Object and walk its + * prototype chain. + * + * The PrototypeIterator can either start at the prototype (default), or + * include the receiver itself. If a PrototypeIterator is constructed for a + * Map, it will always start at the prototype. + * + * The PrototypeIterator can either run to the null_value(), the first + * non-hidden prototype, or a given object. + */ +class PrototypeIterator { + public: + enum WhereToStart { START_AT_RECEIVER, START_AT_PROTOTYPE }; + + enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN }; + + PrototypeIterator(Isolate* isolate, Handle receiver, + WhereToStart where_to_start = START_AT_PROTOTYPE) + : did_jump_to_prototype_chain_(false), + object_(NULL), + handle_(receiver), + isolate_(isolate) { + CHECK(!handle_.is_null()); + if (where_to_start == START_AT_PROTOTYPE) { + Advance(); + } + } + PrototypeIterator(Isolate* isolate, Object* receiver, + WhereToStart where_to_start = START_AT_PROTOTYPE) + : did_jump_to_prototype_chain_(false), + object_(receiver), + isolate_(isolate) { + if (where_to_start == START_AT_PROTOTYPE) { + Advance(); + } + } + explicit PrototypeIterator(Map* receiver_map) + : did_jump_to_prototype_chain_(true), + object_(receiver_map->prototype()), + isolate_(receiver_map->GetIsolate()) {} + ~PrototypeIterator() {} + + Object* GetCurrent() const { + ASSERT(handle_.is_null()); + return object_; + } + static Handle GetCurrent(const PrototypeIterator& iterator) { + ASSERT(!iterator.handle_.is_null()); + return iterator.handle_; + } + void Advance() { + if (handle_.is_null() && object_->IsJSProxy()) { + did_jump_to_prototype_chain_ = true; + object_ = isolate_->heap()->null_value(); + return; + } else if (!handle_.is_null() && handle_->IsJSProxy()) { + did_jump_to_prototype_chain_ = true; + handle_ = handle(isolate_->heap()->null_value(), isolate_); + return; + } + AdvanceIgnoringProxies(); + } + void AdvanceIgnoringProxies() { + if (!did_jump_to_prototype_chain_) { + did_jump_to_prototype_chain_ = true; + if (handle_.is_null()) { + object_ = object_->GetRootMap(isolate_)->prototype(); + } else { + handle_ = handle(handle_->GetRootMap(isolate_)->prototype(), isolate_); + } + } else { + if (handle_.is_null()) { + object_ = HeapObject::cast(object_)->map()->prototype(); + } else { + handle_ = + handle(HeapObject::cast(*handle_)->map()->prototype(), isolate_); + } + } + } + bool IsAtEnd(WhereToEnd where_to_end = END_AT_NULL) const { + if (handle_.is_null()) { + return object_->IsNull() || + (did_jump_to_prototype_chain_ && + where_to_end == END_AT_NON_HIDDEN && + !HeapObject::cast(object_)->map()->is_hidden_prototype()); + } else { + return handle_->IsNull() || + (did_jump_to_prototype_chain_ && + where_to_end == END_AT_NON_HIDDEN && + !Handle::cast(handle_)->map()->is_hidden_prototype()); + } + } + bool IsAtEnd(Object* final_object) { + ASSERT(handle_.is_null()); + return object_->IsNull() || object_ == final_object; + } + bool IsAtEnd(Handle final_object) { + ASSERT(!handle_.is_null()); + return handle_->IsNull() || *handle_ == *final_object; + } + + private: + bool did_jump_to_prototype_chain_; + Object* object_; + Handle handle_; + Isolate* isolate_; + + DISALLOW_COPY_AND_ASSIGN(PrototypeIterator); +}; + + +} // namespace internal + +} // namespace v8 + +#endif // V8_PROTOTYPE_H_ diff --git a/src/runtime.cc b/src/runtime.cc index f3e8e84..17358e4 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -34,6 +34,7 @@ #include "src/liveedit.h" #include "src/misc-intrinsics.h" #include "src/parser.h" +#include "src/prototype.h" #include "src/runtime.h" #include "src/runtime-profiler.h" #include "src/scopeinfo.h" @@ -1808,31 +1809,37 @@ RUNTIME_FUNCTION(Runtime_GetPrototype) { CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0); // We don't expect access checks to be needed on JSProxy objects. ASSERT(!obj->IsAccessCheckNeeded() || obj->IsJSObject()); + PrototypeIterator iter(isolate, obj, PrototypeIterator::START_AT_RECEIVER); do { - if (obj->IsAccessCheckNeeded() && - !isolate->MayNamedAccess(Handle::cast(obj), - isolate->factory()->proto_string(), - v8::ACCESS_GET)) { - isolate->ReportFailedAccessCheck(Handle::cast(obj), - v8::ACCESS_GET); + if (PrototypeIterator::GetCurrent(iter)->IsAccessCheckNeeded() && + !isolate->MayNamedAccess( + Handle::cast(PrototypeIterator::GetCurrent(iter)), + isolate->factory()->proto_string(), v8::ACCESS_GET)) { + isolate->ReportFailedAccessCheck( + Handle::cast(PrototypeIterator::GetCurrent(iter)), + v8::ACCESS_GET); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); return isolate->heap()->undefined_value(); } - obj = Object::GetPrototype(isolate, obj); - } while (obj->IsJSObject() && - JSObject::cast(*obj)->map()->is_hidden_prototype()); - return *obj; + iter.AdvanceIgnoringProxies(); + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + return *PrototypeIterator::GetCurrent(iter); + } + } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)); + return *PrototypeIterator::GetCurrent(iter); } static inline Handle GetPrototypeSkipHiddenPrototypes( Isolate* isolate, Handle receiver) { - Handle current = Object::GetPrototype(isolate, receiver); - while (current->IsJSObject() && - JSObject::cast(*current)->map()->is_hidden_prototype()) { - current = Object::GetPrototype(isolate, current); + PrototypeIterator iter(isolate, receiver); + while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + return PrototypeIterator::GetCurrent(iter); + } + iter.Advance(); } - return current; + return PrototypeIterator::GetCurrent(iter); } @@ -1877,11 +1884,11 @@ RUNTIME_FUNCTION(Runtime_IsInPrototypeChain) { // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8). CONVERT_ARG_HANDLE_CHECKED(Object, O, 0); CONVERT_ARG_HANDLE_CHECKED(Object, V, 1); + PrototypeIterator iter(isolate, V, PrototypeIterator::START_AT_RECEIVER); while (true) { - Handle prototype = Object::GetPrototype(isolate, V); - if (prototype->IsNull()) return isolate->heap()->false_value(); - if (*O == *prototype) return isolate->heap()->true_value(); - V = prototype; + iter.AdvanceIgnoringProxies(); + if (iter.IsAtEnd()) return isolate->heap()->false_value(); + if (iter.IsAtEnd(O)) return isolate->heap()->true_value(); } } @@ -4803,8 +4810,9 @@ MaybeHandle Runtime::GetElementOrCharAt(Isolate* isolate, Handle result; if (object->IsString() || object->IsNumber() || object->IsBoolean()) { - Handle proto(object->GetPrototype(isolate), isolate); - return Object::GetElement(isolate, proto, index); + PrototypeIterator iter(isolate, object); + return Object::GetElement(isolate, PrototypeIterator::GetCurrent(iter), + index); } else { return Object::GetElement(isolate, object, index); } @@ -10688,15 +10696,18 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) { CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]); if (array->elements()->IsDictionary()) { Handle keys = isolate->factory()->empty_fixed_array(); - for (Handle p = array; - !p->IsNull(); - p = Handle(p->GetPrototype(isolate), isolate)) { - if (p->IsJSProxy() || JSObject::cast(*p)->HasIndexedInterceptor()) { + for (PrototypeIterator iter(isolate, array, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() || + JSObject::cast(*PrototypeIterator::GetCurrent(iter)) + ->HasIndexedInterceptor()) { // Bail out if we find a proxy or interceptor, likely not worth // collecting keys in that case. return *isolate->factory()->NewNumberFromUint(length); } - Handle current = Handle::cast(p); + Handle current = + Handle::cast(PrototypeIterator::GetCurrent(iter)); Handle current_keys = isolate->factory()->NewFixedArray(current->NumberOfOwnElements(NONE)); current->GetOwnElementKeys(*current_keys, NONE); @@ -12859,7 +12870,9 @@ static MaybeHandle DebugEvaluate(Isolate* isolate, // Skip the global proxy as it has no properties and always delegates to the // real global object. if (result->IsJSGlobalProxy()) { - result = Handle(JSObject::cast(result->GetPrototype(isolate))); + PrototypeIterator iter(isolate, result); + // TODO(verwaest): This will crash when the global proxy is detached. + result = Handle::cast(PrototypeIterator::GetCurrent(iter)); } // Clear the oneshot breakpoints so that the debugger does not step further. @@ -13035,17 +13048,12 @@ static int DebugReferencedBy(HeapIterator* iterator, // Check instance filter if supplied. This is normally used to avoid // references from mirror objects (see Runtime_IsInPrototypeChain). if (!instance_filter->IsUndefined()) { - Object* V = obj; - while (true) { - Object* prototype = V->GetPrototype(isolate); - if (prototype->IsNull()) { - break; - } - if (instance_filter == prototype) { + for (PrototypeIterator iter(isolate, obj); !iter.IsAtEnd(); + iter.Advance()) { + if (iter.GetCurrent() == instance_filter) { obj = NULL; // Don't add this object. break; } - V = prototype; } } diff --git a/src/string-stream.cc b/src/string-stream.cc index df48153..196e5bb 100644 --- a/src/string-stream.cc +++ b/src/string-stream.cc @@ -5,6 +5,7 @@ #include "src/string-stream.h" #include "src/handles-inl.h" +#include "src/prototype.h" namespace v8 { namespace internal { @@ -499,11 +500,11 @@ void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) { Object* name = fun->shared()->name(); bool print_name = false; Isolate* isolate = fun->GetIsolate(); - for (Object* p = receiver; - p != isolate->heap()->null_value(); - p = p->GetPrototype(isolate)) { - if (p->IsJSObject()) { - Object* key = JSObject::cast(p)->SlowReverseLookup(fun); + for (PrototypeIterator iter(isolate, receiver, + PrototypeIterator::START_AT_RECEIVER); + !iter.IsAtEnd(); iter.Advance()) { + if (iter.GetCurrent()->IsJSObject()) { + Object* key = JSObject::cast(iter.GetCurrent())->SlowReverseLookup(fun); if (key != isolate->heap()->undefined_value()) { if (!name->IsString() || !key->IsString() || diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 0835091..5ab74a4 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -585,6 +585,7 @@ '../../src/property-details.h', '../../src/property.cc', '../../src/property.h', + '../../src/prototype.h', '../../src/regexp-macro-assembler-irregexp-inl.h', '../../src/regexp-macro-assembler-irregexp.cc', '../../src/regexp-macro-assembler-irregexp.h', -- 2.7.4