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 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 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 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 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 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 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 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 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