Line data Source code
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 24447219 : 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 48894438 : seen_proxies_(0) {
41 24447219 : CHECK(!handle_.is_null());
42 24447219 : if (where_to_start == kStartAtPrototype) Advance();
43 24447219 : }
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 3397296 : seen_proxies_(0) {
53 1585959 : if (where_to_start == kStartAtPrototype) Advance();
54 : }
55 :
56 299905 : explicit PrototypeIterator(Map* receiver_map,
57 : WhereToEnd where_to_end = END_AT_NULL)
58 : : isolate_(receiver_map->GetIsolate()),
59 299905 : object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()),
60 : where_to_end_(where_to_end),
61 299905 : is_at_end_(object_->IsNull(isolate_)),
62 1499525 : seen_proxies_(0) {
63 299905 : if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
64 : DCHECK(object_->IsJSReceiver());
65 0 : Map* map = JSReceiver::cast(object_)->map();
66 0 : is_at_end_ = !map->has_hidden_prototype();
67 : }
68 299905 : }
69 :
70 5868156 : 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 5868156 : is_at_end_(handle_->IsNull(isolate_)),
78 29340780 : seen_proxies_(0) {
79 5868156 : if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
80 : DCHECK(handle_->IsJSReceiver());
81 : Map* map = JSReceiver::cast(*handle_)->map();
82 33387 : is_at_end_ = !map->has_hidden_prototype();
83 : }
84 5868156 : }
85 :
86 : ~PrototypeIterator() {}
87 :
88 10876710 : bool HasAccess() const {
89 : // We can only perform access check in the handlified version of the
90 : // PrototypeIterator.
91 : DCHECK(!handle_.is_null());
92 10876710 : if (handle_->IsAccessCheckNeeded()) {
93 : return isolate_->MayAccess(handle(isolate_->context()),
94 630 : 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 60138 : static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
107 : DCHECK(!iterator.handle_.is_null());
108 : DCHECK(iterator.object_ == NULL);
109 60138 : return Handle<T>::cast(iterator.handle_);
110 : }
111 :
112 16827315 : void Advance() {
113 19461064 : if (handle_.is_null() && object_->IsJSProxy()) {
114 1684 : is_at_end_ = true;
115 1684 : object_ = isolate_->heap()->null_value();
116 1684 : return;
117 31019197 : } else if (!handle_.is_null() && handle_->IsJSProxy()) {
118 6 : is_at_end_ = true;
119 12 : handle_ = isolate_->factory()->null_value();
120 6 : return;
121 : }
122 16825625 : AdvanceIgnoringProxies();
123 : }
124 :
125 38714935 : void AdvanceIgnoringProxies() {
126 38714935 : Object* object = handle_.is_null() ? object_ : *handle_;
127 : Map* map = HeapObject::cast(object)->map();
128 :
129 : Object* prototype = map->prototype();
130 38714935 : is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN
131 : ? !map->has_hidden_prototype()
132 83347365 : : prototype->IsNull(isolate_);
133 :
134 38714935 : if (handle_.is_null()) {
135 2651220 : object_ = prototype;
136 : } else {
137 72127430 : handle_ = handle(prototype, isolate_);
138 : }
139 38714935 : }
140 :
141 : // Returns false iff a call to JSProxy::GetPrototype throws.
142 : // TODO(neis): This should probably replace Advance().
143 10876710 : MUST_USE_RESULT bool AdvanceFollowingProxies() {
144 : DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
145 10876710 : if (!HasAccess()) {
146 : // Abort the lookup if we do not have access to the current object.
147 582 : handle_ = isolate_->factory()->null_value();
148 291 : is_at_end_ = true;
149 291 : return true;
150 : }
151 10876419 : return AdvanceFollowingProxiesIgnoringAccessChecks();
152 : }
153 :
154 27572585 : MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() {
155 55145170 : if (handle_.is_null() || !handle_->IsJSProxy()) {
156 21870155 : AdvanceIgnoringProxies();
157 21870155 : 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 5702430 : seen_proxies_++;
163 5702430 : if (seen_proxies_ > kProxyPrototypeLimit) {
164 57 : isolate_->StackOverflow();
165 57 : return false;
166 : }
167 : MaybeHandle<Object> proto =
168 5702373 : JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
169 11404746 : if (!proto.ToHandle(&handle_)) return false;
170 : is_at_end_ =
171 11403610 : where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_);
172 5702177 : return true;
173 : }
174 :
175 32863 : 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_
|