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
*/
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>
# include <LibJS/Runtime/DataView.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/Promise.h>
2023-03-16 14:11:21 -04:00
# include <LibJS/Runtime/TypedArray.h>
2023-03-16 12:59:32 -04:00
# include <LibJS/Runtime/VM.h>
# include <LibWasm/AbstractMachine/Validator.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>
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-10-26 14:09:25 -06:00
static JS : : NonnullGCPtr < WebIDL : : Promise > asynchronously_compile_webassembly_module ( JS : : VM & , ByteBuffer , HTML : : Task : : Source = HTML : : Task : : Source : : Unspecified ) ;
static JS : : NonnullGCPtr < WebIDL : : Promise > instantiate_promise_of_module ( JS : : VM & , JS : : NonnullGCPtr < WebIDL : : Promise > , JS : : GCPtr < JS : : Object > import_object ) ;
static JS : : NonnullGCPtr < WebIDL : : Promise > asynchronously_instantiate_webassembly_module ( JS : : VM & , JS : : NonnullGCPtr < Module > , JS : : GCPtr < JS : : Object > import_object ) ;
2023-03-16 14:11:21 -04:00
namespace Detail {
2024-04-25 19:09:34 +01:00
HashMap < JS : : GCPtr < JS : : Object > , WebAssemblyCache > s_caches ;
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 ( ) ) ;
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 ) ;
}
2023-03-16 12:59:32 -04:00
// https://webassembly.github.io/spec/js-api/#dom-webassembly-validate
2023-11-23 20:07:25 +13:00
bool validate ( JS : : VM & vm , JS : : Handle < 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-10-25 12:38:19 -06:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < WebIDL : : Promise > > compile ( JS : : VM & vm , JS : : Handle < 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
}
// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate
2024-10-26 14:09:25 -06:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < WebIDL : : Promise > > instantiate ( JS : : VM & vm , JS : : Handle < WebIDL : : BufferSource > & bytes , Optional < JS : : Handle < 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.
JS : : GCPtr < JS : : Object > const import_object = import_object_handle . has_value ( ) ? import_object_handle . value ( ) . ptr ( ) : nullptr ;
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-10-25 12:38:19 -06:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < WebIDL : : Promise > > instantiate ( JS : : VM & vm , Module const & module_object , Optional < JS : : Handle < 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.
JS : : NonnullGCPtr < Module > module { const_cast < Module & > ( module_object ) } ;
JS : : GCPtr < JS : : Object > const imports = import_object . has_value ( ) ? import_object . value ( ) . ptr ( ) : nullptr ;
return asynchronously_instantiate_webassembly_module ( vm , module , imports ) ;
2023-03-16 12:59:32 -04:00
}
2023-03-16 14:11:21 -04:00
namespace Detail {
2024-10-26 14:09:25 -06:00
JS : : ThrowCompletionOr < NonnullOwnPtr < Wasm : : ModuleInstance > > instantiate_module ( JS : : VM & vm , Wasm : : Module const & module , JS : : GCPtr < JS : : Object > import_object )
2023-03-16 14:11:21 -04:00
{
Wasm : : Linker linker { module } ;
HashMap < Wasm : : Linker : : Name , Wasm : : ExternValue > resolved_imports ;
2024-04-25 19:09:34 +01:00
auto & cache = get_cache ( * vm . current_realm ( ) ) ;
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 " ) ;
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 ) ;
2023-03-16 14:11:21 -04:00
auto value_or_error = import_object - > get ( import_name . module ) ;
if ( value_or_error . is_error ( ) )
break ;
auto value = value_or_error . release_value ( ) ;
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 ( ) ;
2023-03-16 14:11:21 -04:00
auto import_or_error = object - > get ( import_name . name ) ;
if ( import_or_error . is_error ( ) )
break ;
auto import_ = import_or_error . release_value ( ) ;
TRY ( import_name . type . visit (
[ & ] ( 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 ( ) ] ;
2023-03-16 14:11:21 -04:00
// FIXME: IsCallable()
if ( ! import_ . is_function ( ) )
return { } ;
auto & function = import_ . as_function ( ) ;
2024-07-31 12:34:55 +02:00
cache . add_imported_object ( function ) ;
2023-03-16 14:11:21 -04:00
// FIXME: If this is a function created by create_native_function(),
// just extract its address and resolve to that.
Wasm : : HostFunction host_function {
[ & ] ( auto & , auto & arguments ) - > Wasm : : Result {
JS : : MarkedVector < JS : : Value > argument_values { vm . heap ( ) } ;
2024-08-04 08:06:50 -07:00
size_t index = 0 ;
for ( auto & entry : arguments ) {
argument_values . append ( to_js_value ( vm , entry , type . parameters ( ) [ index ] ) ) ;
+ + index ;
}
2023-03-16 14:11:21 -04:00
2023-11-27 16:45:45 +01:00
auto result = TRY ( JS : : call ( vm , function , JS : : js_undefined ( ) , argument_values . span ( ) ) ) ;
2023-03-16 14:11:21 -04:00
if ( type . results ( ) . is_empty ( ) )
return Wasm : : Result { Vector < Wasm : : Value > { } } ;
if ( type . results ( ) . size ( ) = = 1 )
return Wasm : : Result { Vector < Wasm : : Value > { TRY ( to_webassembly_value ( vm , result , type . results ( ) . first ( ) ) ) } } ;
auto method = TRY ( result . get_method ( vm , vm . names . iterator ) ) ;
if ( method = = JS : : js_undefined ( ) )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotIterable , result . to_string_without_side_effects ( ) ) ;
2023-03-16 14:11:21 -04:00
2023-07-18 15:02:28 -04:00
auto values = TRY ( JS : : iterator_to_list ( vm , TRY ( JS : : get_iterator_from_method ( vm , result , * method ) ) ) ) ;
2023-03-16 14:11:21 -04:00
if ( values . size ( ) ! = type . results ( ) . size ( ) )
2023-12-16 17:49:34 +03:30
return vm . throw_completion < JS : : TypeError > ( ByteString : : formatted ( " Invalid number of return values for multi-value wasm return of {} objects " , type . results ( ) . size ( ) ) ) ;
2023-03-16 14:11:21 -04:00
Vector < Wasm : : Value > wasm_values ;
TRY_OR_THROW_OOM ( vm , wasm_values . try_ensure_capacity ( values . size ( ) ) ) ;
size_t i = 0 ;
for ( auto & value : values )
wasm_values . append ( TRY ( to_webassembly_value ( vm , value , type . results ( ) [ i + + ] ) ) ) ;
return Wasm : : Result { move ( wasm_values ) } ;
} ,
2024-07-08 18:09:00 -07:00
type ,
ByteString : : formatted ( " func{} " , resolved_imports . size ( ) ) ,
2023-03-16 14:11:21 -04:00
} ;
2024-04-25 19:09:34 +01:00
auto address = cache . abstract_machine ( ) . store ( ) . allocate ( move ( host_function ) ) ;
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 ( ) ) ;
resolved_imports . set ( import_name , Wasm : : ExternValue { Wasm : : FunctionAddress { * address } } ) ;
return { } ;
} ,
[ & ] ( Wasm : : GlobalType const & type ) - > JS : : ThrowCompletionOr < void > {
Optional < Wasm : : GlobalAddress > address ;
// https://webassembly.github.io/spec/js-api/#read-the-imports step 5.1
if ( import_ . is_number ( ) | | import_ . is_bigint ( ) ) {
if ( import_ . is_number ( ) & & type . type ( ) . kind ( ) = = Wasm : : ValueType : : I64 ) {
// FIXME: Throw a LinkError instead.
return vm . throw_completion < JS : : TypeError > ( " LinkError: Import resolution attempted to cast a Number to a BigInteger " sv ) ;
}
if ( import_ . is_bigint ( ) & & type . type ( ) . kind ( ) ! = Wasm : : ValueType : : I64 ) {
// FIXME: Throw a LinkError instead.
return vm . throw_completion < JS : : TypeError > ( " LinkError: Import resolution attempted to cast a BigInteger to a Number " sv ) ;
}
auto cast_value = TRY ( to_webassembly_value ( vm , import_ , type . type ( ) ) ) ;
2024-04-25 19:09:34 +01:00
address = cache . abstract_machine ( ) . store ( ) . allocate ( { type . type ( ) , false } , cast_value ) ;
2023-03-16 14:11:21 -04:00
} else {
// FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
// if v implements Global
// let globaladdr be v.[[Global]]
// FIXME: Throw a LinkError instead
return vm . throw_completion < JS : : TypeError > ( " LinkError: Invalid value for global type " sv ) ;
}
resolved_imports . set ( import_name , Wasm : : ExternValue { * address } ) ;
return { } ;
} ,
[ & ] ( Wasm : : MemoryType const & ) - > JS : : ThrowCompletionOr < void > {
if ( ! import_ . is_object ( ) | | ! is < WebAssembly : : Memory > ( import_ . as_object ( ) ) ) {
// FIXME: Throw a LinkError instead
return vm . throw_completion < JS : : TypeError > ( " LinkError: Expected an instance of WebAssembly.Memory for a memory import " sv ) ;
}
auto address = static_cast < WebAssembly : : Memory const & > ( import_ . as_object ( ) ) . address ( ) ;
resolved_imports . set ( import_name , Wasm : : ExternValue { address } ) ;
return { } ;
} ,
[ & ] ( Wasm : : TableType const & ) - > JS : : ThrowCompletionOr < void > {
if ( ! import_ . is_object ( ) | | ! is < WebAssembly : : Table > ( import_ . as_object ( ) ) ) {
// FIXME: Throw a LinkError instead
return vm . throw_completion < JS : : TypeError > ( " LinkError: Expected an instance of WebAssembly.Table for a table import " sv ) ;
}
auto address = static_cast < WebAssembly : : Table const & > ( import_ . as_object ( ) ) . address ( ) ;
resolved_imports . set ( import_name , Wasm : : ExternValue { address } ) ;
return { } ;
} ,
[ & ] ( auto const & ) - > JS : : ThrowCompletionOr < void > {
// FIXME: Implement these.
dbgln ( " Unimplemented import of non-function attempted " ) ;
return vm . throw_completion < JS : : TypeError > ( " LinkError: Not Implemented " sv ) ;
} ) ) ;
}
}
linker . link ( resolved_imports ) ;
auto link_result = linker . finish ( ) ;
if ( link_result . is_error ( ) ) {
// FIXME: Throw a LinkError.
2023-09-06 08:29:01 -04:00
StringBuilder builder ;
builder . append ( " LinkError: Missing " sv ) ;
builder . join ( ' ' , link_result . error ( ) . missing_imports ) ;
return vm . throw_completion < JS : : TypeError > ( 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 ( ) ) {
// FIXME: Throw a LinkError instead.
return vm . throw_completion < JS : : TypeError > ( instance_result . error ( ) . error ) ;
}
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 ( ) ) {
// FIXME: Throw CompileError instead.
2023-12-16 17:49:34 +03:30
return vm . throw_completion < JS : : TypeError > ( 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 ( ) ) {
2023-03-16 14:11:21 -04:00
// FIXME: Throw CompileError instead.
return vm . throw_completion < JS : : TypeError > ( validation_result . error ( ) . error_string ) ;
}
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-05-18 20:01:29 +02:00
JS : : NativeFunction * create_native_function ( JS : : VM & vm , Wasm : : FunctionAddress address , ByteString 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 ;
auto function = JS : : NativeFunction : : create (
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.
if ( result . is_trap ( ) )
return vm . throw_completion < JS : : TypeError > ( TRY_OR_THROW_OOM ( vm , String : : formatted ( " Wasm execution trapped (WIP): {} " , result . trap ( ) . reason ) ) ) ;
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.
auto js_result_values = JS : : MarkedVector < JS : : Value > { realm . heap ( ) } ;
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 ) ) ;
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-08-04 08:06:50 -07:00
return realm . heap ( ) . allocate < JS : : BigInt > ( realm , : : 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 ( ) ;
} ) ;
return create_native_function ( vm , address , name ) ;
}
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
JS : : NonnullGCPtr < WebIDL : : Promise > asynchronously_compile_webassembly_module ( JS : : VM & vm , ByteBuffer bytes , HTML : : Task : : Source task_source )
{
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:
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( [ & vm , bytes = move ( bytes ) , promise , task_source ] ( ) mutable {
HTML : : TemporaryExecutionContext context ( HTML : : relevant_settings_object ( * promise - > promise ( ) ) , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
// 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.
HTML : : queue_a_task ( task_source , nullptr , nullptr , JS : : create_heap_function ( vm . heap ( ) , [ & vm , promise , module_or_error = move ( module_or_error ) ] ( ) mutable {
HTML : : TemporaryExecutionContext context ( HTML : : relevant_settings_object ( * promise - > promise ( ) ) , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
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
auto module_object = vm . heap ( ) . allocate < Module > ( realm , realm , module_or_error . release_value ( ) ) ;
// 2. Resolve promise with moduleObject.
WebIDL : : resolve_promise ( * vm . current_realm ( ) , promise , module_object ) ;
}
} ) ) ;
} ) ;
// 3. Return promise.
return promise ;
}
// https://webassembly.github.io/spec/js-api/#asynchronously-instantiate-a-webassembly-module
JS : : NonnullGCPtr < WebIDL : : Promise > asynchronously_instantiate_webassembly_module ( JS : : VM & vm , JS : : NonnullGCPtr < Module > module_object , JS : : GCPtr < JS : : Object > import_object )
{
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.
HTML : : queue_a_task ( HTML : : Task : : Source : : Unspecified , nullptr , nullptr , JS : : create_heap_function ( vm . heap ( ) , [ & vm , promise , module , import_object ] ( ) {
HTML : : TemporaryExecutionContext context ( HTML : : relevant_settings_object ( * promise - > promise ( ) ) , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
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"
auto instance_object = vm . heap ( ) . allocate < Instance > ( realm , realm , move ( instance ) ) ;
// 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
JS : : NonnullGCPtr < WebIDL : : Promise > instantiate_promise_of_module ( JS : : VM & vm , JS : : NonnullGCPtr < WebIDL : : Promise > promise_of_module , JS : : GCPtr < JS : : Object > import_object )
{
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:
auto fulfillment_steps = JS : : create_heap_function ( vm . heap ( ) , [ & vm , promise , import_object ] ( JS : : Value module_value ) - > WebIDL : : ExceptionOr < JS : : Value > {
VERIFY ( module_value . is_object ( ) & & is < Module > ( module_value . as_object ( ) ) ) ;
auto module = JS : : NonnullGCPtr { static_cast < Module & > ( module_value . as_object ( ) ) } ;
// 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.
auto instantiate_fulfillment_steps = JS : : create_heap_function ( vm . heap ( ) , [ promise , module ] ( JS : : Value instance_value ) - > WebIDL : : ExceptionOr < JS : : Value > {
auto & realm = HTML : : relevant_realm ( * promise - > promise ( ) ) ;
VERIFY ( instance_value . is_object ( ) & & is < Instance > ( instance_value . as_object ( ) ) ) ;
auto instance = JS : : NonnullGCPtr { static_cast < Instance & > ( instance_value . as_object ( ) ) } ;
// 1. Let result be the WebAssemblyInstantiatedSource value «[ "module" → module, "instance" → instance ]».
auto result = JS : : Object : : create ( realm , nullptr ) ;
result - > define_direct_property ( " module " , module , JS : : default_attributes ) ;
result - > define_direct_property ( " instance " , instance , JS : : default_attributes ) ;
// 2. Resolve promise with result.
WebIDL : : resolve_promise ( realm , promise , result ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 3. Upon rejection of innerPromise with reason reason.
auto instantiate_rejection_steps = JS : : create_heap_function ( vm . heap ( ) , [ promise ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
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:
auto rejection_steps = JS : : create_heap_function ( vm . heap ( ) , [ promise ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
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 ;
}
2023-03-16 12:59:32 -04:00
}