Commit 168742b8 authored by jochen@chromium.org's avatar jochen@chromium.org

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
parent df3c11d7
......@@ -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",
......
......@@ -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;
}
......
......@@ -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));
}
......
......@@ -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();
}
......
......@@ -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());
}
......
......@@ -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;
}
......
......@@ -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;
}
......
This diff is collapsed.
......@@ -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);
};
......
// 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_
......@@ -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),
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);
}
return current;
iter.Advance();
}
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;
}
}
......
......@@ -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() ||
......
......@@ -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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment