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 
     26 class PrototypeIterator {
     27  public:
     28   enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN };
     29 
     30   const int kProxyPrototypeLimit = 100 * 1000;
     31 
     32   PrototypeIterator(Isolate* isolate, Handle<JSReceiver> receiver,
     33                     WhereToStart where_to_start = kStartAtPrototype,
     34                     WhereToEnd where_to_end = END_AT_NULL)
     35       : isolate_(isolate),
     36         object_(NULL),
     37         handle_(receiver),
     38         where_to_end_(where_to_end),
     39         is_at_end_(false),
     40         seen_proxies_(0) {
     41     CHECK(!handle_.is_null());
     42     if (where_to_start == kStartAtPrototype) Advance();
     43   }
     44 
     45   PrototypeIterator(Isolate* isolate, JSReceiver* receiver,
     46                     WhereToStart where_to_start = kStartAtPrototype,
     47                     WhereToEnd where_to_end = END_AT_NULL)
     48       : isolate_(isolate),
     49         object_(receiver),
     50         where_to_end_(where_to_end),
     51         is_at_end_(false),
     52         seen_proxies_(0) {
     53     if (where_to_start == kStartAtPrototype) Advance();
     54   }
     55 
     56   explicit PrototypeIterator(Map* receiver_map,
     57                              WhereToEnd where_to_end = END_AT_NULL)
     58       : isolate_(receiver_map->GetIsolate()),
     59         object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()),
     60         where_to_end_(where_to_end),
     61         is_at_end_(object_->IsNull(isolate_)),
     62         seen_proxies_(0) {
     63     if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
     64       DCHECK(object_->IsJSReceiver());
     65       Map* map = JSReceiver::cast(object_)->map();
     66       is_at_end_ = !map->has_hidden_prototype();
     67     }
     68   }
     69 
     70   explicit PrototypeIterator(Handle<Map> receiver_map,
     71                              WhereToEnd where_to_end = END_AT_NULL)
     72       : isolate_(receiver_map->GetIsolate()),
     73         object_(NULL),
     74         handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(),
     75                 isolate_),
     76         where_to_end_(where_to_end),
     77         is_at_end_(handle_->IsNull(isolate_)),
     78         seen_proxies_(0) {
     79     if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
     80       DCHECK(handle_->IsJSReceiver());
     81       Map* map = JSReceiver::cast(*handle_)->map();
     82       is_at_end_ = !map->has_hidden_prototype();
     83     }
     84   }
     85 
     86   ~PrototypeIterator() {}
     87 
     88   bool HasAccess() const {
     89     // We can only perform access check in the handlified version of the
     90     // PrototypeIterator.
     91     DCHECK(!handle_.is_null());
     92     if (handle_->IsAccessCheckNeeded()) {
     93       return isolate_->MayAccess(handle(isolate_->context()),
     94                                  Handle<JSObject>::cast(handle_));
     95     }
     96     return true;
     97   }
     98 
     99   template <typename T = Object>
    100   T* GetCurrent() const {
    101     DCHECK(handle_.is_null());
    102     return T::cast(object_);
    103   }
    104 
    105   template <typename T = Object>
    106   static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
    107     DCHECK(!iterator.handle_.is_null());
    108     DCHECK(iterator.object_ == NULL);
    109     return Handle<T>::cast(iterator.handle_);
    110   }
    111 
    112   void Advance() {
    113     if (handle_.is_null() && object_->IsJSProxy()) {
    114       is_at_end_ = true;
    115       object_ = isolate_->heap()->null_value();
    116       return;
    117     } else if (!handle_.is_null() && handle_->IsJSProxy()) {
    118       is_at_end_ = true;
    119       handle_ = isolate_->factory()->null_value();
    120       return;
    121     }
    122     AdvanceIgnoringProxies();
    123   }
    124 
    125   void AdvanceIgnoringProxies() {
    126     Object* object = handle_.is_null() ? object_ : *handle_;
    127     Map* map = HeapObject::cast(object)->map();
    128 
    129     Object* prototype = map->prototype();
    130     is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN
    131                      ? !map->has_hidden_prototype()
    132                      : prototype->IsNull(isolate_);
    133 
    134     if (handle_.is_null()) {
    135       object_ = prototype;
    136     } else {
    137       handle_ = handle(prototype, isolate_);
    138     }
    139   }
    140 
    141   // Returns false iff a call to JSProxy::GetPrototype throws.
    142   // TODO(neis): This should probably replace Advance().
    143   MUST_USE_RESULT bool AdvanceFollowingProxies() {
    144     DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
    145     if (!HasAccess()) {
    146       // Abort the lookup if we do not have access to the current object.
    147       handle_ = isolate_->factory()->null_value();
    148       is_at_end_ = true;
    149       return true;
    150     }
    151     return AdvanceFollowingProxiesIgnoringAccessChecks();
    152   }
    153 
    154   MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() {
    155     if (handle_.is_null() || !handle_->IsJSProxy()) {
    156       AdvanceIgnoringProxies();
    157       return true;
    158     }
    159 
    160     // Due to possible __proto__ recursion limit the number of Proxies
    161     // we visit to an arbitrarily chosen large number.
    162     seen_proxies_++;
    163     if (seen_proxies_ > kProxyPrototypeLimit) {
    164       isolate_->StackOverflow();
    165       return false;
    166     }
    167     MaybeHandle<Object> proto =
    168         JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
    169     if (!proto.ToHandle(&handle_)) return false;
    170     is_at_end_ =
    171         where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_);
    172     return true;
    173   }
    174 
    175   bool IsAtEnd() const { return is_at_end_; }
    176   Isolate* isolate() const { return isolate_; }
    177 
    178  private:
    179   Isolate* isolate_;
    180   Object* object_;
    181   Handle<Object> handle_;
    182   WhereToEnd where_to_end_;
    183   bool is_at_end_;
    184   int seen_proxies_;
    185 
    186   DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
    187 };
    188 
    189 
    190 }  // namespace internal
    191 
    192 }  // namespace v8
    193 
    194 #endif  // V8_PROTOTYPE_H_
    195