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