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 24441956 : 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 48883912 : seen_proxies_(0) {
41 24441956 : CHECK(!handle_.is_null());
42 24441956 : if (where_to_start == kStartAtPrototype) Advance();
43 24441956 : }
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 3389738 : seen_proxies_(0) {
53 1582936 : if (where_to_start == kStartAtPrototype) Advance();
54 : }
55 :
56 298663 : explicit PrototypeIterator(Map* receiver_map,
57 : WhereToEnd where_to_end = END_AT_NULL)
58 : : isolate_(receiver_map->GetIsolate()),
59 298663 : object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()),
60 : where_to_end_(where_to_end),
61 298663 : is_at_end_(object_->IsNull(isolate_)),
62 1493315 : seen_proxies_(0) {
63 298663 : 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 298663 : }
69 :
70 5854743 : 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 5854743 : is_at_end_(handle_->IsNull(isolate_)),
78 29273715 : seen_proxies_(0) {
79 5854743 : 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 5854743 : }
85 :
86 : ~PrototypeIterator() {}
87 :
88 10886670 : bool HasAccess() const {
89 : // We can only perform access check in the handlified version of the
90 : // PrototypeIterator.
91 : DCHECK(!handle_.is_null());
92 10886670 : 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 16798605 : void Advance() {
113 19424552 : if (handle_.is_null() && object_->IsJSProxy()) {
114 1432 : is_at_end_ = true;
115 1432 : object_ = isolate_->heap()->null_value();
116 1432 : return;
117 30969831 : } 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 16797167 : AdvanceIgnoringProxies();
123 : }
124 :
125 38694031 : void AdvanceIgnoringProxies() {
126 38694031 : Object* object = handle_.is_null() ? object_ : *handle_;
127 : Map* map = HeapObject::cast(object)->map();
128 :
129 : Object* prototype = map->prototype();
130 38694031 : is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN
131 : ? !map->has_hidden_prototype()
132 83314804 : : prototype->IsNull(isolate_);
133 :
134 38694032 : if (handle_.is_null()) {
135 2643670 : object_ = prototype;
136 : } else {
137 72100724 : handle_ = handle(prototype, isolate_);
138 : }
139 38694032 : }
140 :
141 : // Returns false iff a call to JSProxy::GetPrototype throws.
142 : // TODO(neis): This should probably replace Advance().
143 10886670 : MUST_USE_RESULT bool AdvanceFollowingProxies() {
144 : DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
145 10886670 : 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 10886379 : return AdvanceFollowingProxiesIgnoringAccessChecks();
152 : }
153 :
154 27580139 : MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() {
155 55160278 : if (handle_.is_null() || !handle_->IsJSProxy()) {
156 21877709 : AdvanceIgnoringProxies();
157 21877709 : 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_
|