Home | History | Annotate | Download | only in src
      1 // Copyright 2014 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_PROTOTYPE_H_
      6 #define V8_PROTOTYPE_H_
      7 
      8 #include "src/isolate.h"
      9 #include "src/objects.h"
     10 
     11 namespace v8 {
     12 namespace internal {
     13 
     14 /**
     15  * A class to uniformly access the prototype of any Object and walk its
     16  * prototype chain.
     17  *
     18  * The PrototypeIterator can either start at the prototype (default), or
     19  * include the receiver itself. If a PrototypeIterator is constructed for a
     20  * Map, it will always start at the prototype.
     21  *
     22  * The PrototypeIterator can either run to the null_value(), the first
     23  * non-hidden prototype, or a given object.
     24  */
     25 class PrototypeIterator {
     26  public:
     27   enum WhereToStart { START_AT_RECEIVER, START_AT_PROTOTYPE };
     28 
     29   enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN };
     30 
     31   const int kProxyPrototypeLimit = 100 * 1000;
     32 
     33   PrototypeIterator(Isolate* isolate, Handle<Object> receiver,
     34                     WhereToStart where_to_start = START_AT_PROTOTYPE)
     35       : did_jump_to_prototype_chain_(false),
     36         object_(NULL),
     37         handle_(receiver),
     38         isolate_(isolate),
     39         seen_proxies_(0) {
     40     CHECK(!handle_.is_null());
     41     if (where_to_start == START_AT_PROTOTYPE) {
     42       Advance();
     43     }
     44   }
     45 
     46   PrototypeIterator(Isolate* isolate, Object* receiver,
     47                     WhereToStart where_to_start = START_AT_PROTOTYPE)
     48       : did_jump_to_prototype_chain_(false),
     49         object_(receiver),
     50         isolate_(isolate),
     51         seen_proxies_(0) {
     52     if (where_to_start == START_AT_PROTOTYPE) {
     53       Advance();
     54     }
     55   }
     56 
     57   explicit PrototypeIterator(Map* receiver_map)
     58       : did_jump_to_prototype_chain_(true),
     59         object_(receiver_map->prototype()),
     60         isolate_(receiver_map->GetIsolate()) {}
     61 
     62   explicit PrototypeIterator(Handle<Map> receiver_map)
     63       : did_jump_to_prototype_chain_(true),
     64         object_(NULL),
     65         handle_(handle(receiver_map->prototype(), receiver_map->GetIsolate())),
     66         isolate_(receiver_map->GetIsolate()) {}
     67 
     68   ~PrototypeIterator() {}
     69 
     70   bool HasAccess() const {
     71     // We can only perform access check in the handlified version of the
     72     // PrototypeIterator.
     73     DCHECK(!handle_.is_null());
     74     if (handle_->IsAccessCheckNeeded()) {
     75       return isolate_->MayAccess(handle(isolate_->context()),
     76                                  Handle<JSObject>::cast(handle_));
     77     }
     78     return true;
     79   }
     80 
     81   template <typename T = Object>
     82   T* GetCurrent() const {
     83     DCHECK(handle_.is_null());
     84     return T::cast(object_);
     85   }
     86 
     87   template <typename T = Object>
     88   static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
     89     DCHECK(!iterator.handle_.is_null());
     90     DCHECK(iterator.object_ == NULL);
     91     return Handle<T>::cast(iterator.handle_);
     92   }
     93 
     94   void Advance() {
     95     if (handle_.is_null() && object_->IsJSProxy()) {
     96       did_jump_to_prototype_chain_ = true;
     97       object_ = isolate_->heap()->null_value();
     98       return;
     99     } else if (!handle_.is_null() && handle_->IsJSProxy()) {
    100       did_jump_to_prototype_chain_ = true;
    101       handle_ = handle(isolate_->heap()->null_value(), isolate_);
    102       return;
    103     }
    104     AdvanceIgnoringProxies();
    105   }
    106 
    107   void AdvanceIgnoringProxies() {
    108     if (!did_jump_to_prototype_chain_) {
    109       did_jump_to_prototype_chain_ = true;
    110       if (handle_.is_null()) {
    111         object_ = object_->GetRootMap(isolate_)->prototype();
    112       } else {
    113         handle_ = handle(handle_->GetRootMap(isolate_)->prototype(), isolate_);
    114       }
    115     } else {
    116       if (handle_.is_null()) {
    117         object_ = HeapObject::cast(object_)->map()->prototype();
    118       } else {
    119         handle_ =
    120             handle(HeapObject::cast(*handle_)->map()->prototype(), isolate_);
    121       }
    122     }
    123   }
    124 
    125   // Returns false iff a call to JSProxy::GetPrototype throws.
    126   // TODO(neis): This should probably replace Advance().
    127   bool AdvanceFollowingProxies() {
    128     DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
    129     if (!HasAccess()) {
    130       // Abort the lookup if we do not have access to the current object.
    131       handle_ = isolate_->factory()->null_value();
    132       return true;
    133     }
    134     if (handle_.is_null() || !handle_->IsJSProxy()) {
    135       AdvanceIgnoringProxies();
    136       return true;
    137     }
    138     // Due to possible __proto__ recursion limit the number of Proxies
    139     // we visit to an arbitrarily chosen large number.
    140     seen_proxies_++;
    141     if (seen_proxies_ > kProxyPrototypeLimit) {
    142       isolate_->Throw(
    143           *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow));
    144       return false;
    145     }
    146     did_jump_to_prototype_chain_ = true;
    147     MaybeHandle<Object> proto =
    148         JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
    149     return proto.ToHandle(&handle_);
    150   }
    151 
    152   bool IsAtEnd(WhereToEnd where_to_end = END_AT_NULL) const {
    153     if (handle_.is_null()) {
    154       return object_->IsNull() ||
    155              (did_jump_to_prototype_chain_ &&
    156               where_to_end == END_AT_NON_HIDDEN &&
    157               !HeapObject::cast(object_)->map()->is_hidden_prototype());
    158     } else {
    159       return handle_->IsNull() ||
    160              (did_jump_to_prototype_chain_ &&
    161               where_to_end == END_AT_NON_HIDDEN &&
    162               !Handle<HeapObject>::cast(handle_)->map()->is_hidden_prototype());
    163     }
    164   }
    165 
    166   bool IsAtEnd(Object* final_object) {
    167     DCHECK(handle_.is_null());
    168     return object_->IsNull() || object_ == final_object;
    169   }
    170 
    171   bool IsAtEnd(Handle<Object> final_object) {
    172     DCHECK(!handle_.is_null());
    173     return handle_->IsNull() || *handle_ == *final_object;
    174   }
    175 
    176  private:
    177   bool did_jump_to_prototype_chain_;
    178   Object* object_;
    179   Handle<Object> handle_;
    180   Isolate* isolate_;
    181   int seen_proxies_;
    182 
    183   DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
    184 };
    185 
    186 
    187 }  // namespace internal
    188 
    189 }  // namespace v8
    190 
    191 #endif  // V8_PROTOTYPE_H_
    192