blob: 3253791f9078561dba12302b4457398615596743 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
11namespace v8 {
12namespace 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 */
25class PrototypeIterator {
26 public:
27 enum WhereToStart { START_AT_RECEIVER, START_AT_PROTOTYPE };
28
29 enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN };
30
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000031 const int kProxyPrototypeLimit = 100 * 1000;
32
Ben Murdochb8a8cc12014-11-26 15:28:44 +000033 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),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000038 isolate_(isolate),
39 seen_proxies_(0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000040 CHECK(!handle_.is_null());
41 if (where_to_start == START_AT_PROTOTYPE) {
42 Advance();
43 }
44 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000045
Ben Murdochb8a8cc12014-11-26 15:28:44 +000046 PrototypeIterator(Isolate* isolate, Object* receiver,
47 WhereToStart where_to_start = START_AT_PROTOTYPE)
48 : did_jump_to_prototype_chain_(false),
49 object_(receiver),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000050 isolate_(isolate),
51 seen_proxies_(0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000052 if (where_to_start == START_AT_PROTOTYPE) {
53 Advance();
54 }
55 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000056
Ben Murdochb8a8cc12014-11-26 15:28:44 +000057 explicit PrototypeIterator(Map* receiver_map)
58 : did_jump_to_prototype_chain_(true),
59 object_(receiver_map->prototype()),
60 isolate_(receiver_map->GetIsolate()) {}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000061
Ben Murdochb8a8cc12014-11-26 15:28:44 +000062 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()) {}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000067
Ben Murdochb8a8cc12014-11-26 15:28:44 +000068 ~PrototypeIterator() {}
69
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000070 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 {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000083 DCHECK(handle_.is_null());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000084 return T::cast(object_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000085 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000086
87 template <typename T = Object>
88 static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000089 DCHECK(!iterator.handle_.is_null());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000090 DCHECK(iterator.object_ == NULL);
91 return Handle<T>::cast(iterator.handle_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000092 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000093
Ben Murdochb8a8cc12014-11-26 15:28:44 +000094 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 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000106
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000107 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 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000124
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
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000152 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 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000165
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000166 bool IsAtEnd(Object* final_object) {
167 DCHECK(handle_.is_null());
168 return object_->IsNull() || object_ == final_object;
169 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000170
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000171 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_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000181 int seen_proxies_;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000182
183 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
184};
185
186
187} // namespace internal
188
189} // namespace v8
190
191#endif // V8_PROTOTYPE_H_