Merge 7fc5668f62
into 3eefa464ee
This commit is contained in:
commit
5053756719
6 changed files with 170 additions and 38 deletions
|
@ -782,6 +782,7 @@ set(SOURCES
|
|||
WebAssembly/Module.cpp
|
||||
WebAssembly/Table.cpp
|
||||
WebAssembly/WebAssembly.cpp
|
||||
WebAssembly/Error.cpp
|
||||
WebAudio/AudioBuffer.cpp
|
||||
WebAudio/AudioBufferSourceNode.cpp
|
||||
WebAudio/AudioContext.cpp
|
||||
|
|
75
Libraries/LibWeb/WebAssembly/Error.cpp
Normal file
75
Libraries/LibWeb/WebAssembly/Error.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Pavel Shliak <shlyakpavel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Error.h"
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
|
||||
namespace Web::WebAssembly {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(CompileError);
|
||||
|
||||
GC::Ref<CompileError> CompileError::create(JS::Realm& realm)
|
||||
{
|
||||
return realm.heap().allocate<CompileError>(realm.intrinsics().error_prototype());
|
||||
}
|
||||
|
||||
GC::Ref<CompileError> CompileError::create(JS::Realm& realm, StringView message)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
auto error = CompileError::create(realm);
|
||||
u8 attr = JS::Attribute::Writable | JS::Attribute::Configurable;
|
||||
error->define_direct_property(vm.names.message, JS::PrimitiveString::create(vm, message), attr);
|
||||
return error;
|
||||
}
|
||||
|
||||
CompileError::CompileError(JS::Object& prototype)
|
||||
: JS::Error(prototype)
|
||||
{
|
||||
}
|
||||
|
||||
GC_DEFINE_ALLOCATOR(LinkError);
|
||||
|
||||
GC::Ref<LinkError> LinkError::create(JS::Realm& realm)
|
||||
{
|
||||
return realm.heap().allocate<LinkError>(realm.intrinsics().error_prototype());
|
||||
}
|
||||
|
||||
GC::Ref<LinkError> LinkError::create(JS::Realm& realm, StringView message)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
auto error = LinkError::create(realm);
|
||||
u8 attr = JS::Attribute::Writable | JS::Attribute::Configurable;
|
||||
error->define_direct_property(vm.names.message, JS::PrimitiveString::create(vm, message), attr);
|
||||
return error;
|
||||
}
|
||||
|
||||
LinkError::LinkError(JS::Object& prototype)
|
||||
: JS::Error(prototype)
|
||||
{
|
||||
}
|
||||
|
||||
GC_DEFINE_ALLOCATOR(RuntimeError);
|
||||
|
||||
GC::Ref<RuntimeError> RuntimeError::create(JS::Realm& realm)
|
||||
{
|
||||
return realm.heap().allocate<RuntimeError>(realm.intrinsics().error_prototype());
|
||||
}
|
||||
|
||||
GC::Ref<RuntimeError> RuntimeError::create(JS::Realm& realm, StringView message)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
auto error = RuntimeError::create(realm);
|
||||
u8 attr = JS::Attribute::Writable | JS::Attribute::Configurable;
|
||||
error->define_direct_property(vm.names.message, JS::PrimitiveString::create(vm, message), attr);
|
||||
return error;
|
||||
}
|
||||
|
||||
RuntimeError::RuntimeError(JS::Object& prototype)
|
||||
: JS::Error(prototype)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Web::WebAssembly
|
52
Libraries/LibWeb/WebAssembly/Error.h
Normal file
52
Libraries/LibWeb/WebAssembly/Error.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Pavel Shliak <shlyakpavel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
|
||||
namespace Web::WebAssembly {
|
||||
|
||||
class CompileError final : public JS::Error {
|
||||
JS_OBJECT(CompileError, JS::Error);
|
||||
GC_DECLARE_ALLOCATOR(CompileError);
|
||||
|
||||
public:
|
||||
static GC::Ref<CompileError> create(JS::Realm&);
|
||||
static GC::Ref<CompileError> create(JS::Realm&, StringView message);
|
||||
virtual ~CompileError() override = default;
|
||||
|
||||
private:
|
||||
explicit CompileError(JS::Object& prototype);
|
||||
};
|
||||
|
||||
class LinkError final : public JS::Error {
|
||||
JS_OBJECT(LinkError, JS::Error);
|
||||
GC_DECLARE_ALLOCATOR(LinkError);
|
||||
|
||||
public:
|
||||
static GC::Ref<LinkError> create(JS::Realm&);
|
||||
static GC::Ref<LinkError> create(JS::Realm&, StringView message);
|
||||
virtual ~LinkError() override = default;
|
||||
|
||||
private:
|
||||
explicit LinkError(JS::Object& prototype);
|
||||
};
|
||||
|
||||
class RuntimeError final : public JS::Error {
|
||||
JS_OBJECT(RuntimeError, JS::Error);
|
||||
GC_DECLARE_ALLOCATOR(RuntimeError);
|
||||
|
||||
public:
|
||||
static GC::Ref<RuntimeError> create(JS::Realm&);
|
||||
static GC::Ref<RuntimeError> create(JS::Realm&, StringView message);
|
||||
virtual ~RuntimeError() override = default;
|
||||
|
||||
private:
|
||||
explicit RuntimeError(JS::Object& prototype);
|
||||
};
|
||||
|
||||
} // namespace Web::WebAssembly
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/ModulePrototype.h>
|
||||
#include <LibWeb/WebAssembly/Error.h>
|
||||
#include <LibWeb/WebAssembly/Module.h>
|
||||
#include <LibWeb/WebAssembly/WebAssembly.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
|
@ -25,7 +26,7 @@ WebIDL::ExceptionOr<GC::Ref<Module>> Module::construct_impl(JS::Realm& realm, GC
|
|||
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));
|
||||
return vm.throw_completion<CompileError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory));
|
||||
}
|
||||
auto stable_bytes = stable_bytes_or_error.release_value();
|
||||
|
||||
|
|
|
@ -29,11 +29,15 @@ static Wasm::ValueType table_kind_to_value_type(Bindings::TableKind kind)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#tables
|
||||
WebIDL::ExceptionOr<GC::Ref<Table>> Table::construct_impl(JS::Realm& realm, TableDescriptor& descriptor, JS::Value value)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
auto reference_type = table_kind_to_value_type(descriptor.element);
|
||||
// If elementType is not a reftype, throw a TypeError exception.
|
||||
if (!reference_type.is_reference())
|
||||
return vm.throw_completion<JS::TypeError>("elementType is not a reftype"_string);
|
||||
auto reference_value = vm.argument_count() == 1
|
||||
? Detail::default_webassembly_value(vm, reference_type)
|
||||
: TRY(Detail::to_webassembly_value(vm, value, reference_type));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||
|
@ -17,10 +18,12 @@
|
|||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWasm/AbstractMachine/Validator.h>
|
||||
#include <LibWasm/Types.h>
|
||||
#include <LibWeb/Bindings/ResponsePrototype.h>
|
||||
#include <LibWeb/Fetch/Response.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/WebAssembly/Error.h>
|
||||
#include <LibWeb/WebAssembly/Global.h>
|
||||
#include <LibWeb/WebAssembly/Instance.h>
|
||||
#include <LibWeb/WebAssembly/Memory.h>
|
||||
|
@ -157,6 +160,11 @@ namespace Detail {
|
|||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module, GC::Ptr<JS::Object> import_object)
|
||||
{
|
||||
// 1. If module.imports is not empty, and importObject is undefined, throw a TypeError exception.
|
||||
if (!module.import_section().imports().is_empty() && !import_object) {
|
||||
return vm.throw_completion<JS::TypeError>("Import object must be defined when module has imports"sv);
|
||||
}
|
||||
|
||||
Wasm::Linker linker { module };
|
||||
auto& cache = get_cache(*vm.current_realm());
|
||||
// https://webassembly.github.io/spec/js-api/index.html#read-the-imports
|
||||
|
@ -173,27 +181,29 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
dbgln_if(LIBWEB_WASM_DEBUG, "Trying to resolve {}::{}", import_name.module, import_name.name);
|
||||
// 3.1. Let o be ? Get(importObject, moduleName).
|
||||
auto value_or_error = import_object->get(import_name.module);
|
||||
if (value_or_error.is_error())
|
||||
break;
|
||||
if (!value_or_error.is_error() && !value_or_error.release_value().is_object()) {
|
||||
return vm.throw_completion<JS::TypeError>("Module name must resolve to an object"sv);
|
||||
}
|
||||
auto value = value_or_error.release_value();
|
||||
// 3.2. If o is not an Object, throw a TypeError exception.
|
||||
auto object_or_error = value.to_object(vm);
|
||||
if (object_or_error.is_error())
|
||||
break;
|
||||
return vm.throw_completion<JS::TypeError>("Module name must resolve to an object"sv);
|
||||
auto object = object_or_error.release_value();
|
||||
// 3.3. Let v be ? Get(o, componentName).
|
||||
auto import_or_error = object->get(import_name.name);
|
||||
if (import_or_error.is_error())
|
||||
break;
|
||||
return vm.throw_completion<JS::TypeError>("Cannot get component name from module"sv);
|
||||
auto import_ = import_or_error.release_value();
|
||||
TRY(import_name.type.visit(
|
||||
// 3.4. If externtype is of the form func functype,
|
||||
[&](Wasm::TypeIndex index) -> JS::ThrowCompletionOr<void> {
|
||||
dbgln_if(LIBWEB_WASM_DEBUG, "Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
|
||||
auto& type = module.type_section().types()[index.value()];
|
||||
// FIXME: 3.4.1. If IsCallable(v) is false, throw a LinkError exception.
|
||||
if (!import_.is_function())
|
||||
return {};
|
||||
// FIXME: IsCallable()
|
||||
if (!import_.is_function()) {
|
||||
return vm.throw_completion<LinkError>("Imported value is not a callable function"sv);
|
||||
}
|
||||
auto& function = import_.as_function();
|
||||
// 3.4.2. If v has a [[FunctionAddress]] internal slot, and therefore is an Exported Function,
|
||||
Optional<Wasm::FunctionAddress> address;
|
||||
|
@ -263,32 +273,26 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
if (import_.is_number() || import_.is_bigint()) {
|
||||
// 3.5.1.1. If valtype is i64 and v is a Number,
|
||||
if (import_.is_number() && type.type().kind() == Wasm::ValueType::I64) {
|
||||
// FIXME: 3.5.1.1.1. Throw a LinkError exception.
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a Number to a BigInteger"sv);
|
||||
return vm.throw_completion<LinkError>("Import resolution attempted to cast a Number to a BigInteger"sv);
|
||||
}
|
||||
// 3.5.1.2. If valtype is not i64 and v is a BigInt,
|
||||
if (import_.is_bigint() && type.type().kind() != Wasm::ValueType::I64) {
|
||||
// FIXME: 3.5.1.2.1. Throw a LinkError exception.
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a BigInteger to a Number"sv);
|
||||
return vm.throw_completion<LinkError>("Import resolution attempted to cast a BigInteger to a Number"sv);
|
||||
}
|
||||
// 3.5.1.3. If valtype is v128,
|
||||
if (type.type().kind() == Wasm::ValueType::V128) {
|
||||
// FIXME: 3.5.1.3.1. Throw a LinkError exception.
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a Number or BigInt to a V128"sv);
|
||||
return vm.throw_completion<LinkError>("Cannot import v128 type for global"sv);
|
||||
}
|
||||
// 3.5.1.4. Let value be ToWebAssemblyValue(v, valtype).
|
||||
auto cast_value = TRY(to_webassembly_value(vm, import_, type.type()));
|
||||
// 3.5.1.5. Let store be the surrounding agent's associated store.
|
||||
// 3.5.1.6. Let (store, globaladdr) be global_alloc(store, const valtype, value).
|
||||
// 3.5.1.7. Set the surrounding agent's associated store to store.
|
||||
address = cache.abstract_machine().store().allocate({ type.type(), false }, cast_value);
|
||||
}
|
||||
// FIXME: 3.5.2. Otherwise, if v implements Global,
|
||||
// FIXME: 3.5.2.1. Let globaladdr be v.[[Global]].
|
||||
// 3.5.3. Otherwise,
|
||||
else {
|
||||
// FIXME: 3.5.3.1. Throw a LinkError exception.
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Invalid value for global type"sv);
|
||||
} else {
|
||||
// FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
|
||||
// if v implements Global
|
||||
// let globaladdr be v.[[Global]]
|
||||
|
||||
return vm.throw_completion<LinkError>("Invalid value for global type"sv);
|
||||
}
|
||||
|
||||
// 3.5.4. Let externglobal be global globaladdr.
|
||||
|
@ -300,8 +304,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
[&](Wasm::MemoryType const&) -> JS::ThrowCompletionOr<void> {
|
||||
// 3.6.1. If v does not implement Memory, throw a LinkError exception.
|
||||
if (!import_.is_object() || !is<WebAssembly::Memory>(import_.as_object())) {
|
||||
// FIXME: Throw a LinkError instead
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Memory for a memory import"sv);
|
||||
return vm.throw_completion<LinkError>("Expected an instance of WebAssembly.Memory for a memory import"sv);
|
||||
}
|
||||
// 3.6.2. Let externmem be the external value mem v.[[Memory]].
|
||||
auto address = static_cast<WebAssembly::Memory const&>(import_.as_object()).address();
|
||||
|
@ -313,8 +316,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
[&](Wasm::TableType const&) -> JS::ThrowCompletionOr<void> {
|
||||
// 3.7.1. If v does not implement Table, throw a LinkError exception.
|
||||
if (!import_.is_object() || !is<WebAssembly::Table>(import_.as_object())) {
|
||||
// FIXME: Throw a LinkError instead
|
||||
return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Table for a table import"sv);
|
||||
return vm.throw_completion<LinkError>("Expected an instance of WebAssembly.Table for a table import"sv);
|
||||
}
|
||||
// 3.7.2. Let tableaddr be v.[[Table]].
|
||||
// 3.7.3. Let externtable be the external value table tableaddr.
|
||||
|
@ -324,8 +326,9 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
return {};
|
||||
},
|
||||
[&](auto const&) -> JS::ThrowCompletionOr<void> {
|
||||
// (noop)
|
||||
return {};
|
||||
// FIXME: Implement these.
|
||||
dbgln("Unimplemented import of non-function attempted");
|
||||
return vm.throw_completion<JS::TypeError>("Not Implemented"sv);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -334,17 +337,15 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
|
|||
linker.link(resolved_imports);
|
||||
auto link_result = linker.finish();
|
||||
if (link_result.is_error()) {
|
||||
// FIXME: Throw a LinkError.
|
||||
StringBuilder builder;
|
||||
builder.append("LinkError: Missing "sv);
|
||||
builder.append("Missing "sv);
|
||||
builder.join(' ', link_result.error().missing_imports);
|
||||
return vm.throw_completion<JS::TypeError>(MUST(builder.to_string()));
|
||||
return vm.throw_completion<LinkError>(MUST(builder.to_string()));
|
||||
}
|
||||
|
||||
auto instance_result = cache.abstract_machine().instantiate(module, link_result.release_value());
|
||||
if (instance_result.is_error()) {
|
||||
// FIXME: Throw a LinkError instead.
|
||||
return vm.throw_completion<JS::TypeError>(instance_result.error().error);
|
||||
return vm.throw_completion<LinkError>(String::from_byte_string(instance_result.error().error).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
|
||||
return instance_result.release_value();
|
||||
|
@ -356,14 +357,12 @@ JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> compile_a_webass
|
|||
FixedMemoryStream stream { data.bytes() };
|
||||
auto module_result = Wasm::Module::parse(stream);
|
||||
if (module_result.is_error()) {
|
||||
// FIXME: Throw CompileError instead.
|
||||
return vm.throw_completion<JS::TypeError>(Wasm::parse_error_to_byte_string(module_result.error()));
|
||||
return vm.throw_completion<CompileError>(String::from_byte_string(Wasm::parse_error_to_byte_string(module_result.error())).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
|
||||
auto& cache = get_cache(*vm.current_realm());
|
||||
if (auto validation_result = cache.abstract_machine().validate(module_result.value()); validation_result.is_error()) {
|
||||
// FIXME: Throw CompileError instead.
|
||||
return vm.throw_completion<JS::TypeError>(validation_result.error().error_string);
|
||||
return vm.throw_completion<CompileError>(String::from_byte_string(validation_result.error().error_string).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
auto compiled_module = make_ref_counted<CompiledWebAssemblyModule>(module_result.release_value());
|
||||
cache.add_compiled_module(compiled_module);
|
||||
|
|
Loading…
Add table
Reference in a new issue