diff --git a/src/core/execute.ts b/src/core/execute.ts index dd176ae..c1cf9e8 100644 --- a/src/core/execute.ts +++ b/src/core/execute.ts @@ -7,6 +7,7 @@ import { AssertResult } from "../assertResult.js"; import { Imports, ImportsArgument } from "../index.js"; import { IAssertResult, InstrumentResult } from "../interface.js"; import { mockInstruFunc, covInstruFunc } from "../utils/import.js"; +import { parseWasmImports, supplyDefaultFunction } from "../utils/index.js"; const readFile = promises.readFile; function nodeExecutor(wasms: string[], outFolder: string, imports: Imports) { @@ -30,6 +31,9 @@ function nodeExecutor(wasms: string[], outFolder: string, imports: Imports) { ...userDefinedImportsObject, } as ASImports; const binary = await readFile(wasm); + const importList = await parseWasmImports(binary); + // supplying default function here, so no more need to define all of them in as-test.js + supplyDefaultFunction(importList, importObject); const ins = await instantiate(binary, importObject); importsArg.module = ins.module; importsArg.instance = ins.instance; diff --git a/src/utils/index.ts b/src/utils/index.ts index 5fd0ca2..1eacad5 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,5 @@ +import { Imports as ASImports } from "@assemblyscript/loader"; + export function json2map(json: Record): Map { const res = new Map(); for (const key in json) { @@ -40,3 +42,37 @@ export function checkGenerics(functionName: string): string | undefined { } return; } + +// list imports of a given wasm binary (buffer) +// importList format should be as follows: +// [ +// { module: 'env', name: 'memory', kind: 'memory' }, +// { module: 'env', name: 'myFunction', kind: 'function' }, +// ... +// ] +export async function parseWasmImports(binary: Buffer) { + const mod = await WebAssembly.compile(binary); + const importList = WebAssembly.Module.imports(mod); + + return importList; +} + +export function supplyDefaultFunction(importList: WebAssembly.ModuleImportDescriptor[], importObject: ASImports) { + for (const imp of importList) { + if (imp.kind === "function") { + const moduleName = imp.module; + const funcName = imp.name; + if (importObject[moduleName]?.[funcName] === undefined) { + if (importObject[moduleName] === undefined) { + importObject[moduleName] = {}; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any + (importObject[moduleName] as any)[funcName] = (...args: any[]): any => { + // notify that a default function has been called + console.log(`Default stub called for ${moduleName}.${funcName}, args:`, args); + return 0; + }; + } + } + } +} diff --git a/tests/ts/test/utils/utils.test.ts b/tests/ts/test/utils/utils.test.ts index b6ca030..db9013e 100644 --- a/tests/ts/test/utils/utils.test.ts +++ b/tests/ts/test/utils/utils.test.ts @@ -1,8 +1,15 @@ import fs from "fs-extra"; import { join } from "node:path"; +import { Imports as ASImports } from "@assemblyscript/loader"; import { fileURLToPath, URL } from "node:url"; import { DebugInfo, CovDebugInfo } from "../../../../src/interface.js"; -import { isIncluded, json2map, checkFunctionName, checkGenerics } from "../../../../src/utils/index.js"; +import { + isIncluded, + json2map, + checkFunctionName, + checkGenerics, + supplyDefaultFunction, +} from "../../../../src/utils/index.js"; const __dirname = fileURLToPath(new URL(".", import.meta.url)); @@ -65,3 +72,34 @@ test("checkGenerics", () => { expect(checkGenerics("func<")).toEqual(undefined); expect(checkGenerics("fun>a")).toEqual(undefined); }); + +describe("supplyDefaultFunction", () => { + test("supplyTest", () => { + const mockImportList: WebAssembly.ModuleImportDescriptor[] = [ + { kind: "function", module: "myenv", name: "processEvent" }, + { kind: "function", module: "externalMath", name: "add" }, + { kind: "function", module: "system", name: "getStatus" }, + { kind: "function", module: "logger", name: "logWarning" }, + { kind: "function", module: "customOps", name: "combineValues" }, + { kind: "global", module: "myenv", name: "globalVar" }, + { kind: "memory", module: "other", name: "memChange" }, + ]; + + const mockImportObject: ASImports = { + myenv: {}, + externalMath: {}, + system: {}, + logger: {}, + customOps: {}, + }; + + supplyDefaultFunction(mockImportList, mockImportObject); + + expect(typeof mockImportObject["myenv"]?.["processEvent"]).toBe("function"); + expect(typeof mockImportObject["system"]?.["getStatus"]).toBe("function"); + expect(typeof mockImportObject["logger"]?.["logWarning"]).toBe("function"); + expect(typeof mockImportObject["customOps"]?.["combineValues"]).toBe("function"); + expect(mockImportObject["myenv"]?.["globalVar"]).toBeUndefined(); + expect(mockImportObject["other"]?.["memChange"]).toBeUndefined(); + }); +});