mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-10-31 13:20:59 +00:00 
			
		
		
		
	LibJS: Stop converting between Object <-> IteratorRecord all the time
This patch makes IteratorRecord an Object. Although it's not exposed to
author code, this does allow us to store it in a VM register.
Now that we can store it in a VM register, we don't need to convert it
back and forth between IteratorRecord and Object when accessing it from
bytecode.
The big win here is avoiding 3 [[Get]] accesses on every iteration step
of for..of loops. There are also a bunch of smaller efficiencies gained.
20% speed-up on this microbenchmark:
    function go(a) {
        for (const p of a) {
        }
    }
    const a = [];
    a.length = 1_000_000;
    go(a);
			
			
This commit is contained in:
		
							parent
							
								
									4966c083df
								
							
						
					
					
						commit
						4699c81fc1
					
				
				
				Notes:
				
					sideshowbarker
				
				2024-07-16 22:14:49 +09:00 
				
			
			Author: https://github.com/awesomekling
Commit: 4699c81fc1
Pull-request: https://github.com/SerenityOS/serenity/pull/22194
Reviewed-by: https://github.com/trflynn89
			
					 23 changed files with 226 additions and 144 deletions
				
			
		|  | @ -654,31 +654,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> super_call_with_argument_array(VM& vm, V | |||
|     return result; | ||||
| } | ||||
| 
 | ||||
| // FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
 | ||||
| // Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
 | ||||
| Object* iterator_to_object(VM& vm, IteratorRecord iterator) | ||||
| { | ||||
|     auto& realm = *vm.current_realm(); | ||||
|     auto object = Object::create(realm, nullptr); | ||||
|     object->define_direct_property(vm.names.iterator, iterator.iterator, 0); | ||||
|     object->define_direct_property(vm.names.next, iterator.next_method, 0); | ||||
|     object->define_direct_property(vm.names.done, Value(iterator.done), 0); | ||||
|     return object; | ||||
| } | ||||
| 
 | ||||
| IteratorRecord object_to_iterator(VM& vm, Object& object) | ||||
| { | ||||
|     return IteratorRecord { | ||||
|         .iterator = &MUST(object.get(vm.names.iterator)).as_object(), | ||||
|         .next_method = MUST(object.get(vm.names.next)), | ||||
|         .done = MUST(object.get(vm.names.done)).as_bool() | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| ThrowCompletionOr<NonnullGCPtr<Array>> iterator_to_array(VM& vm, Value iterator) | ||||
| { | ||||
|     auto iterator_object = TRY(iterator.to_object(vm)); | ||||
|     auto iterator_record = object_to_iterator(vm, iterator_object); | ||||
|     auto& iterator_record = verify_cast<IteratorRecord>(iterator.as_object()); | ||||
| 
 | ||||
|     auto array = MUST(Array::create(*vm.current_realm(), 0)); | ||||
|     size_t index = 0; | ||||
|  | @ -832,47 +810,42 @@ ThrowCompletionOr<Object*> get_object_property_iterator(VM& vm, Value value) | |||
|                 properties.set(move(property_key)); | ||||
|         } | ||||
|     } | ||||
|     IteratorRecord iterator { | ||||
|         .iterator = object, | ||||
|         .next_method = NativeFunction::create( | ||||
|             *vm.current_realm(), | ||||
|             [items = move(properties)](VM& vm) mutable -> ThrowCompletionOr<Value> { | ||||
|                 auto& realm = *vm.current_realm(); | ||||
|                 auto iterated_object_value = vm.this_value(); | ||||
|                 if (!iterated_object_value.is_object()) | ||||
|                     return vm.throw_completion<InternalError>("Invalid state for GetObjectPropertyIterator.next"sv); | ||||
| 
 | ||||
|                 auto& iterated_object = iterated_object_value.as_object(); | ||||
|                 auto result_object = Object::create(realm, nullptr); | ||||
|                 while (true) { | ||||
|                     if (items.is_empty()) { | ||||
|                         result_object->define_direct_property(vm.names.done, JS::Value(true), default_attributes); | ||||
|                         return result_object; | ||||
|                     } | ||||
| 
 | ||||
|                     auto key = items.take_first(); | ||||
| 
 | ||||
|                     // If the property is deleted, don't include it (invariant no. 2)
 | ||||
|                     if (!TRY(iterated_object.has_property(key))) | ||||
|                         continue; | ||||
| 
 | ||||
|                     result_object->define_direct_property(vm.names.done, JS::Value(false), default_attributes); | ||||
| 
 | ||||
|                     if (key.is_number()) | ||||
|                         result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::number(key.as_number()))), default_attributes); | ||||
|                     else if (key.is_string()) | ||||
|                         result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, key.as_string()), default_attributes); | ||||
|                     else | ||||
|                         VERIFY_NOT_REACHED(); // We should not have non-string/number keys.
 | ||||
|     auto& realm = *vm.current_realm(); | ||||
|     auto callback = NativeFunction::create( | ||||
|         *vm.current_realm(), [items = move(properties)](VM& vm) mutable -> ThrowCompletionOr<Value> { | ||||
|             auto& realm = *vm.current_realm(); | ||||
|             auto iterated_object_value = vm.this_value(); | ||||
|             if (!iterated_object_value.is_object()) | ||||
|                 return vm.throw_completion<InternalError>("Invalid state for GetObjectPropertyIterator.next"sv); | ||||
| 
 | ||||
|             auto& iterated_object = iterated_object_value.as_object(); | ||||
|             auto result_object = Object::create(realm, nullptr); | ||||
|             while (true) { | ||||
|                 if (items.is_empty()) { | ||||
|                     result_object->define_direct_property(vm.names.done, JS::Value(true), default_attributes); | ||||
|                     return result_object; | ||||
|                 } | ||||
|             }, | ||||
|             1, | ||||
|             vm.names.next), | ||||
|         .done = false, | ||||
|     }; | ||||
|     return iterator_to_object(vm, move(iterator)); | ||||
| 
 | ||||
|                 auto key = items.take_first(); | ||||
| 
 | ||||
|                 // If the property is deleted, don't include it (invariant no. 2)
 | ||||
|                 if (!TRY(iterated_object.has_property(key))) | ||||
|                     continue; | ||||
| 
 | ||||
|                 result_object->define_direct_property(vm.names.done, JS::Value(false), default_attributes); | ||||
| 
 | ||||
|                 if (key.is_number()) | ||||
|                     result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::number(key.as_number()))), default_attributes); | ||||
|                 else if (key.is_string()) | ||||
|                     result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, key.as_string()), default_attributes); | ||||
|                 else | ||||
|                     VERIFY_NOT_REACHED(); // We should not have non-string/number keys.
 | ||||
| 
 | ||||
|                 return result_object; | ||||
|             } | ||||
|         }, | ||||
|         1, vm.names.next); | ||||
|     return vm.heap().allocate<IteratorRecord>(realm, realm, object, callback, false).ptr(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling