blob: a3605df55c7070dbb9c60435838cf1a64a3102d3 [file] [log] [blame]
// 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"
#include "src/api/api-inl.h"
#include "src/base/flags.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/keys.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
namespace v8 {
std::unique_ptr<debug::PropertyIterator> debug::PropertyIterator::Create(
v8::Local<v8::Object> v8_object) {
internal::Isolate* isolate =
reinterpret_cast<internal::Isolate*>(v8_object->GetIsolate());
return std::unique_ptr<debug::PropertyIterator>(
new internal::DebugPropertyIterator(isolate,
Utils::OpenHandle(*v8_object)));
}
namespace internal {
DebugPropertyIterator::DebugPropertyIterator(Isolate* isolate,
Handle<JSReceiver> receiver)
: isolate_(isolate),
prototype_iterator_(isolate, receiver, kStartAtReceiver,
PrototypeIterator::END_AT_NULL) {
if (receiver->IsJSProxy()) {
is_own_ = false;
prototype_iterator_.AdvanceIgnoringProxies();
}
if (prototype_iterator_.IsAtEnd()) return;
FillKeysForCurrentPrototypeAndStage();
if (should_move_to_next_stage()) Advance();
}
bool DebugPropertyIterator::Done() const {
return prototype_iterator_.IsAtEnd();
}
void DebugPropertyIterator::Advance() {
++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:
stage_ = kExoticIndices;
is_own_ = false;
prototype_iterator_.AdvanceIgnoringProxies();
break;
}
FillKeysForCurrentPrototypeAndStage();
}
}
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) {
return isolate_->factory()->SizeToString(current_key_index_);
} else {
return Handle<Name>::cast(FixedArray::get(
*keys_, static_cast<int>(current_key_index_), isolate_));
}
}
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);
}
void DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
current_key_index_ = 0;
exotic_length_ = 0;
keys_ = Handle<FixedArray>::null();
if (prototype_iterator_.IsAtEnd()) return;
Handle<JSReceiver> receiver =
PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
bool has_exotic_indices = receiver->IsJSTypedArray();
if (stage_ == kExoticIndices) {
if (!has_exotic_indices) return;
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
exotic_length_ = typed_array->WasDetached() ? 0 : typed_array->length();
return;
}
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();
}
}
bool DebugPropertyIterator::should_move_to_next_stage() const {
if (prototype_iterator_.IsAtEnd()) return false;
if (stage_ == kExoticIndices) return current_key_index_ >= exotic_length_;
return keys_.is_null() ||
current_key_index_ >= static_cast<size_t>(keys_->length());
}
namespace {
base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
Handle<JSReceiver> object, Handle<Name> name) {
Isolate* isolate = object->GetIsolate();
LookupIterator::Key key(isolate, name);
if (key.is_element()) return debug::NativeAccessorType::None;
LookupIterator it(isolate, object, key, LookupIterator::OWN);
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;
#define IS_BUILTIN_ACCESSOR(_, name, ...) \
if (*structure == *isolate->factory()->name##_accessor()) \
return debug::NativeAccessorType::None;
ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACCESSOR, /* not used */)
#undef IS_BUILTIN_ACCESSOR
Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
if (accessor_info->getter() != Object()) {
result |= debug::NativeAccessorType::HasGetter;
}
if (accessor_info->setter() != Object()) {
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