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

2 Likes

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.