mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 13:12:46 +00:00 
			
		
		
		
	LibJS: Implement SuperCall for the Bytecode-VM
This commit is contained in:
		
							parent
							
								
									25be67299e
								
							
						
					
					
						commit
						21ae882cfd
					
				
					 5 changed files with 130 additions and 2 deletions
				
			
		|  | @ -521,6 +521,34 @@ Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Ge | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Generator& generator) const | ||||
| { | ||||
|     Vector<Bytecode::Register> argument_registers; | ||||
| 
 | ||||
|     if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) { | ||||
|         // NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
 | ||||
|         //       shouldn't call @@iterator of %Array.prototype%.
 | ||||
|         VERIFY(m_arguments.size() == 1); | ||||
|         VERIFY(m_arguments[0].is_spread); | ||||
|         auto const& argument = m_arguments[0]; | ||||
|         // This generates a single argument, which will be implicitly passed in accumulator
 | ||||
|         MUST(argument.value->generate_bytecode(generator)); | ||||
|     } else { | ||||
|         argument_registers.ensure_capacity(m_arguments.size()); | ||||
| 
 | ||||
|         for (auto const& arg : m_arguments) { | ||||
|             TRY(arg.value->generate_bytecode(generator)); | ||||
|             auto arg_reg = generator.allocate_register(); | ||||
|             generator.emit<Bytecode::Op::Store>(arg_reg); | ||||
|             argument_registers.unchecked_append(arg_reg); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     generator.emit_with_extra_register_slots<Bytecode::Op::SuperCall>(argument_registers.size(), m_is_synthetic == IsPartOfSyntheticConstructor::Yes, argument_registers); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| static Bytecode::CodeGenerationErrorOr<void> generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Register const& value_reg); | ||||
| 
 | ||||
| Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const | ||||
|  |  | |||
|  | @ -80,6 +80,7 @@ | |||
|     O(StrictlyEquals)                \ | ||||
|     O(StrictlyInequals)              \ | ||||
|     O(Sub)                           \ | ||||
|     O(SuperCall)                     \ | ||||
|     O(Throw)                         \ | ||||
|     O(Typeof)                        \ | ||||
|     O(TypeofVariable)                \ | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include <LibJS/Runtime/DeclarativeEnvironment.h> | ||||
| #include <LibJS/Runtime/ECMAScriptFunctionObject.h> | ||||
| #include <LibJS/Runtime/Environment.h> | ||||
| #include <LibJS/Runtime/FunctionEnvironment.h> | ||||
| #include <LibJS/Runtime/GlobalEnvironment.h> | ||||
| #include <LibJS/Runtime/GlobalObject.h> | ||||
| #include <LibJS/Runtime/Iterator.h> | ||||
|  | @ -525,6 +526,60 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| // 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
 | ||||
| ThrowCompletionOr<void> SuperCall::execute_impl(Bytecode::Interpreter& interpreter) const | ||||
| { | ||||
|     auto& vm = interpreter.vm(); | ||||
|     // 1. Let newTarget be GetNewTarget().
 | ||||
|     auto new_target = vm.get_new_target(); | ||||
| 
 | ||||
|     // 2. Assert: Type(newTarget) is Object.
 | ||||
|     VERIFY(new_target.is_object()); | ||||
| 
 | ||||
|     // 3. Let func be GetSuperConstructor().
 | ||||
|     auto* func = get_super_constructor(vm); | ||||
| 
 | ||||
|     // 4. Let argList be ? ArgumentListEvaluation of Arguments.
 | ||||
|     MarkedVector<Value> arg_list { vm.heap() }; | ||||
|     if (m_is_synthetic) { | ||||
|         auto const& value = interpreter.accumulator(); | ||||
|         VERIFY(value.is_object() && is<Array>(value.as_object())); | ||||
|         auto const& array_value = static_cast<Array const&>(value.as_object()); | ||||
|         auto length = MUST(length_of_array_like(vm, array_value)); | ||||
|         for (size_t i = 0; i < length; ++i) | ||||
|             arg_list.append(array_value.get_without_side_effects(PropertyKey { i })); | ||||
|     } else { | ||||
|         for (size_t i = 0; i < m_argument_count; ++i) | ||||
|             arg_list.append(interpreter.reg(m_arguments[i])); | ||||
|     } | ||||
| 
 | ||||
|     // 5. If IsConstructor(func) is false, throw a TypeError exception.
 | ||||
|     if (!Value(func).is_constructor()) | ||||
|         return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, "Super constructor"); | ||||
| 
 | ||||
|     // 6. Let result be ? Construct(func, argList, newTarget).
 | ||||
|     auto* result = TRY(construct(vm, static_cast<FunctionObject&>(*func), move(arg_list), &new_target.as_function())); | ||||
| 
 | ||||
|     // 7. Let thisER be GetThisEnvironment().
 | ||||
|     auto& this_environment = verify_cast<FunctionEnvironment>(get_this_environment(vm)); | ||||
| 
 | ||||
|     // 8. Perform ? thisER.BindThisValue(result).
 | ||||
|     TRY(this_environment.bind_this_value(vm, result)); | ||||
| 
 | ||||
|     // 9. Let F be thisER.[[FunctionObject]].
 | ||||
|     auto& f = this_environment.function_object(); | ||||
| 
 | ||||
|     // 10. Assert: F is an ECMAScript function object.
 | ||||
|     // NOTE: This is implied by the strong C++ type.
 | ||||
| 
 | ||||
|     // 11. Perform ? InitializeInstanceElements(result, F).
 | ||||
|     TRY(vm.initialize_instance_elements(*result, f)); | ||||
| 
 | ||||
|     // 12. Return result.
 | ||||
|     interpreter.accumulator() = result; | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const | ||||
| { | ||||
|     auto& vm = interpreter.vm(); | ||||
|  | @ -1000,6 +1055,20 @@ String Call::to_string_impl(Bytecode::Executable const&) const | |||
|     return builder.to_string(); | ||||
| } | ||||
| 
 | ||||
| String SuperCall::to_string_impl(Bytecode::Executable const&) const | ||||
| { | ||||
|     StringBuilder builder; | ||||
|     builder.append("SuperCall"sv); | ||||
|     if (m_is_synthetic) { | ||||
|         builder.append(" arguments:[...acc]"sv); | ||||
|     } else if (m_argument_count != 0) { | ||||
|         builder.append(" arguments:["sv); | ||||
|         builder.join(", "sv, Span<Register const>(m_arguments, m_argument_count)); | ||||
|         builder.append(']'); | ||||
|     } | ||||
|     return builder.to_string(); | ||||
| } | ||||
| 
 | ||||
| String NewFunction::to_string_impl(Bytecode::Executable const&) const | ||||
| { | ||||
|     return "NewFunction"; | ||||
|  |  | |||
|  | @ -613,6 +613,33 @@ private: | |||
|     Register m_arguments[]; | ||||
| }; | ||||
| 
 | ||||
| // NOTE: This instruction is variable-width depending on the number of arguments!
 | ||||
| class SuperCall : public Instruction { | ||||
| public: | ||||
|     explicit SuperCall(bool is_synthetic, Vector<Register> const& arguments) | ||||
|         : Instruction(Type::SuperCall) | ||||
|         , m_is_synthetic(is_synthetic) | ||||
|         , m_argument_count(arguments.size()) | ||||
|     { | ||||
|         for (size_t i = 0; i < m_argument_count; ++i) | ||||
|             m_arguments[i] = arguments[i]; | ||||
|     } | ||||
| 
 | ||||
|     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; | ||||
|     String to_string_impl(Bytecode::Executable const&) const; | ||||
|     void replace_references_impl(BasicBlock const&, BasicBlock const&) { } | ||||
| 
 | ||||
|     size_t length_impl() const | ||||
|     { | ||||
|         return sizeof(*this) + sizeof(Register) * m_argument_count; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     bool m_is_synthetic; | ||||
|     size_t m_argument_count { 0 }; | ||||
|     Register m_arguments[]; | ||||
| }; | ||||
| 
 | ||||
| class NewClass final : public Instruction { | ||||
| public: | ||||
|     explicit NewClass(ClassExpression const& class_expression) | ||||
|  | @ -966,9 +993,11 @@ ALWAYS_INLINE size_t Instruction::length() const | |||
| { | ||||
|     if (type() == Type::Call) | ||||
|         return static_cast<Op::Call const&>(*this).length_impl(); | ||||
|     else if (type() == Type::NewArray) | ||||
|     if (type() == Type::SuperCall) | ||||
|         return static_cast<Op::SuperCall const&>(*this).length_impl(); | ||||
|     if (type() == Type::NewArray) | ||||
|         return static_cast<Op::NewArray const&>(*this).length_impl(); | ||||
|     else if (type() == Type::CopyObjectExcludingProperties) | ||||
|     if (type() == Type::CopyObjectExcludingProperties) | ||||
|         return static_cast<Op::CopyObjectExcludingProperties const&>(*this).length_impl(); | ||||
| 
 | ||||
| #define __BYTECODE_OP(op) \ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Hendiadyoin1
						Hendiadyoin1