Introduce a PrototypeIterator class and use it for prototype access
authorjochen@chromium.org <jochen@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 14 Jul 2014 07:19:49 +0000 (07:19 +0000)
committerjochen@chromium.org <jochen@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 14 Jul 2014 07:19:49 +0000 (07:19 +0000)
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

13 files changed:
BUILD.gn
src/accessors.cc
src/api.cc
src/builtins.cc
src/ic-inl.h
src/ic.cc
src/isolate.cc
src/objects.cc
src/objects.h
src/prototype.h [new file with mode: 0644]
src/runtime.cc
src/string-stream.cc
tools/gyp/v8.gyp

index 43e17c8..f8dd39c 100644 (file)
--- 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",
index cfa2049..13c2803 100644 (file)
@@ -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<ExecutableAccessorInfo> Accessors::CloneAccessor(
 
 template <class C>
 static C* FindInstanceOf(Isolate* isolate, Object* obj) {
-  for (Object* cur = obj; !cur->IsNull(); cur = cur->GetPrototype(isolate)) {
-    if (Is<C>(cur)) return C::cast(cur);
+  for (PrototypeIterator iter(isolate, obj,
+                              PrototypeIterator::START_AT_RECEIVER);
+       !iter.IsAtEnd(); iter.Advance()) {
+    if (Is<C>(iter.GetCurrent())) return C::cast(iter.GetCurrent());
   }
   return NULL;
 }
index 106c1fd..2c50d75 100644 (file)
@@ -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<Value> v8::Object::GetPrototype() {
   ON_BAILOUT(isolate, "v8::Object::GetPrototype()", return Local<v8::Value>());
   ENTER_V8(isolate);
   i::Handle<i::Object> self = Utils::OpenHandle(this);
-  i::Handle<i::Object> result(self->GetPrototype(isolate), isolate);
-  return Utils::ToLocal(result);
+  i::PrototypeIterator iter(isolate, self);
+  return Utils::ToLocal(i::PrototypeIterator::GetCurrent(iter));
 }
 
 
index eb5513e..925e8a9 100644 (file)
@@ -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();
 }
index a26d31b..78d42ea 100644 (file)
@@ -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());
 }
 
 
index a9c53ba..e2123d0 100644 (file)
--- 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<Object> 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;
   }
 
index 0dbce1f..5e7fc71 100644 (file)
@@ -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<Object> 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;
     }
