mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 17:40:27 +00:00
LibJS/Bytecode: Implement yield*
This commit is contained in:
parent
758a4cb1a6
commit
4db2efaecb
Notes:
sideshowbarker
2024-07-17 06:20:50 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/4db2efaecb Pull-request: https://github.com/SerenityOS/serenity/pull/16400 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/linusg ✅
5 changed files with 406 additions and 4 deletions
|
@ -16,6 +16,7 @@
|
|||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Bytecode/StringTable.h>
|
||||
#include <LibJS/Runtime/Environment.h>
|
||||
#include <LibJS/Runtime/ErrorTypes.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
|
@ -1611,10 +1612,271 @@ Bytecode::CodeGenerationErrorOr<void> YieldExpression::generate_bytecode(Bytecod
|
|||
};
|
||||
|
||||
if (m_is_yield_from) {
|
||||
return Bytecode::CodeGenerationError {
|
||||
this,
|
||||
"Unimplemented form: `yield*`"sv,
|
||||
};
|
||||
// 15.5.5 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
|
||||
// FIXME: 1. Let generatorKind be GetGeneratorKind().
|
||||
|
||||
// 2. Let exprRef be ? Evaluation of AssignmentExpression.
|
||||
// 3. Let value be ? GetValue(exprRef).
|
||||
VERIFY(m_argument);
|
||||
TRY(m_argument->generate_bytecode(generator));
|
||||
|
||||
// 4. Let iteratorRecord be ? GetIterator(value, generatorKind).
|
||||
// FIXME: Consider generatorKind.
|
||||
auto iterator_record_register = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::GetIterator>();
|
||||
generator.emit<Bytecode::Op::Store>(iterator_record_register);
|
||||
|
||||
// 5. Let iterator be iteratorRecord.[[Iterator]].
|
||||
auto iterator_register = generator.allocate_register();
|
||||
auto iterator_identifier = generator.intern_identifier("iterator");
|
||||
generator.emit<Bytecode::Op::GetById>(iterator_identifier);
|
||||
generator.emit<Bytecode::Op::Store>(iterator_register);
|
||||
|
||||
// Cache iteratorRecord.[[NextMethod]] for use in step 7.a.i.
|
||||
auto next_method_register = generator.allocate_register();
|
||||
auto next_method_identifier = generator.intern_identifier("next");
|
||||
generator.emit<Bytecode::Op::Load>(iterator_record_register);
|
||||
generator.emit<Bytecode::Op::GetById>(next_method_identifier);
|
||||
generator.emit<Bytecode::Op::Store>(next_method_register);
|
||||
|
||||
// 6. Let received be NormalCompletion(undefined).
|
||||
// See get_received_completion_type_and_value above.
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Normal)));
|
||||
generator.emit<Bytecode::Op::Store>(received_completion_type_register);
|
||||
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
|
||||
generator.emit<Bytecode::Op::Store>(received_completion_value_register);
|
||||
|
||||
// 7. Repeat,
|
||||
auto& loop_block = generator.make_block();
|
||||
auto& continuation_block = generator.make_block();
|
||||
auto& loop_end_block = generator.make_block();
|
||||
|
||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_block });
|
||||
generator.switch_to_basic_block(loop_block);
|
||||
|
||||
// a. If received.[[Type]] is normal, then
|
||||
auto& type_is_normal_block = generator.make_block();
|
||||
auto& is_type_throw_block = generator.make_block();
|
||||
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Normal)));
|
||||
generator.emit<Bytecode::Op::StrictlyEquals>(received_completion_type_register);
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { type_is_normal_block },
|
||||
Bytecode::Label { is_type_throw_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_normal_block);
|
||||
|
||||
// i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »).
|
||||
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { received_completion_value_register, received_completion_value_register });
|
||||
generator.emit<Bytecode::Op::Call>(Bytecode::Op::Call::CallType::Call, next_method_register, iterator_register);
|
||||
|
||||
// FIXME: ii. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||
|
||||
// iii. If innerResult is not an Object, throw a TypeError exception.
|
||||
generator.emit<Bytecode::Op::ThrowIfNotObject>();
|
||||
|
||||
auto inner_result_register = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::Store>(inner_result_register);
|
||||
|
||||
// iv. Let done be ? IteratorComplete(innerResult).
|
||||
generator.emit<Bytecode::Op::IteratorResultDone>();
|
||||
|
||||
// v. If done is true, then
|
||||
auto& type_is_normal_done_block = generator.make_block();
|
||||
auto& type_is_normal_not_done_block = generator.make_block();
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { type_is_normal_done_block },
|
||||
Bytecode::Label { type_is_normal_not_done_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_normal_done_block);
|
||||
|
||||
// 1. Return ? IteratorValue(innerResult).
|
||||
generator.emit<Bytecode::Op::Load>(inner_result_register);
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_end_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_normal_not_done_block);
|
||||
|
||||
// FIXME: vi. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))).
|
||||
// vii. Else, set received to Completion(GeneratorYield(innerResult)).
|
||||
// FIXME: Else,
|
||||
generator.emit<Bytecode::Op::Load>(inner_result_register);
|
||||
|
||||
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
|
||||
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { continuation_block });
|
||||
|
||||
// b. Else if received.[[Type]] is throw, then
|
||||
generator.switch_to_basic_block(is_type_throw_block);
|
||||
auto& type_is_throw_block = generator.make_block();
|
||||
auto& type_is_return_block = generator.make_block();
|
||||
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Throw)));
|
||||
generator.emit<Bytecode::Op::StrictlyEquals>(received_completion_type_register);
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { type_is_throw_block },
|
||||
Bytecode::Label { type_is_return_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_throw_block);
|
||||
|
||||
// i. Let throw be ? GetMethod(iterator, "throw").
|
||||
auto throw_method_register = generator.allocate_register();
|
||||
auto throw_identifier = generator.intern_identifier("throw");
|
||||
generator.emit<Bytecode::Op::Load>(iterator_register);
|
||||
generator.emit<Bytecode::Op::GetMethod>(throw_identifier);
|
||||
generator.emit<Bytecode::Op::Store>(throw_method_register);
|
||||
|
||||
// ii. If throw is not undefined, then
|
||||
auto& throw_method_is_defined_block = generator.make_block();
|
||||
auto& throw_method_is_undefined_block = generator.make_block();
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
|
||||
generator.emit<Bytecode::Op::StrictlyInequals>(throw_method_register);
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { throw_method_is_defined_block },
|
||||
Bytecode::Label { throw_method_is_undefined_block });
|
||||
|
||||
generator.switch_to_basic_block(throw_method_is_defined_block);
|
||||
|
||||
// 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »).
|
||||
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { received_completion_value_register, received_completion_value_register });
|
||||
generator.emit<Bytecode::Op::Call>(Bytecode::Op::Call::CallType::Call, throw_method_register, iterator_register);
|
||||
|
||||
// FIXME: 2. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||
|
||||
// 3. NOTE: Exceptions from the inner iterator throw method are propagated. Normal completions from an inner throw method are processed similarly to an inner next.
|
||||
// 4. If innerResult is not an Object, throw a TypeError exception.
|
||||
generator.emit<Bytecode::Op::ThrowIfNotObject>();
|
||||
generator.emit<Bytecode::Op::Store>(inner_result_register);
|
||||
|
||||
// 5. Let done be ? IteratorComplete(innerResult).
|
||||
generator.emit<Bytecode::Op::IteratorResultDone>();
|
||||
|
||||
// 6. If done is true, then
|
||||
auto& type_is_throw_done_block = generator.make_block();
|
||||
auto& type_is_throw_not_done_block = generator.make_block();
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { type_is_throw_done_block },
|
||||
Bytecode::Label { type_is_throw_not_done_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_throw_done_block);
|
||||
|
||||
// a. Return ? IteratorValue(innerResult).
|
||||
generator.emit<Bytecode::Op::Load>(inner_result_register);
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_end_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_throw_not_done_block);
|
||||
|
||||
// FIXME: 7. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))).
|
||||
// 8. Else, set received to Completion(GeneratorYield(innerResult)).
|
||||
// FIXME: Else,
|
||||
generator.emit<Bytecode::Op::Load>(inner_result_register);
|
||||
|
||||
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
|
||||
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { continuation_block });
|
||||
|
||||
generator.switch_to_basic_block(throw_method_is_undefined_block);
|
||||
|
||||
// 1. NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop. But first we need to give iterator a chance to clean up.
|
||||
|
||||
// 2. Let closeCompletion be Completion Record { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
|
||||
// FIXME: 3. If generatorKind is async, perform ? AsyncIteratorClose(iteratorRecord, closeCompletion).
|
||||
// 4. Else, perform ? IteratorClose(iteratorRecord, closeCompletion).
|
||||
// FIXME: Else,
|
||||
generator.emit<Bytecode::Op::Load>(iterator_record_register);
|
||||
generator.emit<Bytecode::Op::IteratorClose>(Completion::Type::Normal, Optional<Value> {});
|
||||
|
||||
// 5. NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation: iterator does not have a throw method.
|
||||
// 6. Throw a TypeError exception.
|
||||
generator.emit<Bytecode::Op::NewTypeError>(generator.intern_string(ErrorType::YieldFromIteratorMissingThrowMethod.message()));
|
||||
generator.perform_needed_unwinds<Bytecode::Op::Throw>();
|
||||
generator.emit<Bytecode::Op::Throw>();
|
||||
|
||||
// c. Else,
|
||||
// i. Assert: received.[[Type]] is return.
|
||||
generator.switch_to_basic_block(type_is_return_block);
|
||||
|
||||
// ii. Let return be ? GetMethod(iterator, "return").
|
||||
auto return_method_register = generator.allocate_register();
|
||||
auto return_identifier = generator.intern_identifier("return");
|
||||
generator.emit<Bytecode::Op::Load>(iterator_register);
|
||||
generator.emit<Bytecode::Op::GetMethod>(return_identifier);
|
||||
generator.emit<Bytecode::Op::Store>(return_method_register);
|
||||
|
||||
// iii. If return is undefined, then
|
||||
auto& return_is_undefined_block = generator.make_block();
|
||||
auto& return_is_defined_block = generator.make_block();
|
||||
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
|
||||
generator.emit<Bytecode::Op::StrictlyEquals>(return_method_register);
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { return_is_undefined_block },
|
||||
Bytecode::Label { return_is_defined_block });
|
||||
|
||||
generator.switch_to_basic_block(return_is_undefined_block);
|
||||
|
||||
// FIXME: 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]).
|
||||
// 2. Return ? received.
|
||||
// NOTE: This will always be a return completion.
|
||||
generator.emit<Bytecode::Op::Load>(received_completion_value_register);
|
||||
generator.perform_needed_unwinds<Bytecode::Op::Yield>();
|
||||
generator.emit<Bytecode::Op::Yield>(nullptr);
|
||||
|
||||
generator.switch_to_basic_block(return_is_defined_block);
|
||||
|
||||
// iv. Let innerReturnResult be ? Call(return, iterator, « received.[[Value]] »).
|
||||
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { received_completion_value_register, received_completion_value_register });
|
||||
generator.emit<Bytecode::Op::Call>(Bytecode::Op::Call::CallType::Call, return_method_register, iterator_register);
|
||||
|
||||
// FIXME: v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult).
|
||||
|
||||
// vi. If innerReturnResult is not an Object, throw a TypeError exception.
|
||||
generator.emit<Bytecode::Op::ThrowIfNotObject>();
|
||||
|
||||
auto inner_return_result_register = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::Store>(inner_return_result_register);
|
||||
|
||||
// vii. Let done be ? IteratorComplete(innerReturnResult).
|
||||
generator.emit<Bytecode::Op::IteratorResultDone>();
|
||||
|
||||
// viii. If done is true, then
|
||||
auto& type_is_return_done_block = generator.make_block();
|
||||
auto& type_is_return_not_done_block = generator.make_block();
|
||||
generator.emit<Bytecode::Op::JumpConditional>(
|
||||
Bytecode::Label { type_is_return_done_block },
|
||||
Bytecode::Label { type_is_return_not_done_block });
|
||||
|
||||
generator.switch_to_basic_block(type_is_return_done_block);
|
||||
|
||||
// 1. Let value be ? IteratorValue(innerReturnResult).
|
||||
generator.emit<Bytecode::Op::Load>(inner_result_register);
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
|
||||
// 2. Return Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
|
||||
generator.perform_needed_unwinds<Bytecode::Op::Yield>();
|
||||
generator.emit<Bytecode::Op::Yield>(nullptr);
|
||||
|
||||
generator.switch_to_basic_block(type_is_return_not_done_block);
|
||||
|
||||
// FIXME: ix. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerReturnResult))).
|
||||
// x. Else, set received to Completion(GeneratorYield(innerReturnResult)).
|
||||
// FIXME: Else,
|
||||
generator.emit<Bytecode::Op::Load>(inner_return_result_register);
|
||||
|
||||
// FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here.
|
||||
generator.emit<Bytecode::Op::IteratorResultValue>();
|
||||
|
||||
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { continuation_block });
|
||||
|
||||
generator.switch_to_basic_block(continuation_block);
|
||||
get_received_completion_type_and_value();
|
||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_block });
|
||||
|
||||
generator.switch_to_basic_block(loop_end_block);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_argument)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
O(GetById) \
|
||||
O(GetByValue) \
|
||||
O(GetIterator) \
|
||||
O(GetMethod) \
|
||||
O(GetNewTarget) \
|
||||
O(GetObjectPropertyIterator) \
|
||||
O(GetVariable) \
|
||||
|
@ -42,6 +43,7 @@
|
|||
O(In) \
|
||||
O(Increment) \
|
||||
O(InstanceOf) \
|
||||
O(IteratorClose) \
|
||||
O(IteratorNext) \
|
||||
O(IteratorResultDone) \
|
||||
O(IteratorResultValue) \
|
||||
|
@ -68,6 +70,7 @@
|
|||
O(NewObject) \
|
||||
O(NewRegExp) \
|
||||
O(NewString) \
|
||||
O(NewTypeError) \
|
||||
O(Not) \
|
||||
O(PushDeclarativeEnvironment) \
|
||||
O(PutById) \
|
||||
|
@ -82,6 +85,7 @@
|
|||
O(Sub) \
|
||||
O(SuperCall) \
|
||||
O(Throw) \
|
||||
O(ThrowIfNotObject) \
|
||||
O(Typeof) \
|
||||
O(TypeofVariable) \
|
||||
O(UnaryMinus) \
|
||||
|
|
|
@ -320,6 +320,21 @@ ThrowCompletionOr<void> NewRegExp::execute_impl(Bytecode::Interpreter& interpret
|
|||
return {};
|
||||
}
|
||||
|
||||
#define JS_DEFINE_NEW_BUILTIN_ERROR_OP(ErrorName) \
|
||||
ThrowCompletionOr<void> New##ErrorName::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||
{ \
|
||||
auto& vm = interpreter.vm(); \
|
||||
auto& realm = *vm.current_realm(); \
|
||||
interpreter.accumulator() = ErrorName::create(realm, interpreter.current_executable().get_string(m_error_string)); \
|
||||
return {}; \
|
||||
} \
|
||||
DeprecatedString New##ErrorName::to_deprecated_string_impl(Bytecode::Executable const& executable) const \
|
||||
{ \
|
||||
return DeprecatedString::formatted("New" #ErrorName " {} (\"{}\")", m_error_string, executable.string_table->get(m_error_string)); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DEFINE_NEW_BUILTIN_ERROR_OP)
|
||||
|
||||
ThrowCompletionOr<void> CopyObjectExcludingProperties::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
|
@ -728,6 +743,14 @@ ThrowCompletionOr<void> Throw::execute_impl(Bytecode::Interpreter& interpreter)
|
|||
return throw_completion(interpreter.accumulator());
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ThrowIfNotObject::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
if (!interpreter.accumulator().is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, interpreter.accumulator().to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> EnterUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.enter_unwind_context(m_handler_target, m_finalizer_target);
|
||||
|
@ -857,6 +880,15 @@ ThrowCompletionOr<void> GetIterator::execute_impl(Bytecode::Interpreter& interpr
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetMethod::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto identifier = interpreter.current_executable().get_identifier(m_property);
|
||||
auto* method = TRY(interpreter.accumulator().get_method(vm, identifier));
|
||||
interpreter.accumulator() = method ?: js_undefined();
|
||||
return {};
|
||||
}
|
||||
|
||||
// 14.7.5.9 EnumerateObjectProperties ( O ), https://tc39.es/ecma262/#sec-enumerate-object-properties
|
||||
ThrowCompletionOr<void> GetObjectPropertyIterator::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
|
@ -938,6 +970,17 @@ ThrowCompletionOr<void> GetObjectPropertyIterator::execute_impl(Bytecode::Interp
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> IteratorClose::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto* iterator_object = TRY(interpreter.accumulator().to_object(vm));
|
||||
auto iterator = object_to_iterator(vm, *iterator_object);
|
||||
|
||||
// FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!)
|
||||
TRY(iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value, {} }));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> IteratorNext::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
|
@ -1210,6 +1253,11 @@ DeprecatedString Throw::to_deprecated_string_impl(Bytecode::Executable const&) c
|
|||
return "Throw";
|
||||
}
|
||||
|
||||
DeprecatedString ThrowIfNotObject::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return "ThrowIfNotObject";
|
||||
}
|
||||
|
||||
DeprecatedString EnterUnwindContext::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
auto handler_string = m_handler_target.has_value() ? DeprecatedString::formatted("{}", *m_handler_target) : "<empty>";
|
||||
|
@ -1283,11 +1331,21 @@ DeprecatedString GetIterator::to_deprecated_string_impl(Executable const&) const
|
|||
return "GetIterator";
|
||||
}
|
||||
|
||||
DeprecatedString GetMethod::to_deprecated_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return DeprecatedString::formatted("GetMethod {} ({})", m_property, executable.identifier_table->get(m_property));
|
||||
}
|
||||
|
||||
DeprecatedString GetObjectPropertyIterator::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return "GetObjectPropertyIterator";
|
||||
}
|
||||
|
||||
DeprecatedString IteratorClose::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return DeprecatedString::formatted("IteratorClose completion_type={} completion_value={}", to_underlying(m_completion_type), m_completion_value.has_value() ? m_completion_value.value().to_string_without_side_effects() : "<empty>");
|
||||
}
|
||||
|
||||
DeprecatedString IteratorNext::to_deprecated_string_impl(Executable const&) const
|
||||
{
|
||||
return "IteratorNext";
|
||||
|
|
|
@ -208,6 +208,34 @@ private:
|
|||
StringTableIndex m_flags_index;
|
||||
};
|
||||
|
||||
#define JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(O) \
|
||||
O(TypeError)
|
||||
|
||||
#define JS_DECLARE_NEW_BUILTIN_ERROR_OP(ErrorName) \
|
||||
class New##ErrorName final : public Instruction { \
|
||||
public: \
|
||||
explicit New##ErrorName(StringTableIndex error_string) \
|
||||
: Instruction(Type::New##ErrorName) \
|
||||
, m_error_string(error_string) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; \
|
||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) \
|
||||
{ \
|
||||
} \
|
||||
void replace_references_impl(Register, Register) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
StringTableIndex m_error_string; \
|
||||
};
|
||||
|
||||
JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DECLARE_NEW_BUILTIN_ERROR_OP)
|
||||
#undef JS_DECLARE_NEW_BUILTIN_ERROR_OP
|
||||
|
||||
// NOTE: This instruction is variable-width depending on the number of excluded names
|
||||
class CopyObjectExcludingProperties final : public Instruction {
|
||||
public:
|
||||
|
@ -819,6 +847,19 @@ public:
|
|||
void replace_references_impl(Register, Register) { }
|
||||
};
|
||||
|
||||
class ThrowIfNotObject final : public Instruction {
|
||||
public:
|
||||
ThrowIfNotObject()
|
||||
: Instruction(Type::ThrowIfNotObject)
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||
void replace_references_impl(Register, Register) { }
|
||||
};
|
||||
|
||||
class EnterUnwindContext final : public Instruction {
|
||||
public:
|
||||
constexpr static bool IsTerminator = true;
|
||||
|
@ -953,6 +994,23 @@ public:
|
|||
void replace_references_impl(Register, Register) { }
|
||||
};
|
||||
|
||||
class GetMethod final : public Instruction {
|
||||
public:
|
||||
GetMethod(IdentifierTableIndex property)
|
||||
: Instruction(Type::GetMethod)
|
||||
, m_property(property)
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||
void replace_references_impl(Register, Register) { }
|
||||
|
||||
private:
|
||||
IdentifierTableIndex m_property;
|
||||
};
|
||||
|
||||
class GetObjectPropertyIterator final : public Instruction {
|
||||
public:
|
||||
GetObjectPropertyIterator()
|
||||
|
@ -966,6 +1024,25 @@ public:
|
|||
void replace_references_impl(Register, Register) { }
|
||||
};
|
||||
|
||||
class IteratorClose final : public Instruction {
|
||||
public:
|
||||
IteratorClose(Completion::Type completion_type, Optional<Value> completion_value)
|
||||
: Instruction(Type::IteratorClose)
|
||||
, m_completion_type(completion_type)
|
||||
, m_completion_value(completion_value)
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||
void replace_references_impl(Register, Register) { }
|
||||
|
||||
private:
|
||||
Completion::Type m_completion_type { Completion::Type::Normal };
|
||||
Optional<Value> m_completion_value;
|
||||
};
|
||||
|
||||
class IteratorNext final : public Instruction {
|
||||
public:
|
||||
IteratorNext()
|
||||
|
|
|
@ -309,6 +309,7 @@
|
|||
M(UnsupportedDeleteSuperProperty, "Can't delete a property on 'super'") \
|
||||
M(WrappedFunctionCallThrowCompletion, "Call of wrapped target function did not complete normally") \
|
||||
M(WrappedFunctionCopyNameAndLengthThrowCompletion, "Trying to copy target name and length did not complete normally") \
|
||||
M(YieldFromIteratorMissingThrowMethod, "yield* protocol violation: iterator must have a throw method") \
|
||||
M(URIMalformed, "URI malformed") /* LibWeb bindings */ \
|
||||
M(NotAByteString, "Argument to {}() must be a byte string") \
|
||||
M(BadArgCountOne, "{}() needs one argument") \
|
||||
|
|
Loading…
Reference in a new issue