1 | // Copyright 2018 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_INL_H_ |
6 | #define V8_PROTOTYPE_INL_H_ |
7 | |
8 | #include "src/prototype.h" |
9 | |
10 | #include "src/handles-inl.h" |
11 | #include "src/objects/js-proxy.h" |
12 | #include "src/objects/map-inl.h" |
13 | |
14 | namespace v8 { |
15 | namespace internal { |
16 | |
17 | PrototypeIterator::PrototypeIterator(Isolate* isolate, |
18 | Handle<JSReceiver> receiver, |
19 | WhereToStart where_to_start, |
20 | WhereToEnd where_to_end) |
21 | : isolate_(isolate), |
22 | handle_(receiver), |
23 | where_to_end_(where_to_end), |
24 | is_at_end_(false), |
25 | seen_proxies_(0) { |
26 | CHECK(!handle_.is_null()); |
27 | if (where_to_start == kStartAtPrototype) Advance(); |
28 | } |
29 | |
30 | PrototypeIterator::PrototypeIterator(Isolate* isolate, JSReceiver receiver, |
31 | WhereToStart where_to_start, |
32 | WhereToEnd where_to_end) |
33 | : isolate_(isolate), |
34 | object_(receiver), |
35 | where_to_end_(where_to_end), |
36 | is_at_end_(false), |
37 | seen_proxies_(0) { |
38 | if (where_to_start == kStartAtPrototype) Advance(); |
39 | } |
40 | |
41 | PrototypeIterator::PrototypeIterator(Isolate* isolate, Map receiver_map, |
42 | WhereToEnd where_to_end) |
43 | : isolate_(isolate), |
44 | object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()), |
45 | where_to_end_(where_to_end), |
46 | is_at_end_(object_->IsNull(isolate_)), |
47 | seen_proxies_(0) { |
48 | if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) { |
49 | DCHECK(object_->IsJSReceiver()); |
50 | Map map = JSReceiver::cast(object_)->map(); |
51 | is_at_end_ = !map->has_hidden_prototype(); |
52 | } |
53 | } |
54 | |
55 | PrototypeIterator::PrototypeIterator(Isolate* isolate, Handle<Map> receiver_map, |
56 | WhereToEnd where_to_end) |
57 | : isolate_(isolate), |
58 | handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(), |
59 | isolate_), |
60 | where_to_end_(where_to_end), |
61 | is_at_end_(handle_->IsNull(isolate_)), |
62 | seen_proxies_(0) { |
63 | if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) { |
64 | DCHECK(handle_->IsJSReceiver()); |
65 | Map map = JSReceiver::cast(*handle_)->map(); |
66 | is_at_end_ = !map->has_hidden_prototype(); |
67 | } |
68 | } |
69 | |
70 | bool PrototypeIterator::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(), isolate_), |
76 | Handle<JSObject>::cast(handle_)); |
77 | } |
78 | return true; |
79 | } |
80 | |
81 | void PrototypeIterator::Advance() { |
82 | if (handle_.is_null() && object_->IsJSProxy()) { |
83 | is_at_end_ = true; |
84 | object_ = ReadOnlyRoots(isolate_).null_value(); |
85 | return; |
86 | } else if (!handle_.is_null() && handle_->IsJSProxy()) { |
87 | is_at_end_ = true; |
88 | handle_ = isolate_->factory()->null_value(); |
89 | return; |
90 | } |
91 | AdvanceIgnoringProxies(); |
92 | } |
93 | |
94 | void PrototypeIterator::AdvanceIgnoringProxies() { |
95 | Object object = handle_.is_null() ? object_ : *handle_; |
96 | Map map = HeapObject::cast(object)->map(); |
97 | |
98 | HeapObject prototype = map->prototype(); |
99 | is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN ? !map->has_hidden_prototype() |
100 | : prototype->IsNull(isolate_); |
101 | |
102 | if (handle_.is_null()) { |
103 | object_ = prototype; |
104 | } else { |
105 | handle_ = handle(prototype, isolate_); |
106 | } |
107 | } |
108 | |
109 | V8_WARN_UNUSED_RESULT bool PrototypeIterator::AdvanceFollowingProxies() { |
110 | DCHECK(!(handle_.is_null() && object_->IsJSProxy())); |
111 | if (!HasAccess()) { |
112 | // Abort the lookup if we do not have access to the current object. |
113 | handle_ = isolate_->factory()->null_value(); |
114 | is_at_end_ = true; |
115 | return true; |
116 | } |
117 | return AdvanceFollowingProxiesIgnoringAccessChecks(); |
118 | } |
119 | |
120 | V8_WARN_UNUSED_RESULT bool |
121 | PrototypeIterator::AdvanceFollowingProxiesIgnoringAccessChecks() { |
122 | if (handle_.is_null() || !handle_->IsJSProxy()) { |
123 | AdvanceIgnoringProxies(); |
124 | return true; |
125 | } |
126 | |
127 | // Due to possible __proto__ recursion limit the number of Proxies |
128 | // we visit to an arbitrarily chosen large number. |
129 | seen_proxies_++; |
130 | if (seen_proxies_ > JSProxy::kMaxIterationLimit) { |
131 | isolate_->StackOverflow(); |
132 | return false; |
133 | } |
134 | MaybeHandle<HeapObject> proto = |
135 | JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_)); |
136 | if (!proto.ToHandle(&handle_)) return false; |
137 | is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_); |
138 | return true; |
139 | } |
140 | |
141 | } // namespace internal |
142 | } // namespace v8 |
143 | |
144 | #endif // V8_PROTOTYPE_INL_H_ |
145 | |