2021-04-26 12:48:13 +04:30
/*
* Copyright ( c ) 2021 , Ali Mohammad Pur < mpfard @ serenityos . org >
2022-01-08 18:41:38 +01:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2021-04-26 12:48:13 +04:30
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-02-18 06:14:25 +03:30
# include <AK/GenericLexer.h>
# include <AK/Hex.h>
2023-01-25 20:19:05 +01:00
# include <AK/MemoryStream.h>
2023-05-28 12:13:43 +02:00
# include <AK/StackInfo.h>
2021-04-26 12:48:13 +04:30
# include <LibCore/ArgsParser.h>
2025-08-08 12:10:39 -07:00
# include <LibCore/EventLoop.h>
2023-02-09 03:02:46 +01:00
# include <LibCore/File.h>
2022-10-24 04:01:40 +03:30
# include <LibCore/MappedFile.h>
2023-05-20 10:39:14 +02:00
# include <LibFileSystem/FileSystem.h>
2025-08-08 12:10:39 -07:00
# if !defined(AK_OS_WINDOWS)
# include <LibLine / Editor.h>
# endif
2022-01-08 18:41:38 +01:00
# include <LibMain/Main.h>
2021-05-01 01:08:51 +04:30
# include <LibWasm/AbstractMachine/AbstractMachine.h>
2021-06-04 21:54:20 +10:00
# include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
2021-04-27 22:13:01 +04:30
# include <LibWasm/Printer/Printer.h>
2021-04-26 12:48:13 +04:30
# include <LibWasm/Types.h>
2025-08-08 12:10:39 -07:00
# if !defined(AK_OS_WINDOWS)
# include <LibWasm / Wasi.h>
# endif
2025-04-22 09:48:26 +02:00
# include <math.h>
2021-05-21 21:10:44 +04:30
# include <signal.h>
2023-02-10 01:00:18 +01:00
static OwnPtr < Stream > g_stdout { } ;
2023-01-20 00:37:03 +01:00
static OwnPtr < Wasm : : Printer > g_printer { } ;
2023-05-28 12:13:43 +02:00
static StackInfo g_stack_info ;
2025-08-02 20:23:26 +02:00
static Wasm : : BytecodeInterpreter g_interpreter ( g_stack_info ) ;
2021-05-21 21:10:44 +04:30
2024-08-04 08:06:50 -07:00
struct ParsedValue {
Wasm : : Value value ;
Wasm : : ValueType type ;
} ;
2024-02-18 06:14:25 +03:30
static Optional < u128 > convert_to_uint ( StringView string )
{
if ( string . is_empty ( ) )
return { } ;
u128 value = 0 ;
auto const characters = string . characters_without_null_termination ( ) ;
for ( size_t i = 0 ; i < string . length ( ) ; i + + ) {
if ( characters [ i ] < ' 0 ' | | characters [ i ] > ' 9 ' )
return { } ;
value * = 10 ;
value + = u128 { static_cast < u64 > ( characters [ i ] - ' 0 ' ) , 0 } ;
}
return value ;
}
static Optional < u128 > convert_to_uint_from_hex ( StringView string )
{
if ( string . is_empty ( ) )
return { } ;
u128 value = 0 ;
auto const count = string . length ( ) ;
auto const upper_bound = NumericLimits < u128 > : : max ( ) ;
for ( size_t i = 0 ; i < count ; i + + ) {
char digit = string [ i ] ;
if ( value > ( upper_bound > > 4 ) )
return { } ;
auto digit_val = decode_hex_digit ( digit ) ;
if ( digit_val = = 255 )
return { } ;
value = ( value < < 4 ) + digit_val ;
}
return value ;
}
2024-08-04 08:06:50 -07:00
static ErrorOr < ParsedValue > parse_value ( StringView spec )
2024-02-18 06:14:25 +03:30
{
constexpr auto is_sep = [ ] ( char c ) { return is_ascii_space ( c ) | | c = = ' : ' ; } ;
// Scalar: 'T.const[:\s]v' (i32.const 42)
auto parse_scalar = [ ] < typename T > ( StringView text ) - > ErrorOr < Wasm : : Value > {
if constexpr ( IsFloatingPoint < T > ) {
if ( text . trim_whitespace ( ) . equals_ignoring_ascii_case ( " nan " sv ) ) {
if constexpr ( IsSame < T , float > )
return Wasm : : Value { nanf ( " " ) } ;
else
return Wasm : : Value { nan ( " " ) } ;
}
if ( text . trim_whitespace ( ) . equals_ignoring_ascii_case ( " inf " sv ) ) {
if constexpr ( IsSame < T , float > )
return Wasm : : Value { HUGE_VALF } ;
else
return Wasm : : Value { HUGE_VAL } ;
}
}
if ( auto v = text . to_number < T > ( ) ; v . has_value ( ) )
return Wasm : : Value { * v } ;
return Error : : from_string_literal ( " Invalid scalar value " ) ;
} ;
// Vector: 'v128.const[:\s]v' (v128.const 0x01000000020000000300000004000000) or 'v(T.const[:\s]v, ...)' (v(i32.const 1, i32.const 2, i32.const 3, i32.const 4))
auto parse_u128 = [ ] ( StringView text ) - > ErrorOr < Wasm : : Value > {
u128 value ;
if ( text . starts_with ( " 0x " sv ) ) {
if ( auto v = convert_to_uint_from_hex ( text ) ; v . has_value ( ) )
value = * v ;
else
return Error : : from_string_literal ( " Invalid hex v128 value " ) ;
} else {
if ( auto v = convert_to_uint ( text ) ; v . has_value ( ) )
value = * v ;
else
return Error : : from_string_literal ( " Invalid v128 value " ) ;
}
return Wasm : : Value { value } ;
} ;
GenericLexer lexer ( spec ) ;
if ( lexer . consume_specific ( " v128.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
// The rest of the string is the value
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_u128 ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : V128 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " i8.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < i8 > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : I32 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " i16.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < i16 > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : I32 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " i32.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < i32 > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : I32 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " i64.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < i64 > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : I64 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " f32.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < float > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : F32 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " f64.const " sv ) ) {
lexer . ignore_while ( is_sep ) ;
auto text = lexer . consume_all ( ) ;
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = TRY ( parse_scalar . operator ( ) < double > ( text ) ) ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : F64 )
} ;
2024-02-18 06:14:25 +03:30
}
if ( lexer . consume_specific ( " v( " sv ) ) {
2024-08-04 08:06:50 -07:00
Vector < ParsedValue > values ;
2024-02-18 06:14:25 +03:30
for ( ; ; ) {
lexer . ignore_while ( is_sep ) ;
if ( lexer . consume_specific ( " ) " sv ) )
break ;
if ( lexer . is_eof ( ) ) {
warnln ( " Expected ')' to close vector " ) ;
break ;
}
auto value = parse_value ( lexer . consume_until ( is_any_of ( " ,) " sv ) ) ) ;
if ( value . is_error ( ) )
return value . release_error ( ) ;
lexer . consume_specific ( ' , ' ) ;
values . append ( value . release_value ( ) ) ;
}
if ( values . is_empty ( ) )
return Error : : from_string_literal ( " Empty vector " ) ;
2024-08-04 08:06:50 -07:00
auto element_type = values . first ( ) . type ;
2024-02-18 06:14:25 +03:30
for ( auto & value : values ) {
2024-08-04 08:06:50 -07:00
if ( value . type ! = element_type )
2024-02-18 06:14:25 +03:30
return Error : : from_string_literal ( " Mixed types in vector " ) ;
}
unsigned total_size = 0 ;
unsigned width = 0 ;
u128 result = 0 ;
u128 last_value = 0 ;
2024-08-04 08:06:50 -07:00
for ( auto & parsed : values ) {
2024-02-18 06:14:25 +03:30
if ( total_size > = 128 )
return Error : : from_string_literal ( " Vector too large " ) ;
2024-08-04 08:06:50 -07:00
switch ( parsed . type . kind ( ) ) {
case Wasm : : ValueType : : F32 :
case Wasm : : ValueType : : I32 :
width = sizeof ( u32 ) ;
break ;
case Wasm : : ValueType : : F64 :
case Wasm : : ValueType : : I64 :
width = sizeof ( u64 ) ;
break ;
case Wasm : : ValueType : : V128 :
case Wasm : : ValueType : : FunctionReference :
case Wasm : : ValueType : : ExternReference :
VERIFY_NOT_REACHED ( ) ;
}
last_value = parsed . value . value ( ) ;
2024-02-18 06:14:25 +03:30
result | = last_value < < total_size ;
total_size + = width * 8 ;
}
if ( total_size < 128 )
warnln ( " Vector value '{}' is only {} bytes wide, repeating last element " , spec , total_size ) ;
while ( total_size < 128 ) {
// Repeat the last value until we fill the 128 bits
result | = last_value < < total_size ;
total_size + = width * 8 ;
}
2024-08-04 08:06:50 -07:00
return ParsedValue {
. value = Wasm : : Value { result } ,
. type = Wasm : : ValueType ( Wasm : : ValueType : : Kind : : V128 )
} ;
2024-02-18 06:14:25 +03:30
}
return Error : : from_string_literal ( " Invalid value " ) ;
}
2024-08-22 01:13:37 +02:00
static RefPtr < Wasm : : Module > parse ( StringView filename )
2021-05-10 17:26:17 +04:30
{
2022-10-24 04:01:40 +03:30
auto result = Core : : MappedFile : : map ( filename ) ;
2021-05-10 17:26:17 +04:30
if ( result . is_error ( ) ) {
warnln ( " Failed to open {}: {} " , filename , result . error ( ) ) ;
return { } ;
}
2023-09-12 20:21:23 +02:00
auto parse_result = Wasm : : Module : : parse ( * result . value ( ) ) ;
2021-05-10 17:26:17 +04:30
if ( parse_result . is_error ( ) ) {
warnln ( " Something went wrong, either the file is invalid, or there's a bug with LibWasm! " ) ;
2023-12-16 17:49:34 +03:30
warnln ( " The parse error was {} " , Wasm : : parse_error_to_byte_string ( parse_result . error ( ) ) ) ;
2021-05-10 17:26:17 +04:30
return { } ;
}
return parse_result . release_value ( ) ;
}
2021-06-04 03:42:11 +04:30
static void print_link_error ( Wasm : : LinkError const & error )
2021-05-10 17:26:17 +04:30
{
2021-06-04 03:42:11 +04:30
for ( auto const & missing : error . missing_imports )
2021-05-10 17:26:17 +04:30
warnln ( " Missing import '{}' " , missing ) ;
}
2025-07-08 12:14:08 +02:00
ErrorOr < int > ladybird_main ( Main : : Arguments arguments )
2021-04-26 12:48:13 +04:30
{
2022-07-11 20:42:03 +00:00
StringView filename ;
2021-04-27 22:13:01 +04:30
bool print = false ;
2025-08-02 20:30:44 +02:00
bool print_compiled = false ;
2021-05-01 01:08:51 +04:30
bool attempt_instantiate = false ;
2021-06-04 03:33:10 +04:30
bool export_all_imports = false ;
2025-08-08 12:10:39 -07:00
[[maybe_unused]] bool wasi = false ;
2023-12-16 17:49:34 +03:30
ByteString exported_function_to_execute ;
2024-08-04 08:06:50 -07:00
Vector < ParsedValue > values_to_push ;
2023-12-16 17:49:34 +03:30
Vector < ByteString > modules_to_link_in ;
2023-04-05 00:54:07 +03:30
Vector < StringView > args_if_wasi ;
Vector < StringView > wasi_preopened_mappings ;
2021-04-26 12:48:13 +04:30
Core : : ArgsParser parser ;
parser . add_positional_argument ( filename , " File name to parse " , " file " ) ;
2021-04-27 22:13:01 +04:30
parser . add_option ( print , " Print the parsed module " , " print " , ' p ' ) ;
2025-08-02 20:30:44 +02:00
parser . add_option ( print_compiled , " Print the compiled module " , " print-compiled " ) ;
2021-05-01 01:08:51 +04:30
parser . add_option ( attempt_instantiate , " Attempt to instantiate the module " , " instantiate " , ' i ' ) ;
2021-05-01 03:19:01 +04:30
parser . add_option ( exported_function_to_execute , " Attempt to execute the named exported function from the module (implies -i) " , " execute " , ' e ' , " name " ) ;
2024-04-21 08:34:56 +12:00
parser . add_option ( export_all_imports , " Export noop functions corresponding to imports " , " export-noop " ) ;
2025-08-08 12:10:39 -07:00
# if !defined(AK_OS_WINDOWS)
2023-04-05 00:54:07 +03:30
parser . add_option ( wasi , " Enable WASI " , " wasi " , ' w ' ) ;
2025-08-08 12:10:39 -07:00
# endif
2023-04-05 00:54:07 +03:30
parser . add_option ( Core : : ArgsParser : : Option {
. argument_mode = Core : : ArgsParser : : OptionArgumentMode : : Required ,
. help_string = " Directory mappings to expose via WASI " ,
. long_name = " wasi-map-dir " ,
. short_name = 0 ,
. value_name = " path[:path] " ,
. accept_value = [ & ] ( StringView str ) {
if ( ! str . is_empty ( ) ) {
wasi_preopened_mappings . append ( str ) ;
return true ;
}
return false ;
} ,
} ) ;
2021-05-10 17:26:17 +04:30
parser . add_option ( Core : : ArgsParser : : Option {
2022-07-12 22:13:38 +02:00
. argument_mode = Core : : ArgsParser : : OptionArgumentMode : : Required ,
2021-05-10 17:26:17 +04:30
. help_string = " Extra modules to link with, use to resolve imports " ,
. long_name = " link " ,
. short_name = ' l ' ,
. value_name = " file " ,
2023-02-21 15:14:41 +03:30
. accept_value = [ & ] ( StringView str ) {
if ( ! str . is_empty ( ) ) {
modules_to_link_in . append ( str ) ;
2021-05-10 17:26:17 +04:30
return true ;
}
return false ;
} ,
} ) ;
2021-05-01 03:19:01 +04:30
parser . add_option ( Core : : ArgsParser : : Option {
2022-07-12 22:13:38 +02:00
. argument_mode = Core : : ArgsParser : : OptionArgumentMode : : Required ,
2024-02-18 06:14:25 +03:30
. help_string = " Supply arguments to the function (default=0) (T.const:v or v(T.const:v, ...)) " ,
2021-05-01 03:19:01 +04:30
. long_name = " arg " ,
. short_name = 0 ,
2024-02-18 06:14:25 +03:30
. value_name = " value " ,
2023-02-21 15:14:41 +03:30
. accept_value = [ & ] ( StringView str ) - > bool {
2024-02-18 06:14:25 +03:30
auto result = parse_value ( str ) ;
if ( result . is_error ( ) ) {
warnln ( " Failed to parse value: {} " , result . error ( ) ) ;
return false ;
2021-05-01 03:19:01 +04:30
}
2024-02-18 06:14:25 +03:30
values_to_push . append ( result . release_value ( ) ) ;
return true ;
2021-05-01 03:19:01 +04:30
} ,
} ) ;
2023-04-05 00:54:07 +03:30
parser . add_positional_argument ( args_if_wasi , " Arguments to pass to the WASI module " , " args " , Core : : ArgsParser : : Required : : No ) ;
2022-01-08 18:41:38 +01:00
parser . parse ( arguments ) ;
2021-04-26 12:48:13 +04:30
2021-05-01 03:19:01 +04:30
if ( ! exported_function_to_execute . is_empty ( ) )
2021-05-01 01:08:51 +04:30
attempt_instantiate = true ;
2021-05-10 17:26:17 +04:30
auto parse_result = parse ( filename ) ;
2024-08-22 01:13:37 +02:00
if ( parse_result . is_null ( ) )
2021-06-04 03:33:10 +04:30
return 1 ;
2023-02-09 03:02:46 +01:00
g_stdout = TRY ( Core : : File : : standard_output ( ) ) ;
2023-01-20 00:37:03 +01:00
g_printer = TRY ( try_make < Wasm : : Printer > ( * g_stdout ) ) ;
2021-05-01 01:08:51 +04:30
if ( print & & ! attempt_instantiate ) {
2023-01-20 00:37:03 +01:00
Wasm : : Printer printer ( * g_stdout ) ;
2024-08-22 01:13:37 +02:00
printer . print ( * parse_result ) ;
2021-04-27 22:13:01 +04:30
}
2025-08-02 20:30:44 +02:00
if ( attempt_instantiate | | print_compiled ) {
2021-05-01 01:08:51 +04:30
Wasm : : AbstractMachine machine ;
2025-08-08 12:10:39 -07:00
# if !defined(AK_OS_WINDOWS)
2023-04-05 00:54:07 +03:30
Optional < Wasm : : Wasi : : Implementation > wasi_impl ;
if ( wasi ) {
wasi_impl . emplace ( Wasm : : Wasi : : Implementation : : Details {
. provide_arguments = [ & ] {
Vector < String > strings ;
for ( auto & string : args_if_wasi )
strings . append ( String : : from_utf8 ( string ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
return strings ; } ,
. provide_environment = { } ,
. provide_preopened_directories = [ & ] {
Vector < Wasm : : Wasi : : Implementation : : MappedPath > paths ;
for ( auto & string : wasi_preopened_mappings ) {
auto split_index = string . find ( ' : ' ) ;
if ( split_index . has_value ( ) ) {
2024-01-15 16:23:24 +00:00
LexicalPath host_path { FileSystem : : real_path ( string . substring_view ( 0 , * split_index ) ) . release_value_but_fixme_should_propagate_errors ( ) } ;
2023-04-05 00:54:07 +03:30
LexicalPath mapped_path { string . substring_view ( * split_index + 1 ) } ;
paths . append ( { move ( host_path ) , move ( mapped_path ) } ) ;
} else {
2024-01-15 16:23:24 +00:00
LexicalPath host_path { FileSystem : : real_path ( string ) . release_value_but_fixme_should_propagate_errors ( ) } ;
2023-04-05 00:54:07 +03:30
LexicalPath mapped_path { string } ;
paths . append ( { move ( host_path ) , move ( mapped_path ) } ) ;
}
}
return paths ; } ,
} ) ;
}
2025-08-08 12:10:39 -07:00
# endif
2023-04-05 00:54:07 +03:30
2021-05-21 21:10:44 +04:30
Core : : EventLoop main_loop ;
2021-05-10 17:26:17 +04:30
// First, resolve the linked modules
2023-03-06 17:16:25 +01:00
Vector < NonnullOwnPtr < Wasm : : ModuleInstance > > linked_instances ;
2024-08-22 01:13:37 +02:00
Vector < NonnullRefPtr < Wasm : : Module > > linked_modules ;
2021-05-10 17:26:17 +04:30
for ( auto & name : modules_to_link_in ) {
auto parse_result = parse ( name ) ;
2024-08-22 01:13:37 +02:00
if ( parse_result . is_null ( ) ) {
2021-05-10 17:26:17 +04:30
warnln ( " Failed to parse linked module '{}' " , name ) ;
return 1 ;
}
2024-08-22 01:13:37 +02:00
linked_modules . append ( parse_result . release_nonnull ( ) ) ;
2021-05-10 17:26:17 +04:30
Wasm : : Linker linker { linked_modules . last ( ) } ;
for ( auto & instance : linked_instances )
2023-03-06 17:16:25 +01:00
linker . link ( * instance ) ;
2021-05-10 17:26:17 +04:30
auto link_result = linker . finish ( ) ;
if ( link_result . is_error ( ) ) {
warnln ( " Linking imported module '{}' failed " , name ) ;
print_link_error ( link_result . error ( ) ) ;
return 1 ;
}
auto instantiation_result = machine . instantiate ( linked_modules . last ( ) , link_result . release_value ( ) ) ;
if ( instantiation_result . is_error ( ) ) {
warnln ( " Instantiation of imported module '{}' failed: {} " , name , instantiation_result . error ( ) . error ) ;
return 1 ;
}
linked_instances . append ( instantiation_result . release_value ( ) ) ;
}
2024-08-22 01:13:37 +02:00
Wasm : : Linker linker { * parse_result } ;
2021-05-10 17:26:17 +04:30
for ( auto & instance : linked_instances )
2023-03-06 17:16:25 +01:00
linker . link ( * instance ) ;
2021-06-04 03:33:10 +04:30
2025-08-08 12:10:39 -07:00
# if !defined(AK_OS_WINDOWS)
2023-04-05 00:54:07 +03:30
if ( wasi ) {
HashMap < Wasm : : Linker : : Name , Wasm : : ExternValue > wasi_exports ;
for ( auto & entry : linker . unresolved_imports ( ) ) {
if ( entry . module ! = " wasi_snapshot_preview1 " sv )
continue ;
auto function = wasi_impl - > function_by_name ( entry . name ) ;
if ( function . is_error ( ) ) {
dbgln ( " wasi function {} not implemented :( " , entry . name ) ;
continue ;
}
auto address = machine . store ( ) . allocate ( function . release_value ( ) ) ;
wasi_exports . set ( entry , * address ) ;
}
linker . link ( wasi_exports ) ;
}
2025-08-08 12:10:39 -07:00
# endif
2023-04-05 00:54:07 +03:30
2021-06-04 03:33:10 +04:30
if ( export_all_imports ) {
HashMap < Wasm : : Linker : : Name , Wasm : : ExternValue > exports ;
for ( auto & entry : linker . unresolved_imports ( ) ) {
if ( ! entry . type . has < Wasm : : TypeIndex > ( ) )
continue ;
2024-08-22 01:13:37 +02:00
auto type = parse_result - > type_section ( ) . types ( ) [ entry . type . get < Wasm : : TypeIndex > ( ) . value ( ) ] ;
2021-06-04 03:33:10 +04:30
auto address = machine . store ( ) . allocate ( Wasm : : HostFunction (
[ name = entry . name , type = type ] ( auto & , auto & arguments ) - > Wasm : : Result {
StringBuilder argument_builder ;
bool first = true ;
2024-08-04 08:06:50 -07:00
size_t index = 0 ;
2021-06-04 03:33:10 +04:30
for ( auto & argument : arguments ) {
2023-01-25 20:19:05 +01:00
AllocatingMemoryStream stream ;
2024-08-04 08:06:50 -07:00
auto value_type = type . parameters ( ) [ index ] ;
Wasm : : Printer { stream } . print ( argument , value_type ) ;
2021-06-04 03:33:10 +04:30
if ( first )
first = false ;
else
argument_builder . append ( " , " sv ) ;
2023-01-09 12:06:13 +01:00
auto buffer = ByteBuffer : : create_uninitialized ( stream . used_buffer_size ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-03-01 15:27:35 +01:00
stream . read_until_filled ( buffer ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-09-10 23:43:39 +02:00
argument_builder . append ( StringView ( buffer ) . trim_whitespace ( ) ) ;
2024-08-04 08:06:50 -07:00
+ + index ;
2021-06-04 03:33:10 +04:30
}
2023-12-16 17:49:34 +03:30
dbgln ( " [wasm runtime] Stub function {} was called with the following arguments: {} " , name , argument_builder . to_byte_string ( ) ) ;
2021-06-04 03:33:10 +04:30
Vector < Wasm : : Value > result ;
result . ensure_capacity ( type . results ( ) . size ( ) ) ;
2024-08-17 15:40:21 -07:00
for ( auto expect_result : type . results ( ) )
result . append ( Wasm : : Value ( expect_result ) ) ;
2021-06-04 03:33:10 +04:30
return Wasm : : Result { move ( result ) } ;
} ,
2024-07-08 18:09:00 -07:00
type ,
entry . name ) ) ;
2021-06-04 03:33:10 +04:30
exports . set ( entry , * address ) ;
}
linker . link ( exports ) ;
}
2021-05-10 17:26:17 +04:30
auto link_result = linker . finish ( ) ;
if ( link_result . is_error ( ) ) {
warnln ( " Linking main module failed " ) ;
print_link_error ( link_result . error ( ) ) ;
return 1 ;
}
2025-08-02 20:30:44 +02:00
2024-08-22 01:13:37 +02:00
auto result = machine . instantiate ( * parse_result , link_result . release_value ( ) ) ;
2021-05-01 01:08:51 +04:30
if ( result . is_error ( ) ) {
warnln ( " Module instantiation failed: {} " , result . error ( ) . error ) ;
return 1 ;
}
2021-05-10 15:40:49 +04:30
auto module_instance = result . release_value ( ) ;
2021-05-01 01:08:51 +04:30
2025-08-02 20:30:44 +02:00
if ( print_compiled ) {
for ( auto address : module_instance - > functions ( ) ) {
auto function = machine . store ( ) . get ( address ) - > get_pointer < Wasm : : WasmFunction > ( ) ;
if ( ! function )
continue ;
auto & expression = function - > code ( ) . func ( ) . body ( ) ;
if ( expression . compiled_instructions . dispatches . is_empty ( ) )
continue ;
ByteString export_name ;
for ( auto & entry : function - > module ( ) . exports ( ) ) {
if ( entry . value ( ) = = address ) {
export_name = ByteString : : formatted ( " '{}' " , entry . name ( ) ) ;
break ;
}
}
TRY ( g_stdout - > write_until_depleted ( ByteString : : formatted ( " Function #{}{} (stack usage = {}): \n " , address . value ( ) , export_name , expression . stack_usage_hint ( ) ) ) ) ;
Wasm : : Printer printer { * g_stdout , 1 } ;
for ( size_t ip = 0 ; ip < expression . compiled_instructions . dispatches . size ( ) ; + + ip ) {
auto & dispatch = expression . compiled_instructions . dispatches [ ip ] ;
ByteString regs ;
auto first = true ;
ssize_t in_count = 0 ;
bool has_out = false ;
# define M(name, _, ins, outs) \
case Wasm : : Instructions : : name . value ( ) : \
in_count = ins ; \
has_out = outs ! = 0 ; \
break ;
switch ( dispatch . instruction - > opcode ( ) . value ( ) ) {
ENUMERATE_WASM_OPCODES ( M )
}
# undef M
constexpr auto reg_name = [ ] ( Wasm : : Dispatch : : RegisterOrStack reg ) - > ByteString {
if ( reg = = Wasm : : Dispatch : : RegisterOrStack : : Stack )
return " stack " sv ;
return ByteString : : formatted ( " reg{} " , to_underlying ( reg ) ) ;
} ;
if ( in_count > - 1 ) {
for ( ssize_t index = 0 ; index < in_count ; + + index ) {
if ( first )
regs = ByteString : : formatted ( " {} ({} " , regs , reg_name ( dispatch . sources [ index ] ) ) ;
else
regs = ByteString : : formatted ( " {}, {} " , regs , reg_name ( dispatch . sources [ index ] ) ) ;
first = false ;
}
if ( has_out ) {
if ( first )
regs = ByteString : : formatted ( " () -> {} " , reg_name ( dispatch . destination ) ) ;
else
regs = ByteString : : formatted ( " {}) -> {} " , regs , reg_name ( dispatch . destination ) ) ;
} else {
if ( first )
regs = ByteString : : formatted ( " () -x " ) ;
else
regs = ByteString : : formatted ( " {}) -x " , regs ) ;
}
}
if ( ! regs . is_empty ( ) )
regs = ByteString : : formatted ( " {{{:<33} }} " , regs ) ;
TRY ( g_stdout - > write_until_depleted ( ByteString : : formatted ( " [{:>03}] " , ip ) ) ) ;
TRY ( g_stdout - > write_until_depleted ( regs . bytes ( ) ) ) ;
printer . print ( * dispatch . instruction ) ;
}
TRY ( g_stdout - > write_until_depleted ( " \n " sv . bytes ( ) ) ) ;
}
}
2021-06-04 03:42:11 +04:30
auto print_func = [ & ] ( auto const & address ) {
2021-05-01 01:08:51 +04:30
Wasm : : FunctionInstance * fn = machine . store ( ) . get ( address ) ;
2024-04-03 21:44:40 -04:00
g_stdout - > write_until_depleted ( ByteString : : formatted ( " - Function with address {}, ptr = {} \n " , address . value ( ) , fn ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-05-01 01:08:51 +04:30
if ( fn ) {
2024-04-03 21:44:40 -04:00
g_stdout - > write_until_depleted ( ByteString : : formatted ( " wasm function? {} \n " , fn - > has < Wasm : : WasmFunction > ( ) ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-05-01 01:08:51 +04:30
fn - > visit (
2021-06-04 03:42:11 +04:30
[ & ] ( Wasm : : WasmFunction const & func ) {
2023-01-20 00:37:03 +01:00
Wasm : : Printer printer { * g_stdout , 3 } ;
2024-04-03 21:44:40 -04:00
g_stdout - > write_until_depleted ( " type: \n " sv ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-05-01 01:08:51 +04:30
printer . print ( func . type ( ) ) ;
2024-04-03 21:44:40 -04:00
g_stdout - > write_until_depleted ( " code: \n " sv ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-05-01 01:08:51 +04:30
printer . print ( func . code ( ) ) ;
} ,
2021-06-04 03:42:11 +04:30
[ ] ( Wasm : : HostFunction const & ) { } ) ;
2021-05-01 01:08:51 +04:30
}
} ;
if ( print ) {
// Now, let's dump the functions!
2021-05-11 04:44:59 +04:30
for ( auto & address : module_instance - > functions ( ) ) {
2021-05-01 01:08:51 +04:30
print_func ( address ) ;
}
}
2021-05-01 03:19:01 +04:30
if ( ! exported_function_to_execute . is_empty ( ) ) {
2021-05-01 01:08:51 +04:30
Optional < Wasm : : FunctionAddress > run_address ;
Vector < Wasm : : Value > values ;
2021-05-11 04:44:59 +04:30
for ( auto & entry : module_instance - > exports ( ) ) {
2021-05-01 03:19:01 +04:30
if ( entry . name ( ) = = exported_function_to_execute ) {
if ( auto addr = entry . value ( ) . get_pointer < Wasm : : FunctionAddress > ( ) )
run_address = * addr ;
2021-05-01 01:08:51 +04:30
}
}
if ( ! run_address . has_value ( ) ) {
2021-05-01 03:19:01 +04:30
warnln ( " No such exported function, sorry :( " ) ;
return 1 ;
}
auto instance = machine . store ( ) . get ( * run_address ) ;
VERIFY ( instance ) ;
if ( instance - > has < Wasm : : HostFunction > ( ) ) {
warnln ( " Exported function is a host function, cannot run that yet " ) ;
2021-05-01 01:08:51 +04:30
return 1 ;
}
2021-05-01 03:19:01 +04:30
for ( auto & param : instance - > get < Wasm : : WasmFunction > ( ) . type ( ) . parameters ( ) ) {
2024-02-18 06:14:25 +03:30
if ( values_to_push . is_empty ( ) ) {
2024-08-17 15:40:21 -07:00
values . append ( Wasm : : Value ( param ) ) ;
2024-08-04 08:06:50 -07:00
} else if ( param = = values_to_push . last ( ) . type ) {
values . append ( values_to_push . take_last ( ) . value ) ;
2024-02-18 06:14:25 +03:30
} else {
2024-08-04 08:06:50 -07:00
warnln ( " Type mismatch in argument: expected {}, but got {} " , Wasm : : ValueType : : kind_name ( param . kind ( ) ) , Wasm : : ValueType : : kind_name ( values_to_push . last ( ) . type . kind ( ) ) ) ;
2024-02-18 06:14:25 +03:30
return 1 ;
}
2021-05-01 03:19:01 +04:30
}
if ( print ) {
outln ( " Executing " ) ;
print_func ( * run_address ) ;
outln ( ) ;
}
2021-05-01 01:08:51 +04:30
2025-04-22 09:48:26 +02:00
auto result = machine . invoke ( g_interpreter , run_address . value ( ) , move ( values ) ) ;
2021-07-13 00:36:50 +04:30
if ( result . is_trap ( ) ) {
2025-04-22 09:48:26 +02:00
auto trap_reason = result . trap ( ) . format ( ) ;
if ( trap_reason . starts_with ( " exit: " sv ) )
return - trap_reason . substring_view ( 5 ) . to_number < i32 > ( ) . value_or ( - 1 ) ;
warnln ( " Execution trapped: {} " , trap_reason ) ;
2021-07-13 00:36:50 +04:30
} else {
if ( ! result . values ( ) . is_empty ( ) )
warnln ( " Returned: " ) ;
2024-08-04 08:06:50 -07:00
auto result_type = instance - > get < Wasm : : WasmFunction > ( ) . type ( ) . results ( ) ;
size_t index = 0 ;
2021-07-13 00:36:50 +04:30
for ( auto & value : result . values ( ) ) {
2023-03-01 17:24:50 +01:00
g_stdout - > write_until_depleted ( " -> " sv . bytes ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2024-08-04 08:06:50 -07:00
g_printer - > print ( value , result_type [ index ] ) ;
+ + index ;
2021-07-13 00:36:50 +04:30
}
2021-05-01 01:08:51 +04:30
}
}
}
2021-04-26 12:48:13 +04:30
return 0 ;
}