1 // Copyright 2014 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_H_ 6 #define V8_PROTOTYPE_H_ 7 8 #include "src/isolate.h" 9 #include "src/objects.h" 10 11 namespace v8 { 12 namespace internal { 13 14 /** 15 * A class to uniformly access the prototype of any Object and walk its 16 * prototype chain. 17 * 18 * The PrototypeIterator can either start at the prototype (default), or 19 * include the receiver itself. If a PrototypeIterator is constructed for a 20 * Map, it will always start at the prototype. 21 * 22 * The PrototypeIterator can either run to the null_value(), the first 23 * non-hidden prototype, or a given object. 24 */ 25 26 class PrototypeIterator { 27 public: 28 enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN }; 29 30 const int kProxyPrototypeLimit = 100 * 1000; 31 32 PrototypeIterator(Isolate* isolate, Handle<JSReceiver> receiver, 33 WhereToStart where_to_start = kStartAtPrototype, 34 WhereToEnd where_to_end = END_AT_NULL) isolate_(isolate)35 : isolate_(isolate), 36 object_(NULL), 37 handle_(receiver), 38 where_to_end_(where_to_end), 39 is_at_end_(false), 40 seen_proxies_(0) { 41 CHECK(!handle_.is_null()); 42 if (where_to_start == kStartAtPrototype) Advance(); 43 } 44 45 PrototypeIterator(Isolate* isolate, JSReceiver* receiver, 46 WhereToStart where_to_start = kStartAtPrototype, 47 WhereToEnd where_to_end = END_AT_NULL) isolate_(isolate)48 : isolate_(isolate), 49 object_(receiver), 50 where_to_end_(where_to_end), 51 is_at_end_(false), 52 seen_proxies_(0) { 53 if (where_to_start == kStartAtPrototype) Advance(); 54 } 55 56 explicit PrototypeIterator(Map* receiver_map, 57 WhereToEnd where_to_end = END_AT_NULL) 58 : isolate_(receiver_map->GetIsolate()), 59 object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()), 60 where_to_end_(where_to_end), 61 is_at_end_(object_->IsNull(isolate_)), 62 seen_proxies_(0) { 63 if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) { 64 DCHECK(object_->IsJSReceiver()); 65 Map* map = JSReceiver::cast(object_)->map(); 66 is_at_end_ = !map->has_hidden_prototype(); 67 } 68 } 69 70 explicit PrototypeIterator(Handle<Map> receiver_map, 71 WhereToEnd where_to_end = END_AT_NULL) 72 : isolate_(receiver_map->GetIsolate()), 73 object_(NULL), 74 handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(), 75 isolate_), 76 where_to_end_(where_to_end), 77 is_at_end_(handle_->IsNull(isolate_)), 78 seen_proxies_(0) { 79 if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) { 80 DCHECK(handle_->IsJSReceiver()); 81 Map* map = JSReceiver::cast(*handle_)->map(); 82 is_at_end_ = !map->has_hidden_prototype(); 83 } 84 } 85 ~PrototypeIterator()86 ~PrototypeIterator() {} 87 HasAccess()88 bool HasAccess() const { 89 // We can only perform access check in the handlified version of the 90 // PrototypeIterator. 91 DCHECK(!handle_.is_null()); 92 if (handle_->IsAccessCheckNeeded()) { 93 return isolate_->MayAccess(handle(isolate_->context()), 94 Handle<JSObject>::cast(handle_)); 95 } 96 return true; 97 } 98 99 template <typename T = Object> GetCurrent()100 T* GetCurrent() const { 101 DCHECK(handle_.is_null()); 102 return T::cast(object_); 103 } 104 105 template <typename T = Object> GetCurrent(const PrototypeIterator & iterator)106 static Handle<T> GetCurrent(const PrototypeIterator& iterator) { 107 DCHECK(!iterator.handle_.is_null()); 108 DCHECK(iterator.object_ == NULL); 109 return Handle<T>::cast(iterator.handle_); 110 } 111 Advance()112 void Advance() { 113 if (handle_.is_null() && object_->IsJSProxy()) { 114 is_at_end_ = true; 115 object_ = isolate_->heap()->null_value(); 116 return; 117 } else if (!handle_.is_null() && handle_->IsJSProxy()) { 118 is_at_end_ = true; 119 handle_ = isolate_->factory()->null_value(); 120 return; 121 } 122 AdvanceIgnoringProxies(); 123 } 124 AdvanceIgnoringProxies()125 void AdvanceIgnoringProxies() { 126 Object* object = handle_.is_null() ? object_ : *handle_; 127 Map* map = HeapObject::cast(object)->map(); 128 129 Object* prototype = map->prototype(); 130 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN 131 ? !map->has_hidden_prototype() 132 : prototype->IsNull(isolate_); 133 134 if (handle_.is_null()) { 135 object_ = prototype; 136 } else { 137 handle_ = handle(prototype, isolate_); 138 } 139 } 140 141 // Returns false iff a call to JSProxy::GetPrototype throws. 142 // TODO(neis): This should probably replace Advance(). AdvanceFollowingProxies()143 MUST_USE_RESULT bool AdvanceFollowingProxies() { 144 DCHECK(!(handle_.is_null() && object_->IsJSProxy())); 145 if (!HasAccess()) { 146 // Abort the lookup if we do not have access to the current object. 147 handle_ = isolate_->factory()->null_value(); 148 is_at_end_ = true; 149 return true; 150 } 151 return AdvanceFollowingProxiesIgnoringAccessChecks(); 152 } 153 AdvanceFollowingProxiesIgnoringAccessChecks()154 MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() { 155 if (handle_.is_null() || !handle_->IsJSProxy()) { 156 AdvanceIgnoringProxies(); 157 return true; 158 } 159 160 // Due to possible __proto__ recursion limit the number of Proxies 161 // we visit to an arbitrarily chosen large number. 162 seen_proxies_++; 163 if (seen_proxies_ > kProxyPrototypeLimit) { 164 isolate_->StackOverflow(); 165 return false; 166 } 167 MaybeHandle<Object> proto = 168 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_)); 169 if (!proto.ToHandle(&handle_)) return false; 170 is_at_end_ = 171 where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_); 172 return true; 173 } 174 IsAtEnd()175 bool IsAtEnd() const { return is_at_end_; } isolate()176 Isolate* isolate() const { return isolate_; } 177 178 private: 179 Isolate* isolate_; 180 Object* object_; 181 Handle<Object> handle_; 182 WhereToEnd where_to_end_; 183 bool is_at_end_; 184 int seen_proxies_; 185 186 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator); 187 }; 188 189 190 } // namespace internal 191 192 } // namespace v8 193 194 #endif // V8_PROTOTYPE_H_ 195