@@ -82,34 +82,44 @@ static void regFuns(Linker & linker, bool useWasi);
8282
8383struct NixWasmInstancePre
8484{
85- Engine & engine;
86- SourcePath wasmPath ;
87- bool useWasi;
85+ Engine & engine = getEngine() ;
86+ std::string name ;
87+ bool useWasi = false ;
8888 InstancePre instancePre;
8989
90- NixWasmInstancePre (SourcePath _wasmPath)
91- : engine(getEngine())
92- , wasmPath(_wasmPath)
93- , useWasi(false )
94- , instancePre(({
95- // Compile the module
96- auto module = unwrap (Module::compile (engine, string2span (wasmPath.readFile ())));
90+ InstancePre compile (std::span<uint8_t > bytes)
91+ {
92+ // Compile the module
93+ auto module = unwrap (Module::compile (engine, bytes));
94+
95+ // Auto-detect WASI by checking for wasi_snapshot_preview1 imports.
96+ for (const auto & ref : module .imports ())
97+ if (const_cast <std::decay_t <decltype (ref)> &>(ref).module () == " wasi_snapshot_preview1" ) {
98+ useWasi = true ;
99+ break ;
100+ }
97101
98- // Auto-detect WASI by checking for wasi_snapshot_preview1 imports.
99- for (const auto & ref : module .imports ())
100- if (const_cast <std::decay_t <decltype (ref)> &>(ref).module () == " wasi_snapshot_preview1" ) {
101- useWasi = true ;
102- break ;
103- }
102+ // Create linker with appropriate WASI support
103+ Linker linker (engine);
104+ if (useWasi)
105+ unwrap (linker.define_wasi ());
106+ regFuns (linker, useWasi);
104107
105- // Create linker with appropriate WASI support
106- Linker linker (engine);
107- if (useWasi)
108- unwrap (linker.define_wasi ());
109- regFuns (linker, useWasi);
108+ return unwrap (instantiate_pre (linker, module ));
109+ }
110110
111- unwrap (instantiate_pre (linker, module ));
112- }))
111+ NixWasmInstancePre (SourcePath wasmPath)
112+ : name(wasmPath.baseName())
113+ , instancePre(compile(string2span(wasmPath.readFile())))
114+ {
115+ }
116+
117+ NixWasmInstancePre (std::string_view wat)
118+ : name(" <inline wat>" )
119+ , instancePre([&] {
120+ auto wasm = unwrap (wat2wasm (wat));
121+ return compile (std::span<uint8_t >(wasm));
122+ }())
113123 {
114124 }
115125};
@@ -139,7 +149,7 @@ struct NixWasmInstance
139149 , wasmCtx(wasmStore)
140150 , instance(unwrap(pre ->instancePre.instantiate(wasmCtx)))
141151 , memory_(getExport<Memory>(" memory" ))
142- , logPrefix(pre ->wasmPath.baseName() )
152+ , logPrefix(pre ->name )
143153 {
144154 wasmCtx.set_data (this );
145155
@@ -173,10 +183,10 @@ struct NixWasmInstance
173183 {
174184 auto ext = instance.get (wasmCtx, name);
175185 if (!ext)
176- throw Error (" Wasm module '%s' does not export '%s'" , pre ->wasmPath , name);
186+ throw Error (" Wasm module '%s' does not export '%s'" , pre ->name , name);
177187 auto res = std::get_if<T>(&*ext);
178188 if (!res)
179- throw Error (" export '%s' of Wasm module '%s' does not have the right type" , name, pre ->wasmPath );
189+ throw Error (" export '%s' of Wasm module '%s' does not have the right type" , name, pre ->name );
180190 return *res;
181191 }
182192
@@ -593,24 +603,30 @@ static void prim_wasm(EvalState & state, const PosIdx pos, Value ** args, Value
593603{
594604 state.forceAttrs (*args[0 ], pos, " while evaluating the first argument to `builtins.wasm`" );
595605
596- // Extract 'path' attribute
597- auto pathAttr = args[0 ]->attrs ()->get (state.symbols .create (" path" ));
598- if (!pathAttr)
599- throw Error (" missing required 'path' attribute in first argument to `builtins.wasm`" );
600- auto wasmPath = state.realisePath (pos, *pathAttr->value );
601-
602606 // Check for unknown attributes
603607 for (auto & attr : *args[0 ]->attrs ()) {
604608 auto name = state.symbols [attr.name ];
605- if (name != " path" && name != " function" )
609+ if (name != " path" && name != " wat " && name != " function" )
606610 throw Error (" unknown attribute '%s' in first argument to `builtins.wasm`" , name);
607611 }
608612
613+ auto pathAttr = args[0 ]->attrs ()->get (state.symbols .create (" path" ));
614+ auto watAttr = args[0 ]->attrs ()->get (state.symbols .create (" wat" ));
615+
616+ if (pathAttr && watAttr)
617+ throw Error (" 'path' and 'wat' are mutually exclusive in first argument to `builtins.wasm`" );
618+ if (!pathAttr && !watAttr)
619+ throw Error (" missing required 'path' or 'wat' attribute in first argument to `builtins.wasm`" );
620+
609621 // Second argument is the value to pass to the function
610622 auto argValue = args[1 ];
611623
612624 try {
613- auto instance = instantiateWasm (state, wasmPath);
625+ auto instance = pathAttr ? instantiateWasm (state, state.realisePath (pos, *pathAttr->value ))
626+ : NixWasmInstance{
627+ state,
628+ make_ref<NixWasmInstancePre>(state.forceStringNoCtx (
629+ *watAttr->value , pos, " while evaluating the 'wat' attribute" ))};
614630
615631 // Extract 'function' attribute (optional for wasi, required for non-wasi)
616632 std::string functionName;
@@ -649,7 +665,10 @@ static void prim_wasm(EvalState & state, const PosIdx pos, Value ** args, Value
649665 auto res = instance.getExport <Func>(functionName).call (instance.wasmCtx , {});
650666 if (!instance.resultId ) {
651667 unwrap (std::move (res));
652- throw Error (" Wasm function '%s' from '%s' finished without returning a value" , functionName, wasmPath);
668+ throw Error (
669+ " Wasm function '%s' from '%s' finished without returning a value" ,
670+ functionName,
671+ instance.pre ->name );
653672 }
654673
655674 auto & vRes = instance.getValue (instance.resultId );
@@ -661,15 +680,17 @@ static void prim_wasm(EvalState & state, const PosIdx pos, Value ** args, Value
661680
662681 auto res = instance.runFunction (functionName, {(int32_t ) argId});
663682 if (res.size () != 1 )
664- throw Error (" Wasm function '%s' from '%s' did not return exactly one value" , functionName, wasmPath);
683+ throw Error (
684+ " Wasm function '%s' from '%s' did not return exactly one value" , functionName, instance.pre ->name );
665685 if (res[0 ].kind () != ValKind::I32)
666- throw Error (" Wasm function '%s' from '%s' did not return an i32 value" , functionName, wasmPath);
686+ throw Error (
687+ " Wasm function '%s' from '%s' did not return an i32 value" , functionName, instance.pre ->name );
667688 auto & vRes = instance.getValue (res[0 ].i32 ());
668689 state.forceValue (vRes, pos);
669690 v = vRes;
670691 }
671692 } catch (Error & e) {
672- e.addTrace (state.positions [pos], " while executing the Wasm function from '%s' " , wasmPath );
693+ e.addTrace (state.positions [pos], " while executing a Wasm module " );
673694 throw ;
674695 }
675696}
@@ -681,9 +702,12 @@ static RegisterPrimOp primop_wasm(
681702 Call a Wasm function with the specified argument.
682703
683704 The first argument must be an attribute set with the following attributes:
684- - `path`: Path to the Wasm module (required)
705+ - `path`: Path to the Wasm module (mutually exclusive with `wat`)
706+ - `wat`: WebAssembly Text format source as a string (mutually exclusive with `path`)
685707 - `function`: Function name to call (required for non-WASI modules, not allowed for WASI modules)
686708
709+ Exactly one of `path` or `wat` must be specified.
710+
687711 The second argument is the value to pass to the function.
688712
689713 WASI mode is automatically enabled if the module imports from `wasi_snapshot_preview1`.
@@ -696,6 +720,14 @@ static RegisterPrimOp primop_wasm(
696720 } 33
697721 ```
698722
723+ Example (reading from a WAT file):
724+ ```nix
725+ builtins.wasm {
726+ wat = builtins.readFile ./fib.wat;
727+ function = "fib";
728+ } 10
729+ ```
730+
699731 Example (WASI):
700732 ```nix
701733 builtins.wasm {
0 commit comments