2023-03-15 20:16:22 -04:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
|
|
|
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
|
|
|
|
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
|
|
|
|
#include <LibJS/Runtime/BigInt.h>
|
2023-03-15 20:16:22 -04:00
|
|
|
#include <LibJS/Runtime/Realm.h>
|
|
|
|
|
#include <LibJS/Runtime/VM.h>
|
2026-06-01 16:40:39 +02:00
|
|
|
#include <LibJS/Runtime/Value.h>
|
2023-03-15 20:16:22 -04:00
|
|
|
#include <LibWasm/Types.h>
|
|
|
|
|
#include <LibWeb/Bindings/Intrinsics.h>
|
2026-04-18 10:54:06 +02:00
|
|
|
#include <LibWeb/Bindings/Table.h>
|
2023-03-15 20:16:22 -04:00
|
|
|
#include <LibWeb/WebAssembly/Table.h>
|
2023-03-16 14:11:21 -04:00
|
|
|
#include <LibWeb/WebAssembly/WebAssembly.h>
|
2026-06-01 16:40:39 +02:00
|
|
|
#include <LibWeb/WebIDL/AbstractOperations.h>
|
2026-06-06 17:01:12 +02:00
|
|
|
#include <LibWeb/WebIDL/Types.h>
|
2023-03-15 20:16:22 -04:00
|
|
|
|
|
|
|
|
namespace Web::WebAssembly {
|
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
GC_DEFINE_ALLOCATOR(Table);
|
2023-11-19 19:47:52 +01:00
|
|
|
|
2023-03-15 20:16:22 -04:00
|
|
|
static Wasm::ValueType table_kind_to_value_type(Bindings::TableKind kind)
|
|
|
|
|
{
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case Bindings::TableKind::Externref:
|
|
|
|
|
return Wasm::ValueType { Wasm::ValueType::ExternReference };
|
|
|
|
|
case Bindings::TableKind::Anyfunc:
|
|
|
|
|
return Wasm::ValueType { Wasm::ValueType::FunctionReference };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// https://webassembly.github.io/spec/js-api/#addressvaluetou64
|
|
|
|
|
static WebIDL::ExceptionOr<u64> address_value_to_u64(JS::VM& vm, JS::Value value, Wasm::AddressType address_type)
|
|
|
|
|
{
|
|
|
|
|
if (address_type == Wasm::AddressType::I64) {
|
|
|
|
|
// 1. If addrtype is i64, then:
|
|
|
|
|
// 1.1. Let n be ? ToBigInt(value).
|
|
|
|
|
auto bigint = TRY(value.to_bigint(vm));
|
|
|
|
|
|
|
|
|
|
// 1.2. If n < 0 or n >= 2^64, throw a TypeError exception.
|
|
|
|
|
if (bigint->big_integer().is_negative())
|
|
|
|
|
return vm.throw_completion<JS::TypeError>("Table size must be non-negative"sv);
|
|
|
|
|
|
|
|
|
|
auto string = TRY_OR_THROW_OOM(vm, bigint->big_integer().to_base(10));
|
|
|
|
|
auto number = string.to_number<u64>();
|
|
|
|
|
if (!number.has_value())
|
|
|
|
|
return vm.throw_completion<JS::TypeError>("Table size is too large"sv);
|
|
|
|
|
|
|
|
|
|
// 1.3. Return n.
|
|
|
|
|
return *number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Otherwise:
|
|
|
|
|
// 2.1. Assert: addrtype is i32.
|
|
|
|
|
// 2.2. Let n be ? Web IDL's convert a JavaScript value to an IDL value of type [EnforceRange] unsigned long, given value.
|
|
|
|
|
auto n = TRY(WebIDL::convert_to_int<WebIDL::UnsignedLong>(vm, value, WebIDL::EnforceRange::Yes));
|
|
|
|
|
|
|
|
|
|
// 2.3. Return n.
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JS::Value table_limit_to_js_value(JS::VM& vm, u64 value, Wasm::AddressType address_type)
|
|
|
|
|
{
|
|
|
|
|
if (address_type == Wasm::AddressType::I64)
|
|
|
|
|
return JS::BigInt::create(vm, ::Crypto::SignedBigInteger { ::Crypto::UnsignedBigInteger { value } });
|
|
|
|
|
return JS::Value { static_cast<u32>(value) };
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-20 20:58:46 +02:00
|
|
|
WebIDL::ExceptionOr<GC::Ref<Table>> Table::construct_impl(JS::Realm& realm, Bindings::TableDescriptor& descriptor, Optional<JS::Value> value)
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
|
|
|
|
auto& vm = realm.vm();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 1. Let elementtype be ToValueType(descriptor["element"]).
|
2023-03-15 20:16:22 -04:00
|
|
|
auto reference_type = table_kind_to_value_type(descriptor.element);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 2. If elementtype is not a reftype, throw a TypeError exception.
|
|
|
|
|
// NOTE: The TableKind IDL enum only accepts reference types.
|
|
|
|
|
|
|
|
|
|
// 3. If descriptor["address"] exists, let addrtype be descriptor["address"]; otherwise, let addrtype be "i32".
|
|
|
|
|
auto address_type = descriptor.address == Bindings::AddressType::I64 ? Wasm::AddressType::I64 : Wasm::AddressType::I32;
|
|
|
|
|
|
|
|
|
|
// 4. Let initial be ? AddressValueToU64(descriptor["initial"], addrtype).
|
|
|
|
|
auto initial = TRY(address_value_to_u64(vm, descriptor.initial, address_type));
|
2024-08-17 17:14:37 -07:00
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 5. If descriptor["maximum"] exists, let maximum be ? AddressValueToU64(descriptor["maximum"], addrtype); otherwise, let maximum be empty.
|
|
|
|
|
auto maximum = descriptor.maximum.has_value()
|
|
|
|
|
? Optional<u64> { TRY(address_value_to_u64(vm, descriptor.maximum.value(), address_type)) }
|
|
|
|
|
: Optional<u64> {};
|
|
|
|
|
|
|
|
|
|
// 6. Let type be the table type addrtype { min initial, max maximum } elementtype.
|
|
|
|
|
Wasm::Limits limits { address_type, initial, maximum };
|
2023-03-15 20:16:22 -04:00
|
|
|
Wasm::TableType table_type { reference_type, move(limits) };
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 7. If type is not valid, throw a RangeError exception.
|
|
|
|
|
if (maximum.has_value() && maximum.value() < initial)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Maximum should not be less than initial in table type"sv);
|
|
|
|
|
|
|
|
|
|
// 8. If value is missing, let ref be DefaultValue(elementtype).
|
|
|
|
|
// 9. Otherwise, let ref be ? ToWebAssemblyValue(value, elementtype).
|
|
|
|
|
auto reference_value = !value.has_value()
|
|
|
|
|
? Detail::default_webassembly_value(vm, reference_type)
|
|
|
|
|
: TRY(Detail::to_webassembly_value(vm, *value, reference_type));
|
|
|
|
|
|
|
|
|
|
// 10. Let store be the surrounding agent's associated store.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& cache = Detail::get_cache(realm);
|
2026-06-01 16:40:39 +02:00
|
|
|
|
|
|
|
|
// 11. Let (store, tableaddr) be table_alloc(store, type, ref). If allocation fails, throw a RangeError exception.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto address = cache.abstract_machine().store().allocate(table_type);
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!address.has_value())
|
2026-06-01 16:40:39 +02:00
|
|
|
return vm.throw_completion<JS::RangeError>("Wasm Table allocation failed"sv);
|
2023-03-15 20:16:22 -04:00
|
|
|
|
2024-08-04 08:06:50 -07:00
|
|
|
auto const& reference = reference_value.to<Wasm::Reference>();
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& table = *cache.abstract_machine().store().get(*address);
|
2023-03-15 20:16:22 -04:00
|
|
|
for (auto& element : table.elements())
|
|
|
|
|
element = reference;
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 12. Set the surrounding agent's associated store to store.
|
|
|
|
|
// NOTE: The store is updated in-place.
|
|
|
|
|
|
|
|
|
|
// 13. Initialize this from tableaddr.
|
2024-11-14 05:50:17 +13:00
|
|
|
return realm.create<Table>(realm, *address);
|
2023-03-15 20:16:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Table::Table(JS::Realm& realm, Wasm::TableAddress address)
|
|
|
|
|
: Bindings::PlatformObject(realm)
|
|
|
|
|
, m_address(address)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 08:41:28 +02:00
|
|
|
void Table::initialize(JS::Realm& realm)
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
2024-03-16 13:13:08 +01:00
|
|
|
WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Table, WebAssembly.Table);
|
2025-04-20 16:22:57 +02:00
|
|
|
Base::initialize(realm);
|
2026-06-01 16:40:39 +02:00
|
|
|
|
|
|
|
|
// https://webassembly.github.io/spec/js-api/#initialize-a-table-object
|
|
|
|
|
// 1. Let map be the surrounding agent's associated Table object cache.
|
|
|
|
|
auto& cache = Detail::get_cache(realm);
|
|
|
|
|
|
|
|
|
|
// 2. Assert: map[tableaddr] doesn't exist.
|
|
|
|
|
auto exists = cache.table_instances().contains(m_address);
|
|
|
|
|
VERIFY(!exists);
|
|
|
|
|
|
|
|
|
|
// 3. Set table.[[Table]] to tableaddr.
|
|
|
|
|
// NOTE: This is already set by the Table constructor.
|
|
|
|
|
|
|
|
|
|
// 4. Set map[tableaddr] to table.
|
|
|
|
|
cache.add_table_instance(m_address, *this);
|
2023-03-15 20:16:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://webassembly.github.io/spec/js-api/#dom-table-grow
|
2026-06-01 16:40:39 +02:00
|
|
|
WebIDL::ExceptionOr<JS::Value> Table::grow(JS::Value delta_value, Optional<JS::Value> value)
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
|
|
|
|
auto& vm = this->vm();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 1. Let tableaddr be this.[[Table]].
|
|
|
|
|
// 2. Let store be the surrounding agent's associated store.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& cache = Detail::get_cache(realm());
|
|
|
|
|
auto* table = cache.abstract_machine().store().get(address());
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!table)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Could not find the memory table to grow"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 3. Let initialSize be table_size(store, tableaddr).
|
2023-03-15 20:16:22 -04:00
|
|
|
auto initial_size = table->elements().size();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 4. Let (addrtype, limits, elementtype) be table_type(store, tableaddr).
|
|
|
|
|
auto address_type = table->type().limits().address_type();
|
|
|
|
|
auto element_type = table->type().element_type();
|
|
|
|
|
|
|
|
|
|
// 5. Let delta64 be ? AddressValueToU64(delta, addrtype).
|
|
|
|
|
auto delta = TRY(address_value_to_u64(vm, delta_value, address_type));
|
|
|
|
|
|
|
|
|
|
// 6. If value is missing, let ref be DefaultValue(elementtype).
|
|
|
|
|
// 7. Otherwise, let ref be ? ToWebAssemblyValue(value, elementtype).
|
2026-05-20 20:58:46 +02:00
|
|
|
auto reference_value = !value.has_value()
|
2026-06-01 16:40:39 +02:00
|
|
|
? Detail::default_webassembly_value(vm, element_type)
|
|
|
|
|
: TRY(Detail::to_webassembly_value(vm, *value, element_type));
|
2024-08-04 08:06:50 -07:00
|
|
|
auto const& reference = reference_value.to<Wasm::Reference>();
|
2023-03-15 20:16:22 -04:00
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 8. Let result be table_grow(store, tableaddr, delta64, ref).
|
|
|
|
|
// 9. If result is error, throw a RangeError exception.
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!table->grow(delta, reference))
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Failed to grow table"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 10. Set the surrounding agent's associated store to result.
|
|
|
|
|
// NOTE: The store is updated in-place.
|
|
|
|
|
|
|
|
|
|
// 11. Return U64ToAddressValue(initialSize, addrtype).
|
|
|
|
|
return table_limit_to_js_value(vm, initial_size, address_type);
|
2023-03-15 20:16:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://webassembly.github.io/spec/js-api/#dom-table-get
|
2026-06-01 16:40:39 +02:00
|
|
|
WebIDL::ExceptionOr<JS::Value> Table::get(JS::Value index_value) const
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
|
|
|
|
auto& vm = this->vm();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 1. Let tableaddr be this.[[Table]].
|
|
|
|
|
// 2. Let store be the surrounding agent's associated store.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& cache = Detail::get_cache(realm());
|
|
|
|
|
auto* table = cache.abstract_machine().store().get(address());
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!table)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 3. Let (addrtype, limits, elementtype) be table_type(store, tableaddr).
|
|
|
|
|
auto address_type = table->type().limits().address_type();
|
|
|
|
|
auto element_type = table->type().element_type();
|
|
|
|
|
|
|
|
|
|
// 4. If elementtype matches exnref, throw a TypeError exception.
|
|
|
|
|
if (element_type.kind() == Wasm::ValueType::ExceptionReference)
|
|
|
|
|
return vm.throw_completion<JS::TypeError>("Cannot get an exnref table element"sv);
|
|
|
|
|
|
|
|
|
|
// 5. Let index64 be ? AddressValueToU64(index, addrtype).
|
|
|
|
|
auto index = TRY(address_value_to_u64(vm, index_value, address_type));
|
|
|
|
|
|
|
|
|
|
// 6. Let result be table_read(store, tableaddr, index64).
|
|
|
|
|
// 7. If result is error, throw a RangeError exception.
|
2023-03-15 20:16:22 -04:00
|
|
|
if (table->elements().size() <= index)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Table element index out of range"sv);
|
|
|
|
|
|
|
|
|
|
auto& ref = table->elements()[index];
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 8. Return ! ToJSValue(result).
|
2024-05-31 17:14:49 -07:00
|
|
|
Wasm::Value wasm_value { ref };
|
2026-06-01 16:40:39 +02:00
|
|
|
return Detail::to_js_value(vm, wasm_value, element_type);
|
2023-03-15 20:16:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://webassembly.github.io/spec/js-api/#dom-table-set
|
2026-06-01 16:40:39 +02:00
|
|
|
WebIDL::ExceptionOr<void> Table::set(JS::Value index_value, Optional<JS::Value> value)
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
|
|
|
|
auto& vm = this->vm();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 1. Let tableaddr be this.[[Table]].
|
|
|
|
|
// 2. Let store be the surrounding agent's associated store.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& cache = Detail::get_cache(realm());
|
|
|
|
|
auto* table = cache.abstract_machine().store().get(address());
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!table)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 3. Let (addrtype, limits, elementtype) be table_type(store, tableaddr).
|
|
|
|
|
auto address_type = table->type().limits().address_type();
|
|
|
|
|
auto element_type = table->type().element_type();
|
|
|
|
|
|
|
|
|
|
// 4. If elementtype matches exnref, throw a TypeError exception.
|
|
|
|
|
if (element_type.kind() == Wasm::ValueType::ExceptionReference)
|
|
|
|
|
return vm.throw_completion<JS::TypeError>("Cannot set an exnref table element"sv);
|
|
|
|
|
|
|
|
|
|
// 5. Let index64 be ? AddressValueToU64(index, addrtype).
|
|
|
|
|
auto index = TRY(address_value_to_u64(vm, index_value, address_type));
|
|
|
|
|
|
2023-03-15 20:16:22 -04:00
|
|
|
if (table->elements().size() <= index)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Table element index out of range"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 6. If value is missing, let ref be DefaultValue(elementtype).
|
|
|
|
|
// 7. Otherwise, let ref be ? ToWebAssemblyValue(value, elementtype).
|
2026-05-20 20:58:46 +02:00
|
|
|
auto reference_value = !value.has_value()
|
2026-06-01 16:40:39 +02:00
|
|
|
? Detail::default_webassembly_value(vm, element_type)
|
|
|
|
|
: TRY(Detail::to_webassembly_value(vm, *value, element_type));
|
2024-08-04 08:06:50 -07:00
|
|
|
auto const& reference = reference_value.to<Wasm::Reference>();
|
2023-03-15 20:16:22 -04:00
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 8. Let store be table_write(store, tableaddr, index64, ref).
|
|
|
|
|
// 9. If store is error, throw a RangeError exception.
|
2023-03-15 20:16:22 -04:00
|
|
|
table->elements()[index] = reference;
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 10. Set the surrounding agent's associated store to store.
|
|
|
|
|
// NOTE: The store is updated in-place.
|
2023-03-15 20:16:22 -04:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://webassembly.github.io/spec/js-api/#dom-table-length
|
2026-06-01 16:40:39 +02:00
|
|
|
WebIDL::ExceptionOr<JS::Value> Table::length() const
|
2023-03-15 20:16:22 -04:00
|
|
|
{
|
|
|
|
|
auto& vm = this->vm();
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 1. Let tableaddr be this.[[Table]].
|
|
|
|
|
// 2. Let store be the surrounding agent's associated store.
|
2024-04-25 19:09:34 +01:00
|
|
|
auto& cache = Detail::get_cache(realm());
|
|
|
|
|
auto* table = cache.abstract_machine().store().get(address());
|
2023-03-15 20:16:22 -04:00
|
|
|
if (!table)
|
|
|
|
|
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
|
|
|
|
|
|
2026-06-01 16:40:39 +02:00
|
|
|
// 3. Let addrtype be the address type in table_type(store, tableaddr).
|
|
|
|
|
auto address_type = table->type().limits().address_type();
|
|
|
|
|
|
|
|
|
|
// 4. Let length64 be table_size(store, tableaddr).
|
|
|
|
|
auto length = table->elements().size();
|
|
|
|
|
|
|
|
|
|
// 5. Return U64ToAddressValue(length64, addrtype).
|
|
|
|
|
return table_limit_to_js_value(vm, length, address_type);
|
2023-03-15 20:16:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|