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(&current_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