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) 35 : object_(NULL), 36 handle_(receiver), 37 isolate_(isolate), 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) 48 : object_(receiver), 49 isolate_(isolate), 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 : object_(receiver_map->prototype()), 58 isolate_(receiver_map->GetIsolate()), 59 where_to_end_(END_AT_NULL), 60 is_at_end_(object_->IsNull(isolate_)), 61 seen_proxies_(0) {} 62 63 explicit PrototypeIterator(Handle<Map> receiver_map) 64 : object_(NULL), 65 handle_(handle(receiver_map->prototype(), receiver_map->GetIsolate())), 66 isolate_(receiver_map->GetIsolate()), 67 where_to_end_(END_AT_NULL), 68 is_at_end_(handle_->IsNull(isolate_)), 69 seen_proxies_(0) {} 70 71 ~PrototypeIterator() {} 72 73 bool HasAccess() const { 74 // We can only perform access check in the handlified version of the 75 // PrototypeIterator. 76 DCHECK(!handle_.is_null()); 77 if (handle_->IsAccessCheckNeeded()) { 78 return isolate_->MayAccess(handle(isolate_->context()), 79 Handle<JSObject>::cast(handle_)); 80 } 81 return true; 82 } 83 84 template <typename T = Object> 85 T* GetCurrent() const { 86 DCHECK(handle_.is_null()); 87 return T::cast(object_); 88 } 89 90 template <typename T = Object> 91 static Handle<T> GetCurrent(const PrototypeIterator& iterator) { 92 DCHECK(!iterator.handle_.is_null()); 93 DCHECK(iterator.object_ == NULL); 94 return Handle<T>::cast(iterator.handle_); 95 } 96 97 void Advance() { 98 if (handle_.is_null() && object_->IsJSProxy()) { 99 is_at_end_ = true; 100 object_ = isolate_->heap()->null_value(); 101 return; 102 } else if (!handle_.is_null() && handle_->IsJSProxy()) { 103 is_at_end_ = true; 104 handle_ = isolate_->factory()->null_value(); 105 return; 106 } 107 AdvanceIgnoringProxies(); 108 } 109 110 void AdvanceIgnoringProxies() { 111 Object* object = handle_.is_null() ? object_ : *handle_; 112 Map* map = HeapObject::cast(object)->map(); 113 114 Object* prototype = map->prototype(); 115 is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN 116 ? !map->has_hidden_prototype() 117 : prototype->IsNull(isolate_); 118 119 if (handle_.is_null()) { 120 object_ = prototype; 121 } else { 122 handle_ = handle(prototype, isolate_); 123 } 124 } 125 126 // Returns false iff a call to JSProxy::GetPrototype throws. 127 // TODO(neis): This should probably replace Advance(). 128 MUST_USE_RESULT bool AdvanceFollowingProxies() { 129 DCHECK(!(handle_.is_null() && object_->IsJSProxy())); 130 if (!HasAccess()) { 131 // Abort the lookup if we do not have access to the current object. 132 handle_ = isolate_->factory()->null_value(); 133 is_at_end_ = true; 134 return true; 135 } 136 return AdvanceFollowingProxiesIgnoringAccessChecks(); 137 } 138 139 MUST_USE_RESULT bool AdvanceFollowingProxiesIgnoringAccessChecks() { 140 if (handle_.is_null() || !handle_->IsJSProxy()) { 141 AdvanceIgnoringProxies(); 142 return true; 143 } 144 145 // Due to possible __proto__ recursion limit the number of Proxies 146 // we visit to an arbitrarily chosen large number. 147 seen_proxies_++; 148 if (seen_proxies_ > kProxyPrototypeLimit) { 149 isolate_->Throw( 150 *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow)); 151 return false; 152 } 153 MaybeHandle<Object> proto = 154 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_)); 155 if (!proto.ToHandle(&handle_)) return false; 156 is_at_end_ = 157 where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_); 158 return true; 159 } 160 161 bool IsAtEnd() const { return is_at_end_; } 162 163 private: 164 Object* object_; 165 Handle<Object> handle_; 166 Isolate* isolate_; 167 WhereToEnd where_to_end_; 168 bool is_at_end_; 169 int seen_proxies_; 170 171 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator); 172 }; 173 174 175 } // namespace internal 176 177 } // namespace v8 178 179 #endif // V8_PROTOTYPE_H_ 180