index ad066af..2763658 100644 (file)
@@ -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> Object::GetElementWithReceiver(Isolate* isolate,
                                                    Handle<Object> object,
                                                    Handle<Object> receiver,
                                                    uint32_t index) {
-  Handle<Object> 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<Object>(holder->GetPrototype(isolate), isolate)) {
-    if (!holder->IsJSObject()) {
-      if (holder->IsJSProxy()) {
-        return JSProxy::GetElementWithHandler(
-            Handle<JSProxy>::cast(holder), receiver, index);
-      } else if (holder->IsUndefined()) {
-        // Undefined has no indexed properties.
-        return isolate->factory()->undefined_value();
-      } else {
-        holder = Handle<Object>(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<JSProxy>::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<JSObject> js_object = Handle<JSObject>::cast(holder);
+    Handle<JSObject> js_object =
+        Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
 
     // Check access rights if needed.
     if (js_object->IsAccessCheckNeeded()) {
@@ -853,11 +854,11 @@ MaybeHandle<Object> 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> Object::GetPrototype(Isolate* isolate,
-                                    Handle<Object> object) {
-  return handle(object->GetPrototype(isolate), isolate);
+  return isolate->heap()->null_value()->map();
 }
 
 
@@ -3033,20 +3027,16 @@ MaybeHandle<Object> JSObject::SetElementWithCallbackSetterInPrototypes(
     bool* found,
     StrictMode strict_mode) {
   Isolate *isolate = object->GetIsolate();
-  for (Handle<Object> 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<JSProxy>::cast(proto),
-          object,
+          Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), object,
           isolate->factory()->Uint32ToString(index),  // name
-          value,
-          NONE,
-          strict_mode,
-          found);
+          value, NONE, strict_mode, found);
     }
-    Handle<JSObject> js_proto = Handle<JSObject>::cast(proto);
+    Handle<JSObject> js_proto =
+        Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
     if (!js_proto->HasDictionaryElements()) {
       continue;
     }
@@ -3524,14 +3514,11 @@ void JSObject::LookupRealNamedPropertyInPrototypes(Handle<Name> 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<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
       JSFunction::cast(isolate->sloppy_arguments_map()->constructor()));
 
   // Only collect keys if access is permitted.
-  for (Handle<Object> p = object;
-       *p != isolate->heap()->null_value();
-       p = Handle<Object>(p->GetPrototype(isolate), isolate)) {
-    if (p->IsJSProxy()) {
-      Handle<JSProxy> proxy(JSProxy::cast(*p), isolate);
+  for (PrototypeIterator iter(isolate, object,
+                              PrototypeIterator::START_AT_RECEIVER);
+       !iter.IsAtEnd(); iter.Advance()) {
+    if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
+      Handle<JSProxy> proxy(JSProxy::cast(*PrototypeIterator::GetCurrent(iter)),
+                            isolate);
       Handle<Object> args[] = { proxy };
       Handle<Object> names;
       ASSIGN_RETURN_ON_EXCEPTION(
@@ -6397,7 +6385,8 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
       break;
     }
 
-    Handle<JSObject> current(JSObject::cast(*p), isolate);
+    Handle<JSObject> current =
+        Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
 
     // Check access rights if required.
     if (current->IsAccessCheckNeeded() &&
@@ -6615,22 +6604,18 @@ void JSObject::DefinePropertyAccessor(Handle<JSObject> 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<JSFunction> function) {
   Handle<Object> prototype;
   if (function->has_instance_prototype()) {
     prototype = handle(function->instance_prototype(), isolate);
-    for (Handle<Object> p = prototype; !p->IsNull() && !p->IsJSProxy();
-         p = Object::GetPrototype(isolate, p)) {
-      JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(p));
+    for (PrototypeIterator iter(isolate, prototype,
+                                PrototypeIterator::START_AT_RECEIVER);
+         !iter.IsAtEnd(); iter.Advance()) {
+      if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
+        break;
+      }
+      JSObject::OptimizeAsPrototype(
+          Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
     }
   } else {
     prototype = isolate->factory()->NewFunctionPrototype(function);
@@ -12203,10 +12193,10 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> 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<Object> error = isolate->factory()->NewError(
           "cyclic_proto", HandleVector<Object>(NULL, 0));
@@ -12221,11 +12211,11 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> 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<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+      iter.Advance();
     }
   }
 
index af8cc28..d742b23 100644 (file)
@@ -1514,10 +1514,6 @@ class Object {
       Handle<Object> receiver,
       uint32_t index);
 
-  // Return the object's prototype (might be Heap::null_value()).
-  Object* GetPrototype(Isolate* isolate);
-  static Handle<Object> GetPrototype(Isolate* isolate, Handle<Object> 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 (file)
index 0000000..aebdcbc
--- /dev/null
@@ -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<Object> 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<Object> 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<HeapObject>::cast(handle_)->map()->is_hidden_prototype());
+    }
+  }
+  bool IsAtEnd(Object* final_object) {
+    ASSERT(handle_.is_null());
+    return object_->IsNull() || object_ == final_object;
+  }
+  bool IsAtEnd(Handle<Object> final_object) {
+    ASSERT(!handle_.is_null());
+    return handle_->IsNull() || *handle_ == *final_object;
+  }
+
+ private:
+  bool did_jump_to_prototype_chain_;
+  Object* object_;
+  Handle<Object> handle_;
+  Isolate* isolate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
+};
+
+
+}  // namespace internal
+
+}  // namespace v8
+
+#endif  // V8_PROTOTYPE_H_
index f3e8e84..17358e4 100644 (file)
@@ -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<JSObject>::cast(obj),
-                                 isolate->factory()->proto_string(),
-                                 v8::ACCESS_GET)) {
-      isolate->ReportFailedAccessCheck(Handle<JSObject>::cast(obj),
-                                       v8::ACCESS_GET);
+    if (PrototypeIterator::GetCurrent(iter)->IsAccessCheckNeeded() &&
+        !isolate->MayNamedAccess(
+            Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)),
+            isolate->factory()->proto_string(), v8::ACCESS_GET)) {
+      isolate->ReportFailedAccessCheck(
+          Handle<JSObject>::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<Object> GetPrototypeSkipHiddenPrototypes(
     Isolate* isolate, Handle<Object> receiver) {
-  Handle<Object> 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<Object> 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<Object> Runtime::GetElementOrCharAt(Isolate* isolate,
 
   Handle<Object> result;
   if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
-    Handle<Object> 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<FixedArray> keys = isolate->factory()->empty_fixed_array();
-    for (Handle<Object> p = array;
-         !p->IsNull();
-         p = Handle<Object>(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<JSObject> current = Handle<JSObject>::cast(p);
+      Handle<JSObject> current =
+          Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
       Handle<FixedArray> current_keys =
           isolate->factory()->NewFixedArray(current->NumberOfOwnElements(NONE));
       current->GetOwnElementKeys(*current_keys, NONE);
@@ -12859,7 +12870,9 @@ static MaybeHandle<Object> 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>(JSObject::cast(result->GetPrototype(isolate)));
+    PrototypeIterator iter(isolate, result);
+    // TODO(verwaest): This will crash when the global proxy is detached.
+    result = Handle<JSObject>::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;
           }
         }
 
index df48153..196e5bb 100644 (file)
@@ -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() ||
index 0835091..5ab74a4 100644 (file)
         '../../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',