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_OBJECTS_PROTOTYPE_INL_H_
6 #define V8_OBJECTS_PROTOTYPE_INL_H_
7
8 #include "src/objects/prototype.h"
9
10 #include "src/handles/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
PrototypeIterator(Isolate * isolate,Handle<JSReceiver> receiver,WhereToStart where_to_start,WhereToEnd where_to_end)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
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.IsJSGlobalProxyMap();
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 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.IsJSGlobalProxyMap();
67 }
68 }
69
HasAccess()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
Advance()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
AdvanceIgnoringProxies()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_ =
100 prototype.IsNull(isolate_) ||
101 (where_to_end_ == END_AT_NON_HIDDEN && !map.IsJSGlobalProxyMap());
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<HeapObject> 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_OBJECTS_PROTOTYPE_INL_H_
146