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