|  | // 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 WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN }; | 
|  |  | 
|  | const int kProxyPrototypeLimit = 100 * 1000; | 
|  |  | 
|  | PrototypeIterator(Isolate* isolate, Handle<JSReceiver> receiver, | 
|  | WhereToStart where_to_start = kStartAtPrototype, | 
|  | WhereToEnd where_to_end = END_AT_NULL) | 
|  | : object_(NULL), | 
|  | handle_(receiver), | 
|  | isolate_(isolate), | 
|  | where_to_end_(where_to_end), | 
|  | is_at_end_(false), | 
|  | seen_proxies_(0) { | 
|  | CHECK(!handle_.is_null()); | 
|  | if (where_to_start == kStartAtPrototype) Advance(); | 
|  | } | 
|  |  | 
|  | PrototypeIterator(Isolate* isolate, JSReceiver* receiver, | 
|  | WhereToStart where_to_start = kStartAtPrototype, | 
|  | WhereToEnd where_to_end = END_AT_NULL) | 
|  | : object_(receiver), | 
|  | isolate_(isolate), | 
|  | where_to_end_(where_to_end), | 
|  | is_at_end_(false), | 
|  | seen_proxies_(0) { | 
|  | if (where_to_start == kStartAtPrototype) Advance(); | 
|  | } | 
|  |  | 
|  | explicit PrototypeIterator(Map* receiver_map) | 
|  | : object_(receiver_map->prototype()), | 
|  | isolate_(receiver_map->GetIsolate()), | 
|  | where_to_end_(END_AT_NULL), | 
|  | is_at_end_(object_->IsNull(isolate_)), | 
|  | seen_proxies_(0) {} | 
|  |  | 
|  | explicit PrototypeIterator(Handle<Map> receiver_map) | 
|  | : object_(NULL), | 
|  | handle_(handle(receiver_map->prototype(), receiver_map->GetIsolate())), | 
|  | isolate_(receiver_map->GetIsolate()), | 
|  | where_to_end_(END_AT_NULL), | 
|  | is_at_end_(handle_->IsNull(isolate_)), | 
|  | seen_proxies_(0) {} | 
|  |  | 
|  | ~PrototypeIterator() {} | 
|  |  | 
|  | bool HasAccess() const { | 
|  | // We can only perform access check in the handlified version of the | 
|  | // PrototypeIterator. | 
|  | DCHECK(!handle_.is_null()); | 
|  | if (handle_->IsAccessCheckNeeded()) { | 
|  | return isolate_->MayAccess(handle(isolate_->context()), | 
|  | Handle<JSObject>::cast(handle_)); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename T = Object> | 
|  | T* GetCurrent() const { | 
|  | DCHECK(handle_.is_null()); | 
|  | return T::cast(object_); | 
|  | } | 
|  |  | 
|  | template <typename T = Object> | 
|  | static Handle<T> GetCurrent(const PrototypeIterator& iterator) { | 
|  | DCHECK(!iterator.handle_.is_null()); | 
|  | DCHECK(iterator.object_ == NULL); | 
|  | return Handle<T>::cast(iterator.handle_); | 
|  | } | 
|  |  | 
|  | void Advance() { | 
|  | if (handle_.is_null() && object_->IsJSProxy()) { | 
|  | is_at_end_ = true; | 
|  | object_ = isolate_->heap()->null_value(); | 
|  | return; | 
|  | } else if (!handle_.is_null() && handle_->IsJSProxy()) { | 
|  | is_at_end_ = true; | 
|  | handle_ = isolate_->factory()->null_value(); | 
|  | return; | 
|  | } | 
|  | AdvanceIgnoringProxies(); | 
|  | } | 
|  |  | 
|  | void AdvanceIgnoringProxies() { | 
|  | Object* object = handle_.is_null() ? object_ : *handle_; | 
|  | Map* map = HeapObject::cast(object)->map(); | 
|  |  | 
|  | Object* prototype = map->prototype(); | 
|  | is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN | 
|  | ? !map->has_hidden_prototype() | 
|  | : prototype->IsNull(isolate_); | 
|  |  | 
|  | if (handle_.is_null()) { | 
|  | object_ = prototype; | 
|  | } else { | 
|  | handle_ = handle(prototype, isolate_); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns false iff a call to JSProxy::GetPrototype throws. | 
|  | // TODO(neis): This should probably replace Advance(). | 
|  | MUST_USE_RESULT bool AdvanceFollowingProxies() { | 
|  | DCHECK(!(handle_.is_null() && object_->IsJSProxy())); | 
|  | if (!HasAccess()) { | 
|  | // Abort the lookup if we do not have access to the current object. | 
|  | handle_ = isolate_->factory()->null_value(); | 
|  | is_at_end_ = true; | 
|  | return true; | 
|  | } | 
|  | return AdvanceFollowingProxiesIgnoringAccessChecks(); | 
|  | } | 
|  |  | 
|  | MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() { | 
|  | if (handle_.is_null() || !handle_->IsJSProxy()) { | 
|  | AdvanceIgnoringProxies(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Due to possible __proto__ recursion limit the number of Proxies | 
|  | // we visit to an arbitrarily chosen large number. | 
|  | seen_proxies_++; | 
|  | if (seen_proxies_ > kProxyPrototypeLimit) { | 
|  | isolate_->Throw( | 
|  | *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow)); | 
|  | return false; | 
|  | } | 
|  | MaybeHandle<Object> proto = | 
|  | JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_)); | 
|  | if (!proto.ToHandle(&handle_)) return false; | 
|  | is_at_end_ = | 
|  | where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsAtEnd() const { return is_at_end_; } | 
|  |  | 
|  | private: | 
|  | Object* object_; | 
|  | Handle<Object> handle_; | 
|  | Isolate* isolate_; | 
|  | WhereToEnd where_to_end_; | 
|  | bool is_at_end_; | 
|  | int seen_proxies_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(PrototypeIterator); | 
|  | }; | 
|  |  | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | }  // namespace v8 | 
|  |  | 
|  | #endif  // V8_PROTOTYPE_H_ |