mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 09:46:04 -05:00
180 lines
6.6 KiB
C++
180 lines
6.6 KiB
C++
|
/*
|
|||
|
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
|||
|
*
|
|||
|
* SPDX-License-Identifier: BSD-2-Clause
|
|||
|
*/
|
|||
|
|
|||
|
#include <LibJS/Runtime/Realm.h>
|
|||
|
#include <LibJS/Runtime/VM.h>
|
|||
|
#include <LibWasm/Types.h>
|
|||
|
#include <LibWeb/Bindings/GlobalPrototype.h>
|
|||
|
#include <LibWeb/Bindings/Intrinsics.h>
|
|||
|
#include <LibWeb/WebAssembly/Global.h>
|
|||
|
#include <LibWeb/WebAssembly/WebAssembly.h>
|
|||
|
|
|||
|
namespace Web::WebAssembly {
|
|||
|
|
|||
|
GC_DEFINE_ALLOCATOR(Global);
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#tovaluetype
|
|||
|
static Wasm::ValueType to_value_type(Bindings::ValueType type)
|
|||
|
{
|
|||
|
switch (type) {
|
|||
|
case Bindings::ValueType::I32:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::I32 };
|
|||
|
case Bindings::ValueType::I64:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::I64 };
|
|||
|
case Bindings::ValueType::F32:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::F32 };
|
|||
|
case Bindings::ValueType::F64:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::F64 };
|
|||
|
case Bindings::ValueType::V128:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::V128 };
|
|||
|
case Bindings::ValueType::Anyfunc:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::FunctionReference };
|
|||
|
case Bindings::ValueType::Externref:
|
|||
|
return Wasm::ValueType { Wasm::ValueType::ExternReference };
|
|||
|
}
|
|||
|
|
|||
|
VERIFY_NOT_REACHED();
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#dom-global-global
|
|||
|
WebIDL::ExceptionOr<GC::Ref<Global>> Global::construct_impl(JS::Realm& realm, GlobalDescriptor& descriptor, JS::Value v)
|
|||
|
{
|
|||
|
auto& vm = realm.vm();
|
|||
|
|
|||
|
// 1. Let mutable be descriptor["mutable"].
|
|||
|
auto mutable_ = descriptor.mutable_;
|
|||
|
|
|||
|
// 2. Let valuetype be ToValueType(descriptor["value"]).
|
|||
|
auto value_type = to_value_type(descriptor.value);
|
|||
|
|
|||
|
// 3. If valuetype is v128,
|
|||
|
// 3.1 Throw a TypeError exception.
|
|||
|
if (value_type.kind() == Wasm::ValueType::V128)
|
|||
|
return vm.throw_completion<JS::TypeError>("V128 is not supported as a global value type"sv);
|
|||
|
|
|||
|
// 4. If v is missing,
|
|||
|
// 4.1 Let value be DefaultValue(valuetype).
|
|||
|
// 5. Otherwise,
|
|||
|
// 5.1 Let value be ToWebAssemblyValue(v, valuetype).
|
|||
|
// FIXME: https://github.com/WebAssembly/spec/issues/1861
|
|||
|
// Is there a difference between *missing* and undefined for optional any values?
|
|||
|
auto value = v.is_undefined()
|
|||
|
? Detail::default_webassembly_value(vm, value_type)
|
|||
|
: TRY(Detail::to_webassembly_value(vm, v, value_type));
|
|||
|
|
|||
|
// 6. If mutable is true, let globaltype be var valuetype; otherwise, let globaltype be const valuetype.
|
|||
|
auto global_type = Wasm::GlobalType { value_type, mutable_ };
|
|||
|
|
|||
|
// 7. Let store be the current agent’s associated store.
|
|||
|
// 8. Let (store, globaladdr) be global_alloc(store, globaltype, value).
|
|||
|
// 9. Set the current agent’s associated store to store.
|
|||
|
// 10. Initialize this from globaladdr.
|
|||
|
|
|||
|
auto& cache = Detail::get_cache(realm);
|
|||
|
auto address = cache.abstract_machine().store().allocate(global_type, value);
|
|||
|
if (!address.has_value())
|
|||
|
return vm.throw_completion<JS::TypeError>("Wasm Global allocation failed"sv);
|
|||
|
|
|||
|
return realm.create<Global>(realm, *address);
|
|||
|
}
|
|||
|
|
|||
|
Global::Global(JS::Realm& realm, Wasm::GlobalAddress address)
|
|||
|
: Bindings::PlatformObject(realm)
|
|||
|
, m_address(address)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#initialize-a-global-object
|
|||
|
void Global::initialize(JS::Realm& realm)
|
|||
|
{
|
|||
|
Base::initialize(realm);
|
|||
|
WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Global, WebAssembly.Global);
|
|||
|
|
|||
|
// 1. Let map be the surrounding agent's associated Global object cache.
|
|||
|
// 2. Assert: map[globaladdr] doesn’t exist.
|
|||
|
auto& cache = Detail::get_cache(realm);
|
|||
|
auto exists = cache.global_instances().contains(m_address);
|
|||
|
VERIFY(!exists);
|
|||
|
|
|||
|
// 3. Set global.[[Global]] to globaladdr.
|
|||
|
// 4. Set map[globaladdr] to global.
|
|||
|
cache.add_global_instance(m_address, *this);
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#getglobalvalue
|
|||
|
static WebIDL::ExceptionOr<JS::Value> get_global_value(Global const& global)
|
|||
|
{
|
|||
|
// 1. Let store be the current agent’s associated store.
|
|||
|
// 2. Let globaladdr be global.[[Global]].
|
|||
|
// 3. Let globaltype be global_type(store, globaladdr).
|
|||
|
// 4. If globaltype is of the form mut v128, throw a TypeError.
|
|||
|
|
|||
|
auto& cache = Detail::get_cache(global.realm());
|
|||
|
auto* global_instance = cache.abstract_machine().store().get(global.address());
|
|||
|
if (!global_instance)
|
|||
|
return global.vm().throw_completion<JS::RangeError>("Could not find the global instance"sv);
|
|||
|
|
|||
|
auto value_type = global_instance->type().type();
|
|||
|
if (value_type.kind() == Wasm::ValueType::V128)
|
|||
|
return global.vm().throw_completion<JS::TypeError>("V128 is not supported as a global value type"sv);
|
|||
|
|
|||
|
// 5. Let value be global_read(store, globaladdr).
|
|||
|
auto value = global_instance->value();
|
|||
|
|
|||
|
// 6. Return ToJSValue(value).
|
|||
|
return Detail::to_js_value(global.vm(), value, value_type);
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#dom-global-value
|
|||
|
WebIDL::ExceptionOr<JS::Value> Global::value() const
|
|||
|
{
|
|||
|
return get_global_value(*this);
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#dom-global-valueof
|
|||
|
WebIDL::ExceptionOr<JS::Value> Global::value_of() const
|
|||
|
{
|
|||
|
return get_global_value(*this);
|
|||
|
}
|
|||
|
|
|||
|
// https://webassembly.github.io/spec/js-api/#dom-global-value
|
|||
|
WebIDL::ExceptionOr<void> Global::set_value(JS::Value the_given_value)
|
|||
|
{
|
|||
|
auto& realm = this->realm();
|
|||
|
auto& vm = this->vm();
|
|||
|
// 1. Let store be the current agent’s associated store.
|
|||
|
// 2. Let globaladdr be this.[[Global]].
|
|||
|
// 3. Let mut valuetype be global_type(store, globaladdr).
|
|||
|
// 4. If valuetype is v128, throw a TypeError.
|
|||
|
// 5. If mut is const, throw a TypeError.
|
|||
|
|
|||
|
auto& cache = Detail::get_cache(realm);
|
|||
|
auto* global_instance = cache.abstract_machine().store().get(address());
|
|||
|
if (!global_instance)
|
|||
|
return vm.throw_completion<JS::RangeError>("Could not find the global instance"sv);
|
|||
|
|
|||
|
auto mut_value_type = global_instance->type();
|
|||
|
if (mut_value_type.type().kind() == Wasm::ValueType::V128)
|
|||
|
return vm.throw_completion<JS::TypeError>("Cannot set the value of a V128 global"sv);
|
|||
|
|
|||
|
if (!mut_value_type.is_mutable())
|
|||
|
return vm.throw_completion<JS::TypeError>("Cannot set the value of a const global"sv);
|
|||
|
|
|||
|
// 6. Let value be ToWebAssemblyValue(the given value, valuetype).
|
|||
|
auto value = TRY(Detail::to_webassembly_value(vm, the_given_value, mut_value_type.type()));
|
|||
|
|
|||
|
// 7. Let store be global_write(store, globaladdr, value).
|
|||
|
// 8. If store is error, throw a RangeError exception.
|
|||
|
// 9. Set the current agent’s associated store to store.
|
|||
|
// Note: The store cannot fail, because we checked for mut/val above.
|
|||
|
|
|||
|
global_instance->set_value(value);
|
|||
|
|
|||
|
return {};
|
|||
|
}
|
|||
|
|
|||
|
}
|