2021-06-11 01:38:30 +04:30
/*
* Copyright ( c ) 2021 , Ali Mohammad Pur < mpfard @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/TemporaryChange.h>
# include <LibJS/Bytecode/Generator.h>
# include <LibJS/Bytecode/Interpreter.h>
# include <LibJS/Runtime/GeneratorObject.h>
2022-01-16 14:14:42 +01:00
# include <LibJS/Runtime/GeneratorPrototype.h>
2021-06-11 01:38:30 +04:30
# include <LibJS/Runtime/GlobalObject.h>
2023-07-19 06:54:48 -04:00
# include <LibJS/Runtime/Iterator.h>
2021-06-11 01:38:30 +04:30
namespace JS {
2023-11-19 09:45:05 +01:00
JS_DEFINE_ALLOCATOR ( GeneratorObject ) ;
2024-05-01 19:33:49 +02:00
ThrowCompletionOr < NonnullGCPtr < GeneratorObject > > GeneratorObject : : create ( Realm & realm , Value initial_value , ECMAScriptFunctionObject * generating_function , NonnullOwnPtr < ExecutionContext > execution_context )
2021-06-11 01:38:30 +04:30
{
2022-08-16 00:20:49 +01:00
auto & vm = realm . vm ( ) ;
2021-06-15 00:04:08 -07:00
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
2021-11-15 01:48:55 +01:00
Value generating_function_prototype ;
if ( generating_function - > kind ( ) = = FunctionKind : : Async ) {
// We implement async functions by transforming them to generator function in the bytecode
// interpreter. However an async function does not have a prototype and should not be
// changed thus we hardcode the prototype.
2022-08-27 00:54:55 +01:00
generating_function_prototype = realm . intrinsics ( ) . generator_prototype ( ) ;
2021-11-15 01:48:55 +01:00
} else {
2022-08-16 00:20:49 +01:00
generating_function_prototype = TRY ( generating_function - > get ( vm . names . prototype ) ) ;
2021-11-15 01:48:55 +01:00
}
2023-04-13 15:26:41 +02:00
auto generating_function_prototype_object = TRY ( generating_function_prototype . to_object ( vm ) ) ;
2024-11-14 05:50:17 +13:00
auto object = realm . create < GeneratorObject > ( realm , generating_function_prototype_object , move ( execution_context ) ) ;
2021-06-11 01:38:30 +04:30
object - > m_generating_function = generating_function ;
object - > m_previous_value = initial_value ;
2022-12-14 17:40:33 +00:00
return object ;
2021-06-11 01:38:30 +04:30
}
2023-11-27 16:45:45 +01:00
GeneratorObject : : GeneratorObject ( Realm & , Object & prototype , NonnullOwnPtr < ExecutionContext > context , Optional < StringView > generator_brand )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-11-11 00:46:07 +03:30
, m_execution_context ( move ( context ) )
2023-07-16 14:37:35 -04:00
, m_generator_brand ( move ( generator_brand ) )
2021-06-11 01:38:30 +04:30
{
}
void GeneratorObject : : visit_edges ( Cell : : Visitor & visitor )
{
2021-09-11 14:05:12 +02:00
Base : : visit_edges ( visitor ) ;
2021-06-11 01:38:30 +04:30
visitor . visit ( m_generating_function ) ;
2021-09-11 19:17:38 +03:00
visitor . visit ( m_previous_value ) ;
2023-12-13 10:24:30 +01:00
m_execution_context - > visit_edges ( visitor ) ;
2021-06-11 01:38:30 +04:30
}
2022-11-25 23:14:08 +00:00
// 27.5.3.2 GeneratorValidate ( generator, generatorBrand ), https://tc39.es/ecma262/#sec-generatorvalidate
2023-07-16 14:33:20 -04:00
ThrowCompletionOr < GeneratorObject : : GeneratorState > GeneratorObject : : validate ( VM & vm , Optional < StringView > const & generator_brand )
2021-06-11 01:38:30 +04:30
{
2022-11-25 23:14:08 +00:00
// 1. Perform ? RequireInternalSlot(generator, [[GeneratorState]]).
// 2. Perform ? RequireInternalSlot(generator, [[GeneratorBrand]]).
// NOTE: Already done by the caller of resume or resume_abrupt, as they wouldn't have a GeneratorObject otherwise.
// 3. If generator.[[GeneratorBrand]] is not the same value as generatorBrand, throw a TypeError exception.
if ( m_generator_brand ! = generator_brand )
2023-07-16 14:33:20 -04:00
return vm . throw_completion < TypeError > ( ErrorType : : GeneratorBrandMismatch , m_generator_brand . value_or ( " <empty> " sv ) , generator_brand . value_or ( " <empty> " sv ) ) ;
2022-11-25 23:14:08 +00:00
// 4. Assert: generator also has a [[GeneratorContext]] internal slot.
// NOTE: Done by already being a GeneratorObject.
// 5. Let state be generator.[[GeneratorState]].
auto state = m_generator_state ;
// 6. If state is executing, throw a TypeError exception.
if ( state = = GeneratorState : : Executing )
return vm . throw_completion < TypeError > ( ErrorType : : GeneratorAlreadyExecuting ) ;
// 7. Return state.
return state ;
}
ThrowCompletionOr < Value > GeneratorObject : : execute ( VM & vm , Completion const & completion )
{
// Loosely based on step 4 of https://tc39.es/ecma262/#sec-generatorstart mixed with https://tc39.es/ecma262/#sec-generatoryield at the end.
VERIFY ( completion . value ( ) . has_value ( ) ) ;
2021-06-11 01:38:30 +04:30
2022-11-25 23:14:08 +00:00
auto generated_value = [ ] ( Value value ) - > Value {
2021-06-11 01:38:30 +04:30
if ( value . is_object ( ) )
2022-11-25 23:14:08 +00:00
return value . as_object ( ) . get_without_side_effects ( " result " ) ;
2021-06-11 01:38:30 +04:30
return value . is_empty ( ) ? js_undefined ( ) : value ;
} ;
2024-05-06 06:44:08 +02:00
auto generated_continuation = [ & ] ( Value value ) - > Optional < size_t > {
2021-10-16 22:11:08 +03:00
if ( value . is_object ( ) ) {
2022-11-25 23:14:08 +00:00
auto number_value = value . as_object ( ) . get_without_side_effects ( " continuation " ) ;
2024-05-06 06:44:08 +02:00
if ( number_value . is_null ( ) )
return { } ;
return static_cast < u64 > ( number_value . as_double ( ) ) ;
2021-10-16 22:11:08 +03:00
}
2024-05-06 06:44:08 +02:00
return { } ;
2021-06-11 01:38:30 +04:30
} ;
2022-11-25 23:14:08 +00:00
auto & realm = * vm . current_realm ( ) ;
2022-12-13 20:49:50 +00:00
auto completion_object = Object : : create ( realm , nullptr ) ;
2022-11-25 23:14:08 +00:00
completion_object - > define_direct_property ( vm . names . type , Value ( to_underlying ( completion . type ( ) ) ) , default_attributes ) ;
completion_object - > define_direct_property ( vm . names . value , completion . value ( ) . value ( ) , default_attributes ) ;
2023-06-22 15:59:18 +02:00
auto & bytecode_interpreter = vm . bytecode_interpreter ( ) ;
2021-06-11 01:38:30 +04:30
2024-05-06 06:44:08 +02:00
auto const next_block = generated_continuation ( m_previous_value ) ;
2021-06-11 01:38:30 +04:30
2022-11-25 23:14:08 +00:00
// We should never enter `execute` again after the generator is complete.
2024-05-06 06:44:08 +02:00
VERIFY ( next_block . has_value ( ) ) ;
2021-06-11 01:38:30 +04:30
2024-05-06 06:44:08 +02:00
auto next_result = bytecode_interpreter . run_executable ( * m_generating_function - > bytecode_executable ( ) , next_block , completion_object ) ;
2021-11-11 00:46:07 +03:30
vm . pop_execution_context ( ) ;
2021-06-11 01:38:30 +04:30
2022-11-25 23:14:08 +00:00
auto result_value = move ( next_result . value ) ;
if ( result_value . is_throw_completion ( ) ) {
// Uncaught exceptions disable the generator.
m_generator_state = GeneratorState : : Completed ;
return result_value ;
}
m_previous_value = result_value . release_value ( ) ;
2024-05-06 06:44:08 +02:00
bool done = ! generated_continuation ( m_previous_value ) . has_value ( ) ;
2022-11-25 23:14:08 +00:00
m_generator_state = done ? GeneratorState : : Completed : GeneratorState : : SuspendedYield ;
return create_iterator_result_object ( vm , generated_value ( m_previous_value ) , done ) ;
}
// 27.5.3.3 GeneratorResume ( generator, value, generatorBrand ), https://tc39.es/ecma262/#sec-generatorresume
2023-07-16 14:33:20 -04:00
ThrowCompletionOr < Value > GeneratorObject : : resume ( VM & vm , Value value , Optional < StringView > const & generator_brand )
2022-11-25 23:14:08 +00:00
{
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
auto state = TRY ( validate ( vm , generator_brand ) ) ;
// 2. If state is completed, return CreateIterResultObject(undefined, true).
if ( state = = GeneratorState : : Completed )
return create_iterator_result_object ( vm , js_undefined ( ) , true ) ;
// 3. Assert: state is either suspendedStart or suspendedYield.
VERIFY ( state = = GeneratorState : : SuspendedStart | | state = = GeneratorState : : SuspendedYield ) ;
// 4. Let genContext be generator.[[GeneratorContext]].
auto & generator_context = m_execution_context ;
// 5. Let methodContext be the running execution context.
auto const & method_context = vm . running_execution_context ( ) ;
// FIXME: 6. Suspend methodContext.
// 8. Push genContext onto the execution context stack; genContext is now the running execution context.
// NOTE: This is done out of order as to not permanently disable the generator if push_execution_context throws,
// as `resume` will immediately throw when [[GeneratorState]] is "executing", never allowing the state to change.
2023-11-27 16:45:45 +01:00
TRY ( vm . push_execution_context ( * generator_context , { } ) ) ;
2022-11-25 23:14:08 +00:00
// 7. Set generator.[[GeneratorState]] to executing.
m_generator_state = GeneratorState : : Executing ;
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.
auto result = execute ( vm , normal_completion ( value ) ) ;
// 10. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context.
VERIFY ( & vm . running_execution_context ( ) = = & method_context ) ;
// 11. Return ? result.
return result ;
}
// 27.5.3.4 GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand ), https://tc39.es/ecma262/#sec-generatorresumeabrupt
2023-07-16 14:33:20 -04:00
ThrowCompletionOr < Value > GeneratorObject : : resume_abrupt ( JS : : VM & vm , JS : : Completion abrupt_completion , Optional < StringView > const & generator_brand )
2022-11-25 23:14:08 +00:00
{
// Not part of the spec, but the spec assumes abruptCompletion.[[Value]] is not empty.
VERIFY ( abrupt_completion . value ( ) . has_value ( ) ) ;
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
auto state = TRY ( validate ( vm , generator_brand ) ) ;
// 2. If state is suspendedStart, then
if ( state = = GeneratorState : : SuspendedStart ) {
// a. Set generator.[[GeneratorState]] to completed.
m_generator_state = GeneratorState : : Completed ;
// b. Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with generator can be discarded at this point.
// We don't currently discard anything.
// c. Set state to completed.
state = GeneratorState : : Completed ;
}
// 3. If state is completed, then
if ( state = = GeneratorState : : Completed ) {
// a. If abruptCompletion.[[Type]] is return, then
if ( abrupt_completion . type ( ) = = Completion : : Type : : Return ) {
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
return create_iterator_result_object ( vm , abrupt_completion . value ( ) . value ( ) , true ) ;
}
// b. Return ? abruptCompletion.
return abrupt_completion ;
}
// 4. Assert: state is suspendedYield.
VERIFY ( state = = GeneratorState : : SuspendedYield ) ;
// 5. Let genContext be generator.[[GeneratorContext]].
auto & generator_context = m_execution_context ;
// 6. Let methodContext be the running execution context.
auto const & method_context = vm . running_execution_context ( ) ;
// FIXME: 7. Suspend methodContext.
// 9. Push genContext onto the execution context stack; genContext is now the running execution context.
// NOTE: This is done out of order as to not permanently disable the generator if push_execution_context throws,
// as `resume_abrupt` will immediately throw when [[GeneratorState]] is "executing", never allowing the state to change.
2023-11-27 16:45:45 +01:00
TRY ( vm . push_execution_context ( * generator_context , { } ) ) ;
2022-11-25 23:14:08 +00:00
// 8. Set generator.[[GeneratorState]] to executing.
m_generator_state = GeneratorState : : Executing ;
// 10. Resume the suspended evaluation of genContext using abruptCompletion as the result of the operation that suspended it. Let result be the Completion Record returned by the resumed computation.
auto result = execute ( vm , abrupt_completion ) ;
2021-11-11 04:11:56 +03:30
2022-11-25 23:14:08 +00:00
// 11. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context.
VERIFY ( & vm . running_execution_context ( ) = = & method_context ) ;
2021-06-11 01:38:30 +04:30
2022-11-25 23:14:08 +00:00
// 12. Return ? result.
2021-06-11 01:38:30 +04:30
return result ;
}
}