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 class PrototypeIterator { 26 public: 27 enum WhereToStart { START_AT_RECEIVER, START_AT_PROTOTYPE }; 28 29 enum WhereToEnd { END_AT_NULL, END_AT_NON_HIDDEN }; 30 31 const int kProxyPrototypeLimit = 100 * 1000; 32 33 PrototypeIterator(Isolate* isolate, Handle<Object> receiver, 34 WhereToStart where_to_start = START_AT_PROTOTYPE) did_jump_to_prototype_chain_(false)35 : did_jump_to_prototype_chain_(false), 36 object_(NULL), 37 handle_(receiver), 38 isolate_(isolate), 39 seen_proxies_(0) { 40 CHECK(!handle_.is_null()); 41 if (where_to_start == START_AT_PROTOTYPE) { 42 Advance(); 43 } 44 } 45 46 PrototypeIterator(Isolate* isolate, Object* receiver, 47 WhereToStart where_to_start = START_AT_PROTOTYPE) did_jump_to_prototype_chain_(false)48 : did_jump_to_prototype_chain_(false), 49 object_(receiver), 50 isolate_(isolate), 51 seen_proxies_(0) { 52 if (where_to_start == START_AT_PROTOTYPE) { 53 Advance(); 54 } 55 } 56 PrototypeIterator(Map * receiver_map)57 explicit PrototypeIterator(Map* receiver_map) 58 : did_jump_to_prototype_chain_(true), 59 object_(receiver_map->prototype()), 60 isolate_(receiver_map->GetIsolate()) {} 61 PrototypeIterator(Handle<Map> receiver_map)62 explicit PrototypeIterator(Handle<Map> receiver_map) 63 : did_jump_to_prototype_chain_(true), 64 object_(NULL), 65 handle_(handle(receiver_map->prototype(), receiver_map->GetIsolate())), 66 isolate_(receiver_map->GetIsolate()) {} 67 ~PrototypeIterator()68 ~PrototypeIterator() {} 69 HasAccess()70 bool 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()), 76 Handle<JSObject>::cast(handle_)); 77 } 78 return true; 79 } 80 81 template <typename T = Object> GetCurrent()82 T* GetCurrent() const { 83 DCHECK(handle_.is_null()); 84 return T::cast(object_); 85 } 86 87 template <typename T = Object> GetCurrent(const PrototypeIterator & iterator)88 static Handle<T> GetCurrent(const PrototypeIterator& iterator) { 89 DCHECK(!iterator.handle_.is_null()); 90 DCHECK(iterator.object_ == NULL); 91 return Handle<T>::cast(iterator.handle_); 92 } 93 Advance()94 void Advance() { 95 if (handle_.is_null() && object_->IsJSProxy()) { 96 did_jump_to_prototype_chain_ = true; 97 object_ = isolate_->heap()->null_value(); 98 return; 99 } else if (!handle_.is_null() && handle_->IsJSProxy()) { 100 did_jump_to_prototype_chain_ = true; 101 handle_ = handle(isolate_->heap()->null_value(), isolate_); 102 return; 103 } 104 AdvanceIgnoringProxies(); 105 } 106 AdvanceIgnoringProxies()107 void AdvanceIgnoringProxies() { 108 if (!did_jump_to_prototype_chain_) { 109 did_jump_to_prototype_chain_ = true; 110 if (handle_.is_null()) { 111 object_ = object_->GetRootMap(isolate_)->prototype(); 112 } else { 113 handle_ = handle(handle_->GetRootMap(isolate_)->prototype(), isolate_); 114 } 115 } else { 116 if (handle_.is_null()) { 117 object_ = HeapObject::cast(object_)->map()->prototype(); 118 } else { 119 handle_ = 120 handle(HeapObject::cast(*handle_)->map()->prototype(), isolate_); 121 } 122 } 123 } 124 125 // Returns false iff a call to JSProxy::GetPrototype throws. 126 // TODO(neis): This should probably replace Advance(). AdvanceFollowingProxies()127 bool AdvanceFollowingProxies() { 128 DCHECK(!(handle_.is_null() && object_->IsJSProxy())); 129 if (!HasAccess()) { 130 // Abort the lookup if we do not have access to the current object. 131 handle_ = isolate_->factory()->null_value(); 132 return true; 133 } 134 if (handle_.is_null() || !handle_->IsJSProxy()) { 135 AdvanceIgnoringProxies(); 136 return true; 137 } 138 // Due to possible __proto__ recursion limit the number of Proxies 139 // we visit to an arbitrarily chosen large number. 140 seen_proxies_++; 141 if (seen_proxies_ > kProxyPrototypeLimit) { 142 isolate_->Throw( 143 *isolate_->factory()->NewRangeError(MessageTemplate::kStackOverflow)); 144 return false; 145 } 146 did_jump_to_prototype_chain_ = true; 147 MaybeHandle<Object> proto = 148 JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_)); 149 return proto.ToHandle(&handle_); 150 } 151 152 bool IsAtEnd(WhereToEnd where_to_end = END_AT_NULL) const { 153 if (handle_.is_null()) { 154 return object_->IsNull() || 155 (did_jump_to_prototype_chain_ && 156 where_to_end == END_AT_NON_HIDDEN && 157 !HeapObject::cast(object_)->map()->is_hidden_prototype()); 158 } else { 159 return handle_->IsNull() || 160 (did_jump_to_prototype_chain_ && 161 where_to_end == END_AT_NON_HIDDEN && 162 !Handle<HeapObject>::cast(handle_)->map()->is_hidden_prototype()); 163 } 164 } 165 IsAtEnd(Object * final_object)166 bool IsAtEnd(Object* final_object) { 167 DCHECK(handle_.is_null()); 168 return object_->IsNull() || object_ == final_object; 169 } 170 IsAtEnd(Handle<Object> final_object)171 bool IsAtEnd(Handle<Object> final_object) { 172 DCHECK(!handle_.is_null()); 173 return handle_->IsNull() || *handle_ == *final_object; 174 } 175 176 private: 177 bool did_jump_to_prototype_chain_; 178 Object* object_; 179 Handle<Object> handle_; 180 Isolate* isolate_; 181 int seen_proxies_; 182 183 DISALLOW_COPY_AND_ASSIGN(PrototypeIterator); 184 }; 185 186 187 } // namespace internal 188 189 } // namespace v8 190 191 #endif // V8_PROTOTYPE_H_ 192