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