Skip to content

Commit 5ecf749

Browse files
committed
finish plugin and tests
1 parent 822c252 commit 5ecf749

10 files changed

+97
-44
lines changed

declarations.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ declare module "@webassemblyjs/ast" {
9494
signature: Signature;
9595
}
9696
export class Signature {
97+
type: "Signature";
9798
params: FuncParam[];
9899
results: string[];
99100
}
@@ -118,7 +119,7 @@ declare module "@webassemblyjs/ast" {
118119
init: Node[]
119120
): ObjectInstruction;
120121
export function signature(params: FuncParam[], results: string[]): Signature;
121-
export function func(initFuncId, Signature, funcBody): Func;
122+
export function func(initFuncId, signature: Signature, funcBody): Func;
122123
export function typeInstruction(
123124
id: Identifier,
124125
functype: Signature
@@ -134,6 +135,10 @@ declare module "@webassemblyjs/ast" {
134135
): ModuleExportDescr;
135136

136137
export function getSectionMetadata(ast: any, section: string);
138+
export class FuncSignature {
139+
args: string[];
140+
result: string[];
141+
}
137142
}
138143

139144
/**

lib/ModuleDependencyError.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class ModuleDependencyError extends WebpackError {
2626
this.module = module;
2727
this.loc = loc;
2828
this.error = err;
29+
this.origin = module.issuer;
2930

3031
Error.captureStackTrace(this, this.constructor);
3132
}

lib/ModuleDependencyWarning.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = class ModuleDependencyWarning extends WebpackError {
1818
this.module = module;
1919
this.loc = loc;
2020
this.error = err;
21+
this.origin = module.issuer;
2122

2223
Error.captureStackTrace(this, this.constructor);
2324
}

lib/dependencies/WebAssemblyImportDependency.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class WebAssemblyImportDependency extends ModuleDependency {
3232
) {
3333
return [
3434
new UnsupportedWebAssemblyFeatureError(
35-
`Import with ${
35+
`Import "${this.name}" from "${this.request}" with ${
3636
this.onlyDirectImport
3737
} can only be used for direct wasm to wasm dependencies`
3838
)

lib/wasm/WasmFinalizeExportsPlugin.js

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,53 @@
66

77
const UnsupportedWebAssemblyFeatureError = require("../wasm/UnsupportedWebAssemblyFeatureError");
88

9-
const error = new UnsupportedWebAssemblyFeatureError(
10-
"JavaScript modules can not use a WebAssembly export with an incompatible type signature"
11-
);
12-
139
class WasmFinalizeExportsPlugin {
1410
apply(compiler) {
1511
compiler.hooks.compilation.tap("WasmFinalizeExportsPlugin", compilation => {
1612
compilation.hooks.finishModules.tap(
1713
"WasmFinalizeExportsPlugin",
1814
modules => {
1915
for (const module of modules) {
20-
const jsIncompatibleExports =
21-
module.buildMeta.jsIncompatibleExports;
22-
23-
if (
24-
typeof jsIncompatibleExports === "undefined" ||
25-
jsIncompatibleExports.length === 0
26-
) {
27-
continue;
28-
}
29-
3016
// 1. if a WebAssembly module
3117
if (module.type.startsWith("webassembly") === true) {
18+
const jsIncompatibleExports =
19+
module.buildMeta.jsIncompatibleExports;
20+
21+
if (jsIncompatibleExports === undefined) {
22+
continue;
23+
}
24+
3225
for (const reason of module.reasons) {
3326
// 2. is referenced by a non-WebAssembly module
3427
if (reason.module.type.startsWith("webassembly") === false) {
35-
// const ref = reason.dependency.getReference();
36-
37-
// ref.importedNames // returns true?
28+
const ref = reason.dependency.getReference();
3829

39-
const names = [];
30+
const importedNames = ref.importedNames;
4031

41-
names.forEach(name => {
42-
// 3. and uses a func with an incompatible JS signature
43-
if (jsIncompatibleExports.indexOf(name) !== -1) {
44-
// 4. error
45-
compilation.errors.push(error);
46-
}
47-
});
32+
if (Array.isArray(importedNames)) {
33+
importedNames.forEach(name => {
34+
// 3. and uses a func with an incompatible JS signature
35+
if (
36+
Object.prototype.hasOwnProperty.call(
37+
jsIncompatibleExports,
38+
name
39+
)
40+
) {
41+
// 4. error
42+
/** @type {any} */
43+
const error = new UnsupportedWebAssemblyFeatureError(
44+
`Export "${name}" with ${
45+
jsIncompatibleExports[name]
46+
} can only be used for direct wasm to wasm dependencies`
47+
);
48+
error.module = module;
49+
error.origin = reason.module;
50+
error.originLoc = reason.dependency.loc;
51+
error.dependencies = [reason.dependency];
52+
compilation.errors.push(error);
53+
}
54+
});
55+
}
4856
}
4957
}
5058
}

