Home | History | Annotate | Download | only in src
      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