Skip to content

Commit e05ac6d

Browse files
xpiradmeowbmw
andauthored
feat: inject default function when WASM has undefined import function (#9)
Co-authored-by: meowbmw <[email protected]>
1 parent 6ddf859 commit e05ac6d

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

src/core/execute.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AssertResult } from "../assertResult.js";
77
import { Imports, ImportsArgument } from "../index.js";
88
import { IAssertResult, InstrumentResult } from "../interface.js";
99
import { mockInstruFunc, covInstruFunc } from "../utils/import.js";
10+
import { parseWasmImports, supplyDefaultFunction } from "../utils/index.js";
1011
const readFile = promises.readFile;
1112

1213
function nodeExecutor(wasms: string[], outFolder: string, imports: Imports) {
@@ -30,6 +31,9 @@ function nodeExecutor(wasms: string[], outFolder: string, imports: Imports) {
3031
...userDefinedImportsObject,
3132
} as ASImports;
3233
const binary = await readFile(wasm);
34+
const importList = await parseWasmImports(binary);
35+
// supplying default function here, so no more need to define all of them in as-test.js
36+
supplyDefaultFunction(importList, importObject);
3337
const ins = await instantiate(binary, importObject);
3438
importsArg.module = ins.module;
3539
importsArg.instance = ins.instance;

src/utils/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Imports as ASImports } from "@assemblyscript/loader";
2+
13
export function json2map<V>(json: Record<string, V>): Map<string, V> {
24
const res = new Map<string, V>();
35
for (const key in json) {
@@ -40,3 +42,37 @@ export function checkGenerics(functionName: string): string | undefined {
4042
}
4143
return;
4244
}
45+
46+
// list imports of a given wasm binary (buffer)
47+
// importList format should be as follows:
48+
// [
49+
// { module: 'env', name: 'memory', kind: 'memory' },
50+
// { module: 'env', name: 'myFunction', kind: 'function' },
51+
// ...
52+
// ]
53+
export async function parseWasmImports(binary: Buffer) {
54+
const mod = await WebAssembly.compile(binary);
55+
const importList = WebAssembly.Module.imports(mod);
56+
57+
return importList;
58+
}
59+
60+
export function supplyDefaultFunction(importList: WebAssembly.ModuleImportDescriptor[], importObject: ASImports) {
61+
for (const imp of importList) {
62+
if (imp.kind === "function") {
63+
const moduleName = imp.module;
64+
const funcName = imp.name;
65+
if (importObject[moduleName]?.[funcName] === undefined) {
66+
if (importObject[moduleName] === undefined) {
67+
importObject[moduleName] = {};
68+
}
69+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
70+
(importObject[moduleName] as any)[funcName] = (...args: any[]): any => {
71+
// notify that a default function has been called
72+
console.log(`Default stub called for ${moduleName}.${funcName}, args:`, args);
73+
return 0;
74+
};
75+
}
76+
}
77+
}
78+
}

tests/ts/test/utils/utils.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import fs from "fs-extra";
22
import { join } from "node:path";
3+
import { Imports as ASImports } from "@assemblyscript/loader";
34
import { fileURLToPath, URL } from "node:url";
45
import { DebugInfo, CovDebugInfo } from "../../../../src/interface.js";
5-
import { isIncluded, json2map, checkFunctionName, checkGenerics } from "../../../../src/utils/index.js";
6+
import {
7+
isIncluded,
8+
json2map,
9+
checkFunctionName,
10+
checkGenerics,
11+
supplyDefaultFunction,
12+
} from "../../../../src/utils/index.js";
613

714
const __dirname = fileURLToPath(new URL(".", import.meta.url));
815

@@ -65,3 +72,34 @@ test("checkGenerics", () => {
6572
expect(checkGenerics("func<")).toEqual(undefined);
6673
expect(checkGenerics("fun>a")).toEqual(undefined);
6774
});
75+
76+
describe("supplyDefaultFunction", () => {
77+
test("supplyTest", () => {
78+
const mockImportList: WebAssembly.ModuleImportDescriptor[] = [
79+
{ kind: "function", module: "myenv", name: "processEvent" },
80+
{ kind: "function", module: "externalMath", name: "add" },
81+
{ kind: "function", module: "system", name: "getStatus" },
82+
{ kind: "function", module: "logger", name: "logWarning" },
83+
{ kind: "function", module: "customOps", name: "combineValues" },
84+
{ kind: "global", module: "myenv", name: "globalVar" },
85+
{ kind: "memory", module: "other", name: "memChange" },
86+
];
87+
88+
const mockImportObject: ASImports = {
89+
myenv: {},
90+
externalMath: {},
91+
system: {},
92+
logger: {},
93+
customOps: {},
94+
};
95+
96+
supplyDefaultFunction(mockImportList, mockImportObject);
97+
98+
expect(typeof mockImportObject["myenv"]?.["processEvent"]).toBe("function");
99+
expect(typeof mockImportObject["system"]?.["getStatus"]).toBe("function");
100+
expect(typeof mockImportObject["logger"]?.["logWarning"]).toBe("function");
101+
expect(typeof mockImportObject["customOps"]?.["combineValues"]).toBe("function");
102+
expect(mockImportObject["myenv"]?.["globalVar"]).toBeUndefined();
103+
expect(mockImportObject["other"]?.["memChange"]).toBeUndefined();
104+
});
105+
});

0 commit comments

Comments
 (0)