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/map-inl.h"
12
13 namespace v8 {
14 namespace internal {
15
PrototypeIterator(Isolate * isolate,Handle<JSReceiver> receiver,WhereToStart where_to_start,WhereToEnd where_to_end)16 PrototypeIterator::PrototypeIterator(Isolate* isolate,
17 Handle<JSReceiver> receiver,
18 WhereToStart where_to_start,
19 WhereToEnd where_to_end)
20 : isolate_(isolate),
21 object_(nullptr),
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
PrototypeIterator(Isolate * isolate,JSReceiver * receiver,WhereToStart where_to_start,WhereToEnd where_to_end)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
PrototypeIterator(Isolate * isolate,Map * receiver_map,WhereToEnd where_to_end)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
PrototypeIterator(Isolate * isolate,Handle<Map> receiver_map,WhereToEnd where_to_end)55 PrototypeIterator::PrototypeIterator(Isolate* isolate, Handle<Map> receiver_map,
56 WhereToEnd where_to_end)
57 : isolate_(isolate),
58 object_(nullptr),
59 handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(),
60 isolate_),
61 where_to_end_(where_to_end),
62 is_at_end_(handle_->IsNull(isolate_)),
63 seen_proxies_(0) {
64 if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
65 DCHECK(handle_->IsJSReceiver());
66 Map* map = JSReceiver::cast(*handle_)->map();
67 is_at_end_ = !map->has_hidden_prototype();
68 }
69 }
70
HasAccess()71 bool PrototypeIterator::HasAccess() const {
72 // We can only perform access check in the handlified version of the
73 // PrototypeIterator.
74 DCHECK(!handle_.is_null());
75 if (handle_->IsAccessCheckNeeded()) {
76 return isolate_->MayAccess(handle(isolate_->context(), isolate_),
77 Handle<JSObject>::cast(handle_));
78 }
79 return true;
80 }
81
Advance()82 void PrototypeIterator::Advance() {
83 if (handle_.is_null() && object_->IsJSProxy()) {
84 is_at_end_ = true;
85 object_ = ReadOnlyRoots(isolate_).null_value();
86 return;
87 } else if (!handle_.is_null() && handle_->IsJSProxy()) {
88 is_at_end_ = true;
89 handle_ = isolate_->factory()->null_value();
90 return;
91 }
92 AdvanceIgnoringProxies();
93 }
94
AdvanceIgnoringProxies()95 void PrototypeIterator::AdvanceIgnoringProxies() {
96 Object* object = handle_.is_null() ? object_ : *handle_;
97 Map* map = HeapObject::cast(object)->map();
98
99 Object* prototype = map->prototype();
100 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN ? !map->has_hidden_prototype()
101 : prototype->IsNull(isolate_);
102
103 if (handle_.is_null()) {
104 object_ = prototype;
105 } else {
106 handle_ = handle(prototype, isolate_);
107 }
108 }
109
AdvanceFollowingProxies()110 V8_WARN_UNUSED_RESULT bool PrototypeIterator::AdvanceFollowingProxies() {
111 DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
112 if (!HasAccess()) {
113 // Abort the lookup if we do not have access to the current object.
114 handle_ = isolate_->factory()->null_value();
115 is_at_end_ = true;
116 return true;
117 }
118 return AdvanceFollowingProxiesIgnoringAccessChecks();
119 }
120
121 V8_WARN_UNUSED_RESULT bool
AdvanceFollowingProxiesIgnoringAccessChecks()122 PrototypeIterator::AdvanceFollowingProxiesIgnoringAccessChecks() {
123 if (handle_.is_null() || !handle_->IsJSProxy()) {
124 AdvanceIgnoringProxies();
125 return true;
126 }
127
128 // Due to possible __proto__ recursion limit the number of Proxies
129 // we visit to an arbitrarily chosen large number.
130 seen_proxies_++;
131 if (seen_proxies_ > JSProxy::kMaxIterationLimit) {
132 isolate_->StackOverflow();
133 return false;
134 }
135 MaybeHandle<Object> proto =
136 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
137 if (!proto.ToHandle(&handle_)) return false;
138 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_);
139 return true;
140 }
141
142 } // namespace internal
143 } // namespace v8
144
145 #endif // V8_PROTOTYPE_INL_H_
146