mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:38:11 +00:00
LibJS: Add basic support for module code with top-level await
For now, we handle this by creating a synthetic async function to wrap the top-level module code. This allows us to piggyback on the async function driver wrapper mechanism.
This commit is contained in:
parent
26c21fba8e
commit
a2c3db8367
4 changed files with 35 additions and 3 deletions
|
@ -1192,7 +1192,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto declaration_result = function_declaration_instantiation();
|
auto declaration_result = [&]() -> ThrowCompletionOr<void> {
|
||||||
|
if (is_module_wrapper())
|
||||||
|
return {};
|
||||||
|
return function_declaration_instantiation();
|
||||||
|
}();
|
||||||
|
|
||||||
if (m_kind == FunctionKind::Normal || m_kind == FunctionKind::Generator || m_kind == FunctionKind::AsyncGenerator) {
|
if (m_kind == FunctionKind::Normal || m_kind == FunctionKind::Generator || m_kind == FunctionKind::AsyncGenerator) {
|
||||||
if (declaration_result.is_error())
|
if (declaration_result.is_error())
|
||||||
|
|
|
@ -49,6 +49,9 @@ public:
|
||||||
|
|
||||||
void make_method(Object& home_object);
|
void make_method(Object& home_object);
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_module_wrapper() const { return m_is_module_wrapper; }
|
||||||
|
void set_is_module_wrapper(bool b) { m_is_module_wrapper = b; }
|
||||||
|
|
||||||
Statement const& ecmascript_code() const { return m_ecmascript_code; }
|
Statement const& ecmascript_code() const { return m_ecmascript_code; }
|
||||||
Vector<FunctionParameter> const& formal_parameters() const { return m_formal_parameters; }
|
Vector<FunctionParameter> const& formal_parameters() const { return m_formal_parameters; }
|
||||||
|
|
||||||
|
@ -153,6 +156,7 @@ private:
|
||||||
HashTable<DeprecatedFlyString> m_parameter_names;
|
HashTable<DeprecatedFlyString> m_parameter_names;
|
||||||
Vector<FunctionDeclaration const&> m_functions_to_initialize;
|
Vector<FunctionDeclaration const&> m_functions_to_initialize;
|
||||||
bool m_arguments_object_needed { false };
|
bool m_arguments_object_needed { false };
|
||||||
|
bool m_is_module_wrapper { false };
|
||||||
Vector<VariableNameToInitialize> m_var_names_to_initialize_binding;
|
Vector<VariableNameToInitialize> m_var_names_to_initialize_binding;
|
||||||
Vector<DeprecatedFlyString> m_function_names_to_initialize_binding;
|
Vector<DeprecatedFlyString> m_function_names_to_initialize_binding;
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
#include <AK/QuickSort.h>
|
#include <AK/QuickSort.h>
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
#include <LibJS/Parser.h>
|
#include <LibJS/Parser.h>
|
||||||
|
#include <LibJS/Runtime/AsyncFunctionDriverWrapper.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||||
#include <LibJS/Runtime/ModuleEnvironment.h>
|
#include <LibJS/Runtime/ModuleEnvironment.h>
|
||||||
|
#include <LibJS/Runtime/PromiseCapability.h>
|
||||||
#include <LibJS/SourceTextModule.h>
|
#include <LibJS/SourceTextModule.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -748,7 +750,29 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
|
||||||
VERIFY(capability != nullptr);
|
VERIFY(capability != nullptr);
|
||||||
|
|
||||||
// b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
|
// b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
|
||||||
async_block_start<NonnullRefPtr<Statement const>>(vm, m_ecmascript_code, *capability, *module_context);
|
|
||||||
|
// AD-HOC: We implement asynchronous execution via synthetic generator functions,
|
||||||
|
// so we fake "AsyncBlockStart" here by creating an async function to wrap
|
||||||
|
// the top-level module code.
|
||||||
|
// FIXME: Improve this situation, so we can match the spec better.
|
||||||
|
|
||||||
|
auto module_wrapper_function = ECMAScriptFunctionObject::create(
|
||||||
|
realm(), "module code with top-level await", StringView {}, this->m_ecmascript_code,
|
||||||
|
{}, 0, {}, environment(), nullptr, FunctionKind::Async, true, false, false);
|
||||||
|
module_wrapper_function->set_is_module_wrapper(true);
|
||||||
|
|
||||||
|
// AD-HOC: We push/pop the moduleContext around the call to ensure that the async execution context
|
||||||
|
// captures the module execution context.
|
||||||
|
vm.push_execution_context(*module_context);
|
||||||
|
auto result = call(vm, Value { module_wrapper_function }, js_undefined(), ReadonlySpan<Value> {});
|
||||||
|
vm.pop_execution_context();
|
||||||
|
|
||||||
|
// AD-HOC: This is basically analogous to what AsyncBlockStart would do.
|
||||||
|
if (result.is_throw_completion()) {
|
||||||
|
MUST(call(vm, *capability->reject(), js_undefined(), result.throw_completion().value().value()));
|
||||||
|
} else {
|
||||||
|
MUST(call(vm, *capability->resolve(), js_undefined(), result.value()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11. Return unused.
|
// 11. Return unused.
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe("normal behavior", () => {
|
||||||
expect(passed).toBeTrue();
|
expect(passed).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.xfail("value from async module", () => {
|
test("value from async module", () => {
|
||||||
const shadowRealm = new ShadowRealm();
|
const shadowRealm = new ShadowRealm();
|
||||||
const promise = shadowRealm.importValue("./async-module.mjs", "foo");
|
const promise = shadowRealm.importValue("./async-module.mjs", "foo");
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toBeInstanceOf(Promise);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue