assemblyscript icon indicating copy to clipboard operation
assemblyscript copied to clipboard

`env` module isn't imported, using `moduleImports` and `--importMemory` together

Open a-skua opened this issue 11 months ago • 0 comments

Bug description

moduleImpors(cases where @external or declare are used) together with the --importMemory flag, the glue code does not include the imports of the env module.

e.g. build assembly/index.ts with the following settings in asconfig.json.

{
  "targets": {
    "release": {
      "outFile": "build/release.wasm",
      "textFile": "build/release.wat",
      "sourceMap": true,
      "converge": false,
      "noAssert": true
    }
  },
  "options": {
    "bindings": "raw",
    "runtime": "minimal",
    "importMemory": true
  }
}
@external("app", "add")
declare function add(a: i32, b: i32): i32;

export function run(): void {
  add(1, 2);
}

Wasm expects imports for app.add and env.memory, but in the actual generated build/release.js, only the app module is imported, and the import for the env module is missing.

(module
 (type $0 (func (param i32 i32) (result i32)))
 (type $1 (func))
 (import "env" "memory" (memory $0 0))
 (import "app" "add" (func $assembly/index/add (param i32 i32) (result i32)))
 (table $0 1 1 funcref)
 (elem $0 (i32.const 1))
 (export "run" (func $assembly/index/run))
 (export "memory" (memory $0))
 (func $assembly/index/run
  i32.const 1
  i32.const 2
  call $assembly/index/add
  drop
 )
)
export async function instantiate(module, imports = {}) {
  const __module0 = imports.app;
  const adaptedImports = {
    app: __module0,
  };
  const { exports } = await WebAssembly.instantiate(module, adaptedImports);
  return exports;
}

While changing app.add to env.add can circumvent this issue, the desired outcome is to control the size of the memory from outside Wasm. So, the expectation is for code similar to the following to be generated.

export async function instantiate(module, imports = {}) {
  const __module0 = imports.app;
  const adaptedImports = {
    app: __module0,
+   env: Object.assign(Object.create(globalThis), imports.env || {}),
  };
  const { exports } = await WebAssembly.instantiate(module, adaptedImports);
  return exports;
}

Steps to reproduce

asconfig.json

{
  "targets": {
    "release": {
      "outFile": "build/release.wasm",
      "textFile": "build/release.wat",
      "sourceMap": true,
      "converge": false,
      "noAssert": true
    }
  },
  "options": {
    "bindings": "raw",
    "runtime": "minimal",
    "importMemory": true
  }
}

assembly/index.ts

@external("app", "add")
declare function add(a: i32, b: i32): i32;

export function run(): void {
  add(1, 2);
}

index.js

import { instantiate } from "./build/release.js";
const exports = await instantiate(await WebAssembly.compileStreaming(fetch(new URL("./build/release.wasm", import.meta.url))), {
  app: {
    add(a: number, b: number) {
      return a + b;
    },
  },
  env: {
    memory: new WebAssembly.Memory({
      initial: 1,
      maximum: 1,
    }),
  },
});

exports.run();

[!CAUTION]

error: Uncaught (in promise) TypeError: WebAssembly.instantiate(): Import #1 module="env": module is not an object or function
  const { exports } = await WebAssembly.instantiate(module, adaptedImports);
                                        ^

AssemblyScript version

v0.27.24

a-skua avatar Mar 05 '24 15:03 a-skua