mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:42:44 +00:00 
			
		
		
		
	LibJS: Add support for SpreadExpressions in array literals for bytecode
For this it adds another opcode `Append $lhs` which appends the accumulator to the Array in $lhs, optionally spreading it.
This commit is contained in:
		
							parent
							
								
									4235b2020f
								
							
						
					
					
						commit
						ae52ae8f9f
					
				
					 4 changed files with 109 additions and 27 deletions
				
			
		|  | @ -7,6 +7,7 @@ | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <AK/Find.h> | ||||||
| #include <AK/Format.h> | #include <AK/Format.h> | ||||||
| #include <LibJS/AST.h> | #include <LibJS/AST.h> | ||||||
| #include <LibJS/Bytecode/Generator.h> | #include <LibJS/Bytecode/Generator.h> | ||||||
|  | @ -1085,34 +1086,50 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco | ||||||
| 
 | 
 | ||||||
| Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const | Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecode::Generator& generator) const | ||||||
| { | { | ||||||
|     Vector<Bytecode::Register> element_regs; |     if (m_elements.is_empty()) { | ||||||
|     for (auto& element : m_elements) { |  | ||||||
|         if (element && is<SpreadExpression>(*element)) { |  | ||||||
|             return Bytecode::CodeGenerationError { |  | ||||||
|                 this, |  | ||||||
|                 "Unimplemented element kind: SpreadExpression"sv, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|         element_regs.append(generator.allocate_register()); |  | ||||||
|     } |  | ||||||
|     size_t i = 0; |  | ||||||
|     for (auto& element : m_elements) { |  | ||||||
|         if (element) { |  | ||||||
|             TRY(element->generate_bytecode(generator)); |  | ||||||
| 
 |  | ||||||
|             if (is<SpreadExpression>(*element)) |  | ||||||
|                 VERIFY_NOT_REACHED(); |  | ||||||
|         } else { |  | ||||||
|             generator.emit<Bytecode::Op::LoadImmediate>(Value {}); |  | ||||||
|         } |  | ||||||
|         auto& element_reg = element_regs[i++]; |  | ||||||
|         generator.emit<Bytecode::Op::Store>(element_reg); |  | ||||||
|     } |  | ||||||
|     if (element_regs.is_empty()) { |  | ||||||
|         generator.emit<Bytecode::Op::NewArray>(); |         generator.emit<Bytecode::Op::NewArray>(); | ||||||
|     } else { |         return {}; | ||||||
|         generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2u, AK::Array { element_regs.first(), element_regs.last() }); |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     auto first_spread = find_if(m_elements.begin(), m_elements.end(), [](auto el) { return el && is<SpreadExpression>(*el); }); | ||||||
|  | 
 | ||||||
|  |     Bytecode::Register args_start_reg { 0 }; | ||||||
|  |     for (auto it = m_elements.begin(); it != first_spread; ++it) { | ||||||
|  |         auto reg = generator.allocate_register(); | ||||||
|  |         if (args_start_reg.index() == 0) | ||||||
|  |             args_start_reg = reg; | ||||||
|  |     } | ||||||
|  |     u32 i = 0; | ||||||
|  |     for (auto it = m_elements.begin(); it != first_spread; ++it, ++i) { | ||||||
|  |         Bytecode::Register reg { args_start_reg.index() + i }; | ||||||
|  |         if (!*it) | ||||||
|  |             generator.emit<Bytecode::Op::LoadImmediate>(Value {}); | ||||||
|  |         else { | ||||||
|  |             TRY((*it)->generate_bytecode(generator)); | ||||||
|  |         } | ||||||
|  |         generator.emit<Bytecode::Op::Store>(reg); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (first_spread.index() != 0) | ||||||
|  |         generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2u, AK::Array { args_start_reg, Bytecode::Register { args_start_reg.index() + static_cast<u32>(first_spread.index() - 1) } }); | ||||||
|  |     else | ||||||
|  |         generator.emit<Bytecode::Op::NewArray>(); | ||||||
|  | 
 | ||||||
|  |     if (first_spread != m_elements.end()) { | ||||||
|  |         auto array_reg = generator.allocate_register(); | ||||||
|  |         generator.emit<Bytecode::Op::Store>(array_reg); | ||||||
|  |         for (auto it = first_spread; it != m_elements.end(); ++it) { | ||||||
|  |             if (!*it) { | ||||||
|  |                 generator.emit<Bytecode::Op::LoadImmediate>(Value {}); | ||||||
|  |                 generator.emit<Bytecode::Op::Append>(array_reg, false); | ||||||
|  |             } else { | ||||||
|  |                 TRY((*it)->generate_bytecode(generator)); | ||||||
|  |                 generator.emit<Bytecode::Op::Append>(array_reg, *it && is<SpreadExpression>(**it)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         generator.emit<Bytecode::Op::Load>(array_reg); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2389,5 +2406,4 @@ Bytecode::CodeGenerationErrorOr<void> MetaProperty::generate_bytecode(Bytecode:: | ||||||
| 
 | 
 | ||||||
|     VERIFY_NOT_REACHED(); |     VERIFY_NOT_REACHED(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| 
 | 
 | ||||||
| #define ENUMERATE_BYTECODE_OPS(O)    \ | #define ENUMERATE_BYTECODE_OPS(O)    \ | ||||||
|     O(Add)                           \ |     O(Add)                           \ | ||||||
|  |     O(Append)                        \ | ||||||
|     O(BitwiseAnd)                    \ |     O(BitwiseAnd)                    \ | ||||||
|     O(BitwiseNot)                    \ |     O(BitwiseNot)                    \ | ||||||
|     O(BitwiseOr)                     \ |     O(BitwiseOr)                     \ | ||||||
|  |  | ||||||
|  | @ -182,6 +182,46 @@ ThrowCompletionOr<void> NewArray::execute_impl(Bytecode::Interpreter& interprete | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ThrowCompletionOr<void> Append::execute_impl(Bytecode::Interpreter& interpreter) const | ||||||
|  | { | ||||||
|  |     // Note: This OpCode is used to construct array literals containing at least one spread element,
 | ||||||
|  |     //       Iterating over such a spread element to unpack it has to be visible by
 | ||||||
|  |     //       the user courtesy of
 | ||||||
|  |     //       https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
 | ||||||
|  |     //          SpreadElement : ... AssignmentExpression
 | ||||||
|  |     //              1. Let spreadRef be ? Evaluation of AssignmentExpression.
 | ||||||
|  |     //              2. Let spreadObj be ? GetValue(spreadRef).
 | ||||||
|  |     //              3. Let iteratorRecord be ? GetIterator(spreadObj).
 | ||||||
|  |     //              4. Repeat,
 | ||||||
|  |     //                  a. Let next be ? IteratorStep(iteratorRecord).
 | ||||||
|  |     //                  b. If next is false, return nextIndex.
 | ||||||
|  |     //                  c. Let nextValue be ? IteratorValue(next).
 | ||||||
|  |     //                  d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), nextValue).
 | ||||||
|  |     //                  e. Set nextIndex to nextIndex + 1.
 | ||||||
|  | 
 | ||||||
|  |     auto& vm = interpreter.vm(); | ||||||
|  | 
 | ||||||
|  |     // Note: We know from codegen, that lhs is a plain array with only indexed properties
 | ||||||
|  |     auto& lhs = interpreter.reg(m_lhs).as_array(); | ||||||
|  |     auto lhs_size = lhs.indexed_properties().array_like_size(); | ||||||
|  | 
 | ||||||
|  |     auto rhs = interpreter.accumulator(); | ||||||
|  | 
 | ||||||
|  |     if (m_is_spread) { | ||||||
|  |         // ...rhs
 | ||||||
|  |         size_t i = lhs_size; | ||||||
|  |         TRY(get_iterator_values(vm, rhs, [&i, &lhs](Value iterator_value) -> Optional<Completion> { | ||||||
|  |             lhs.indexed_properties().put(i, iterator_value, default_attributes); | ||||||
|  |             ++i; | ||||||
|  |             return {}; | ||||||
|  |         })); | ||||||
|  |     } else { | ||||||
|  |         lhs.indexed_properties().put(lhs_size, rhs, default_attributes); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // 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.
 | // 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.
 | // Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
 | ||||||
| static Object* iterator_to_object(VM& vm, Iterator iterator) | static Object* iterator_to_object(VM& vm, Iterator iterator) | ||||||
|  | @ -943,6 +983,13 @@ String NewArray::to_string_impl(Bytecode::Executable const&) const | ||||||
|     return builder.to_string(); |     return builder.to_string(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | String Append::to_string_impl(Bytecode::Executable const&) const | ||||||
|  | { | ||||||
|  |     if (m_is_spread) | ||||||
|  |         return String::formatted("Append lhs: **{}", m_lhs); | ||||||
|  |     return String::formatted("Append lhs: {}", m_lhs); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| String IteratorToArray::to_string_impl(Bytecode::Executable const&) const | String IteratorToArray::to_string_impl(Bytecode::Executable const&) const | ||||||
| { | { | ||||||
|     return "IteratorToArray"; |     return "IteratorToArray"; | ||||||
|  |  | ||||||
|  | @ -254,6 +254,24 @@ private: | ||||||
|     Register m_elements[]; |     Register m_elements[]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class Append final : public Instruction { | ||||||
|  | public: | ||||||
|  |     Append(Register lhs, bool is_spread) | ||||||
|  |         : Instruction(Type::Append) | ||||||
|  |         , m_lhs(lhs) | ||||||
|  |         , m_is_spread(is_spread) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; | ||||||
|  |     String to_string_impl(Bytecode::Executable const&) const; | ||||||
|  |     void replace_references_impl(BasicBlock const&, BasicBlock const&) { } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Register m_lhs; | ||||||
|  |     bool m_is_spread = false; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class IteratorToArray final : public Instruction { | class IteratorToArray final : public Instruction { | ||||||
| public: | public: | ||||||
|     IteratorToArray() |     IteratorToArray() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Hendiadyoin1
						Hendiadyoin1