• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   // This should almost never happen, however we have seen cases where we do
113   // trigger this check. In these rare events, it typically is a
114   // misconfiguration by an embedder (such as Blink) in how the embedder
115   // processes properities.
116   //
117   // In the case of crbug.com/1262066 we discovered that Blink was returning
118   // a list of properties to contain in an object, after which V8 queries each
119   // property individually. But, Blink incorrectly claimed that the property
120   // in question did *not* exist. As such, V8 is instructed to process a
121   // property, requests the embedder for more information and then suddenly the
122   // embedder claims it doesn't exist. In these cases, we hit this DCHECK.
123   //
124   // If you are running into this problem, check your embedder implementation
125   // and verify that the data from both sides matches. If there is a mismatch,
126   // V8 will crash.
127 
128 #if DEBUG
129   base::ScopedVector<char> property_message(128);
130   base::ScopedVector<char> name_buffer(100);
131   raw_name()->NameShortPrint(name_buffer);
132   v8::base::SNPrintF(property_message, "Invalid result for property \"%s\"\n",
133                      name_buffer.begin());
134   DCHECK_WITH_MSG(result.FromJust() != ABSENT, property_message.begin());
135 #endif
136   return Just(static_cast<v8::PropertyAttribute>(result.FromJust()));
137 }
138 
descriptor()139 v8::Maybe<v8::debug::PropertyDescriptor> DebugPropertyIterator::descriptor() {
140   Handle<JSReceiver> receiver =
141       PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
142 
143   PropertyDescriptor descriptor;
144   Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
145       isolate_, receiver, raw_name(), &descriptor);
146   if (did_get_descriptor.IsNothing()) {
147     return Nothing<v8::debug::PropertyDescriptor>();
148   }
149   DCHECK(did_get_descriptor.FromJust());
150   return Just(v8::debug::PropertyDescriptor{
151       descriptor.enumerable(), descriptor.has_enumerable(),
152       descriptor.configurable(), descriptor.has_configurable(),
153       descriptor.writable(), descriptor.has_writable(),
154       descriptor.has_value() ? Utils::ToLocal(descriptor.value())
155                              : v8::Local<v8::Value>(),
156       descriptor.has_get() ? Utils::ToLocal(descriptor.get())
157                            : v8::Local<v8::Value>(),
158       descriptor.has_set() ? Utils::ToLocal(descriptor.set())
159                            : v8::Local<v8::Value>(),
160   });
161 }
162 
is_own()163 bool DebugPropertyIterator::is_own() { return is_own_; }
164 
is_array_index()165 bool DebugPropertyIterator::is_array_index() {
166   if (stage_ == kExoticIndices) return true;
167   PropertyKey key(isolate_, raw_name());
168   return key.is_element();
169 }
170 
FillKeysForCurrentPrototypeAndStage()171 bool DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
172   current_key_index_ = 0;
173   current_keys_ = isolate_->factory()->empty_fixed_array();
174   current_keys_length_ = 0;
175   if (is_done_) return true;
176   Handle<JSReceiver> receiver =
177       PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
178   if (stage_ == kExoticIndices) {
179     if (skip_indices_ || !receiver->IsJSTypedArray()) return true;
180     Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
181     current_keys_length_ =
182         typed_array->WasDetached() ? 0 : typed_array->length();
183     return true;
184   }
185   PropertyFilter filter =
186       stage_ == kEnumerableStrings ? ENUMERABLE_STRINGS : ALL_PROPERTIES;
187   if (KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
188                               GetKeysConversion::kConvertToString, false,
189                               skip_indices_ || receiver->IsJSTypedArray())
190           .ToHandle(&current_keys_)) {
191     current_keys_length_ = current_keys_->length();
192     return true;
193   }
194   return false;
195 }
196 
should_move_to_next_stage() const197 bool DebugPropertyIterator::should_move_to_next_stage() const {
198   return !is_done_ && current_key_index_ >= current_keys_length_;
199 }
200 
201 namespace {
GetNativeAccessorDescriptorInternal(Handle<JSReceiver> object,Handle<Name> name)202 base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
203     Handle<JSReceiver> object, Handle<Name> name) {
204   Isolate* isolate = object->GetIsolate();
205   PropertyKey key(isolate, name);
206   if (key.is_element()) return debug::NativeAccessorType::None;
207   LookupIterator it(isolate, object, key, LookupIterator::OWN);
208   if (!it.IsFound()) return debug::NativeAccessorType::None;
209   if (it.state() != LookupIterator::ACCESSOR) {
210     return debug::NativeAccessorType::None;
211   }
212   Handle<Object> structure = it.GetAccessors();
213   if (!structure->IsAccessorInfo()) return debug::NativeAccessorType::None;
214   base::Flags<debug::NativeAccessorType, int> result;
215 #define IS_BUILTIN_ACCESSOR(_, name, ...)                   \
216   if (*structure == *isolate->factory()->name##_accessor()) \
217     return debug::NativeAccessorType::None;
218   ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACCESSOR, /* not used */)
219 #undef IS_BUILTIN_ACCESSOR
220   Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
221   if (accessor_info->getter() != Object()) {
222     result |= debug::NativeAccessorType::HasGetter;
223   }
224   if (accessor_info->setter() != Object()) {
225     result |= debug::NativeAccessorType::HasSetter;
226   }
227   return result;
228 }
229 }  // anonymous namespace
230 
CalculateNativeAccessorFlags()231 void DebugPropertyIterator::CalculateNativeAccessorFlags() {
232   if (calculated_native_accessor_flags_) return;
233   if (stage_ == kExoticIndices) {
234     native_accessor_flags_ = 0;
235   } else {
236     Handle<JSReceiver> receiver =
237         PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
238     native_accessor_flags_ =
239         GetNativeAccessorDescriptorInternal(receiver, raw_name());
240   }
241   calculated_native_accessor_flags_ = true;
242 }
243 }  // namespace internal
244 }  // namespace v8
245