LibWeb: Refactor WebAssembly API to use spec-defined promise AOs
This commit is contained in:
parent
3504370281
commit
ba6dcd7521
Notes:
github-actions[bot]
2024-11-01 17:43:26 +00:00
Author: https://github.com/ADKaster Commit: https://github.com/LadybirdBrowser/ladybird/commit/ba6dcd75218 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1982
6 changed files with 278 additions and 142 deletions
|
@ -2,15 +2,51 @@
|
|||
ArrayBuffer
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
FIXME: Run test for Uint8Array. Not running due to flakiness.
|
||||
FIXME: Run test for Uint8ClampedArray. Not running due to flakiness.
|
||||
FIXME: Run test for Uint16Array. Not running due to flakiness.
|
||||
FIXME: Run test for Uint32Array. Not running due to flakiness.
|
||||
FIXME: Run test for Int8Array. Not running due to flakiness.
|
||||
FIXME: Run test for Int16Array. Not running due to flakiness.
|
||||
FIXME: Run test for Float32Array. Not running due to flakiness.
|
||||
FIXME: Run test for Float64Array. Not running due to flakiness.
|
||||
FIXME: Run test for BigUint64Array. Not running due to flakiness.
|
||||
FIXME: Run test for BigInt64Array. Not running due to flakiness.
|
||||
FIXME: Run test for DataView. Not running due to flakiness.
|
||||
FIXME: Run test for WebAssembly.Module. Not running due to flakiness.
|
||||
-------------
|
||||
Uint8Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Uint8ClampedArray
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Uint16Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Uint32Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Int8Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Int16Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Float32Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
Float64Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
BigUint64Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
BigInt64Array
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
DataView
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
-------------
|
||||
WebAssembly.Module
|
||||
-------------
|
||||
Hello from wasm!!!!!!
|
||||
|
|
|
@ -9,13 +9,8 @@
|
|||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachedUint8Memory0 = null;
|
||||
|
||||
function getUint8Memory0() {
|
||||
if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
|
||||
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8Memory0;
|
||||
return new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
|
@ -58,24 +53,30 @@
|
|||
]).buffer;
|
||||
|
||||
const BUFFER_SOURCES = [
|
||||
{ constructor: Uint8Array, flaky: true },
|
||||
{ constructor: Uint8ClampedArray, flaky: true },
|
||||
{ constructor: Uint16Array, flaky: true },
|
||||
{ constructor: Uint32Array, flaky: true },
|
||||
{ constructor: Int8Array, flaky: true },
|
||||
{ constructor: Int16Array, flaky: true },
|
||||
{ constructor: Float32Array, flaky: true },
|
||||
{ constructor: Float64Array, flaky: true },
|
||||
{ constructor: BigUint64Array, flaky: true },
|
||||
{ constructor: BigInt64Array, flaky: true },
|
||||
{ constructor: DataView, flaky: true },
|
||||
{ constructor: Uint8Array},
|
||||
{ constructor: Uint8ClampedArray },
|
||||
{ constructor: Uint16Array },
|
||||
{ constructor: Uint32Array },
|
||||
{ constructor: Int8Array },
|
||||
{ constructor: Int16Array },
|
||||
{ constructor: Float32Array },
|
||||
{ constructor: Float64Array },
|
||||
{ constructor: BigUint64Array },
|
||||
{ constructor: BigInt64Array },
|
||||
{ constructor: DataView },
|
||||
];
|
||||
|
||||
|
||||
async function runTest(buffer) {
|
||||
println("-------------")
|
||||
println(buffer.constructor.name);
|
||||
println("-------------")
|
||||
const module = await WebAssembly.instantiate(buffer, exports);
|
||||
const module = await WebAssembly.instantiate(buffer, exports).catch(e => {
|
||||
println(`FIXME: Failed to instantiate with ${buffer.constructor.name}: ${e.name}: ${e.message}`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
if (!module) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
wasm = module.instance?.exports ?? module.exports;
|
||||
try {
|
||||
wasm.greet();
|
||||
|
@ -87,23 +88,16 @@
|
|||
|
||||
await runTest(arrayBuffer);
|
||||
|
||||
for (const { constructor, flaky } of BUFFER_SOURCES) {
|
||||
if (!flaky) {
|
||||
await runTest(new constructor(arrayBuffer));
|
||||
} else {
|
||||
// The flakiness is the runtime either trapping with a RangeError, or the printed string being complete garbage,
|
||||
// which more prominently happens with DataView and the arrays bigger than u8. However, the RangeError flake is
|
||||
// still possible with u8.
|
||||
println(`FIXME: Run test for ${constructor.name}. Not running due to flakiness.`);
|
||||
}
|
||||
for (const { constructor } of BUFFER_SOURCES) {
|
||||
await runTest(new constructor(arrayBuffer));
|
||||
}
|
||||
|
||||
if (false) {
|
||||
const compiledModule = await WebAssembly.compile(arrayBuffer);
|
||||
const compiledModule = await WebAssembly.compile(arrayBuffer).catch(() => {
|
||||
println(`FIXME: Failed to compile with ArrayBuffer`);
|
||||
return Promise.resolve()
|
||||
});
|
||||
if (compiledModule) {
|
||||
await runTest(compiledModule);
|
||||
} else {
|
||||
// Same issues as above.
|
||||
println(`FIXME: Run test for WebAssembly.Module. Not running due to flakiness.`);
|
||||
}
|
||||
|
||||
done();
|
||||
|
|
|
@ -21,14 +21,13 @@ namespace Web::WebAssembly {
|
|||
|
||||
JS_DEFINE_ALLOCATOR(Instance);
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<Instance>> Instance::construct_impl(JS::Realm& realm, Module& module, Optional<JS::Handle<JS::Object>>& import_object)
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<Instance>> Instance::construct_impl(JS::Realm& realm, Module& module, Optional<JS::Handle<JS::Object>>& import_object_handle)
|
||||
{
|
||||
// FIXME: Implement the importObject parameter.
|
||||
(void)import_object;
|
||||
JS::GCPtr<JS::Object> import_object = import_object_handle.has_value() ? import_object_handle.value().ptr() : nullptr;
|
||||
|
||||
auto& vm = realm.vm();
|
||||
|
||||
auto module_instance = TRY(Detail::instantiate_module(vm, module.compiled_module()->module));
|
||||
auto module_instance = TRY(Detail::instantiate_module(vm, module.compiled_module()->module, import_object));
|
||||
return vm.heap().allocate<Instance>(realm, realm, move(module_instance));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <LibWeb/Bindings/ModulePrototype.h>
|
||||
#include <LibWeb/WebAssembly/Module.h>
|
||||
#include <LibWeb/WebAssembly/WebAssembly.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/Buffers.h>
|
||||
|
||||
namespace Web::WebAssembly {
|
||||
|
@ -21,7 +22,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Module>> Module::construct_impl(JS::Realm&
|
|||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
auto compiled_module = TRY(Detail::parse_module(vm, bytes->raw_object()));
|
||||
auto stable_bytes_or_error = WebIDL::get_buffer_source_copy(bytes->raw_object());
|
||||
if (stable_bytes_or_error.is_error()) {
|
||||
VERIFY(stable_bytes_or_error.error().code() == ENOMEM);
|
||||
return vm.throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory));
|
||||
}
|
||||
auto stable_bytes = stable_bytes_or_error.release_value();
|
||||
|
||||
auto compiled_module = TRY(Detail::compile_a_webassembly_module(vm, move(stable_bytes)));
|
||||
return vm.heap().allocate<Module>(realm, realm, move(compiled_module));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,17 +19,23 @@
|
|||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWasm/AbstractMachine/Validator.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/WebAssembly/Instance.h>
|
||||
#include <LibWeb/WebAssembly/Memory.h>
|
||||
#include <LibWeb/WebAssembly/Module.h>
|
||||
#include <LibWeb/WebAssembly/Table.h>
|
||||
#include <LibWeb/WebAssembly/WebAssembly.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/Buffers.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
namespace Web::WebAssembly {
|
||||
|
||||
static JS::NonnullGCPtr<WebIDL::Promise> asynchronously_compile_webassembly_module(JS::VM&, ByteBuffer, HTML::Task::Source = HTML::Task::Source::Unspecified);
|
||||
static JS::NonnullGCPtr<WebIDL::Promise> instantiate_promise_of_module(JS::VM&, JS::NonnullGCPtr<WebIDL::Promise>, JS::GCPtr<JS::Object> import_object);
|
||||
static JS::NonnullGCPtr<WebIDL::Promise> asynchronously_instantiate_webassembly_module(JS::VM&, JS::NonnullGCPtr<Module>, JS::GCPtr<JS::Object> import_object);
|
||||
|
||||
namespace Detail {
|
||||
|
||||
HashMap<JS::GCPtr<JS::Object>, WebAssemblyCache> s_caches;
|
||||
|
@ -62,21 +68,19 @@ void finalize(JS::Object& object)
|
|||
bool validate(JS::VM& vm, JS::Handle<WebIDL::BufferSource>& bytes)
|
||||
{
|
||||
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
|
||||
// Note: There's no need to copy the bytes here as the buffer data cannot change while we're compiling the module.
|
||||
auto stable_bytes = WebIDL::get_buffer_source_copy(*bytes->raw_object());
|
||||
if (stable_bytes.is_error()) {
|
||||
VERIFY(stable_bytes.error().code() == ENOMEM);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Compile stableBytes as a WebAssembly module and store the results as module.
|
||||
auto module_or_error = Detail::parse_module(vm, bytes->raw_object());
|
||||
auto module_or_error = Detail::compile_a_webassembly_module(vm, stable_bytes.release_value());
|
||||
|
||||
// 3. If module is error, return false.
|
||||
if (module_or_error.is_error())
|
||||
return false;
|
||||
|
||||
// 3 continued - our "compile" step is lazy with validation, explicitly do the validation.
|
||||
auto compiled_module = module_or_error.release_value();
|
||||
auto& cache = Detail::get_cache(*vm.current_realm());
|
||||
if (cache.abstract_machine().validate(compiled_module->module).is_error())
|
||||
return false;
|
||||
|
||||
// 4. Return true.
|
||||
return true;
|
||||
}
|
||||
|
@ -86,87 +90,54 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> compile(JS::VM& vm, JS::H
|
|||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// FIXME: This shouldn't block!
|
||||
auto compiled_module_or_error = Detail::parse_module(vm, bytes->raw_object());
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
if (compiled_module_or_error.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, compiled_module_or_error.error_value());
|
||||
} else {
|
||||
auto module_object = vm.heap().allocate<Module>(realm, realm, compiled_module_or_error.release_value());
|
||||
WebIDL::resolve_promise(realm, promise, module_object);
|
||||
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
|
||||
auto stable_bytes = WebIDL::get_buffer_source_copy(*bytes->raw_object());
|
||||
if (stable_bytes.is_error()) {
|
||||
VERIFY(stable_bytes.error().code() == ENOMEM);
|
||||
return WebIDL::create_rejected_promise_from_exception(realm, vm.throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory)));
|
||||
}
|
||||
|
||||
return promise;
|
||||
// 2. Asynchronously compile a WebAssembly module from stableBytes and return the result.
|
||||
return asynchronously_compile_webassembly_module(vm, stable_bytes.release_value());
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> instantiate(JS::VM& vm, JS::Handle<WebIDL::BufferSource>& bytes, Optional<JS::Handle<JS::Object>>& import_object)
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> instantiate(JS::VM& vm, JS::Handle<WebIDL::BufferSource>& bytes, Optional<JS::Handle<JS::Object>>& import_object_handle)
|
||||
{
|
||||
// FIXME: Implement the importObject parameter.
|
||||
(void)import_object;
|
||||
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// FIXME: This shouldn't block!
|
||||
auto compiled_module_or_error = Detail::parse_module(vm, bytes->raw_object());
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
if (compiled_module_or_error.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, compiled_module_or_error.error_value());
|
||||
return promise;
|
||||
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
|
||||
auto stable_bytes = WebIDL::get_buffer_source_copy(*bytes->raw_object());
|
||||
if (stable_bytes.is_error()) {
|
||||
VERIFY(stable_bytes.error().code() == ENOMEM);
|
||||
return WebIDL::create_rejected_promise_from_exception(realm, vm.throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory)));
|
||||
}
|
||||
|
||||
auto compiled_module = compiled_module_or_error.release_value();
|
||||
auto result = Detail::instantiate_module(vm, compiled_module->module);
|
||||
// 2. Asynchronously compile a WebAssembly module from stableBytes and let promiseOfModule be the result.
|
||||
auto promise_of_module = asynchronously_compile_webassembly_module(vm, stable_bytes.release_value());
|
||||
|
||||
if (result.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, result.error_value());
|
||||
} else {
|
||||
auto module_object = vm.heap().allocate<Module>(realm, realm, move(compiled_module));
|
||||
auto instance_object = vm.heap().allocate<Instance>(realm, realm, result.release_value());
|
||||
|
||||
auto object = JS::Object::create(realm, nullptr);
|
||||
object->define_direct_property("module", module_object, JS::default_attributes);
|
||||
object->define_direct_property("instance", instance_object, JS::default_attributes);
|
||||
WebIDL::resolve_promise(realm, promise, object);
|
||||
}
|
||||
|
||||
return promise;
|
||||
// 3. Instantiate promiseOfModule with imports importObject and return the result.
|
||||
JS::GCPtr<JS::Object> const import_object = import_object_handle.has_value() ? import_object_handle.value().ptr() : nullptr;
|
||||
return instantiate_promise_of_module(vm, promise_of_module, import_object);
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate-moduleobject-importobject
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> instantiate(JS::VM& vm, Module const& module_object, Optional<JS::Handle<JS::Object>>& import_object)
|
||||
{
|
||||
// FIXME: Implement the importObject parameter.
|
||||
(void)import_object;
|
||||
|
||||
auto& realm = *vm.current_realm();
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// FIXME: This shouldn't block!
|
||||
auto const& compiled_module = module_object.compiled_module();
|
||||
auto result = Detail::instantiate_module(vm, compiled_module->module);
|
||||
|
||||
if (result.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, result.error_value());
|
||||
} else {
|
||||
auto instance_object = vm.heap().allocate<Instance>(realm, realm, result.release_value());
|
||||
WebIDL::resolve_promise(realm, promise, instance_object);
|
||||
}
|
||||
|
||||
return promise;
|
||||
// 1. Asynchronously instantiate the WebAssembly module moduleObject importing importObject, and return the result.
|
||||
JS::NonnullGCPtr<Module> module { const_cast<Module&>(module_object) };
|
||||
JS::GCPtr<JS::Object> const imports = import_object.has_value() ? import_object.value().ptr() : nullptr;
|
||||
return asynchronously_instantiate_webassembly_module(vm, module, imports);
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module)
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module, JS::GCPtr<JS::Object> import_object)
|
||||
{
|
||||
Wasm::Linker linker { module };
|
||||
HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
|
||||
auto import_argument = vm.argument(1);
|
||||
auto& cache = get_cache(*vm.current_realm());
|
||||
if (!import_argument.is_undefined()) {
|
||||
auto import_object = TRY(import_argument.to_object(vm));
|
||||
if (import_object) {
|
||||
dbgln_if(LIBWEB_WASM_DEBUG, "Trying to resolve stuff because import object was specified");
|
||||
for (Wasm::Linker::Name const& import_name : linker.unresolved_imports()) {
|
||||
dbgln_if(LIBWEB_WASM_DEBUG, "Trying to resolve {}::{}", import_name.module, import_name.name);
|
||||
|
@ -309,32 +280,10 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
return instance_result.release_value();
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> parse_module(JS::VM& vm, JS::Object* buffer_object)
|
||||
// // https://webassembly.github.io/spec/js-api/#compile-a-webassembly-module
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> compile_a_webassembly_module(JS::VM& vm, ByteBuffer data)
|
||||
{
|
||||
ReadonlyBytes data;
|
||||
if (is<JS::ArrayBuffer>(buffer_object)) {
|
||||
auto& buffer = static_cast<JS::ArrayBuffer&>(*buffer_object);
|
||||
data = buffer.buffer();
|
||||
} else if (is<JS::TypedArrayBase>(buffer_object)) {
|
||||
auto& buffer = static_cast<JS::TypedArrayBase&>(*buffer_object);
|
||||
|
||||
auto typed_array_record = JS::make_typed_array_with_buffer_witness_record(buffer, JS::ArrayBuffer::Order::SeqCst);
|
||||
if (JS::is_typed_array_out_of_bounds(typed_array_record))
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::BufferOutOfBounds, "TypedArray"sv);
|
||||
|
||||
data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), JS::typed_array_byte_length(typed_array_record));
|
||||
} else if (is<JS::DataView>(buffer_object)) {
|
||||
auto& buffer = static_cast<JS::DataView&>(*buffer_object);
|
||||
|
||||
auto view_record = JS::make_data_view_with_buffer_witness_record(buffer, JS::ArrayBuffer::Order::SeqCst);
|
||||
if (JS::is_view_out_of_bounds(view_record))
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::BufferOutOfBounds, "DataView"sv);
|
||||
|
||||
data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), JS::get_view_byte_length(view_record));
|
||||
} else {
|
||||
return vm.throw_completion<JS::TypeError>("Not a BufferSource"sv);
|
||||
}
|
||||
FixedMemoryStream stream { data };
|
||||
FixedMemoryStream stream { data.bytes() };
|
||||
auto module_result = Wasm::Module::parse(stream);
|
||||
if (module_result.is_error()) {
|
||||
// FIXME: Throw CompileError instead.
|
||||
|
@ -525,4 +474,154 @@ JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type)
|
|||
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#asynchronously-compile-a-webassembly-module
|
||||
JS::NonnullGCPtr<WebIDL::Promise> asynchronously_compile_webassembly_module(JS::VM& vm, ByteBuffer bytes, HTML::Task::Source task_source)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let promise be a new Promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 2. Run the following steps in parallel:
|
||||
Platform::EventLoopPlugin::the().deferred_invoke([&vm, bytes = move(bytes), promise, task_source]() mutable {
|
||||
HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*promise->promise()), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
// 1. Compile the WebAssembly module bytes and store the result as module.
|
||||
auto module_or_error = Detail::compile_a_webassembly_module(vm, move(bytes));
|
||||
|
||||
// 2. Queue a task to perform the following steps. If taskSource was provided, queue the task on that task source.
|
||||
HTML::queue_a_task(task_source, nullptr, nullptr, JS::create_heap_function(vm.heap(), [&vm, promise, module_or_error = move(module_or_error)]() mutable {
|
||||
HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*promise->promise()), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
auto& realm = HTML::relevant_realm(*promise->promise());
|
||||
|
||||
// 1. If module is error, reject promise with a CompileError exception.
|
||||
if (module_or_error.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, module_or_error.error_value());
|
||||
}
|
||||
|
||||
// 2. Otherwise,
|
||||
else {
|
||||
// 1. Construct a WebAssembly module object from module and bytes, and let moduleObject be the result.
|
||||
// FIXME: Save bytes to the Module instance instead of moving into compile_a_webassembly_module
|
||||
auto module_object = vm.heap().allocate<Module>(realm, realm, module_or_error.release_value());
|
||||
|
||||
// 2. Resolve promise with moduleObject.
|
||||
WebIDL::resolve_promise(*vm.current_realm(), promise, module_object);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
// 3. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#asynchronously-instantiate-a-webassembly-module
|
||||
JS::NonnullGCPtr<WebIDL::Promise> asynchronously_instantiate_webassembly_module(JS::VM& vm, JS::NonnullGCPtr<Module> module_object, JS::GCPtr<JS::Object> import_object)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let promise be a new promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 2. Let module be moduleObject.[[Module]].
|
||||
auto module = module_object->compiled_module();
|
||||
|
||||
// 3. Read the imports of module with imports importObject, and let imports be the result.
|
||||
// If this operation throws an exception, catch it, reject promise with the exception, and return promise.
|
||||
// Note: We do this at the same time as instantiation in instantiate_module.
|
||||
|
||||
// 4. Run the following steps in parallel:
|
||||
// 1. Queue a task to perform the following steps: Note: Implementation-specific work may be performed here.
|
||||
HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, JS::create_heap_function(vm.heap(), [&vm, promise, module, import_object]() {
|
||||
HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*promise->promise()), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
auto& realm = HTML::relevant_realm(*promise->promise());
|
||||
|
||||
// 1. Instantiate the core of a WebAssembly module module with imports, and let instance be the result.
|
||||
// If this throws an exception, catch it, reject promise with the exception, and terminate these substeps.
|
||||
auto result = Detail::instantiate_module(vm, module->module, import_object);
|
||||
if (result.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, result.error_value());
|
||||
return;
|
||||
}
|
||||
auto instance = result.release_value();
|
||||
|
||||
// 2. Let instanceObject be a new Instance.
|
||||
// 3. Initialize instanceObject from module and instance. If this throws an exception, catch it, reject promise with the exception, and terminate these substeps.
|
||||
// FIXME: Investigate whether we are doing all the proper steps for "initialize an instance object"
|
||||
auto instance_object = vm.heap().allocate<Instance>(realm, realm, move(instance));
|
||||
|
||||
// 4. Resolve promise with instanceObject.
|
||||
WebIDL::resolve_promise(realm, promise, instance_object);
|
||||
}));
|
||||
|
||||
// 5. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#instantiate-a-promise-of-a-module
|
||||
JS::NonnullGCPtr<WebIDL::Promise> instantiate_promise_of_module(JS::VM& vm, JS::NonnullGCPtr<WebIDL::Promise> promise_of_module, JS::GCPtr<JS::Object> import_object)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let promise be a new Promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// FIXME: Spec should use react to promise here instead of separate upon fulfillment and upon rejection steps
|
||||
|
||||
// 2. Upon fulfillment of promiseOfModule with value module:
|
||||
auto fulfillment_steps = JS::create_heap_function(vm.heap(), [&vm, promise, import_object](JS::Value module_value) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
VERIFY(module_value.is_object() && is<Module>(module_value.as_object()));
|
||||
auto module = JS::NonnullGCPtr { static_cast<Module&>(module_value.as_object()) };
|
||||
|
||||
// 1. Instantiate the WebAssembly module module importing importObject, and let innerPromise be the result.
|
||||
auto inner_promise = asynchronously_instantiate_webassembly_module(vm, module, import_object);
|
||||
|
||||
// 2. Upon fulfillment of innerPromise with value instance.
|
||||
auto instantiate_fulfillment_steps = JS::create_heap_function(vm.heap(), [promise, module](JS::Value instance_value) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
auto& realm = HTML::relevant_realm(*promise->promise());
|
||||
|
||||
VERIFY(instance_value.is_object() && is<Instance>(instance_value.as_object()));
|
||||
auto instance = JS::NonnullGCPtr { static_cast<Instance&>(instance_value.as_object()) };
|
||||
|
||||
// 1. Let result be the WebAssemblyInstantiatedSource value «[ "module" → module, "instance" → instance ]».
|
||||
auto result = JS::Object::create(realm, nullptr);
|
||||
result->define_direct_property("module", module, JS::default_attributes);
|
||||
result->define_direct_property("instance", instance, JS::default_attributes);
|
||||
|
||||
// 2. Resolve promise with result.
|
||||
WebIDL::resolve_promise(realm, promise, result);
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
// 3. Upon rejection of innerPromise with reason reason.
|
||||
auto instantiate_rejection_steps = JS::create_heap_function(vm.heap(), [promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
auto& realm = HTML::relevant_realm(*promise->promise());
|
||||
|
||||
// 1. Reject promise with reason.
|
||||
WebIDL::reject_promise(realm, promise, reason);
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
WebIDL::react_to_promise(inner_promise, instantiate_fulfillment_steps, instantiate_rejection_steps);
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
// 3. Upon rejection of promiseOfModule with reason reason:
|
||||
auto rejection_steps = JS::create_heap_function(vm.heap(), [promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
auto& realm = HTML::relevant_realm(*promise->promise());
|
||||
|
||||
// 1. Reject promise with reason.
|
||||
WebIDL::reject_promise(realm, promise, reason);
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
WebIDL::react_to_promise(promise_of_module, fulfillment_steps, rejection_steps);
|
||||
|
||||
// 4. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ private:
|
|||
|
||||
WebAssemblyCache& get_cache(JS::Realm&);
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM&, Wasm::Module const&);
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> parse_module(JS::VM&, JS::Object* buffer);
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM&, Wasm::Module const&, JS::GCPtr<JS::Object> import_object);
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> compile_a_webassembly_module(JS::VM&, ByteBuffer);
|
||||
JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, ByteString const& name, Instance* instance = nullptr);
|
||||
JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
|
||||
Wasm::Value default_webassembly_value(JS::VM&, Wasm::ValueType type);
|
||||
|
|
Loading…
Add table
Reference in a new issue