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