prototype-inl.h 4.55 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_PROTOTYPE_INL_H_
#define V8_PROTOTYPE_INL_H_

#include "src/prototype.h"

10 11 12
#include "src/handles-inl.h"
#include "src/objects/map-inl.h"

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
namespace v8 {
namespace internal {

PrototypeIterator::PrototypeIterator(Isolate* isolate,
                                     Handle<JSReceiver> receiver,
                                     WhereToStart where_to_start,
                                     WhereToEnd where_to_end)
    : isolate_(isolate),
      handle_(receiver),
      where_to_end_(where_to_end),
      is_at_end_(false),
      seen_proxies_(0) {
  CHECK(!handle_.is_null());
  if (where_to_start == kStartAtPrototype) Advance();
}

29
PrototypeIterator::PrototypeIterator(Isolate* isolate, JSReceiver receiver,
30 31 32 33 34 35 36 37 38 39
                                     WhereToStart where_to_start,
                                     WhereToEnd where_to_end)
    : isolate_(isolate),
      object_(receiver),
      where_to_end_(where_to_end),
      is_at_end_(false),
      seen_proxies_(0) {
  if (where_to_start == kStartAtPrototype) Advance();
}

40
PrototypeIterator::PrototypeIterator(Isolate* isolate, Map receiver_map,
41 42 43 44 45 46 47 48
                                     WhereToEnd where_to_end)
    : isolate_(isolate),
      object_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype()),
      where_to_end_(where_to_end),
      is_at_end_(object_->IsNull(isolate_)),
      seen_proxies_(0) {
  if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
    DCHECK(object_->IsJSReceiver());
49
    Map map = JSReceiver::cast(object_)->map();
50 51 52 53 54 55 56 57 58 59 60 61 62 63
    is_at_end_ = !map->has_hidden_prototype();
  }
}

PrototypeIterator::PrototypeIterator(Isolate* isolate, Handle<Map> receiver_map,
                                     WhereToEnd where_to_end)
    : isolate_(isolate),
      handle_(receiver_map->GetPrototypeChainRootMap(isolate_)->prototype(),
              isolate_),
      where_to_end_(where_to_end),
      is_at_end_(handle_->IsNull(isolate_)),
      seen_proxies_(0) {
  if (!is_at_end_ && where_to_end_ == END_AT_NON_HIDDEN) {
    DCHECK(handle_->IsJSReceiver());
64
    Map map = JSReceiver::cast(*handle_)->map();
65 66 67 68
    is_at_end_ = !map->has_hidden_prototype();
  }
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
bool PrototypeIterator::HasAccess() const {
  // We can only perform access check in the handlified version of the
  // PrototypeIterator.
  DCHECK(!handle_.is_null());
  if (handle_->IsAccessCheckNeeded()) {
    return isolate_->MayAccess(handle(isolate_->context(), isolate_),
                               Handle<JSObject>::cast(handle_));
  }
  return true;
}

void PrototypeIterator::Advance() {
  if (handle_.is_null() && object_->IsJSProxy()) {
    is_at_end_ = true;
    object_ = ReadOnlyRoots(isolate_).null_value();
    return;
  } else if (!handle_.is_null() && handle_->IsJSProxy()) {
    is_at_end_ = true;
    handle_ = isolate_->factory()->null_value();
    return;
  }
  AdvanceIgnoringProxies();
}

void PrototypeIterator::AdvanceIgnoringProxies() {
94
  Object object = handle_.is_null() ? object_ : *handle_;
95
  Map map = HeapObject::cast(object)->map();
96

97
  Object prototype = map->prototype();
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN ? !map->has_hidden_prototype()
                                                  : prototype->IsNull(isolate_);

  if (handle_.is_null()) {
    object_ = prototype;
  } else {
    handle_ = handle(prototype, isolate_);
  }
}

V8_WARN_UNUSED_RESULT bool PrototypeIterator::AdvanceFollowingProxies() {
  DCHECK(!(handle_.is_null() && object_->IsJSProxy()));
  if (!HasAccess()) {
    // Abort the lookup if we do not have access to the current object.
    handle_ = isolate_->factory()->null_value();
    is_at_end_ = true;
    return true;
  }
  return AdvanceFollowingProxiesIgnoringAccessChecks();
}

V8_WARN_UNUSED_RESULT bool
PrototypeIterator::AdvanceFollowingProxiesIgnoringAccessChecks() {
  if (handle_.is_null() || !handle_->IsJSProxy()) {
    AdvanceIgnoringProxies();
    return true;
  }

  // Due to possible __proto__ recursion limit the number of Proxies
  // we visit to an arbitrarily chosen large number.
  seen_proxies_++;
  if (seen_proxies_ > JSProxy::kMaxIterationLimit) {
    isolate_->StackOverflow();
    return false;
  }
  MaybeHandle<Object> proto =
      JSProxy::GetPrototype(Handle<JSProxy>::cast(handle_));
  if (!proto.ToHandle(&handle_)) return false;
  is_at_end_ = where_to_end_ == END_AT_NON_HIDDEN || handle_->IsNull(isolate_);
  return true;
}

}  // namespace internal
}  // namespace v8

#endif  // V8_PROTOTYPE_INL_H_