Skip to content

Supply default function #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 20, 2025
Merged
4 changes: 4 additions & 0 deletions src/core/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
36 changes: 36 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Imports as ASImports } from "@assemblyscript/loader";

export function json2map<V>(json: Record<string, V>): Map<string, V> {
const res = new Map<string, V>();
for (const key in json) {
Expand Down Expand Up @@ -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;
};
}
}
}
}
40 changes: 39 additions & 1 deletion tests/ts/test/utils/utils.test.ts
Original file line number Diff line number Diff line change
@@ -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));

Expand Down Expand Up @@ -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();
});
});