2023-03-16 12:59:32 -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
*/
2024-12-21 18:09:36 +01:00
# include <AK/ByteBuffer.h>
2023-03-16 14:11:21 -04:00
# include <AK/MemoryStream.h>
2023-03-16 12:59:32 -04:00
# include <AK/ScopeGuard.h>
2023-09-06 08:29:01 -04:00
# include <AK/StringBuilder.h>
2023-03-16 14:11:21 -04:00
# include <LibJS/Runtime/Array.h>
# include <LibJS/Runtime/ArrayBuffer.h>
# include <LibJS/Runtime/BigInt.h>
2025-05-03 13:22:44 +03:30
# include <LibJS/Runtime/ErrorConstructor.h>
# include <LibJS/Runtime/Intrinsics.h>
2023-07-19 06:54:48 -04:00
# include <LibJS/Runtime/Iterator.h>
2023-03-16 12:59:32 -04:00
# include <LibJS/Runtime/NativeFunction.h>
# include <LibJS/Runtime/Object.h>
# include <LibJS/Runtime/VM.h>
2025-04-09 12:29:53 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2023-03-16 12:59:32 -04:00
# include <LibWasm/AbstractMachine/Validator.h>
2025-05-03 13:22:44 +03:30
# include <LibWeb/Bindings/Intrinsics.h>
2024-10-26 17:34:09 -06:00
# include <LibWeb/Bindings/ResponsePrototype.h>
# include <LibWeb/Fetch/Response.h>
2024-10-26 14:09:25 -06:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2024-10-25 12:38:19 -06:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2024-12-23 14:47:25 -07:00
# include <LibWeb/WebAssembly/Global.h>
2023-03-16 12:59:32 -04:00
# include <LibWeb/WebAssembly/Instance.h>
# include <LibWeb/WebAssembly/Memory.h>
# include <LibWeb/WebAssembly/Module.h>
# include <LibWeb/WebAssembly/Table.h>
# include <LibWeb/WebAssembly/WebAssembly.h>
2024-10-26 14:09:25 -06:00
# include <LibWeb/WebIDL/AbstractOperations.h>
2023-11-23 20:07:25 +13:00
# include <LibWeb/WebIDL/Buffers.h>
2024-10-25 12:38:19 -06:00
# include <LibWeb/WebIDL/Promise.h>
2023-03-16 12:59:32 -04:00
namespace Web : : WebAssembly {
2024-11-15 04:01:23 +13:00
static GC : : Ref < WebIDL : : Promise > asynchronously_compile_webassembly_module ( JS : : VM & , ByteBuffer , HTML : : Task : : Source = HTML : : Task : : Source : : Unspecified ) ;
static GC : : Ref < WebIDL : : Promise > instantiate_promise_of_module ( JS : : VM & , GC : : Ref < WebIDL : : Promise > , GC : : Ptr < JS : : Object > import_object ) ;
static GC : : Ref < WebIDL : : Promise > asynchronously_instantiate_webassembly_module ( JS : : VM & , GC : : Ref < Module > , GC : : Ptr < JS : : Object > import_object ) ;
static GC : : Ref < WebIDL : : Promise > compile_potential_webassembly_response ( JS : : VM & , GC : : Ref < WebIDL : : Promise > ) ;
2024-10-26 14:09:25 -06:00
2023-03-16 14:11:21 -04:00
namespace Detail {
2024-11-15 04:01:23 +13:00
HashMap < GC : : Ptr < JS : : Object > , WebAssemblyCache > s_caches ;
2024-04-25 19:09:34 +01:00
WebAssemblyCache & get_cache ( JS : : Realm & realm )
{
return s_caches . ensure ( realm . global_object ( ) ) ;
}
2023-03-16 14:11:21 -04:00
}
2024-04-25 19:09:34 +01:00
void visit_edges ( JS : : Object & object , JS : : Cell : : Visitor & visitor )
2023-03-16 12:59:32 -04:00
{
2024-04-25 19:09:34 +01:00
auto & global_object = HTML : : relevant_global_object ( object ) ;
if ( auto maybe_cache = Detail : : s_caches . get ( global_object ) ; maybe_cache . has_value ( ) ) {
auto & cache = maybe_cache . release_value ( ) ;
visitor . visit ( cache . function_instances ( ) ) ;
2024-07-31 12:34:55 +02:00
visitor . visit ( cache . imported_objects ( ) ) ;
2024-08-17 15:40:21 -07:00
visitor . visit ( cache . extern_values ( ) ) ;
2024-12-23 14:47:25 -07:00
visitor . visit ( cache . global_instances ( ) ) ;
2025-04-22 09:48:26 +02:00
cache . abstract_machine ( ) . visit_external_resources ( { . visit_trap = [ & visitor ] ( Wasm : : ExternallyManagedTrap const & trap ) {
auto & completion = trap . unsafe_external_object_as < JS : : Completion > ( ) ;
visitor . visit ( completion . value ( ) ) ;
} } ) ;
2023-03-16 12:59:32 -04:00
}
}
2024-04-25 19:09:34 +01:00
void finalize ( JS : : Object & object )
{
auto & global_object = HTML : : relevant_global_object ( object ) ;
Detail : : s_caches . remove ( global_object ) ;
}
2025-05-03 13:22:44 +03:30
// https://webassembly.github.io/spec/js-api/#error-objects
void initialize ( JS : : Object & self , JS : : Realm & realm )
{
// 1. Let namespaceObject be the namespace object.
auto & namespace_object = self ;
// 2. For each error of « "CompileError", "LinkError", "RuntimeError" »,
// 2.1. Let constructor be a new object, implementing the NativeError Object Structure, with NativeError set to error.
// 2.2. ! DefineMethodProperty(namespaceObject, error, constructor, false).
// 2..2.2 for error=CompileError:
auto descriptor = JS : : PropertyDescriptor {
. writable = true ,
. enumerable = false ,
. configurable = true ,
} ;
descriptor . value = & Bindings : : ensure_web_constructor < CompileErrorPrototype > ( realm , " CompileError " _fly_string ) ;
MUST ( namespace_object . define_property_or_throw ( " CompileError " _string , descriptor ) ) ;
// 2..2.2 for error=LinkError:
descriptor . value = & Bindings : : ensure_web_constructor < LinkErrorPrototype > ( realm , " LinkError " _fly_string ) ;
MUST ( namespace_object . define_property_or_throw ( " LinkError " _string , descriptor ) ) ;
// 2..2.2 for error=RuntimeError:
descriptor . value = & Bindings : : ensure_web_constructor < RuntimeErrorPrototype > ( realm , " RuntimeError " _fly_string ) ;
MUST ( namespace_object . define_property_or_throw ( " RuntimeError " _string , descriptor ) ) ;
}
2023-03-16 12:59:32 -04:00
// https://webassembly.github.io/spec/js-api/#dom-webassembly-validate
2024-11-15 04:01:23 +13:00
bool validate ( JS : : VM & vm , GC : : Root < WebIDL : : BufferSource > & bytes )
2023-03-16 12:59:32 -04:00
{
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
2024-10-26 14:09:25 -06:00
auto stable_bytes = WebIDL : : get_buffer_source_copy ( * bytes - > raw_object ( ) ) ;
if ( stable_bytes . is_error ( ) ) {
VERIFY ( stable_bytes . error ( ) . code ( ) = = ENOMEM ) ;
return false ;
}
2023-03-16 12:59:32 -04:00
// 2. Compile stableBytes as a WebAssembly module and store the results as module.
2024-10-26 14:09:25 -06:00
auto module_or_error = Detail : : compile_a_webassembly_module ( vm , stable_bytes . release_value ( ) ) ;
2023-03-16 12:59:32 -04:00
// 3. If module is error, return false.
2024-04-25 19:09:34 +01:00
if ( module_or_error . is_error ( ) )
2023-03-16 12:59:32 -04:00
return false ;
// 4. Return true.
return true ;
}
// https://webassembly.github.io/spec/js-api/#dom-webassembly-compile
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > compile ( JS : : VM & vm , GC : : Root < WebIDL : : BufferSource > & bytes )
2023-03-16 12:59:32 -04:00
{
auto & realm = * vm . current_realm ( ) ;
2024-10-26 14:09:25 -06:00
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
auto stable_bytes = WebIDL : : get_buffer_source_copy ( * bytes - > raw_object ( ) ) ;
if ( stable_bytes . is_error ( ) ) {
VERIFY ( stable_bytes . error ( ) . code ( ) = = ENOMEM ) ;
return WebIDL : : create_rejected_promise_from_exception ( realm , vm . throw_completion < JS : : InternalError > ( vm . error_message ( JS : : VM : : ErrorMessage : : OutOfMemory ) ) ) ;
2023-03-16 12:59:32 -04:00
}
2024-10-26 14:09:25 -06:00
// 2. Asynchronously compile a WebAssembly module from stableBytes and return the result.
return asynchronously_compile_webassembly_module ( vm , stable_bytes . release_value ( ) ) ;
2023-03-16 12:59:32 -04:00
}
2024-10-26 17:34:09 -06:00
// https://webassembly.github.io/spec/web-api/index.html#dom-webassembly-compilestreaming
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > compile_streaming ( JS : : VM & vm , GC : : Root < WebIDL : : Promise > source )
2024-10-26 17:34:09 -06:00
{
// The compileStreaming(source) method, when invoked, returns the result of compiling a potential WebAssembly response with source.
return compile_potential_webassembly_response ( vm , * source ) ;
}
2023-03-16 12:59:32 -04:00
// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > instantiate ( JS : : VM & vm , GC : : Root < WebIDL : : BufferSource > & bytes , Optional < GC : : Root < JS : : Object > > & import_object_handle )
2023-03-16 12:59:32 -04:00
{
auto & realm = * vm . current_realm ( ) ;
2024-10-26 14:09:25 -06:00
// 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
auto stable_bytes = WebIDL : : get_buffer_source_copy ( * bytes - > raw_object ( ) ) ;
if ( stable_bytes . is_error ( ) ) {
VERIFY ( stable_bytes . error ( ) . code ( ) = = ENOMEM ) ;
return WebIDL : : create_rejected_promise_from_exception ( realm , vm . throw_completion < JS : : InternalError > ( vm . error_message ( JS : : VM : : ErrorMessage : : OutOfMemory ) ) ) ;
2023-03-16 12:59:32 -04:00
}
2024-10-26 14:09:25 -06:00
// 2. Asynchronously compile a WebAssembly module from stableBytes and let promiseOfModule be the result.
auto promise_of_module = asynchronously_compile_webassembly_module ( vm , stable_bytes . release_value ( ) ) ;
2023-03-16 12:59:32 -04:00
2024-10-26 14:09:25 -06:00
// 3. Instantiate promiseOfModule with imports importObject and return the result.
2024-11-15 04:01:23 +13:00
GC : : Ptr < JS : : Object > const import_object = import_object_handle . has_value ( ) ? import_object_handle . value ( ) . ptr ( ) : nullptr ;
2024-10-26 14:09:25 -06:00
return instantiate_promise_of_module ( vm , promise_of_module , import_object ) ;
2023-03-16 12:59:32 -04:00
}
// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate-moduleobject-importobject
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > instantiate ( JS : : VM & vm , Module const & module_object , Optional < GC : : Root < JS : : Object > > & import_object )
2023-03-16 12:59:32 -04:00
{
2024-10-26 14:09:25 -06:00
// 1. Asynchronously instantiate the WebAssembly module moduleObject importing importObject, and return the result.
2024-11-15 04:01:23 +13:00
GC : : Ref < Module > module { const_cast < Module & > ( module_object ) } ;
GC : : Ptr < JS : : Object > const imports = import_object . has_value ( ) ? import_object . value ( ) . ptr ( ) : nullptr ;
2024-10-26 14:09:25 -06:00
return asynchronously_instantiate_webassembly_module ( vm , module , imports ) ;
2023-03-16 12:59:32 -04:00
}
2024-10-26 17:34:09 -06:00
// https://webassembly.github.io/spec/web-api/index.html#dom-webassembly-instantiatestreaming
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > instantiate_streaming ( JS : : VM & vm , GC : : Root < WebIDL : : Promise > source , Optional < GC : : Root < JS : : Object > > & import_object )
2024-10-26 17:34:09 -06:00
{
// The instantiateStreaming(source, importObject) method, when invoked, performs the following steps:
// 1. Let promiseOfModule be the result of compiling a potential WebAssembly response with source.
auto promise_of_module = compile_potential_webassembly_response ( vm , * source ) ;
// 2. Return the result of instantiating the promise of a module promiseOfModule with imports importObject.
2024-11-15 04:01:23 +13:00
auto imports = GC : : Ptr { import_object . has_value ( ) ? import_object . value ( ) . ptr ( ) : nullptr } ;
2024-10-26 17:34:09 -06:00
return instantiate_promise_of_module ( vm , promise_of_module , imports ) ;
}
2023-03-16 14:11:21 -04:00
namespace Detail {
2025-04-22 09:48:26 +02:00
# define TRY_OR_RETURN_TRAP(...) \
( { \
/* Ignore -Wshadow to allow nesting the macro. */ \
AK_IGNORE_DIAGNOSTIC ( " -Wshadow " , \
auto & & _temporary_result = ( __VA_ARGS__ ) ) ; \
static_assert ( ! : : AK : : Detail : : IsLvalueReference < decltype ( _temporary_result . release_value ( ) ) > , \
" Do not return a reference from a fallible expression " ) ; \
if ( _temporary_result . is_error ( ) ) [[unlikely]] \
return Wasm : : Trap : : from_external_object ( _temporary_result . release_error ( ) ) ; \
_temporary_result . release_value ( ) ; \
} )
# define TRY_OR_RETURN_OOM_TRAP(vm, ...) \
( { \
/* Ignore -Wshadow to allow nesting the macro. */ \
AK_IGNORE_DIAGNOSTIC ( " -Wshadow " , \
auto & & _temporary_result = ( __VA_ARGS__ ) ) ; \
if ( _temporary_result . is_error ( ) ) { \
VERIFY ( _temporary_result . error ( ) . code ( ) = = ENOMEM ) ; \
return Wasm : : Trap : : from_external_object ( ( vm ) . throw_completion < JS : : InternalError > ( ( vm ) . error_message ( : : JS : : VM : : ErrorMessage : : OutOfMemory ) ) ) ; \
} \
static_assert ( ! : : AK : : Detail : : IsLvalueReference < decltype ( _temporary_result . release_value ( ) ) > , \
" Do not return a reference from a fallible expression " ) ; \
_temporary_result . release_value ( ) ; \
} )
2024-11-15 04:01:23 +13:00
JS : : ThrowCompletionOr < NonnullOwnPtr < Wasm : : ModuleInstance > > instantiate_module ( JS : : VM & vm , Wasm : : Module const & module , GC : : Ptr < JS : : Object > import_object )
2023-03-16 14:11:21 -04:00
{
Wasm : : Linker linker { module } ;
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( * vm . current_realm ( ) ) ;
2024-12-21 18:09:36 +01:00
// https://webassembly.github.io/spec/js-api/index.html#read-the-imports
// 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 > ( " ImportObject must be provided when module has imports " sv ) ;
}
// 2. Let imports be « ».
HashMap < Wasm : : Linker : : Name , Wasm : : ExternValue > resolved_imports ;
2024-10-26 14:09:25 -06:00
if ( import_object ) {
2024-09-07 17:16:37 +01:00
dbgln_if ( LIBWEB_WASM_DEBUG , " Trying to resolve stuff because import object was specified " ) ;
2024-12-21 18:09:36 +01:00
// 3. For each (moduleName, componentName, externtype) of module_imports(module),
2023-03-16 14:11:21 -04:00
for ( Wasm : : Linker : : Name const & import_name : linker . unresolved_imports ( ) ) {
2024-09-07 17:16:37 +01:00
dbgln_if ( LIBWEB_WASM_DEBUG , " Trying to resolve {}::{} " , import_name . module , import_name . name ) ;
2024-12-21 18:09:36 +01:00
// 3.1. Let o be ? Get(importObject, moduleName).
2025-03-16 20:45:02 -05:00
auto value_or_error = import_object - > get ( MUST ( String : : from_byte_string ( import_name . module ) ) ) ;
2023-03-16 14:11:21 -04:00
if ( value_or_error . is_error ( ) )
break ;
auto value = value_or_error . release_value ( ) ;
2024-12-21 18:09:36 +01:00
// 3.2. If o is not an Object, throw a TypeError exception.
2023-03-16 14:11:21 -04:00
auto object_or_error = value . to_object ( vm ) ;
if ( object_or_error . is_error ( ) )
break ;
2023-04-13 15:26:41 +02:00
auto object = object_or_error . release_value ( ) ;
2024-12-21 18:09:36 +01:00
// 3.3. Let v be ? Get(o, componentName).
2025-03-16 20:45:02 -05:00
auto import_or_error = object - > get ( MUST ( String : : from_byte_string ( import_name . name ) ) ) ;
2023-03-16 14:11:21 -04:00
if ( import_or_error . is_error ( ) )
break ;
auto import_ = import_or_error . release_value ( ) ;
TRY ( import_name . type . visit (
2024-12-21 18:09:36 +01:00
// 3.4. If externtype is of the form func functype,
2023-03-16 14:11:21 -04:00
[ & ] ( Wasm : : TypeIndex index ) - > JS : : ThrowCompletionOr < void > {
2024-09-07 17:16:37 +01:00
dbgln_if ( LIBWEB_WASM_DEBUG , " Trying to resolve a function {}::{}, type index {} " , import_name . module , import_name . name , index . value ( ) ) ;
2024-07-29 19:56:00 -07:00
auto & type = module . type_section ( ) . types ( ) [ index . value ( ) ] ;
2024-12-21 18:09:36 +01:00
// FIXME: 3.4.1. If IsCallable(v) is false, throw a LinkError exception.
2023-03-16 14:11:21 -04:00
if ( ! import_ . is_function ( ) )
return { } ;
auto & function = import_ . as_function ( ) ;
2024-12-21 18:09:36 +01:00
// 3.4.2. If v has a [[FunctionAddress]] internal slot, and therefore is an Exported Function,
Optional < Wasm : : FunctionAddress > address ;
if ( is < ExportedWasmFunction > ( function ) ) {
// 3.4.2.1. Let funcaddr be the value of v’ s [[FunctionAddress]] internal slot.
auto & exported_function = static_cast < ExportedWasmFunction & > ( function ) ;
address = exported_function . exported_address ( ) ;
}
// 3.4.3. Otherwise,
else {
// 3.4.3.1. Create a host function from v and functype, and let funcaddr be the result.
cache . add_imported_object ( function ) ;
Wasm : : HostFunction host_function {
[ & ] ( auto & , auto & arguments ) - > Wasm : : Result {
2024-12-26 14:32:52 +01:00
GC : : RootVector < JS : : Value > argument_values { vm . heap ( ) } ;
2024-12-21 18:09:36 +01:00
size_t index = 0 ;
for ( auto & entry : arguments ) {
argument_values . append ( to_js_value ( vm , entry , type . parameters ( ) [ index ] ) ) ;
+ + index ;
}
2025-04-22 09:48:26 +02:00
auto result = TRY_OR_RETURN_TRAP ( JS : : call ( vm , function , JS : : js_undefined ( ) , argument_values . span ( ) ) ) ;
2024-12-21 18:09:36 +01:00
if ( type . results ( ) . is_empty ( ) )
return Wasm : : Result { Vector < Wasm : : Value > { } } ;
if ( type . results ( ) . size ( ) = = 1 )
2025-04-22 09:48:26 +02:00
return Wasm : : Result { Vector < Wasm : : Value > { TRY_OR_RETURN_TRAP ( to_webassembly_value ( vm , result , type . results ( ) . first ( ) ) ) } } ;
2024-12-21 18:09:36 +01:00
2025-04-22 09:48:26 +02:00
auto method = TRY_OR_RETURN_TRAP ( result . get_method ( vm , vm . names . iterator ) ) ;
2024-12-21 18:09:36 +01:00
if ( method = = JS : : js_undefined ( ) )
2025-04-22 09:48:26 +02:00
return Wasm : : Trap : : from_external_object ( vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotIterable , result . to_string_without_side_effects ( ) ) ) ;
2024-12-21 18:09:36 +01:00
2025-04-22 09:48:26 +02:00
auto values = TRY_OR_RETURN_TRAP ( JS : : iterator_to_list ( vm , TRY_OR_RETURN_TRAP ( JS : : get_iterator_from_method ( vm , result , * method ) ) ) ) ;
2024-12-21 18:09:36 +01:00
if ( values . size ( ) ! = type . results ( ) . size ( ) )
2025-04-22 09:48:26 +02:00
return Wasm : : Trap : : from_external_object ( vm . throw_completion < JS : : TypeError > ( ByteString : : formatted ( " Invalid number of return values for multi-value wasm return of {} objects " , type . results ( ) . size ( ) ) ) ) ;
2024-12-21 18:09:36 +01:00
Vector < Wasm : : Value > wasm_values ;
2025-04-22 09:48:26 +02:00
TRY_OR_RETURN_OOM_TRAP ( vm , wasm_values . try_ensure_capacity ( values . size ( ) ) ) ;
2024-12-21 18:09:36 +01:00
size_t i = 0 ;
for ( auto & value : values )
2025-04-22 09:48:26 +02:00
wasm_values . append ( TRY_OR_RETURN_TRAP ( to_webassembly_value ( vm , value , type . results ( ) [ i + + ] ) ) ) ;
2024-12-21 18:09:36 +01:00
return Wasm : : Result { move ( wasm_values ) } ;
} ,
type ,
ByteString : : formatted ( " func{} " , resolved_imports . size ( ) ) ,
} ;
address = cache . abstract_machine ( ) . store ( ) . allocate ( move ( host_function ) ) ;
// FIXME: 3.4.3.2. Let index be the number of external functions in imports. This value index is known as the index of the host function funcaddr.
// 'index' doesn't seem to be used anywhere?
}
2024-09-07 17:16:37 +01:00
dbgln_if ( LIBWEB_WASM_DEBUG , " Resolved to {} " , address - > value ( ) ) ;
2023-03-16 14:11:21 -04:00
// FIXME: LinkError instead.
VERIFY ( address . has_value ( ) ) ;
2024-12-21 18:09:36 +01:00
// 3.4.4. Let externfunc be the external value func funcaddr.
// 3.4.5. Append externfunc to imports.
2023-03-16 14:11:21 -04:00
resolved_imports . set ( import_name , Wasm : : ExternValue { Wasm : : FunctionAddress { * address } } ) ;
return { } ;
} ,
2024-12-21 18:09:36 +01:00
// 3.5. If externtype is of the form global mut valtype,
2023-03-16 14:11:21 -04:00
[ & ] ( Wasm : : GlobalType const & type ) - > JS : : ThrowCompletionOr < void > {
Optional < Wasm : : GlobalAddress > address ;
2024-12-21 18:09:36 +01:00
// 3.5.1. If v is a Number or v is a BigInt,
2023-03-16 14:11:21 -04:00
if ( import_ . is_number ( ) | | import_ . is_bigint ( ) ) {
2024-12-21 18:09:36 +01:00
// 3.5.1.1. If valtype is i64 and v is a Number,
2023-03-16 14:11:21 -04:00
if ( import_ . is_number ( ) & & type . type ( ) . kind ( ) = = Wasm : : ValueType : : I64 ) {
2025-05-03 13:23:32 +03:30
// 3.5.1.1.1. Throw a LinkError exception.
return vm . throw_completion < LinkError > ( " Import resolution attempted to cast a Number to a BigInteger " sv ) ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
// 3.5.1.2. If valtype is not i64 and v is a BigInt,
2023-03-16 14:11:21 -04:00
if ( import_ . is_bigint ( ) & & type . type ( ) . kind ( ) ! = Wasm : : ValueType : : I64 ) {
2025-05-03 13:23:32 +03:30
// 3.5.1.2.1. Throw a LinkError exception.
return vm . throw_completion < LinkError > ( " Import resolution attempted to cast a BigInteger to a Number " sv ) ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
// 3.5.1.3. If valtype is v128,
if ( type . type ( ) . kind ( ) = = Wasm : : ValueType : : V128 ) {
2025-05-03 13:23:32 +03:30
// 3.5.1.3.1. Throw a LinkError exception.
return vm . throw_completion < LinkError > ( " Import resolution attempted to cast a Number or BigInt to a V128 " sv ) ;
2024-12-21 18:09:36 +01:00
}
// 3.5.1.4. Let value be ToWebAssemblyValue(v, valtype).
2023-03-16 14:11:21 -04:00
auto cast_value = TRY ( to_webassembly_value ( vm , import_ , type . type ( ) ) ) ;
2024-12-21 18:09:36 +01:00
// 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.
2024-04-25 19:09:34 +01:00
address = cache . abstract_machine ( ) . store ( ) . allocate ( { type . type ( ) , false } , cast_value ) ;
2024-12-21 18:09:36 +01:00
}
// FIXME: 3.5.2. Otherwise, if v implements Global,
// FIXME: 3.5.2.1. Let globaladdr be v.[[Global]].
// 3.5.3. Otherwise,
else {
2025-05-03 13:23:32 +03:30
// 3.5.3.1. Throw a LinkError exception.
return vm . throw_completion < LinkError > ( " Invalid value for global type " sv ) ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
// 3.5.4. Let externglobal be global globaladdr.
// 3.5.5. Append externglobal to imports.
2023-03-16 14:11:21 -04:00
resolved_imports . set ( import_name , Wasm : : ExternValue { * address } ) ;
return { } ;
} ,
2024-12-21 18:09:36 +01:00
// 3.6. If externtype is of the form mem memtype,
2023-03-16 14:11:21 -04:00
[ & ] ( Wasm : : MemoryType const & ) - > JS : : ThrowCompletionOr < void > {
2024-12-21 18:09:36 +01:00
// 3.6.1. If v does not implement Memory, throw a LinkError exception.
2023-03-16 14:11:21 -04:00
if ( ! import_ . is_object ( ) | | ! is < WebAssembly : : Memory > ( import_ . as_object ( ) ) ) {
2025-05-03 13:23:32 +03:30
return vm . throw_completion < LinkError > ( " Expected an instance of WebAssembly.Memory for a memory import " sv ) ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
// 3.6.2. Let externmem be the external value mem v.[[Memory]].
2023-03-16 14:11:21 -04:00
auto address = static_cast < WebAssembly : : Memory const & > ( import_ . as_object ( ) ) . address ( ) ;
2024-12-21 18:09:36 +01:00
// 3.6.3. Append externmem to imports.
2023-03-16 14:11:21 -04:00
resolved_imports . set ( import_name , Wasm : : ExternValue { address } ) ;
return { } ;
} ,
2024-12-21 18:09:36 +01:00
// 3.7. If externtype is of the form table tabletype,
2023-03-16 14:11:21 -04:00
[ & ] ( Wasm : : TableType const & ) - > JS : : ThrowCompletionOr < void > {
2024-12-21 18:09:36 +01:00
// 3.7.1. If v does not implement Table, throw a LinkError exception.
2023-03-16 14:11:21 -04:00
if ( ! import_ . is_object ( ) | | ! is < WebAssembly : : Table > ( import_ . as_object ( ) ) ) {
2025-05-03 13:23:32 +03:30
return vm . throw_completion < LinkError > ( " Expected an instance of WebAssembly.Table for a table import " sv ) ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
// 3.7.2. Let tableaddr be v.[[Table]].
// 3.7.3. Let externtable be the external value table tableaddr.
2023-03-16 14:11:21 -04:00
auto address = static_cast < WebAssembly : : Table const & > ( import_ . as_object ( ) ) . address ( ) ;
2024-12-21 18:09:36 +01:00
// 3.7.4. Append externtable to imports.
2023-03-16 14:11:21 -04:00
resolved_imports . set ( import_name , Wasm : : ExternValue { address } ) ;
return { } ;
} ,
[ & ] ( auto const & ) - > JS : : ThrowCompletionOr < void > {
2024-12-21 18:09:36 +01:00
// (noop)
return { } ;
2023-03-16 14:11:21 -04:00
} ) ) ;
}
}
2024-12-21 18:09:36 +01:00
// (inlined) 4. Return imports.
2023-03-16 14:11:21 -04:00
linker . link ( resolved_imports ) ;
auto link_result = linker . finish ( ) ;
if ( link_result . is_error ( ) ) {
2023-09-06 08:29:01 -04:00
StringBuilder builder ;
2025-05-03 13:23:32 +03:30
builder . append ( " Missing " sv ) ;
2023-09-06 08:29:01 -04:00
builder . join ( ' ' , link_result . error ( ) . missing_imports ) ;
2025-05-03 13:23:32 +03:30
return vm . throw_completion < LinkError > ( MUST ( builder . to_string ( ) ) ) ;
2023-03-16 14:11:21 -04:00
}
2024-04-25 19:09:34 +01:00
auto instance_result = cache . abstract_machine ( ) . instantiate ( module , link_result . release_value ( ) ) ;
2023-03-16 14:11:21 -04:00
if ( instance_result . is_error ( ) ) {
2025-05-03 13:23:32 +03:30
return vm . throw_completion < LinkError > ( instance_result . error ( ) . error ) ;
2023-03-16 14:11:21 -04:00
}
2024-04-25 19:09:34 +01:00
return instance_result . release_value ( ) ;
2023-03-16 14:11:21 -04:00
}
2024-10-26 14:09:25 -06:00
// // https://webassembly.github.io/spec/js-api/#compile-a-webassembly-module
JS : : ThrowCompletionOr < NonnullRefPtr < CompiledWebAssemblyModule > > compile_a_webassembly_module ( JS : : VM & vm , ByteBuffer data )
2023-03-16 14:11:21 -04:00
{
2024-10-26 14:09:25 -06:00
FixedMemoryStream stream { data . bytes ( ) } ;
2023-03-16 14:11:21 -04:00
auto module_result = Wasm : : Module : : parse ( stream ) ;
if ( module_result . is_error ( ) ) {
2025-05-03 13:23:32 +03:30
return vm . throw_completion < CompileError > ( Wasm : : parse_error_to_byte_string ( module_result . error ( ) ) ) ;
2023-03-16 14:11:21 -04:00
}
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( * vm . current_realm ( ) ) ;
if ( auto validation_result = cache . abstract_machine ( ) . validate ( module_result . value ( ) ) ; validation_result . is_error ( ) ) {
2025-05-03 13:23:32 +03:30
return vm . throw_completion < CompileError > ( validation_result . error ( ) . error_string ) ;
2023-03-16 14:11:21 -04:00
}
2024-04-25 19:09:34 +01:00
auto compiled_module = make_ref_counted < CompiledWebAssemblyModule > ( module_result . release_value ( ) ) ;
cache . add_compiled_module ( compiled_module ) ;
return compiled_module ;
2023-03-16 14:11:21 -04:00
}
2024-12-21 18:09:36 +01:00
GC_DEFINE_ALLOCATOR ( ExportedWasmFunction ) ;
2025-03-18 18:08:02 -05:00
GC : : Ref < ExportedWasmFunction > ExportedWasmFunction : : create ( JS : : Realm & realm , FlyString const & name , Function < JS : : ThrowCompletionOr < JS : : Value > ( JS : : VM & ) > behavior , Wasm : : FunctionAddress exported_address )
2024-12-21 18:09:36 +01:00
{
auto prototype = realm . intrinsics ( ) . function_prototype ( ) ;
return realm . create < ExportedWasmFunction > (
name ,
2025-04-20 13:08:56 +02:00
move ( behavior ) ,
2024-12-21 18:09:36 +01:00
exported_address ,
prototype ) ;
}
2025-04-20 13:08:56 +02:00
ExportedWasmFunction : : ExportedWasmFunction ( FlyString name , AK : : Function < JS : : ThrowCompletionOr < JS : : Value > ( JS : : VM & ) > behavior , Wasm : : FunctionAddress exported_address , JS : : Object & prototype )
2024-12-21 18:09:36 +01:00
: NativeFunction ( move ( name ) , move ( behavior ) , prototype )
, m_exported_address ( exported_address )
{
}
2025-03-18 18:08:02 -05:00
JS : : NativeFunction * create_native_function ( JS : : VM & vm , Wasm : : FunctionAddress address , String const & name , Instance * instance )
2023-03-16 14:11:21 -04:00
{
auto & realm = * vm . current_realm ( ) ;
Optional < Wasm : : FunctionType > type ;
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( realm ) ;
cache . abstract_machine ( ) . store ( ) . get ( address ) - > visit ( [ & ] ( auto const & value ) { type = value . type ( ) ; } ) ;
if ( auto entry = cache . get_function_instance ( address ) ; entry . has_value ( ) )
2023-03-16 14:11:21 -04:00
return * entry ;
2024-12-21 18:09:36 +01:00
auto function = ExportedWasmFunction : : create (
2023-03-16 14:11:21 -04:00
realm ,
name ,
2024-05-18 20:01:29 +02:00
[ address , type = type . release_value ( ) , instance ] ( JS : : VM & vm ) - > JS : : ThrowCompletionOr < JS : : Value > {
( void ) instance ;
2023-03-16 14:11:21 -04:00
auto & realm = * vm . current_realm ( ) ;
Vector < Wasm : : Value > values ;
values . ensure_capacity ( type . parameters ( ) . size ( ) ) ;
// Grab as many values as needed and convert them.
size_t index = 0 ;
for ( auto & type : type . parameters ( ) )
values . append ( TRY ( to_webassembly_value ( vm , vm . argument ( index + + ) , type ) ) ) ;
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( realm ) ;
auto result = cache . abstract_machine ( ) . invoke ( address , move ( values ) ) ;
2023-03-16 14:11:21 -04:00
// FIXME: Use the convoluted mapping of errors defined in the spec.
2025-04-22 09:48:26 +02:00
if ( result . is_trap ( ) ) {
if ( auto ptr = result . trap ( ) . data . get_pointer < Wasm : : ExternallyManagedTrap > ( ) )
return ptr - > unsafe_external_object_as < JS : : Completion > ( ) ;
2025-05-03 13:23:32 +03:30
return vm . throw_completion < RuntimeError > ( TRY_OR_THROW_OOM ( vm , String : : formatted ( " Wasm execution trapped (WIP): {} " , result . trap ( ) . format ( ) ) ) ) ;
2025-04-22 09:48:26 +02:00
}
2023-03-16 14:11:21 -04:00
if ( result . values ( ) . is_empty ( ) )
return JS : : js_undefined ( ) ;
if ( result . values ( ) . size ( ) = = 1 )
2024-08-04 08:06:50 -07:00
return to_js_value ( vm , result . values ( ) . first ( ) , type . results ( ) . first ( ) ) ;
2023-03-16 14:11:21 -04:00
2024-08-26 13:02:27 +00:00
// Put result values into a JS::Array in reverse order.
2024-12-26 14:32:52 +01:00
auto js_result_values = GC : : RootVector < JS : : Value > { realm . heap ( ) } ;
2024-08-26 13:02:27 +00:00
js_result_values . ensure_capacity ( result . values ( ) . size ( ) ) ;
for ( size_t i = result . values ( ) . size ( ) ; i > 0 ; i - - ) {
// Safety: ensure_capacity is called just before this.
js_result_values . unchecked_append ( to_js_value ( vm , result . values ( ) . at ( i - 1 ) , type . results ( ) . at ( i - 1 ) ) ) ;
}
return JS : : Value ( JS : : Array : : create_from ( realm , js_result_values ) ) ;
2024-12-21 18:09:36 +01:00
} ,
address ) ;
2023-03-16 14:11:21 -04:00
2024-04-25 19:09:34 +01:00
cache . add_function_instance ( address , function ) ;
2023-03-16 14:11:21 -04:00
return function ;
}
JS : : ThrowCompletionOr < Wasm : : Value > to_webassembly_value ( JS : : VM & vm , JS : : Value value , Wasm : : ValueType const & type )
{
static : : Crypto : : SignedBigInteger two_64 = " 1 " _sbigint . shift_left ( 64 ) ;
switch ( type . kind ( ) ) {
case Wasm : : ValueType : : I64 : {
auto bigint = TRY ( value . to_bigint ( vm ) ) ;
auto value = bigint - > big_integer ( ) . divided_by ( two_64 ) . remainder ;
VERIFY ( value . unsigned_value ( ) . trimmed_length ( ) < = 2 ) ;
i64 integer = static_cast < i64 > ( value . unsigned_value ( ) . to_u64 ( ) ) ;
if ( value . is_negative ( ) )
integer = - integer ;
return Wasm : : Value { integer } ;
}
case Wasm : : ValueType : : I32 : {
auto _i32 = TRY ( value . to_i32 ( vm ) ) ;
return Wasm : : Value { static_cast < i32 > ( _i32 ) } ;
}
case Wasm : : ValueType : : F64 : {
auto number = TRY ( value . to_double ( vm ) ) ;
return Wasm : : Value { static_cast < double > ( number ) } ;
}
case Wasm : : ValueType : : F32 : {
auto number = TRY ( value . to_double ( vm ) ) ;
return Wasm : : Value { static_cast < float > ( number ) } ;
}
2024-07-07 19:01:28 -07:00
case Wasm : : ValueType : : FunctionReference : {
2023-03-16 14:11:21 -04:00
if ( value . is_null ( ) )
2024-08-17 15:40:21 -07:00
return Wasm : : Value ( Wasm : : ValueType { Wasm : : ValueType : : Kind : : FunctionReference } ) ;
2023-03-16 14:11:21 -04:00
if ( value . is_function ( ) ) {
auto & function = value . as_function ( ) ;
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( * vm . current_realm ( ) ) ;
for ( auto & entry : cache . function_instances ( ) ) {
2023-03-16 14:11:21 -04:00
if ( entry . value = = & function )
2024-08-22 01:13:37 +02:00
return Wasm : : Value { Wasm : : Reference { Wasm : : Reference : : Func { entry . key , cache . abstract_machine ( ) . store ( ) . get_module_for ( entry . key ) } } } ;
2023-03-16 14:11:21 -04:00
}
}
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAnObjectOfType , " Exported function " ) ;
}
2024-08-17 15:40:21 -07:00
case Wasm : : ValueType : : ExternReference : {
if ( value . is_null ( ) )
return Wasm : : Value ( Wasm : : ValueType { Wasm : : ValueType : : Kind : : ExternReference } ) ;
auto & cache = get_cache ( * vm . current_realm ( ) ) ;
for ( auto & entry : cache . extern_values ( ) ) {
if ( entry . value = = value )
return Wasm : : Value { Wasm : : Reference { Wasm : : Reference : : Extern { entry . key } } } ;
}
Wasm : : ExternAddress extern_addr = cache . extern_values ( ) . size ( ) ;
cache . add_extern_value ( extern_addr , value ) ;
return Wasm : : Value { Wasm : : Reference { Wasm : : Reference : : Extern { extern_addr } } } ;
}
2023-06-12 13:04:22 +03:30
case Wasm : : ValueType : : V128 :
return vm . throw_completion < JS : : TypeError > ( " Cannot convert a vector value to a javascript value " sv ) ;
2023-03-16 14:11:21 -04:00
}
VERIFY_NOT_REACHED ( ) ;
}
2024-08-17 17:02:59 -07:00
Wasm : : Value default_webassembly_value ( JS : : VM & vm , Wasm : : ValueType type )
{
switch ( type . kind ( ) ) {
case Wasm : : ValueType : : I32 :
case Wasm : : ValueType : : I64 :
case Wasm : : ValueType : : F32 :
case Wasm : : ValueType : : F64 :
case Wasm : : ValueType : : V128 :
case Wasm : : ValueType : : FunctionReference :
return Wasm : : Value ( type ) ;
case Wasm : : ValueType : : ExternReference :
return MUST ( to_webassembly_value ( vm , JS : : js_undefined ( ) , type ) ) ;
}
VERIFY_NOT_REACHED ( ) ;
}
2024-08-17 15:40:21 -07:00
// https://webassembly.github.io/spec/js-api/#tojsvalue
2024-08-04 08:06:50 -07:00
JS : : Value to_js_value ( JS : : VM & vm , Wasm : : Value & wasm_value , Wasm : : ValueType type )
2023-03-16 14:11:21 -04:00
{
auto & realm = * vm . current_realm ( ) ;
2024-08-04 08:06:50 -07:00
switch ( type . kind ( ) ) {
2023-03-16 14:11:21 -04:00
case Wasm : : ValueType : : I64 :
2024-11-14 05:50:17 +13:00
return realm . create < JS : : BigInt > ( : : Crypto : : SignedBigInteger { wasm_value . to < i64 > ( ) } ) ;
2023-03-16 14:11:21 -04:00
case Wasm : : ValueType : : I32 :
2024-08-04 08:06:50 -07:00
return JS : : Value ( wasm_value . to < i32 > ( ) ) ;
2023-03-16 14:11:21 -04:00
case Wasm : : ValueType : : F64 :
2024-08-04 08:06:50 -07:00
return JS : : Value ( wasm_value . to < double > ( ) ) ;
2023-03-16 14:11:21 -04:00
case Wasm : : ValueType : : F32 :
2024-08-04 08:06:50 -07:00
return JS : : Value ( static_cast < double > ( wasm_value . to < float > ( ) ) ) ;
2024-07-08 18:09:00 -07:00
case Wasm : : ValueType : : FunctionReference : {
2024-08-04 08:06:50 -07:00
auto ref_ = wasm_value . to < Wasm : : Reference > ( ) ;
2024-07-30 10:24:25 -07:00
if ( ref_ . ref ( ) . has < Wasm : : Reference : : Null > ( ) )
return JS : : js_null ( ) ;
auto address = ref_ . ref ( ) . get < Wasm : : Reference : : Func > ( ) . address ;
2024-07-08 18:09:00 -07:00
auto & cache = get_cache ( realm ) ;
auto * function = cache . abstract_machine ( ) . store ( ) . get ( address ) ;
auto name = function - > visit (
[ & ] ( Wasm : : WasmFunction & wasm_function ) {
auto index = * wasm_function . module ( ) . functions ( ) . find_first_index ( address ) ;
return ByteString : : formatted ( " func{} " , index ) ;
} ,
[ ] ( Wasm : : HostFunction & host_function ) {
return host_function . name ( ) ;
} ) ;
2025-03-18 18:08:02 -05:00
return create_native_function ( vm , address , MUST ( String : : from_byte_string ( name ) ) ) ;
2024-07-08 18:09:00 -07:00
}
2024-08-17 15:40:21 -07:00
case Wasm : : ValueType : : ExternReference : {
auto ref_ = wasm_value . to < Wasm : : Reference > ( ) ;
if ( ref_ . ref ( ) . has < Wasm : : Reference : : Null > ( ) )
return JS : : js_null ( ) ;
auto address = ref_ . ref ( ) . get < Wasm : : Reference : : Extern > ( ) . address ;
auto & cache = get_cache ( realm ) ;
auto value = cache . get_extern_value ( address ) ;
return value . release_value ( ) ;
}
2023-06-12 13:04:22 +03:30
case Wasm : : ValueType : : V128 :
2024-08-17 15:40:21 -07:00
VERIFY_NOT_REACHED ( ) ;
2023-03-16 14:11:21 -04:00
}
VERIFY_NOT_REACHED ( ) ;
}
}
2024-10-26 14:09:25 -06:00
// https://webassembly.github.io/spec/js-api/#asynchronously-compile-a-webassembly-module
2024-11-15 04:01:23 +13:00
GC : : Ref < WebIDL : : Promise > asynchronously_compile_webassembly_module ( JS : : VM & vm , ByteBuffer bytes , HTML : : Task : : Source task_source )
2024-10-26 14:09:25 -06:00
{
auto & realm = * vm . current_realm ( ) ;
// 1. Let promise be a new Promise.
auto promise = WebIDL : : create_promise ( realm ) ;
// 2. Run the following steps in parallel:
2024-11-15 04:01:23 +13:00
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( GC : : create_function ( vm . heap ( ) , [ & vm , & realm , bytes = move ( bytes ) , promise , task_source ] ( ) mutable {
2024-10-24 20:39:18 +13:00
HTML : : TemporaryExecutionContext context ( realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
2024-10-26 14:09:25 -06:00
// 1. Compile the WebAssembly module bytes and store the result as module.
auto module_or_error = Detail : : compile_a_webassembly_module ( vm , move ( bytes ) ) ;
// 2. Queue a task to perform the following steps. If taskSource was provided, queue the task on that task source.
2024-11-15 04:01:23 +13:00
HTML : : queue_a_task ( task_source , nullptr , nullptr , GC : : create_function ( vm . heap ( ) , [ & realm , promise , module_or_error = move ( module_or_error ) ] ( ) mutable {
2024-10-24 20:39:18 +13:00
HTML : : TemporaryExecutionContext context ( realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
2024-10-26 14:09:25 -06:00
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
// 1. If module is error, reject promise with a CompileError exception.
if ( module_or_error . is_error ( ) ) {
WebIDL : : reject_promise ( realm , promise , module_or_error . error_value ( ) ) ;
}
// 2. Otherwise,
else {
// 1. Construct a WebAssembly module object from module and bytes, and let moduleObject be the result.
// FIXME: Save bytes to the Module instance instead of moving into compile_a_webassembly_module
2024-11-14 05:50:17 +13:00
auto module_object = realm . create < Module > ( realm , module_or_error . release_value ( ) ) ;
2024-10-26 14:09:25 -06:00
// 2. Resolve promise with moduleObject.
2024-11-14 05:50:17 +13:00
WebIDL : : resolve_promise ( realm , promise , module_object ) ;
2024-10-26 14:09:25 -06:00
}
} ) ) ;
2024-11-01 12:15:40 -06:00
} ) ) ;
2024-10-26 14:09:25 -06:00
// 3. Return promise.
return promise ;
}
// https://webassembly.github.io/spec/js-api/#asynchronously-instantiate-a-webassembly-module
2024-11-15 04:01:23 +13:00
GC : : Ref < WebIDL : : Promise > asynchronously_instantiate_webassembly_module ( JS : : VM & vm , GC : : Ref < Module > module_object , GC : : Ptr < JS : : Object > import_object )
2024-10-26 14:09:25 -06:00
{
auto & realm = * vm . current_realm ( ) ;
// 1. Let promise be a new promise.
auto promise = WebIDL : : create_promise ( realm ) ;
// 2. Let module be moduleObject.[[Module]].
auto module = module_object - > compiled_module ( ) ;
// 3. Read the imports of module with imports importObject, and let imports be the result.
// If this operation throws an exception, catch it, reject promise with the exception, and return promise.
// Note: We do this at the same time as instantiation in instantiate_module.
// 4. Run the following steps in parallel:
// 1. Queue a task to perform the following steps: Note: Implementation-specific work may be performed here.
2024-11-15 04:01:23 +13:00
HTML : : queue_a_task ( HTML : : Task : : Source : : Unspecified , nullptr , nullptr , GC : : create_function ( vm . heap ( ) , [ & vm , & realm , promise , module , import_object ] ( ) {
2024-10-24 20:39:18 +13:00
HTML : : TemporaryExecutionContext context ( realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
2024-10-26 14:09:25 -06:00
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
// 1. Instantiate the core of a WebAssembly module module with imports, and let instance be the result.
// If this throws an exception, catch it, reject promise with the exception, and terminate these substeps.
auto result = Detail : : instantiate_module ( vm , module - > module , import_object ) ;
if ( result . is_error ( ) ) {
WebIDL : : reject_promise ( realm , promise , result . error_value ( ) ) ;
return ;
}
auto instance = result . release_value ( ) ;
// 2. Let instanceObject be a new Instance.
// 3. Initialize instanceObject from module and instance. If this throws an exception, catch it, reject promise with the exception, and terminate these substeps.
// FIXME: Investigate whether we are doing all the proper steps for "initialize an instance object"
2024-11-14 05:50:17 +13:00
auto instance_object = realm . create < Instance > ( realm , move ( instance ) ) ;
2024-10-26 14:09:25 -06:00
// 4. Resolve promise with instanceObject.
WebIDL : : resolve_promise ( realm , promise , instance_object ) ;
} ) ) ;
// 5. Return promise.
return promise ;
}
// https://webassembly.github.io/spec/js-api/#instantiate-a-promise-of-a-module
2024-11-15 04:01:23 +13:00
GC : : Ref < WebIDL : : Promise > instantiate_promise_of_module ( JS : : VM & vm , GC : : Ref < WebIDL : : Promise > promise_of_module , GC : : Ptr < JS : : Object > import_object )
2024-10-26 14:09:25 -06:00
{
auto & realm = * vm . current_realm ( ) ;
// 1. Let promise be a new Promise.
auto promise = WebIDL : : create_promise ( realm ) ;
// FIXME: Spec should use react to promise here instead of separate upon fulfillment and upon rejection steps
// 2. Upon fulfillment of promiseOfModule with value module:
2024-11-15 04:01:23 +13:00
auto fulfillment_steps = GC : : create_function ( vm . heap ( ) , [ & vm , promise , import_object ] ( JS : : Value module_value ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 14:09:25 -06:00
VERIFY ( module_value . is_object ( ) & & is < Module > ( module_value . as_object ( ) ) ) ;
2024-11-15 04:01:23 +13:00
auto module = GC : : Ref { static_cast < Module & > ( module_value . as_object ( ) ) } ;
2024-10-26 14:09:25 -06:00
// 1. Instantiate the WebAssembly module module importing importObject, and let innerPromise be the result.
auto inner_promise = asynchronously_instantiate_webassembly_module ( vm , module , import_object ) ;
// 2. Upon fulfillment of innerPromise with value instance.
2024-11-15 04:01:23 +13:00
auto instantiate_fulfillment_steps = GC : : create_function ( vm . heap ( ) , [ promise , module ] ( JS : : Value instance_value ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 14:09:25 -06:00
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
VERIFY ( instance_value . is_object ( ) & & is < Instance > ( instance_value . as_object ( ) ) ) ;
2024-11-15 04:01:23 +13:00
auto instance = GC : : Ref { static_cast < Instance & > ( instance_value . as_object ( ) ) } ;
2024-10-26 14:09:25 -06:00
// 1. Let result be the WebAssemblyInstantiatedSource value «[ "module" → module, "instance" → instance ]».
auto result = JS : : Object : : create ( realm , nullptr ) ;
2025-03-16 21:25:29 -05:00
result - > define_direct_property ( " module " _fly_string , module , JS : : default_attributes ) ;
result - > define_direct_property ( " instance " _fly_string , instance , JS : : default_attributes ) ;
2024-10-26 14:09:25 -06:00
// 2. Resolve promise with result.
WebIDL : : resolve_promise ( realm , promise , result ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 3. Upon rejection of innerPromise with reason reason.
2024-11-15 04:01:23 +13:00
auto instantiate_rejection_steps = GC : : create_function ( vm . heap ( ) , [ promise ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 14:09:25 -06:00
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
// 1. Reject promise with reason.
WebIDL : : reject_promise ( realm , promise , reason ) ;
return JS : : js_undefined ( ) ;
} ) ;
WebIDL : : react_to_promise ( inner_promise , instantiate_fulfillment_steps , instantiate_rejection_steps ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 3. Upon rejection of promiseOfModule with reason reason:
2024-11-15 04:01:23 +13:00
auto rejection_steps = GC : : create_function ( vm . heap ( ) , [ promise ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 14:09:25 -06:00
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
// 1. Reject promise with reason.
WebIDL : : reject_promise ( realm , promise , reason ) ;
return JS : : js_undefined ( ) ;
} ) ;
WebIDL : : react_to_promise ( promise_of_module , fulfillment_steps , rejection_steps ) ;
// 4. Return promise.
return promise ;
}
2024-10-26 17:34:09 -06:00
// https://webassembly.github.io/spec/web-api/index.html#compile-a-potential-webassembly-response
2024-11-15 04:01:23 +13:00
GC : : Ref < WebIDL : : Promise > compile_potential_webassembly_response ( JS : : VM & vm , GC : : Ref < WebIDL : : Promise > source )
2024-10-26 17:34:09 -06:00
{
auto & realm = * vm . current_realm ( ) ;
// Note: This algorithm accepts a Response object, or a promise for one, and compiles and instantiates the resulting bytes of the response.
// This compilation can be performed in the background and in a streaming manner.
// If the Response is not CORS-same-origin, does not represent an ok status, or does not match the `application/wasm` MIME type,
// the returned promise will be rejected with a TypeError; if compilation or instantiation fails,
// the returned promise will be rejected with a CompileError or other relevant error type, depending on the cause of failure.
// 1. Let returnValue be a new promise
auto return_value = WebIDL : : create_promise ( realm ) ;
// 2. Upon fulfillment of source with value unwrappedSource:
2024-11-15 04:01:23 +13:00
auto fulfillment_steps = GC : : create_function ( vm . heap ( ) , [ & vm , return_value ] ( JS : : Value unwrapped_source ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 17:34:09 -06:00
auto & realm = HTML : : relevant_realm ( * return_value - > promise ( ) ) ;
// 1. Let response be unwrappedSource’ s response.
if ( ! unwrapped_source . is_object ( ) | | ! is < Fetch : : Response > ( unwrapped_source . as_object ( ) ) ) {
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( realm , return_value , vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAnObjectOfType , " Response " ) . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
auto & response_object = static_cast < Fetch : : Response & > ( unwrapped_source . as_object ( ) ) ;
auto response = response_object . response ( ) ;
// 2. Let mimeType be the result of getting `Content-Type` from response’ s header list.
// 3. If mimeType is null, reject returnValue with a TypeError and abort these substeps.
// 4. Remove all HTTP tab or space byte from the start and end of mimeType.
// 5. If mimeType is not a byte-case-insensitive match for `application/wasm`, reject returnValue with a TypeError and abort these substeps.
// Note: extra parameters are not allowed, including the empty `application/wasm;`.
// FIXME: Validate these extra constraints that are not checked by extract_mime_type()
if ( auto mime = response - > header_list ( ) - > extract_mime_type ( ) ; ! mime . has_value ( ) | | mime . value ( ) . essence ( ) ! = " application/wasm " sv ) {
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( realm , return_value , vm . throw_completion < JS : : TypeError > ( " Response does not match the application/wasm MIME type " sv ) . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
// 6. If response is not CORS-same-origin, reject returnValue with a TypeError and abort these substeps.
2024-12-20 16:51:31 +00:00
if ( ! response - > is_cors_same_origin ( ) ) {
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( realm , return_value , vm . throw_completion < JS : : TypeError > ( " Response is not CORS-same-origin " sv ) . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
// 7. If response’ s status is not an ok status, reject returnValue with a TypeError and abort these substeps.
if ( ! response_object . ok ( ) ) {
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( realm , return_value , vm . throw_completion < JS : : TypeError > ( " Response does not represent an ok status " sv ) . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
// 8. Consume response’ s body as an ArrayBuffer, and let bodyPromise be the result.
auto body_promise_or_error = response_object . array_buffer ( ) ;
if ( body_promise_or_error . is_error ( ) ) {
2024-11-04 14:37:27 +01:00
auto throw_completion = Bindings : : exception_to_throw_completion ( realm . vm ( ) , body_promise_or_error . release_error ( ) ) ;
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( realm , return_value , throw_completion . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
auto body_promise = body_promise_or_error . release_value ( ) ;
// 9. Upon fulfillment of bodyPromise with value bodyArrayBuffer:
2024-11-15 04:01:23 +13:00
auto body_fulfillment_steps = GC : : create_function ( vm . heap ( ) , [ & vm , return_value ] ( JS : : Value body_array_buffer ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 17:34:09 -06:00
// 1. Let stableBytes be a copy of the bytes held by the buffer bodyArrayBuffer.
VERIFY ( body_array_buffer . is_object ( ) ) ;
auto stable_bytes = WebIDL : : get_buffer_source_copy ( body_array_buffer . as_object ( ) ) ;
if ( stable_bytes . is_error ( ) ) {
VERIFY ( stable_bytes . error ( ) . code ( ) = = ENOMEM ) ;
2025-04-04 18:11:45 +02:00
WebIDL : : reject_promise ( HTML : : relevant_realm ( * return_value - > promise ( ) ) , return_value , vm . throw_completion < JS : : InternalError > ( vm . error_message ( JS : : VM : : ErrorMessage : : OutOfMemory ) ) . value ( ) ) ;
2024-10-26 17:34:09 -06:00
return JS : : js_undefined ( ) ;
}
// 2. Asynchronously compile the WebAssembly module stableBytes using the networking task source and resolve returnValue with the result.
auto result = asynchronously_compile_webassembly_module ( vm , stable_bytes . release_value ( ) , HTML : : Task : : Source : : Networking ) ;
// Need to manually convert WebIDL promise to an ECMAScript value here to resolve
WebIDL : : resolve_promise ( HTML : : relevant_realm ( * return_value - > promise ( ) ) , return_value , result - > promise ( ) ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 10. Upon rejection of bodyPromise with reason reason:
2024-11-15 04:01:23 +13:00
auto body_rejection_steps = GC : : create_function ( vm . heap ( ) , [ return_value ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 17:34:09 -06:00
// 1. Reject returnValue with reason.
WebIDL : : reject_promise ( HTML : : relevant_realm ( * return_value - > promise ( ) ) , return_value , reason ) ;
return JS : : js_undefined ( ) ;
} ) ;
WebIDL : : react_to_promise ( body_promise , body_fulfillment_steps , body_rejection_steps ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 3. Upon rejection of source with reason reason:
2024-11-15 04:01:23 +13:00
auto rejection_steps = GC : : create_function ( vm . heap ( ) , [ return_value ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
2024-10-26 17:34:09 -06:00
// 1. Reject returnValue with reason.
WebIDL : : reject_promise ( HTML : : relevant_realm ( * return_value - > promise ( ) ) , return_value , reason ) ;
return JS : : js_undefined ( ) ;
} ) ;
WebIDL : : react_to_promise ( source , fulfillment_steps , rejection_steps ) ;
// 4. Return returnValue.
return return_value ;
}
2025-05-03 13:22:44 +03:30
# define DEFINE_WASM_NATIVE_ERROR(ClassName, snake_name, PrototypeName, ConstructorName) \
GC_DEFINE_ALLOCATOR ( ClassName ) ; \
GC : : Ref < ClassName > ClassName : : create ( JS : : Realm & realm ) \
{ \
return realm . create < ClassName > ( Bindings : : ensure_web_prototype < PrototypeName > ( realm , # ClassName # # _fly_string ) ) ; \
} \
\
GC : : Ref < ClassName > ClassName : : create ( JS : : Realm & realm , String message ) \
{ \
auto & vm = realm . vm ( ) ; \
auto error = ClassName : : create ( realm ) ; \
u8 const attr = JS : : Attribute : : Writable | JS : : Attribute : : Configurable ; \
error - > define_direct_property ( vm . names . message , JS : : PrimitiveString : : create ( vm , move ( message ) ) , attr ) ; \
return error ; \
} \
\
GC : : Ref < ClassName > ClassName : : create ( JS : : Realm & realm , StringView message ) \
{ \
return create ( realm , MUST ( String : : from_utf8 ( message ) ) ) ; \
} \
\
ClassName : : ClassName ( JS : : Object & prototype ) \
: Error ( prototype ) \
{ \
}
DEFINE_WASM_NATIVE_ERROR ( CompileError , compile_error , CompileErrorPrototype , CompileErrorConstructor )
DEFINE_WASM_NATIVE_ERROR ( LinkError , link_error , LinkErrorPrototype , LinkErrorConstructor )
DEFINE_WASM_NATIVE_ERROR ( RuntimeError , runtime_error , RuntimeErrorPrototype , RuntimeErrorConstructor )
# undef DEFINE_WASM_NATIVE_ERROR
# define DEFINE_WASM_NATIVE_ERROR_CONSTRUCTOR(ClassName, snake_name, PrototypeName, ConstructorName) \
GC_DEFINE_ALLOCATOR ( ConstructorName ) ; \
ConstructorName : : ConstructorName ( JS : : Realm & realm ) \
: NativeFunction ( # ClassName # # _string , * realm . intrinsics ( ) . error_constructor ( ) ) \
{ \
} \
\
void ConstructorName : : initialize ( JS : : Realm & realm ) \
{ \
auto & vm = this - > vm ( ) ; \
Base : : initialize ( realm ) ; \
\
/* 20.5.6.2.1 NativeError.prototype, https://tc39.es/ecma262/#sec-nativeerror.prototype */ \
define_direct_property ( vm . names . prototype , & Bindings : : ensure_web_prototype < PrototypeName > ( realm , # ClassName # # _fly_string ) , 0 ) ; \
\
define_direct_property ( vm . names . length , JS : : Value ( 1 ) , JS : : Attribute : : Configurable ) ; \
} \
\
ConstructorName : : ~ ConstructorName ( ) = default ; \
\
/* 20.5.6.1.1 NativeError ( message [ , options ] ), https://tc39.es/ecma262/#sec-nativeerror */ \
JS : : ThrowCompletionOr < JS : : Value > ConstructorName : : call ( ) \
{ \
/* 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. */ \
return TRY ( construct ( * this ) ) ; \
} \
\
/* 20.5.6.1.1 NativeError ( message [ , options ] ), https://tc39.es/ecma262/#sec-nativeerror */ \
JS : : ThrowCompletionOr < GC : : Ref < JS : : Object > > ConstructorName : : construct ( JS : : FunctionObject & new_target ) \
{ \
auto & vm = this - > vm ( ) ; \
\
auto message = vm . argument ( 0 ) ; \
auto options = vm . argument ( 1 ) ; \
\
/* 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). */ \
auto error = TRY ( ordinary_create_from_constructor < ClassName > ( vm , new_target , & JS : : Intrinsics : : error_prototype ) ) ; \
\
/* 3. If message is not undefined, then */ \
if ( ! message . is_undefined ( ) ) { \
/* a. Let msg be ? ToString(message). */ \
auto msg = TRY ( message . to_string ( vm ) ) ; \
\
/* b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). */ \
error - > create_non_enumerable_data_property_or_throw ( vm . names . message , JS : : PrimitiveString : : create ( vm , move ( msg ) ) ) ; \
} \
\
/* 4. Perform ? InstallErrorCause(O, options). */ \
TRY ( error - > install_error_cause ( options ) ) ; \
\
/* 5. Return O. */ \
return error ; \
}
DEFINE_WASM_NATIVE_ERROR_CONSTRUCTOR ( CompileError , compile_error , CompileErrorPrototype , CompileErrorConstructor )
DEFINE_WASM_NATIVE_ERROR_CONSTRUCTOR ( LinkError , link_error , LinkErrorPrototype , LinkErrorConstructor )
DEFINE_WASM_NATIVE_ERROR_CONSTRUCTOR ( RuntimeError , runtime_error , RuntimeErrorPrototype , RuntimeErrorConstructor )
# undef DEFINE_WASM_NATIVE_ERROR_CONSTRUCTOR
# define DEFINE_WASM_NATIVE_ERROR_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName) \
GC_DEFINE_ALLOCATOR ( PrototypeName ) ; \
\
PrototypeName : : PrototypeName ( JS : : Realm & realm ) \
: PrototypeObject ( realm . intrinsics ( ) . error_prototype ( ) ) \
{ \
} \
\
void PrototypeName : : initialize ( JS : : Realm & realm ) \
{ \
auto & vm = this - > vm ( ) ; \
Base : : initialize ( realm ) ; \
u8 const attr = JS : : Attribute : : Writable | JS : : Attribute : : Configurable ; \
define_direct_property ( vm . names . name , JS : : PrimitiveString : : create ( vm , # ClassName # # _string ) , attr ) ; \
define_direct_property ( vm . names . message , JS : : PrimitiveString : : create ( vm , String { } ) , attr ) ; \
}
DEFINE_WASM_NATIVE_ERROR_PROTOTYPE ( CompileError , compile_error , CompileErrorPrototype , CompileErrorConstructor )
DEFINE_WASM_NATIVE_ERROR_PROTOTYPE ( LinkError , link_error , LinkErrorPrototype , LinkErrorConstructor )
DEFINE_WASM_NATIVE_ERROR_PROTOTYPE ( RuntimeError , runtime_error , RuntimeErrorPrototype , RuntimeErrorConstructor )
# undef DEFINE_WASM_NATIVE_ERROR_PROTOTYPE
}
# define DEFINE_WASM_ERROR_PROTOTYPE_AND_CONSTRUCTOR_WEB_INTRINSIC(ClassName, snake_name, PrototypeName, ConstructorName) \
template < > \
void Intrinsics : : create_web_prototype_and_constructor < WebAssembly : : PrototypeName > ( JS : : Realm & realm ) \
{ \
auto & vm = realm . vm ( ) ; \
auto prototype = heap ( ) . allocate < WebAssembly : : PrototypeName > ( realm ) ; \
m_prototypes . set ( # ClassName # # _string , prototype ) ; \
\
auto constructor = heap ( ) . allocate < WebAssembly : : ConstructorName > ( realm ) ; \
m_constructors . set ( # ClassName # # _string , constructor ) ; \
\
prototype - > define_direct_property ( vm . names . constructor , constructor . ptr ( ) , JS : : Attribute : : Writable | JS : : Attribute : : Configurable ) ; \
constructor - > define_direct_property ( vm . names . name , JS : : PrimitiveString : : create ( vm , # ClassName # # _string ) , JS : : Attribute : : Configurable ) ; \
}
namespace Web : : Bindings {
DEFINE_WASM_ERROR_PROTOTYPE_AND_CONSTRUCTOR_WEB_INTRINSIC ( CompileError , compile_error , CompileErrorPrototype , CompileErrorConstructor )
DEFINE_WASM_ERROR_PROTOTYPE_AND_CONSTRUCTOR_WEB_INTRINSIC ( LinkError , link_error , LinkErrorPrototype , LinkErrorConstructor )
DEFINE_WASM_ERROR_PROTOTYPE_AND_CONSTRUCTOR_WEB_INTRINSIC ( RuntimeError , runtime_error , RuntimeErrorPrototype , RuntimeErrorConstructor )
2023-03-16 12:59:32 -04:00
}
2025-05-03 13:22:44 +03:30
# undef DEFINE_WASM_ERROR_PROTOTYPE_AND_CONSTRUCTOR_WEB_INTRINSIC