Skip to content

Commit fbd9d0d

Browse files
committed
feat(wasm): add finalizer for checking exports
1 parent d2aca56 commit fbd9d0d

File tree

13 files changed

+145
-17
lines changed

13 files changed

+145
-17
lines changed

declarations.d.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,16 @@ declare module "@webassemblyjs/ast" {
6060
}
6161
export class ModuleExport extends Node {
6262
name: string;
63+
descr: ModuleExportDescr;
64+
}
65+
type Index = Identifier | NumberLiteral;
66+
export class ModuleExportDescr extends Node {
67+
exportType: string;
68+
id: Index;
69+
}
70+
export class NumberLiteral extends Node {
71+
value: number;
6372
}
64-
export class ModuleExportDescr extends Node {}
65-
export class IndexLiteral extends Node {}
66-
export class NumberLiteral extends Node {}
6773
export class FloatLiteral extends Node {}
6874
export class Global extends Node {}
6975
export class FuncParam extends Node {
@@ -81,14 +87,14 @@ declare module "@webassemblyjs/ast" {
8187
}
8288
export class TypeInstruction extends Node {}
8389
export class IndexInFuncSection extends Node {}
84-
export function indexLiteral(index: number): IndexLiteral;
90+
export function indexLiteral(index: number): Index;
8591
export function numberLiteralFromRaw(num: number): NumberLiteral;
8692
export function floatLiteral(value: number, nan?: boolean, inf?: boolean, raw?: string): FloatLiteral;
8793
export function global(globalType: string, nodes: Node[]): Global;
8894
export function identifier(indentifier: string): Identifier;
8995
export function funcParam(valType: string, id: Identifier): FuncParam;
9096
export function instruction(inst: string, args: Node[]): Instruction;
91-
export function callInstruction(funcIndex: IndexLiteral): CallInstruction;
97+
export function callInstruction(funcIndex: Index): CallInstruction;
9298
export function objectInstruction(
9399
kind: string,
94100
type: string,
@@ -97,15 +103,15 @@ declare module "@webassemblyjs/ast" {
97103
export function signature(params: FuncParam[], results: string[]): Signature;
98104
export function func(initFuncId, Signature, funcBody): Func;
99105
export function typeInstruction(id: Identifier, functype: Signature): TypeInstruction;
100-
export function indexInFuncSection(index: IndexLiteral): IndexInFuncSection;
106+
export function indexInFuncSection(index: Index): IndexInFuncSection;
101107
export function moduleExport(
102108
identifier: string,
103109
descr: ModuleExportDescr
104110
): ModuleExport;
105111
export function moduleExportDescr(
106112
type: string,
107-
index: ModuleExportDescr
108-
): ModuleExport;
113+
index: Index
114+
): ModuleExportDescr;
109115

110116
export function getSectionMetadata(ast: any, section: string);
111117
}

lib/WebpackOptionsApply.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const NamedModulesPlugin = require("./NamedModulesPlugin");
5858
const NamedChunksPlugin = require("./NamedChunksPlugin");
5959
const DefinePlugin = require("./DefinePlugin");
6060
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
61+
const WasmFinalizeExportsPlugin = require("./wasm/WasmFinalizeExportsPlugin");
6162

6263
class WebpackOptionsApply extends OptionsApply {
6364
constructor() {
@@ -345,6 +346,11 @@ class WebpackOptionsApply extends OptionsApply {
345346
new SizeLimitsPlugin(options.performance).apply(compiler);
346347
}
347348

349+
// FIXME(sven): this should be conditional
350+
// if (options.optimization.jsIncompatibleExports) {
351+
new WasmFinalizeExportsPlugin().apply(compiler);
352+
// }
353+
348354
new TemplatedPathPlugin().apply(compiler);
349355

350356
new RecordIdsPlugin({
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
7+
const Queue = require("../util/Queue");
8+
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
9+
const UnsupportedWebAssemblyFeatureError = require("../wasm/UnsupportedWebAssemblyFeatureError");
10+
11+
class WasmFinalizeExportsPlugin {
12+
apply(compiler) {
13+
compiler.hooks.compilation.tap("WasmFinalizeExportsPlugin", compilation => {
14+
compilation.hooks.finishModules.tap(
15+
"WasmFinalizeExportsPlugin",
16+
modules => {
17+
const queue = new Queue();
18+
19+
let module;
20+
let jsIncompatibleExports = [];
21+
22+
for (const module of modules) {
23+
if (module.buildMeta.jsIncompatibleExports) {
24+
jsIncompatibleExports.push(
25+
...module.buildMeta.jsIncompatibleExports
26+
);
27+
}
28+
29+
queue.enqueue(module);
30+
}
31+
32+
while (queue.length > 0) {
33+
module = queue.dequeue();
34+
35+
// 1. if a non WebAssembly module
36+
if (module.type.startsWith("webassembly") === false) {
37+
for (const dep of module.dependencies) {
38+
// 2. imports a WebAssembly module
39+
// FIXME(sven): pseudo code from here
40+
if (dep.type === "webassembly") {
41+
// 3. if the used import is flaged as invalid
42+
if (jsIncompatibleExports.indexOf(dep.usedName)) {
43+
throw new UnsupportedWebAssemblyFeatureError(
44+
"JavaScript modules can not use WebAssembly export with an incompatible type signature"
45+
);
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
52+
);
53+
});
54+
}
55+
}
56+
57+
module.exports = WasmFinalizeExportsPlugin;

lib/wasm/WebAssemblyGenerator.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function getCountImportedFunc(ast) {
129129
* Get next type index
130130
*
131131
* @param {Object} ast - Module's AST
132-
* @returns {t.IndexLiteral} - index
132+
* @returns {t.Index} - index
133133
*/
134134
function getNextTypeIndex(ast) {
135135
const typeSectionMetadata = t.getSectionMetadata(ast, "type");
@@ -150,7 +150,7 @@ function getNextTypeIndex(ast) {
150150
*
151151
* @param {Object} ast - Module's AST
152152
* @param {Number} countImportedFunc - number of imported funcs
153-
* @returns {t.IndexLiteral} - index
153+
* @returns {t.Index} - index
154154
*/
155155
function getNextFuncIndex(ast, countImportedFunc) {
156156
const funcSectionMetadata = t.getSectionMetadata(ast, "func");
@@ -272,10 +272,10 @@ const rewriteImports = ({ ast, usedDependencyMap }) => bin => {
272272
* @param {Object} state transformation state
273273
* @param {Object} state.ast - Module's ast
274274
* @param {t.Identifier} state.initFuncId identifier of the init function
275-
* @param {t.IndexLiteral} state.startAtFuncIndex index of the start function
275+
* @param {t.Index} state.startAtFuncIndex index of the start function
276276
* @param {t.ModuleImport[]} state.importedGlobals list of imported globals
277-
* @param {t.IndexLiteral} state.nextFuncIndex index of the next function
278-
* @param {t.IndexLiteral} state.nextTypeIndex index of the next type
277+
* @param {t.Index} state.nextFuncIndex index of the next function
278+
* @param {t.Index} state.nextTypeIndex index of the next type
279279
* @returns {ArrayBufferTransform} transform
280280
*/
281281
const addInitFunction = ({

lib/wasm/WebAssemblyParser.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
const t = require("@webassemblyjs/ast");
88
const { decode } = require("@webassemblyjs/wasm-parser");
9+
const {
10+
moduleContextFromModuleAST
11+
} = require("@webassemblyjs/helper-module-context");
912

1013
const { Tapable } = require("tapable");
1114
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
@@ -45,7 +48,10 @@ const getJsIncompatibleType = moduleImport => {
4548

4649
const decoderOpts = {
4750
ignoreCodeSection: true,
48-
ignoreDataSection: true
51+
ignoreDataSection: true,
52+
53+
// this will avoid having to lookup with identifiers in the ModuleContext
54+
ignoreCustomNameSection: true
4955
};
5056

5157
class WebAssemblyParser extends Tapable {
@@ -60,12 +66,36 @@ class WebAssemblyParser extends Tapable {
6066
state.module.buildMeta.exportsType = "namespace";
6167

6268
// parse it
63-
const ast = decode(binary, decoderOpts);
69+
const program = decode(binary, decoderOpts);
70+
const module = program.body[0];
71+
72+
const moduleContext = moduleContextFromModuleAST(module);
6473

6574
// extract imports and exports
6675
const exports = (state.module.buildMeta.providedExports = []);
67-
t.traverse(ast, {
76+
const jsIncompatibleExports = (state.module.buildMeta.jsIncompatibleExports = []);
77+
78+
t.traverse(module, {
6879
ModuleExport({ node }) {
80+
const descriptor = node.descr;
81+
82+
if (descriptor.exportType === "Func") {
83+
const funcidx = descriptor.id.value;
84+
85+
const funcSignature = moduleContext.getFunction(funcidx);
86+
87+
const hasIncompatibleArg = funcSignature.args.some(
88+
t => !JS_COMPAT_TYPES.has(t)
89+
);
90+
const hasIncompatibleResult = funcSignature.result.some(
91+
t => !JS_COMPAT_TYPES.has(t)
92+
);
93+
94+
if (hasIncompatibleArg === true || hasIncompatibleResult === true) {
95+
jsIncompatibleExports.push(node.name);
96+
}
97+
}
98+
6999
exports.push(node.name);
70100
},
71101

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"license": "MIT",
77
"dependencies": {
88
"@webassemblyjs/ast": "1.5.9",
9+
"@webassemblyjs/helper-module-context": "^1.5.9",
910
"@webassemblyjs/wasm-edit": "1.5.9",
1011
"@webassemblyjs/wasm-opt": "1.5.9",
1112
"@webassemblyjs/wasm-parser": "1.5.9",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const n = 1;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(module
2+
(func (export "a") (param i64) (nop))
3+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(module
2+
(func (export "a") (result i64)
3+
(i64.const 1)
4+
)
5+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(module
2+
(import "./env.js" "n" (global i64))
3+
)

0 commit comments

Comments
 (0)