lib/wasm/WebAssemblyParser.js

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,38 @@ const isGlobalImport = n => n.descr.type === "GlobalType";
4343
const JS_COMPAT_TYPES = new Set(["i32", "f32", "f64"]);
4444

4545
/**
46-
* @param {t.ModuleImport} moduleImport the import
46+
* @param {t.Signature} signature the func signature
4747
* @returns {null | string} the type incompatible with js types
4848
*/
49-
const getJsIncompatibleType = moduleImport => {
50-
if (moduleImport.descr.type !== "FuncImportDescr") return null;
51-
const signature = moduleImport.descr.signature;
49+
const getJsIncompatibleType = signature => {
5250
for (const param of signature.params) {
53-
if (!JS_COMPAT_TYPES.has(param.valtype))
51+
if (!JS_COMPAT_TYPES.has(param.valtype)) {
5452
return `${param.valtype} as parameter`;
53+
}
5554
}
5655
for (const type of signature.results) {
5756
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
5857
}
5958
return null;
6059
};
6160

61+
/**
62+
* TODO why are there two different Signature types?
63+
* @param {t.FuncSignature} signature the func signature
64+
* @returns {null | string} the type incompatible with js types
65+
*/
66+
const getJsIncompatibleTypeOfFuncSignature = signature => {
67+
for (const param of signature.args) {
68+
if (!JS_COMPAT_TYPES.has(param)) {
69+
return `${param} as parameter`;
70+
}
71+
}
72+
for (const type of signature.result) {
73+
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
74+
}
75+
return null;
76+
};
77+
6278
const decoderOpts = {
6379
ignoreCodeSection: true,
6480
ignoreDataSection: true,
@@ -96,17 +112,15 @@ class WebAssemblyParser extends Tapable {
96112
if (descriptor.exportType === "Func") {
97113
const funcidx = descriptor.id.value;
98114

115+
/** @type {t.FuncSignature} */
99116
const funcSignature = moduleContext.getFunction(funcidx);
100117

101-
const hasIncompatibleArg = funcSignature.args.some(
102-
t => !JS_COMPAT_TYPES.has(t)
103-
);
104-
const hasIncompatibleResult = funcSignature.result.some(
105-
t => !JS_COMPAT_TYPES.has(t)
118+
const incompatibleType = getJsIncompatibleTypeOfFuncSignature(
119+
funcSignature
106120
);
107121

108-
if (hasIncompatibleArg === true || hasIncompatibleResult === true) {
109-
jsIncompatibleExports.push(node.name);
122+
if (incompatibleType) {
123+
jsIncompatibleExports[node.name] = incompatibleType;
110124
}
111125
}
112126

@@ -151,10 +165,15 @@ class WebAssemblyParser extends Tapable {
151165
} else if (isTableImport(node) === true) {
152166
onlyDirectImport = "Table";
153167
} else if (isFuncImport(node) === true) {
154-
const incompatibleType = getJsIncompatibleType(node);
168+
const incompatibleType = getJsIncompatibleType(node.descr.signature);
155169
if (incompatibleType) {
156170
onlyDirectImport = `Non-JS-compatible Func Sigurature (${incompatibleType})`;
157171
}
172+
} else if (isGlobalImport(node) === true) {
173+
const type = node.descr.valtype;
174+
if (!JS_COMPAT_TYPES.has(type)) {
175+
onlyDirectImport = `Non-JS-compatible Global Type (${type})`;
176+
}
158177
}
159178

160179
const dep = new WebAssemblyImportDependency(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = [
2+
[
3+
/export-i64-param\.wat/,
4+
/Export "a" with i64 as parameter can only be used for direct wasm to wasm dependencies/,
5+
/export-i64-param\.js/
6+
],
7+
[
8+
/export-i64-result\.wat/,
9+
/Export "a" with i64 as result can only be used for direct wasm to wasm dependencies/,
10+
/export-i64-result\.js/
11+
],
12+
[
13+
/import-i64\.wat/,
14+
/Import "n" from "\.\/env.js" with Non-JS-compatible Global Type \(i64\) can only be used for direct wasm to wasm dependencies/,
15+
/index\.js/
16+
]
17+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { a } from "./export-i64-param.wat";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { a } from "./export-i64-result.wat";

test/cases/wasm/js-incompatible-type/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
it("should disallow exporting a func signature with result i64", function() {
2-
return import("./export-i64-result.wat").then(({a}) => {
3-
expect(a).toThrow(/incompatible type signature/);
2+
return import("./export-i64-result").then(({a}) => {
3+
expect(() => a()).toThrow(/invalid type/);
44
});
55
});
66

77
it("should disallow exporting a func signature with param i64", function() {
8-
return import("./export-i64-param.wat").then(({a}) => {
9-
expect(a).toThrow(/incompatible type signature/);
8+
return import("./export-i64-param").then(({a}) => {
9+
expect(() => a()).toThrow(/invalid type/);
1010
});
1111
});
1212

0 commit comments

Comments
 (0)