debug-property-iterator.cc 7.75 KB
Newer Older
1 2 3 4 5 6
// 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.

#include "src/debug/debug-property-iterator.h"

7
#include "src/api/api-inl.h"
8 9
#include "src/base/flags.h"
#include "src/objects/js-array-buffer-inl.h"
10
#include "src/objects/keys.h"
11 12
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
13 14

namespace v8 {
15
namespace internal {
16

17 18 19 20 21
std::unique_ptr<DebugPropertyIterator> DebugPropertyIterator::Create(
    Isolate* isolate, Handle<JSReceiver> receiver) {
  // Can't use std::make_unique as Ctor is private.
  auto iterator = std::unique_ptr<DebugPropertyIterator>(
      new DebugPropertyIterator(isolate, receiver));
22

23
  if (receiver->IsJSProxy()) {
24
    iterator->AdvanceToPrototype();
25
  }
26
  if (iterator->Done()) return iterator;
27 28 29 30 31 32 33 34

  if (!iterator->FillKeysForCurrentPrototypeAndStage()) return nullptr;
  if (iterator->should_move_to_next_stage() && !iterator->AdvanceInternal()) {
    return nullptr;
  }

  return iterator;
}
35 36 37 38 39

DebugPropertyIterator::DebugPropertyIterator(Isolate* isolate,
                                             Handle<JSReceiver> receiver)
    : isolate_(isolate),
      prototype_iterator_(isolate, receiver, kStartAtReceiver,
40
                          PrototypeIterator::END_AT_NULL) {}
41

42 43 44 45 46 47 48 49
bool DebugPropertyIterator::Done() const { return is_done_; }

void DebugPropertyIterator::AdvanceToPrototype() {
  stage_ = kExoticIndices;
  is_own_ = false;
  if (!prototype_iterator_.HasAccess()) is_done_ = true;
  prototype_iterator_.AdvanceIgnoringProxies();
  if (prototype_iterator_.IsAtEnd()) is_done_ = true;
50 51
}

52
bool DebugPropertyIterator::AdvanceInternal() {
53 54 55 56 57 58 59 60 61 62 63
  ++current_key_index_;
  calculated_native_accessor_flags_ = false;
  while (should_move_to_next_stage()) {
    switch (stage_) {
      case Stage::kExoticIndices:
        stage_ = Stage::kEnumerableStrings;
        break;
      case Stage::kEnumerableStrings:
        stage_ = Stage::kAllProperties;
        break;
      case Stage::kAllProperties:
64
        AdvanceToPrototype();
65 66
        break;
    }
67
    if (!FillKeysForCurrentPrototypeAndStage()) return false;
68
  }
69
  return true;
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
}

bool DebugPropertyIterator::is_native_accessor() {
  if (stage_ == kExoticIndices) return false;
  CalculateNativeAccessorFlags();
  return native_accessor_flags_;
}

bool DebugPropertyIterator::has_native_getter() {
  if (stage_ == kExoticIndices) return false;
  CalculateNativeAccessorFlags();
  return native_accessor_flags_ &
         static_cast<int>(debug::NativeAccessorType::HasGetter);
}

bool DebugPropertyIterator::has_native_setter() {
  if (stage_ == kExoticIndices) return false;
  CalculateNativeAccessorFlags();
  return native_accessor_flags_ &
         static_cast<int>(debug::NativeAccessorType::HasSetter);
}

Handle<Name> DebugPropertyIterator::raw_name() const {
  DCHECK(!Done());
  if (stage_ == kExoticIndices) {
95
    return isolate_->factory()->SizeToString(current_key_index_);
96
  } else {
97 98
    return Handle<Name>::cast(FixedArray::get(
        *keys_, static_cast<int>(current_key_index_), isolate_));
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 144 145 146
  }
}

v8::Local<v8::Name> DebugPropertyIterator::name() const {
  return Utils::ToLocal(raw_name());
}

v8::Maybe<v8::PropertyAttribute> DebugPropertyIterator::attributes() {
  Handle<JSReceiver> receiver =
      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
  auto result = JSReceiver::GetPropertyAttributes(receiver, raw_name());
  if (result.IsNothing()) return Nothing<v8::PropertyAttribute>();
  DCHECK(result.FromJust() != ABSENT);
  return Just(static_cast<v8::PropertyAttribute>(result.FromJust()));
}

v8::Maybe<v8::debug::PropertyDescriptor> DebugPropertyIterator::descriptor() {
  Handle<JSReceiver> receiver =
      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);

