finish plugin and tests
This commit is contained in:
parent
822c252a09
commit
5ecf74917f
|
@ -94,6 +94,7 @@ declare module "@webassemblyjs/ast" {
|
|||
signature: Signature;
|
||||
}
|
||||
export class Signature {
|
||||
type: "Signature";
|
||||
params: FuncParam[];
|
||||
results: string[];
|
||||
}
|
||||
|
@ -118,7 +119,7 @@ declare module "@webassemblyjs/ast" {
|
|||
init: Node[]
|
||||
): ObjectInstruction;
|
||||
export function signature(params: FuncParam[], results: string[]): Signature;
|
||||
export function func(initFuncId, Signature, funcBody): Func;
|
||||
export function func(initFuncId, signature: Signature, funcBody): Func;
|
||||
export function typeInstruction(
|
||||
id: Identifier,
|
||||
functype: Signature
|
||||
|
@ -134,6 +135,10 @@ declare module "@webassemblyjs/ast" {
|
|||
): ModuleExportDescr;
|
||||
|
||||
export function getSectionMetadata(ast: any, section: string);
|
||||
export class FuncSignature {
|
||||
args: string[];
|
||||
result: string[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ class ModuleDependencyError extends WebpackError {
|
|||
this.module = module;
|
||||
this.loc = loc;
|
||||
this.error = err;
|
||||
this.origin = module.issuer;
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ module.exports = class ModuleDependencyWarning extends WebpackError {
|
|||
this.module = module;
|
||||
this.loc = loc;
|
||||
this.error = err;
|
||||
this.origin = module.issuer;
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class WebAssemblyImportDependency extends ModuleDependency {
|
|||
) {
|
||||
return [
|
||||
new UnsupportedWebAssemblyFeatureError(
|
||||
`Import with ${
|
||||
`Import "${this.name}" from "${this.request}" with ${
|
||||
this.onlyDirectImport
|
||||
} can only be used for direct wasm to wasm dependencies`
|
||||
)
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
const UnsupportedWebAssemblyFeatureError = require("../wasm/UnsupportedWebAssemblyFeatureError");
|
||||
|
||||
const error = new UnsupportedWebAssemblyFeatureError(
|
||||
"JavaScript modules can not use a WebAssembly export with an incompatible type signature"
|
||||
);
|
||||
|
||||
class WasmFinalizeExportsPlugin {
|
||||
apply(compiler) {
|
||||
compiler.hooks.compilation.tap("WasmFinalizeExportsPlugin", compilation => {
|
||||
|
@ -17,34 +13,46 @@ class WasmFinalizeExportsPlugin {
|
|||
"WasmFinalizeExportsPlugin",
|
||||
modules => {
|
||||
for (const module of modules) {
|
||||
const jsIncompatibleExports =
|
||||
module.buildMeta.jsIncompatibleExports;
|
||||
|
||||
if (
|
||||
typeof jsIncompatibleExports === "undefined" ||
|
||||
jsIncompatibleExports.length === 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. if a WebAssembly module
|
||||
if (module.type.startsWith("webassembly") === true) {
|
||||
const jsIncompatibleExports =
|
||||
module.buildMeta.jsIncompatibleExports;
|
||||
|
||||
if (jsIncompatibleExports === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const reason of module.reasons) {
|
||||
// 2. is referenced by a non-WebAssembly module
|
||||
if (reason.module.type.startsWith("webassembly") === false) {
|
||||
// const ref = reason.dependency.getReference();
|
||||
const ref = reason.dependency.getReference();
|
||||
|
||||
// ref.importedNames // returns true?
|
||||
const importedNames = ref.importedNames;
|
||||
|
||||
const names = [];
|
||||
|
||||
names.forEach(name => {
|
||||
// 3. and uses a func with an incompatible JS signature
|
||||
if (jsIncompatibleExports.indexOf(name) !== -1) {
|
||||
// 4. error
|
||||
compilation.errors.push(error);
|
||||
}
|
||||
});
|
||||
if (Array.isArray(importedNames)) {
|
||||
importedNames.forEach(name => {
|
||||
// 3. and uses a func with an incompatible JS signature
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
jsIncompatibleExports,
|
||||
name
|
||||
)
|
||||
) {
|
||||
// 4. error
|
||||
/** @type {any} */
|
||||
const error = new UnsupportedWebAssemblyFeatureError(
|
||||
`Export "${name}" with ${
|
||||
jsIncompatibleExports[name]
|
||||
} can only be used for direct wasm to wasm dependencies`
|
||||
);
|
||||
error.module = module;
|
||||
error.origin = reason.module;
|
||||
error.originLoc = reason.dependency.loc;
|
||||
error.dependencies = [reason.dependency];
|
||||
compilation.errors.push(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,14 @@ const isGlobalImport = n => n.descr.type === "GlobalType";
|
|||
const JS_COMPAT_TYPES = new Set(["i32", "f32", "f64"]);
|
||||
|
||||
/**
|
||||
* @param {t.ModuleImport} moduleImport the import
|
||||
* @param {t.Signature} signature the func signature
|
||||
* @returns {null | string} the type incompatible with js types
|
||||
*/
|
||||
const getJsIncompatibleType = moduleImport => {
|
||||
if (moduleImport.descr.type !== "FuncImportDescr") return null;
|
||||
const signature = moduleImport.descr.signature;
|
||||
const getJsIncompatibleType = signature => {
|
||||
for (const param of signature.params) {
|
||||
if (!JS_COMPAT_TYPES.has(param.valtype))
|
||||
if (!JS_COMPAT_TYPES.has(param.valtype)) {
|
||||
return `${param.valtype} as parameter`;
|
||||
}
|
||||
}
|
||||
for (const type of signature.results) {
|
||||
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
|
||||
|
@ -59,6 +58,23 @@ const getJsIncompatibleType = moduleImport => {
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO why are there two different Signature types?
|
||||
* @param {t.FuncSignature} signature the func signature
|
||||
* @returns {null | string} the type incompatible with js types
|
||||
*/
|
||||
const getJsIncompatibleTypeOfFuncSignature = signature => {
|
||||
for (const param of signature.args) {
|
||||
if (!JS_COMPAT_TYPES.has(param)) {
|
||||
return `${param} as parameter`;
|
||||
}
|
||||
}
|
||||
for (const type of signature.result) {
|
||||
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const decoderOpts = {
|
||||
ignoreCodeSection: true,
|
||||
ignoreDataSection: true,
|
||||
|
@ -96,17 +112,15 @@ class WebAssemblyParser extends Tapable {
|
|||
if (descriptor.exportType === "Func") {
|
||||
const funcidx = descriptor.id.value;
|
||||
|
||||
/** @type {t.FuncSignature} */
|
||||
const funcSignature = moduleContext.getFunction(funcidx);
|
||||
|
||||
const hasIncompatibleArg = funcSignature.args.some(
|
||||
t => !JS_COMPAT_TYPES.has(t)
|
||||
);
|
||||
const hasIncompatibleResult = funcSignature.result.some(
|
||||
t => !JS_COMPAT_TYPES.has(t)
|
||||
const incompatibleType = getJsIncompatibleTypeOfFuncSignature(
|
||||
funcSignature
|
||||
);
|
||||
|
||||
if (hasIncompatibleArg === true || hasIncompatibleResult === true) {
|
||||
jsIncompatibleExports.push(node.name);
|
||||
if (incompatibleType) {
|
||||
jsIncompatibleExports[node.name] = incompatibleType;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,10 +165,15 @@ class WebAssemblyParser extends Tapable {
|
|||
} else if (isTableImport(node) === true) {
|
||||
onlyDirectImport = "Table";
|
||||
} else if (isFuncImport(node) === true) {
|
||||
const incompatibleType = getJsIncompatibleType(node);
|
||||
const incompatibleType = getJsIncompatibleType(node.descr.signature);
|
||||
if (incompatibleType) {
|
||||
onlyDirectImport = `Non-JS-compatible Func Sigurature (${incompatibleType})`;
|
||||
}
|
||||
} else if (isGlobalImport(node) === true) {
|
||||
const type = node.descr.valtype;
|
||||
if (!JS_COMPAT_TYPES.has(type)) {
|
||||
onlyDirectImport = `Non-JS-compatible Global Type (${type})`;
|
||||
}
|
||||
}
|
||||
|
||||
const dep = new WebAssemblyImportDependency(
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = [
|
||||
[
|
||||
/export-i64-param\.wat/,
|
||||
/Export "a" with i64 as parameter can only be used for direct wasm to wasm dependencies/,
|
||||
/export-i64-param\.js/
|
||||
],
|
||||
[
|
||||
/export-i64-result\.wat/,
|
||||
/Export "a" with i64 as result can only be used for direct wasm to wasm dependencies/,
|
||||
/export-i64-result\.js/
|
||||
],
|
||||
[
|
||||
/import-i64\.wat/,
|
||||
/Import "n" from "\.\/env.js" with Non-JS-compatible Global Type \(i64\) can only be used for direct wasm to wasm dependencies/,
|
||||
/index\.js/
|
||||
]
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
export { a } from "./export-i64-param.wat";
|
|
@ -0,0 +1 @@
|
|||
export { a } from "./export-i64-result.wat";
|
|
@ -1,12 +1,12 @@
|
|||
it("should disallow exporting a func signature with result i64", function() {
|
||||
return import("./export-i64-result.wat").then(({a}) => {
|
||||
expect(a).toThrow(/incompatible type signature/);
|
||||
return import("./export-i64-result").then(({a}) => {
|
||||
expect(() => a()).toThrow(/invalid type/);
|
||||
});
|
||||
});
|
||||
|
||||
it("should disallow exporting a func signature with param i64", function() {
|
||||
return import("./export-i64-param.wat").then(({a}) => {
|
||||
expect(a).toThrow(/incompatible type signature/);
|
||||
return import("./export-i64-param").then(({a}) => {
|
||||
expect(() => a()).toThrow(/invalid type/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue