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 #include "src/debug/debug-property-iterator.h"
6
7 #include "src/api/api-inl.h"
8 #include "src/base/flags.h"
9 #include "src/objects/js-array-buffer-inl.h"
10 #include "src/objects/keys.h"
11 #include "src/objects/property-descriptor.h"
12 #include "src/objects/property-details.h"
13
14 namespace v8 {
15 namespace internal {
16
Create(Isolate * isolate,Handle<JSReceiver> receiver,bool skip_indices)17 std::unique_ptr<DebugPropertyIterator> DebugPropertyIterator::Create(
18 Isolate* isolate, Handle<JSReceiver> receiver, bool skip_indices) {
19 // Can't use std::make_unique as Ctor is private.
20 auto iterator = std::unique_ptr<DebugPropertyIterator>(
21 new DebugPropertyIterator(isolate, receiver, skip_indices));
22
23 if (receiver->IsJSProxy()) {
24 iterator->AdvanceToPrototype();
25 }
26
27 if (!iterator->FillKeysForCurrentPrototypeAndStage()) return nullptr;
28 if (iterator->should_move_to_next_stage() && !iterator->AdvanceInternal()) {
29 return nullptr;
30 }
31
32 return iterator;
33 }
34
DebugPropertyIterator(Isolate * isolate,Handle<JSReceiver> receiver,bool skip_indices)35 DebugPropertyIterator::DebugPropertyIterator(Isolate* isolate,
36 Handle<JSReceiver> receiver,
37 bool skip_indices)
38 : isolate_(isolate),
39 prototype_iterator_(isolate, receiver, kStartAtReceiver,
40 PrototypeIterator::END_AT_NULL),
41 skip_indices_(skip_indices),
42 current_key_index_(0),
43 current_keys_(isolate_->factory()->empty_fixed_array()),
44 current_keys_length_(0) {}
45
Done() const46 bool DebugPropertyIterator::Done() const { return is_done_; }
47
AdvanceToPrototype()48 void DebugPropertyIterator::AdvanceToPrototype() {
49 stage_ = kExoticIndices;
50 is_own_ = false;
51 if (!prototype_iterator_.HasAccess()) is_done_ = true;
52 prototype_iterator_.AdvanceIgnoringProxies();
53 if (prototype_iterator_.IsAtEnd()) is_done_ = true;
54 }
55
AdvanceInternal()56 bool DebugPropertyIterator::AdvanceInternal() {
57 ++current_key_index_;
58 calculated_native_accessor_flags_ = false;
59 while (should_move_to_next_stage()) {
60 switch (stage_) {
61 case kExoticIndices:
62 stage_ = kEnumerableStrings;
63 break;
64 case kEnumerableStrings:
65 stage_ = kAllProperties;
66 break;
67 case kAllProperties:
68 AdvanceToPrototype();
69 break;
70 }
71 if (!FillKeysForCurrentPrototypeAndStage()) return false;
72 }
73 return true;
74 }
75
is_native_accessor()76 bool DebugPropertyIterator::is_native_accessor() {
77 CalculateNativeAccessorFlags();
78 return native_accessor_flags_;
79 }
80
has_native_getter()81 bool DebugPropertyIterator::has_native_getter() {
82 CalculateNativeAccessorFlags();
83 return native_accessor_flags_ &
84 static_cast<int>(debug::NativeAccessorType::HasGetter);
85 }
86
has_native_setter()87 bool DebugPropertyIterator::has_native_setter() {
88 CalculateNativeAccessorFlags();
89 return native_accessor_flags_ &
90 static_cast<int>(debug::NativeAccessorType::HasSetter);
91 }
92
raw_name() const93 Handle<Name> DebugPropertyIterator::raw_name() const {
94 DCHECK(!Done());
95 if (stage_ == kExoticIndices) {
96 return isolate_->factory()->SizeToString(current_key_index_);
97 } else {
98 return Handle<Name>::cast(FixedArray::get(
99 *current_keys_, static_cast<int>(current_key_index_), isolate_));
100 }
101 }
102
name() const103 v8::Local<v8::Name> DebugPropertyIterator::name() const {
104 return Utils::ToLocal(raw_name());
105 }
106
attributes()107 v8::Maybe<v8::PropertyAttribute> DebugPropertyIterator::attributes() {
108 Handle<JSReceiver> receiver =
109 PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
110 auto result = JSReceiver::GetPropertyAttributes(receiver, raw_name());
111 if (result.IsNothing()) return Nothing<v8::PropertyAttribute>();
112 DCHECK(result.FromJust() != ABSENT);
113 return Just(static_cast<v8::PropertyAttribute>(result.FromJust()));
114 }
115
descriptor()116 v8::Maybe<v8::debug::PropertyDescriptor> DebugPropertyIterator::descriptor() {
117 Handle<JSReceiver> receiver =
118 PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
119
120 PropertyDescriptor descriptor;
121 Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
122 isolate_, receiver, raw_name(), &descriptor);
123 if (did_get_descriptor.IsNothing()) {
124 return Nothing<v8::debug::PropertyDescriptor>();
125 }
126 DCHECK(did_get_descriptor.FromJust());
127 return Just(v8::debug::PropertyDescriptor{
128 descriptor.enumerable(), descriptor.has_enumerable(),
129 descriptor.configurable(), descriptor.has_configurable(),
130 descriptor.writable(), descriptor.has_writable(),
131 descriptor.has_value() ? Utils::ToLocal(descriptor.value())
132 : v8::Local<v8::Value>(),
133 descriptor.has_get() ? Utils::ToLocal(descriptor.get())
134 : v8::Local<v8::Value>(),
135 descriptor.has_set() ? Utils::ToLocal(descriptor.set())
136 : v8::Local<v8::Value>(),
137 });
138 }
139
is_own()140 bool DebugPropertyIterator::is_own() { return is_own_; }
141
is_array_index()142 bool DebugPropertyIterator::is_array_index() {
143 if (stage_ == kExoticIndices) return true;
144 PropertyKey key(isolate_, raw_name());
145 return key.is_element();
146 }
147
FillKeysForCurrentPrototypeAndStage()148 bool DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
149 current_key_index_ = 0;
150 current_keys_ = isolate_->factory()->empty_fixed_array();
151 current_keys_length_ = 0;
152 if (is_done_) return true;
153 Handle<JSReceiver> receiver =
154 PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
155 if (stage_ == kExoticIndices) {
156 if (skip_indices_ || !receiver->IsJSTypedArray()) return true;
157 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
158 current_keys_length_ =
159 typed_array->WasDetached() ? 0 : typed_array->length();
160 return true;
161 }
162 PropertyFilter filter =
163 stage_ == kEnumerableStrings ? ENUMERABLE_STRINGS : ALL_PROPERTIES;
164 if (KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
165 GetKeysConversion::kConvertToString, false,
166 skip_indices_ || receiver->IsJSTypedArray())
167 .ToHandle(¤t_keys_)) {
168 current_keys_length_ = current_keys_->length();
169 return true;
170 }
171 return false;
172 }
173
should_move_to_next_stage() const174 bool DebugPropertyIterator::should_move_to_next_stage() const {
175 return !is_done_ && current_key_index_ >= current_keys_length_;
176 }
177
178 namespace {
GetNativeAccessorDescriptorInternal(Handle<JSReceiver> object,Handle<Name> name)179 base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
180 Handle<JSReceiver> object, Handle<Name> name) {
181 Isolate* isolate = object->GetIsolate();
182 PropertyKey key(isolate, name);
183 if (key.is_element()) return debug::NativeAccessorType::None;
184 LookupIterator it(isolate, object, key, LookupIterator::OWN);
185 if (!it.IsFound()) return debug::NativeAccessorType::None;
186 if (it.state() != LookupIterator::ACCESSOR) {
187 return debug::NativeAccessorType::None;
188 }
189 Handle<Object> structure = it.GetAccessors();
190 if (!structure->IsAccessorInfo()) return debug::NativeAccessorType::None;
191 base::Flags<debug::NativeAccessorType, int> result;
192 #define IS_BUILTIN_ACCESSOR(_, name, ...) \
193 if (*structure == *isolate->factory()->name##_accessor()) \
194 return debug::NativeAccessorType::None;
195 ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACCESSOR, /* not used */)
196 #undef IS_BUILTIN_ACCESSOR
197 Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
198 if (accessor_info->getter() != Object()) {
199 result |= debug::NativeAccessorType::HasGetter;
200 }
201 if (accessor_info->setter() != Object()) {
202 result |= debug::NativeAccessorType::HasSetter;
203 }
204 return result;
205 }
206 } // anonymous namespace
207
CalculateNativeAccessorFlags()208 void DebugPropertyIterator::CalculateNativeAccessorFlags() {
209 if (calculated_native_accessor_flags_) return;
210 if (stage_ == kExoticIndices) {
211 native_accessor_flags_ = 0;
212 } else {
213 Handle<JSReceiver> receiver =
214 PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
215 native_accessor_flags_ =
216 GetNativeAccessorDescriptorInternal(receiver, raw_name());
217 }
218 calculated_native_accessor_flags_ = true;
219 }
220 } // namespace internal
221 } // namespace v8
222