Compile Motoko to Wasm

Is it possible to compile Motoko code directly to wasm file without conister ?
like if i would compile c/c++ code to wasm and call it from nodejs or other languages ?

Hi, you can explore using the standalone moc compiler:

https://sdk.dfinity.org/docs/language-guide/compiler-ref.html

To run it from the sdk install directory:

$(dfx cache show)/moc

You could also set an alias importing the base library:

alias moc="$(dfx cache show)/moc --package base $(dfx cache show)/base"

Then just pass it the .mo file, eg if you want to see your Debug.print() output you can use:

moc main.mo -r

Or to output a wasm file:

moc main.mo

1 Like

i tried to call function add
i get this error :
(index):1 Uncaught (in promise) TypeError: WebAssembly.instantiate(): Import #0 module=“ic0” error: module is not an object or function

actor {

public func add(val1:Int,val2:Int):async Int{

    return val1 + val2;

};

public func sub(val1:Int,val2:Int):async Int{

    return val1 - val2;

};

};

here is javascript code:

function add(wasm){

const add = wasm.exports.add

console.log(add(2,2))

}

function wfadd(){

fetch("./src/main/main.wasm").then(response=>

    response.arrayBuffer()

).then((bytes)=>

    WebAssembly.instantiate(bytes,importObj)

).then(result=>

    result.instance

).then(add)

}

wfadd()

Hmmm I’ve actually been using Rust or C++ for any in-browser Wasm myself, so I’m not sure of the workflow for Motoko, perhaps @claudio can help here? I know you can target other runtimes via WASI straight from moc.

Edit: The actor { } class isn’t needed for non-canister wasm output, so try removing that.

You can use the -wasi-system-api flag on the compiler to make it emit WASI compatible code, but the only interface that is implemented is logging. That’s good enough to write unit tests and run all kinds of pure synchronous Motoko in wasmtime, but I don’t think the browser engines implement WASI.

Check out the testing setup of the base or the matchers libraries for examples.

2 Likes

How does it work when I want to interpret the program only? The solution from @Ori seems to get me closer to what I want to achieve but does not quite work in the following simple case:

# test.mo
let x = 1;
x;

If I start motoko in the interpreter with ~/.cache/dfinity/versions/0.6.13/moc -i , I can call x exactly in the way above:

Motoko compiler (revision czsashv5-30c3v29x-c1z1bswy-5zfr0s50)
> let x = 1;
let x : Nat = 1
> x;
1 : Nat

However, when it try to call the file with the -r option flag, no result is returned.

I tried both:

~/.cache/dfinity/versions/0.6.13/moc -r test.mo

and

~/.cache/dfinity/versions/0.6.13/moc test.mo -r

Moreover, I don’t quite catch how to import the Debug library. Does this work with the regular dfx setup or do I have to clone the motoko-base repo?

Ideally, I want to write small functions and test them as I write them with Coderunner.

Importing the Debug module is actually the answer:

test.mo

import Debug "mo:base/Debug";

let x = 1;
Debug.print(debug_show(x));

Then in the terminal:

alias moc="$(dfx cache show)/moc --package base $(dfx cache show)/base"
moc test.mo -r

This will output 1 as you’re expecting.

The --package flag in that alias assigns the base library’s install directory to ‘base’, so you can then just import any base library module within your .mo files as above, eg import List "mo:base/List", the same way you do in the regular dfx/canister setup.

There’s more reference on the base library modules here: https://sdk.dfinity.org/docs/base-libraries/stdlib-intro.html or you can just read through the base library files themselves, you’ll find them in the install directory here: cd $(dfx cache show)/base .

1 Like

Thanks, Ori. That works.

One more edge case though is that I had to run dfx upgrade and you guys are quite active :wink:. So the dfx cache show variable in the alias was pointing me to me installed version rather than to upgraded version. It also works with prepending the -r to the script. This is useful for the coderunner config. I find this quite interesting in my motoko journey and will make another icp.news blog article on this.

Thanks.

1 Like

You can run dfx cache install after upgrading to update that.
Older versions of the sdk will still remain in the $(dfx cache show)/.. directory when you upgrade, this lets you run canisters against older versions even after upgrading. In the canister environment you’d be able to change your dfx.json file to do so.

1 Like

Very neat! :broom::face_with_monocle: Thanks.