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
14namespace v8 {
15namespace internal {
16
17PrototypeIterator::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
30PrototypeIterator::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
41PrototypeIterator::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
55PrototypeIterator::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
70bool 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
81void 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
94void 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
109V8_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
120V8_WARN_UNUSED_RESULT bool
121PrototypeIterator::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