• 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 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)
did_jump_to_prototype_chain_(false)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)
did_jump_to_prototype_chain_(false)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 
PrototypeIterator(Map * receiver_map)57   explicit PrototypeIterator(Map* receiver_map)
58       : did_jump_to_prototype_chain_(true),
59         object_(receiver_map->prototype()),
60         isolate_(receiver_map->GetIsolate()) {}
61 
PrototypeIterator(Handle<Map> receiver_map)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 
~PrototypeIterator()68   ~PrototypeIterator() {}
69 
HasAccess()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>
GetCurrent()82   T* GetCurrent() const {
83     DCHECK(handle_.is_null());
84     return T::cast(object_);
85   }
86 
87   template <typename T = Object>
GetCurrent(const PrototypeIterator & iterator)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 
Advance()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 
AdvanceIgnoringProxies()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().
AdvanceFollowingProxies()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 
IsAtEnd(Object * final_object)166   bool IsAtEnd(Object* final_object) {
167     DCHECK(handle_.is_null());
168     return object_->IsNull() || object_ == final_object;
169   }
170 
IsAtEnd(Handle<Object> final_object)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