Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: src/key-accumulator.cc

Issue 1707743002: [key-accumulator] Starting to reimplement the key-accumulator (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: WIP fast path for collecting receiver-only elements Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/key-accumulator.h" 5 #include "src/key-accumulator.h"
6 6
7 #include "src/elements.h" 7 #include "src/elements.h"
8 #include "src/factory.h" 8 #include "src/factory.h"
9 #include "src/isolate-inl.h" 9 #include "src/isolate-inl.h"
10 #include "src/objects-inl.h" 10 #include "src/objects-inl.h"
11 #include "src/property-descriptor.h" 11 #include "src/property-descriptor.h"
12 12 #include "src/prototype.h"
13 13
14 namespace v8 { 14 namespace v8 {
15 namespace internal { 15 namespace internal {
16 16
17
18 KeyAccumulator::~KeyAccumulator() { 17 KeyAccumulator::~KeyAccumulator() {
19 for (size_t i = 0; i < elements_.size(); i++) { 18 for (size_t i = 0; i < elements_.size(); i++) {
20 delete elements_[i]; 19 delete elements_[i];
21 } 20 }
22 } 21 }
23 22
24
25 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) { 23 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
26 if (length_ == 0) { 24 if (length_ == 0) {
27 return isolate_->factory()->empty_fixed_array(); 25 return isolate_->factory()->empty_fixed_array();
28 } 26 }
29 // Make sure we have all the lengths collected. 27 // Make sure we have all the lengths collected.
30 NextPrototype(); 28 NextPrototype();
31 29
32 if (type_ == OWN_ONLY && !ownProxyKeys_.is_null()) { 30 if (type_ == OWN_ONLY && !ownProxyKeys_.is_null()) {
33 return ownProxyKeys_; 31 return ownProxyKeys_;
34 } 32 }
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 bool AccumulatorHasKey(std::vector<uint32_t>* sub_elements, uint32_t key) { 90 bool AccumulatorHasKey(std::vector<uint32_t>* sub_elements, uint32_t key) {
93 return std::binary_search(sub_elements->begin(), sub_elements->end(), key); 91 return std::binary_search(sub_elements->begin(), sub_elements->end(), key);
94 } 92 }
95 93
96 } // namespace 94 } // namespace
97 95
98 bool KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) { 96 bool KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
99 return AddKey(handle(key, isolate_), convert); 97 return AddKey(handle(key, isolate_), convert);
100 } 98 }
101 99
102
103 bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { 100 bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
104 if (key->IsSymbol()) { 101 if (key->IsSymbol()) {
105 if (filter_ & SKIP_SYMBOLS) return false; 102 if (filter_ & SKIP_SYMBOLS) return false;
106 if (Handle<Symbol>::cast(key)->is_private()) return false; 103 if (Handle<Symbol>::cast(key)->is_private()) return false;
107 return AddSymbolKey(key); 104 return AddSymbolKey(key);
108 } 105 }
109 if (filter_ & SKIP_STRINGS) return false; 106 if (filter_ & SKIP_STRINGS) return false;
110 // Make sure we do not add keys to a proxy-level (see AddKeysFromProxy). 107 // Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
111 DCHECK_LE(0, level_string_length_); 108 DCHECK_LE(0, level_string_length_);
112 // In some cases (e.g. proxies) we might get in String-converted ints which 109 // In some cases (e.g. proxies) we might get in String-converted ints which
(...skipping 16 matching lines...) Expand all
129 return false; 126 return false;
130 } 127 }
131 length_ = prev_length; 128 length_ = prev_length;
132 level_string_length_ = prev_proto; 129 level_string_length_ = prev_proto;
133 } 130 }
134 } 131 }
135 } 132 }
136 return AddStringKey(key, convert); 133 return AddStringKey(key, convert);
137 } 134 }
138 135
139
140 bool KeyAccumulator::AddKey(uint32_t key) { return AddIntegerKey(key); } 136 bool KeyAccumulator::AddKey(uint32_t key) { return AddIntegerKey(key); }
141 137
142
143 bool KeyAccumulator::AddIntegerKey(uint32_t key) { 138 bool KeyAccumulator::AddIntegerKey(uint32_t key) {
144 // Make sure we do not add keys to a proxy-level (see AddKeysFromProxy). 139 // Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
145 // We mark proxy-levels with a negative length 140 // We mark proxy-levels with a negative length
146 DCHECK_LE(0, level_string_length_); 141 DCHECK_LE(0, level_string_length_);
147 // Binary search over all but the last level. The last one might not be 142 // Binary search over all but the last level. The last one might not be
148 // sorted yet. 143 // sorted yet.
149 for (size_t i = 1; i < elements_.size(); i++) { 144 for (size_t i = 1; i < elements_.size(); i++) {
150 if (AccumulatorHasKey(elements_[i - 1], key)) return false; 145 if (AccumulatorHasKey(elements_[i - 1], key)) return false;
151 } 146 }
152 elements_.back()->push_back(key); 147 elements_.back()->push_back(key);
153 length_++; 148 length_++;
154 return true; 149 return true;
155 } 150 }
156 151
157
158 bool KeyAccumulator::AddStringKey(Handle<Object> key, 152 bool KeyAccumulator::AddStringKey(Handle<Object> key,
159 AddKeyConversion convert) { 153 AddKeyConversion convert) {
160 if (string_properties_.is_null()) { 154 if (string_properties_.is_null()) {
161 string_properties_ = OrderedHashSet::Allocate(isolate_, 16); 155 string_properties_ = OrderedHashSet::Allocate(isolate_, 16);
162 } 156 }
163 // TODO(cbruni): remove this conversion once we throw the correct TypeError 157 // TODO(cbruni): remove this conversion once we throw the correct TypeError
164 // for non-string/symbol elements returned by proxies 158 // for non-string/symbol elements returned by proxies
165 if (convert == PROXY_MAGIC && key->IsNumber()) { 159 if (convert == PROXY_MAGIC && key->IsNumber()) {
166 key = isolate_->factory()->NumberToString(key); 160 key = isolate_->factory()->NumberToString(key);
167 } 161 }
168 int prev_size = string_properties_->NumberOfElements(); 162 int prev_size = string_properties_->NumberOfElements();
169 string_properties_ = OrderedHashSet::Add(string_properties_, key); 163 string_properties_ = OrderedHashSet::Add(string_properties_, key);
170 if (prev_size < string_properties_->NumberOfElements()) { 164 if (prev_size < string_properties_->NumberOfElements()) {
171 length_++; 165 length_++;
172 level_string_length_++; 166 level_string_length_++;
173 return true; 167 return true;
174 } else { 168 } else {
175 return false; 169 return false;
176 } 170 }
177 } 171 }
178 172
179
180 bool KeyAccumulator::AddSymbolKey(Handle<Object> key) { 173 bool KeyAccumulator::AddSymbolKey(Handle<Object> key) {
181 if (symbol_properties_.is_null()) { 174 if (symbol_properties_.is_null()) {
182 symbol_properties_ = OrderedHashSet::Allocate(isolate_, 16); 175 symbol_properties_ = OrderedHashSet::Allocate(isolate_, 16);
183 } 176 }
184 int prev_size = symbol_properties_->NumberOfElements(); 177 int prev_size = symbol_properties_->NumberOfElements();
185 symbol_properties_ = OrderedHashSet::Add(symbol_properties_, key); 178 symbol_properties_ = OrderedHashSet::Add(symbol_properties_, key);
186 if (prev_size < symbol_properties_->NumberOfElements()) { 179 if (prev_size < symbol_properties_->NumberOfElements()) {
187 length_++; 180 length_++;
188 level_symbol_length_++; 181 level_symbol_length_++;
189 return true; 182 return true;
190 } else { 183 } else {
191 return false; 184 return false;
192 } 185 }
193 } 186 }
194 187
195
196 void KeyAccumulator::AddKeys(Handle<FixedArray> array, 188 void KeyAccumulator::AddKeys(Handle<FixedArray> array,
197 AddKeyConversion convert) { 189 AddKeyConversion convert) {
198 int add_length = array->length(); 190 int add_length = array->length();
199 if (add_length == 0) return; 191 if (add_length == 0) return;
200 for (int i = 0; i < add_length; i++) { 192 for (int i = 0; i < add_length; i++) {
201 Handle<Object> current(array->get(i), isolate_); 193 Handle<Object> current(array->get(i), isolate_);
202 AddKey(current, convert); 194 AddKey(current, convert);
203 } 195 }
204 } 196 }
205 197
206
207 void KeyAccumulator::AddKeys(Handle<JSObject> array_like, 198 void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
208 AddKeyConversion convert) { 199 AddKeyConversion convert) {
209 DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); 200 DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
210 ElementsAccessor* accessor = array_like->GetElementsAccessor(); 201 ElementsAccessor* accessor = array_like->GetElementsAccessor();
211 accessor->AddElementsToKeyAccumulator(array_like, this, convert); 202 accessor->AddElementsToKeyAccumulator(array_like, this, convert);
212 } 203 }
213 204
214
215 void KeyAccumulator::AddKeysFromProxy(Handle<JSObject> array_like) { 205 void KeyAccumulator::AddKeysFromProxy(Handle<JSObject> array_like) {
216 // Proxies define a complete list of keys with no distinction of 206 // Proxies define a complete list of keys with no distinction of
217 // elements and properties, which breaks the normal assumption for the 207 // elements and properties, which breaks the normal assumption for the
218 // KeyAccumulator. 208 // KeyAccumulator.
219 AddKeys(array_like, PROXY_MAGIC); 209 AddKeys(array_like, PROXY_MAGIC);
220 // Invert the current length to indicate a present proxy, so we can ignore 210 // Invert the current length to indicate a present proxy, so we can ignore
221 // element keys for this level. Otherwise we would not fully respect the order 211 // element keys for this level. Otherwise we would not fully respect the order
222 // given by the proxy. 212 // given by the proxy.
223 level_string_length_ = -level_string_length_; 213 level_string_length_ = -level_string_length_;
224 } 214 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 } else { 260 } else {
271 AddKeys(keys, PROXY_MAGIC); 261 AddKeys(keys, PROXY_MAGIC);
272 } 262 }
273 // Invert the current length to indicate a present proxy, so we can ignore 263 // Invert the current length to indicate a present proxy, so we can ignore
274 // element keys for this level. Otherwise we would not fully respect the order 264 // element keys for this level. Otherwise we would not fully respect the order
275 // given by the proxy. 265 // given by the proxy.
276 level_string_length_ = -level_string_length_; 266 level_string_length_ = -level_string_length_;
277 return Just(true); 267 return Just(true);
278 } 268 }
279 269
280
281 void KeyAccumulator::AddElementKeysFromInterceptor( 270 void KeyAccumulator::AddElementKeysFromInterceptor(
282 Handle<JSObject> array_like) { 271 Handle<JSObject> array_like) {
283 AddKeys(array_like, CONVERT_TO_ARRAY_INDEX); 272 AddKeys(array_like, CONVERT_TO_ARRAY_INDEX);
284 // The interceptor might introduce duplicates for the current level, since 273 // The interceptor might introduce duplicates for the current level, since
285 // these keys get added after the objects's normal element keys. 274 // these keys get added after the objects's normal element keys.
286 SortCurrentElementsListRemoveDuplicates(); 275 SortCurrentElementsListRemoveDuplicates();
287 } 276 }
288 277
289
290 void KeyAccumulator::SortCurrentElementsListRemoveDuplicates() { 278 void KeyAccumulator::SortCurrentElementsListRemoveDuplicates() {
291 // Sort and remove duplicates from the current elements level and adjust. 279 // Sort and remove duplicates from the current elements level and adjust.
292 // the lengths accordingly. 280 // the lengths accordingly.
293 auto last_level = elements_.back(); 281 auto last_level = elements_.back();
294 size_t nof_removed_keys = last_level->size(); 282 size_t nof_removed_keys = last_level->size();
295 std::sort(last_level->begin(), last_level->end()); 283 std::sort(last_level->begin(), last_level->end());
296 last_level->erase(std::unique(last_level->begin(), last_level->end()), 284 last_level->erase(std::unique(last_level->begin(), last_level->end()),
297 last_level->end()); 285 last_level->end());
298 // Adjust total length by the number of removed duplicates. 286 // Adjust total length by the number of removed duplicates.
299 nof_removed_keys -= last_level->size(); 287 nof_removed_keys -= last_level->size();
300 length_ -= static_cast<int>(nof_removed_keys); 288 length_ -= static_cast<int>(nof_removed_keys);
301 } 289 }
302 290
303
304 void KeyAccumulator::SortCurrentElementsList() { 291 void KeyAccumulator::SortCurrentElementsList() {
305 if (elements_.empty()) return; 292 if (elements_.empty()) return;
306 auto element_keys = elements_.back(); 293 auto element_keys = elements_.back();
307 std::sort(element_keys->begin(), element_keys->end()); 294 std::sort(element_keys->begin(), element_keys->end());
308 } 295 }
309 296
310
311 void KeyAccumulator::NextPrototype() { 297 void KeyAccumulator::NextPrototype() {
312 // Store the protoLength on the first call of this method. 298 // Store the protoLength on the first call of this method.
313 if (!elements_.empty()) { 299 if (!elements_.empty()) {
314 level_lengths_.push_back(level_string_length_); 300 level_lengths_.push_back(level_string_length_);
315 level_lengths_.push_back(level_symbol_length_); 301 level_lengths_.push_back(level_symbol_length_);
316 } 302 }
317 elements_.push_back(new std::vector<uint32_t>()); 303 elements_.push_back(new std::vector<uint32_t>());
318 level_string_length_ = 0; 304 level_string_length_ = 0;
319 level_symbol_length_ = 0; 305 level_symbol_length_ = 0;
320 } 306 }
321 307
308 // ============================================================================
309 void FastKeyAccumulator::UseVars() {
310 USE(type_);
311 USE(filter_);
312 }
313
314 namespace {
315
316 bool TrySettingEmptyEnumCache(JSReceiver* object) {
317 Map* map = object->map();
318 if (!map->OnlyHasSimpleProperties()) return false;
319 if (map->IsJSProxyMap()) return false;
320 if (map->NumberOfOwnDescriptors() > 0) {
321 int number_of_enumerable_own_properties =
322 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
323 if (number_of_enumerable_own_properties > 0) return false;
324 }
325 DCHECK(object->IsJSObject());
326 if (JSObject::cast(object)->HasEnumerableElements()) return false;
327 object->map()->SetEnumLength(0);
328 return true;
329 }
330
331 bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) {
332 int enum_length = object->map()->EnumLength();
333 if (enum_length == kInvalidEnumCacheSentinel) {
334 return TrySettingEmptyEnumCache(object);
335 } else if (enum_length == 0) {
336 DCHECK(object->IsJSObject());
337 return !JSObject::cast(object)->HasEnumerableElements();
338 }
339 return false;
340 }
341 } // namespace
342
343 void FastKeyAccumulator::Prepare() {
344 DisallowHeapAllocation no_gc;
345 // Directly go for the fast path for OWN_ONLY keys.
346 if (type_ == OWN_ONLY) return;
347 // Fully walk the prototype chain and find the last prototype with keys.
348 is_simple_enum_ = false;
349 has_empty_prototype_ = true;
350 JSReceiver* first_non_empty_prototype;
351
352 for (PrototypeIterator iter(isolate_, *receiver_,
353 PrototypeIterator::START_AT_PROTOTYPE);
354 !iter.IsAtEnd(); iter.Advance()) {
355 JSReceiver* current = iter.GetCurrent<JSReceiver>();
356 if (CheckAndInitalizeSimpleEnumCache(current)) continue;
357 has_empty_prototype_ = false;
358 first_non_empty_prototype = current;
359 return;
360 }
361 DCHECK(has_empty_prototype_);
362 if (receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel) {
363 is_simple_enum_ = !JSObject::cast(*receiver_)->HasEnumerableElements();
364 }
365 }
366
367 namespace {
368 Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
369 Handle<JSObject> object,
370 GetKeysConversion convert) {
371 Handle<FixedArray> keys = JSObject::GetFastEnumPropertyKeys(isolate, object);
372 ElementsAccessor* accessor = object->GetElementsAccessor();
373 return accessor->PrependElementIndices(object, keys, convert);
374 }
375
376 MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache(
377 Isolate* isolate, Handle<JSObject> object) {
378 // Uninitalized enum cache
379 Map* map = object->map();
380 if (object->elements() != isolate->heap()->empty_fixed_array() ||
381 object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
382 // TODO(cbruni): avoud HasEnumerableElements for very large holey elements.
Toon Verwaest 2016/02/24 15:07:06 avoid
383 if (object->HasEnumerableElements()) return MaybeHandle<FixedArray>();
384 }
385 int number_of_own_descriptors = map->NumberOfOwnDescriptors();
386 if (number_of_own_descriptors == 0) {
387 map->SetEnumLength(0);
388 return isolate->factory()->empty_fixed_array();
389 }
390 // We have no elements but possibly enumerable property keys, hence we can
391 // directly initialize the enum cache.
392 return JSObject::GetFastEnumPropertyKeys(isolate, object);
393 }
394
395 } // namespace
396
397 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) {
398 Handle<FixedArray> keys;
399 if (GetKeysFast(convert).ToHandle(&keys)) {
400 return keys;
401 }
402 return GetKeysSlow(convert);
403 }
404
405 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
406 GetKeysConversion convert) {
407 bool own_only = has_empty_prototype_ || type_ == OWN_ONLY;
408 if (!own_only || receiver_->IsJSProxy() ||
409 !receiver_->map()->OnlyHasSimpleProperties()) {
410 return MaybeHandle<FixedArray>();
411 }
412
413 Handle<FixedArray> keys;
414 DCHECK(receiver_->IsJSObject());
415 Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
416
417 int enum_length = receiver_->map()->EnumLength();
418 if (enum_length == kInvalidEnumCacheSentinel) {
419 // Try initializing the enum cache and return own properties.
420 if (GetOwnKeysWithUninitializedEnumCache(isolate_, object)
421 .ToHandle(&keys)) {
422 is_simple_enum_ =
423 object->map()->EnumLength() != kInvalidEnumCacheSentinel;
424 return keys;
425 }
426 }
427 DCHECK(object->HasEnumerableElements());
428 return GetOwnKeysWithElements(isolate_, object, convert);
429 }
430
431 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
432 GetKeysConversion convert) {
433 return JSReceiver::GetKeys(receiver_, type_, ENUMERABLE_STRINGS);
434 }
322 435
323 } // namespace internal 436 } // namespace internal
324 } // namespace v8 437 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698