• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
isolate_(isolate)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)
isolate_(isolate)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 
~PrototypeIterator()86   ~PrototypeIterator() {}
87 
HasAccess()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>
GetCurrent()100   T* GetCurrent() const {
101     DCHECK(handle_.is_null());
102     return T::cast(object_);
103   }
104 
105   template <typename T = Object>
GetCurrent(const PrototypeIterator & iterator)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 
Advance()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 
AdvanceIgnoringProxies()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().
AdvanceFollowingProxies()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 
AdvanceFollowingProxiesIgnoringAccessChecks()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 
IsAtEnd()175   bool IsAtEnd() const { return is_at_end_; }
isolate()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