mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 09:46:04 -05:00
179 lines
6.6 KiB
C++
179 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 {};
|
||
}
|
||
|
||
}
|