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 13340210 : PrototypeIterator(Isolate* isolate, Handle<JSReceiver> receiver,
31 : WhereToStart where_to_start = kStartAtPrototype,
32 : WhereToEnd where_to_end = END_AT_NULL)
33 : : isolate_(isolate),
34 : object_(nullptr),
35 : handle_(receiver),
36 : where_to_end_(where_to_end),
37 : is_at_end_(false),
38 26680420 : seen_proxies_(0) {
39 13340210 : CHECK(!handle_.is_null());
40 13340210 : if (where_to_start == kStartAtPrototype) Advance();
41 13340210 : }
42 :
43 : PrototypeIterator(Isolate* isolate, JSReceiver* receiver,
44 : WhereToStart where_to_start = kStartAtPrototype,
45 : WhereToEnd where_to_end = END_AT_NULL)
46 : : isolate_(isolate),
47 : object_(receiver),
48 : where_to_end_(where_to_end),
49 : is_at_end_(false),
50 2318466 : seen_proxies_(0) {
51 954424 : if (where_to_start == kStartAtPrototype) Advance();
52 : }
53 :
54 172253 : explicit PrototypeIterator(Map* receiver_map,
55 : WhereToEnd where_to_end = END_AT_NULL)
56 : : isolate_(receiver_map->GetIsolate()),
57 172253 : object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()),
58 : where_to_end_(where_to_end),
59 172253 : is_at_end_(object_->IsNull(isolate_)),
60 689012 : seen_proxies_(0) {
61 172253 : if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
62 : DCHECK(object_->IsJSReceiver());
63 0 : Map* map = JSReceiver::cast(object_)->map();
64 0 : is_at_end_ = !map->has_hidden_prototype();
65 : }
66 172253 : }
67 :
68 2003796 : explicit PrototypeIterator(Handle<Map> receiver_map,
69 : WhereToEnd where_to_end = END_AT_NULL)
70 : : isolate_(receiver_map->GetIsolate()),
71 : object_(nullptr),
72 : handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(),
73 : isolate_),
74 : where_to_end_(where_to_end),
75 2003796 : is_at_end_(handle_->IsNull(isolate_)),
76 8015184 : seen_proxies_(0) {
77 2003796 : if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
78 : DCHECK(handle_->IsJSReceiver());
79 : Map* map = JSReceiver::cast(*handle_)->map();
80 10684 : is_at_end_ = !map->has_hidden_prototype();
81 : }
82 2003796 : }
83 :
84 : ~PrototypeIterator() {}
85 :
86 7246831 : bool HasAccess() const {
87 : // We can only perform access check in the handlified version of the
88 : // PrototypeIterator.
89 : DCHECK(!handle_.is_null());
90 7246831 : if (handle_->IsAccessCheckNeeded()) {
91 : return isolate_->MayAccess(handle(isolate_->context()),
92 426 : Handle<JSObject>::cast(handle_));
93 : }
94 : return true;
95 : }
96 :
97 : template <typename T = Object>
98 : T* GetCurrent() const {
99 : DCHECK(handle_.is_null());
100 : return T::cast(object_);
101 : }
102 :
103 : template <typename T = Object>
104 16583 : static Handle<T> GetCurrent(const PrototypeIterator& iterator) {
105 : DCHECK(!iterator.handle_.is_null());
106 : DCHECK_NULL(iterator.object_);
107 16583 : return Handle<T>::cast(iterator.handle_);
108 : }
109 :
110 8379633 : void Advance() {
111 10238095 : if (handle_.is_null() && object_->IsJSProxy()) {
112 1238 : is_at_end_ = true;
113 1238 : object_ = isolate_->heap()->null_value();
114 1238 : return;
115 14899568 : } else if (!handle_.is_null() && handle_->IsJSProxy()) {
116 177 : is_at_end_ = true;
117 354 : handle_ = isolate_->factory()->null_value();
118 177 : return;
119 : }
120 8378218 : AdvanceIgnoringProxies();
121 : }
122 :
123 21126853 : void AdvanceIgnoringProxies() {
124 21126853 : Object* object = handle_.is_null() ? object_ : *handle_;
125 : Map* map = HeapObject::cast(object)->map();
126 :
127 : Object* prototype = map->prototype();
128 21126853 : is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN
129 : ? !map->has_hidden_prototype()
130 46086774 : : prototype->IsNull(isolate_);
131 :
132 21126853 : if (handle_.is_null()) {
133 1870525 : object_ = prototype;
134 : } else {
135 38512655 : handle_ = handle(prototype, isolate_);
136 : }
137 21126852 : }
138 :
139 : // Returns false iff a call to JSProxy::GetPrototype throws.
140 : // TODO(neis): This should probably replace Advance().
141 7246831 : MUST_USE_RESULT bool AdvanceFollowingProxies() {
142 : DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
143 7246831 : if (!HasAccess()) {
144 : // Abort the lookup if we do not have access to the current object.
145 386 : handle_ = isolate_->factory()->null_value();
146 193 : is_at_end_ = true;
147 193 : return true;
148 : }
149 7246638 : return AdvanceFollowingProxiesIgnoringAccessChecks();
150 : }
151 :
152 16525754 : MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() {
153 33051508 : if (handle_.is_null() || !handle_->IsJSProxy()) {
154 12735332 : AdvanceIgnoringProxies();
155 12735332 : return true;
156 : }
157 :
158 : // Due to possible __proto__ recursion limit the number of Proxies
159 : // we visit to an arbitrarily chosen large number.
160 3790422 : seen_proxies_++;
161 3790422 : if (seen_proxies_ > JSProxy::kMaxIterationLimit) {
162 37 : isolate_->StackOverflow();
163 37 : return false;
164 : }
165 : MaybeHandle<Object> proto =
166 3790385 : JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
167 7580770 : if (!proto.ToHandle(&handle_)) return false;
168 : is_at_end_ =
169 7580030 : where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_);
170 3790259 : return true;
171 : }
172 :
173 22144 : bool IsAtEnd() const { return is_at_end_; }
174 : Isolate* isolate() const { return isolate_; }
175 :
176 : private:
177 : Isolate* isolate_;
178 : Object* object_;
179 : Handle<Object> handle_;
180 : WhereToEnd where_to_end_;
181 : bool is_at_end_;
182 : int seen_proxies_;
183 :
184 : DISALLOW_COPY_AND_ASSIGN(PrototypeIterator);
185 : };
186 :
187 :
188 : } // namespace internal
189 :
190 : } // namespace v8
191 :
192 : #endif // V8_PROTOTYPE_H_
|