  PropertyDescriptor descriptor;
  Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
      isolate_, receiver, raw_name(), &descriptor);
  if (did_get_descriptor.IsNothing()) {
    return Nothing<v8::debug::PropertyDescriptor>();
  }
  DCHECK(did_get_descriptor.FromJust());
  return Just(v8::debug::PropertyDescriptor{
      descriptor.enumerable(), descriptor.has_enumerable(),
      descriptor.configurable(), descriptor.has_configurable(),
      descriptor.writable(), descriptor.has_writable(),
      descriptor.has_value() ? Utils::ToLocal(descriptor.value())
                             : v8::Local<v8::Value>(),
      descriptor.has_get() ? Utils::ToLocal(descriptor.get())
                           : v8::Local<v8::Value>(),
      descriptor.has_set() ? Utils::ToLocal(descriptor.set())
                           : v8::Local<v8::Value>(),
  });
}

bool DebugPropertyIterator::is_own() { return is_own_; }

bool DebugPropertyIterator::is_array_index() {
  if (stage_ == kExoticIndices) return true;
  uint32_t index = 0;
  return raw_name()->AsArrayIndex(&index);
}

147
bool DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
148 149 150
  current_key_index_ = 0;
  exotic_length_ = 0;
  keys_ = Handle<FixedArray>::null();
151
  if (is_done_) return true;
152 153 154 155
  Handle<JSReceiver> receiver =
      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
  bool has_exotic_indices = receiver->IsJSTypedArray();
  if (stage_ == kExoticIndices) {
156
    if (!has_exotic_indices) return true;
157
    Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
158
    exotic_length_ = typed_array->WasDetached() ? 0 : typed_array->length();
159
    return true;
160 161 162 163 164 165 166 167 168
  }
  bool skip_indices = has_exotic_indices;
  PropertyFilter filter =
      stage_ == kEnumerableStrings ? ENUMERABLE_STRINGS : ALL_PROPERTIES;
  if (!KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
                               GetKeysConversion::kConvertToString, false,
                               skip_indices)
           .ToHandle(&keys_)) {
    keys_ = Handle<FixedArray>::null();
169
    return false;
170
  }
171
  return true;
172 173 174
}

bool DebugPropertyIterator::should_move_to_next_stage() const {
175
  if (is_done_) return false;
176 177
  if (stage_ == kExoticIndices) return current_key_index_ >= exotic_length_;
  return keys_.is_null() ||
178
         current_key_index_ >= static_cast<size_t>(keys_->length());
179 180 181 182 183
}

namespace {
base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
    Handle<JSReceiver> object, Handle<Name> name) {
184
  Isolate* isolate = object->GetIsolate();
185
  PropertyKey key(isolate, name);
186 187
  if (key.is_element()) return debug::NativeAccessorType::None;
  LookupIterator it(isolate, object, key, LookupIterator::OWN);
188 189 190 191 192 193 194
  if (!it.IsFound()) return debug::NativeAccessorType::None;
  if (it.state() != LookupIterator::ACCESSOR) {
    return debug::NativeAccessorType::None;
  }
  Handle<Object> structure = it.GetAccessors();
  if (!structure->IsAccessorInfo()) return debug::NativeAccessorType::None;
  base::Flags<debug::NativeAccessorType, int> result;
195
#define IS_BUILTIN_ACCESSOR(_, name, ...)                   \
196 197
  if (*structure == *isolate->factory()->name##_accessor()) \
    return debug::NativeAccessorType::None;
198 199
  ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACCESSOR, /* not used */)
#undef IS_BUILTIN_ACCESSOR
200
  Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
201
  if (accessor_info->getter() != Object()) {
202 203
    result |= debug::NativeAccessorType::HasGetter;
  }
204
  if (accessor_info->setter() != Object()) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    result |= debug::NativeAccessorType::HasSetter;
  }
  return result;
}
}  // anonymous namespace

void DebugPropertyIterator::CalculateNativeAccessorFlags() {
  if (calculated_native_accessor_flags_) return;
  Handle<JSReceiver> receiver =
      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
  native_accessor_flags_ =
      GetNativeAccessorDescriptorInternal(receiver, raw_name());
  calculated_native_accessor_flags_ = true;
}
}  // namespace internal
}  // namespace v8