mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-10-31 13:20:59 +00: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 {};
 | ||
| }
 | ||
| 
 | ||
| }
 | 
