Skip to content

Commit 60a2fc4

Browse files
committed
wasm-merge: check that the types of imports and exports match.
1 parent c853560 commit 60a2fc4

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

src/tools/wasm-merge.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,37 @@ void copyModuleContents(Module& input, Name inputName) {
381381
// TODO: type names, features, debug info, custom sections, dylink info, etc.
382382
}
383383

384+
void reportTypeMismatch(bool& valid, const char* kind, Importable* import) {
385+
valid = false;
386+
std::cerr << "Type mismatch when importing " << kind << " " << import->base
387+
<< " from module " << import->module << ": ";
388+
}
389+
390+
// Check that the export and import limits match.
391+
template<typename T>
392+
void checkLimit(bool& valid, const char* kind, T* export_, T* import) {
393+
if (export_->initial < import->initial) {
394+
reportTypeMismatch(valid, kind, import);
395+
std::cerr << "minimal size " << export_->initial
396+
<< " is smaller than expected minimal size " << import->initial
397+
<< ".\n";
398+
}
399+
if (import->hasMax()) {
400+
if (!export_->hasMax()) {
401+
reportTypeMismatch(valid, kind, import);
402+
std::cerr << "expecting a bounded " << kind
403+
<< " but the "
404+
"imported "
405+
<< kind << " is unbounded.\n";
406+
} else if (export_->max > import->max) {
407+
reportTypeMismatch(valid, kind, import);
408+
std::cerr << "maximal size " << export_->max
409+
<< " is larger than expected maximal size " << import->max
410+
<< ".\n";
411+
}
412+
}
413+
}
414+
384415
// Find pairs of matching imports and exports, and make uses of the import refer
385416
// to the exported item (which has been merged into the module).
386417
void fuseImportsAndExports() {
@@ -428,6 +459,82 @@ void fuseImportsAndExports() {
428459
}
429460
});
430461

462+
// Make sure that the export types match the import types.
463+
bool valid = true;
464+
ModuleUtils::iterImportedFunctions(merged, [&](Function* import) {
465+
auto internalName = kindModuleExportMaps[ExternalKind::Function]
466+
[import->module][import->base];
467+
if (internalName.is()) {
468+
auto* export_ = merged.getFunction(internalName);
469+
if (!HeapType::isSubType(export_->type, import->type)) {
470+
reportTypeMismatch(valid, "function", import);
471+
std::cerr << "type " << export_->type << " is not a subtype of "
472+
<< import->type << ".\n";
473+
}
474+
}
475+
});
476+
ModuleUtils::iterImportedTables(merged, [&](Table* import) {
477+
auto internalName =
478+
kindModuleExportMaps[ExternalKind::Table][import->module][import->base];
479+
if (internalName.is()) {
480+
auto* export_ = merged.getTable(internalName);
481+
checkLimit(valid, "table", export_, import);
482+
if (export_->type != import->type) {
483+
reportTypeMismatch(valid, "table", import);
484+
std::cerr << "export type " << export_->type
485+
<< " is different from import type " << import->type << ".\n";
486+
}
487+
}
488+
});
489+
ModuleUtils::iterImportedMemories(merged, [&](Memory* import) {
490+
auto internalName =
491+
kindModuleExportMaps[ExternalKind::Memory][import->module][import->base];
492+
if (internalName.is()) {
493+
auto* export_ = merged.getMemory(internalName);
494+
if (export_->is64() != import->is64()) {
495+
reportTypeMismatch(valid, "memory", import);
496+
std::cerr << "index type should match.\n";
497+
}
498+
checkLimit(valid, "memory", export_, import);
499+
}
500+
});
501+
ModuleUtils::iterImportedGlobals(merged, [&](Global* import) {
502+
auto internalName =
503+
kindModuleExportMaps[ExternalKind::Global][import->module][import->base];
504+
if (internalName.is()) {
505+
auto* export_ = merged.getGlobal(internalName);
506+
if (export_->mutable_ != import->mutable_) {
507+
reportTypeMismatch(valid, "global", import);
508+
std::cerr << "mutability should match.\n";
509+
}
510+
if (export_->mutable_ && export_->type != import->type) {
511+
reportTypeMismatch(valid, "global", import);
512+
std::cerr << "export type " << export_->type
513+
<< " is different from import type " << import->type << ".\n";
514+
}
515+
if (!export_->mutable_ && !Type::isSubType(export_->type, import->type)) {
516+
reportTypeMismatch(valid, "global", import);
517+
std::cerr << "type " << export_->type << " is not a subtype of "
518+
<< import->type << ".\n";
519+
}
520+
}
521+
});
522+
ModuleUtils::iterImportedTags(merged, [&](Tag* import) {
523+
auto internalName =
524+
kindModuleExportMaps[ExternalKind::Tag][import->module][import->base];
525+
if (internalName.is()) {
526+
auto* export_ = merged.getTag(internalName);
527+
if (HeapType(export_->sig) != HeapType(import->sig)) {
528+
reportTypeMismatch(valid, "tag", import);
529+
std::cerr << "export type " << export_->sig
530+
<< " is different from import type " << import->sig << ".\n";
531+
}
532+
}
533+
});
534+
if (!valid) {
535+
Fatal() << "import/export mismatches";
536+
}
537+
431538
// Update the things we found.
432539
updateNames(merged, kindNameUpdates);
433540
}

test/lit/merge/types.wat

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
;; RUN: not wasm-merge %s env -all 2>&1 | filecheck %s
2+
3+
;; Test of exports / imports type matching
4+
5+
;; CHECK: Type mismatch when importing function f1 from module env: type (type $func.0 (func)) is not a subtype of (type $func.0 (func (param (ref eq)))).
6+
;; CHECK-NEXT: Type mismatch when importing function f3 from module env: type (type $func.0 (sub (func (result anyref)))) is not a subtype of (type $func.0 (sub $func.1 (func (result eqref)))).
7+
;; CHECK-NEXT: Type mismatch when importing table t1 from module env: minimal size 10 is smaller than expected minimal size 11.
8+
;; CHECK-NEXT: Type mismatch when importing table t1 from module env: maximal size 100 is larger than expected maximal size 99.
9+
;; CHECK-NEXT: Type mismatch when importing table t2 from module env: expecting a bounded table but the imported table is unbounded.
10+
;; CHECK-NEXT: Type mismatch when importing table t3 from module env: export type anyref is different from import type funcref.
11+
;; CHECK-NEXT: Type mismatch when importing memory m1 from module env: minimal size 10 is smaller than expected minimal size 11.
12+
;; CHECK-NEXT: Type mismatch when importing memory m1 from module env: maximal size 100 is larger than expected maximal size 99.
13+
;; CHECK-NEXT: Type mismatch when importing memory m2 from module env: expecting a bounded memory but the imported memory is unbounded.
14+
;; CHECK-NEXT: Type mismatch when importing memory m3 from module env: index type should match.
15+
;; CHECK-NEXT: Type mismatch when importing global g1 from module env: mutability should match.
16+
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: mutability should match.
17+
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: export type eqref is different from import type i31ref.
18+
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: export type eqref is different from import type anyref.
19+
;; CHECK-NEXT: Type mismatch when importing global g1 from module env: type eqref is not a subtype of i31ref.
20+
;; CHECK-NEXT: Type mismatch when importing tag t from module env: export type (func (param eqref)) is different from import type (func (param anyref)).
21+
;; CHECK-NEXT: Type mismatch when importing tag t from module env: export type (func (param eqref)) is different from import type (func (param i31ref)).
22+
;; CHECK-NEXT: Fatal: import/export mismatches
23+
24+
(module
25+
(type $f (sub (func (result anyref))))
26+
(type $g (sub $f (func (result eqref))))
27+
28+
(func (export "f1"))
29+
(func (export "f2") (type $g) (ref.null eq))
30+
(func (export "f3") (type $f) (ref.null eq))
31+
32+
(import "env" "f1" (func))
33+
(import "env" "f2" (func (type $f)))
34+
35+
(import "env" "f1" (func (param (ref eq))))
36+
(import "env" "f3" (func (type $g)))
37+
38+
(table (export "t1") 10 100 funcref)
39+
(table (export "t2") 10 funcref)
40+
(table (export "t3") 10 anyref)
41+
42+
(import "env" "t1" (table 10 funcref))
43+
(import "env" "t1" (table 10 100 funcref))
44+
(import "env" "t2" (table 10 funcref))
45+
(import "env" "t3" (table 10 anyref))
46+
47+
(import "env" "t1" (table 11 funcref))
48+
(import "env" "t1" (table 10 99 funcref))
49+
(import "env" "t2" (table 10 100 funcref))
50+
(import "env" "t3" (table 10 funcref))
51+
52+
(memory (export "m1") 10 100)
53+
(memory (export "m2") 10)
54+
(memory (export "m3") i64 10)
55+
56+
(import "env" "m1" (memory 10))
57+
(import "env" "m1" (memory 10 100))
58+
(import "env" "m2" (memory 10))
59+
(import "env" "m3" (memory i64 10))
60+
61+
(import "env" "m1" (memory 11))
62+
(import "env" "m1" (memory 10 99))
63+
(import "env" "m2" (memory 10 100))
64+
(import "env" "m3" (memory 10))
65+
66+
(global (export "g1") eqref (ref.null eq))
67+
(global (export "g2") (mut eqref) (ref.null eq))
68+
69+
(import "env" "g1" (global anyref))
70+
(import "env" "g2" (global (mut eqref)))
71+
72+
(import "env" "g1" (global (mut eqref)))
73+
(import "env" "g2" (global eqref))
74+
(import "env" "g2" (global (mut i31ref)))
75+
(import "env" "g2" (global (mut anyref)))
76+
(import "env" "g1" (global i31ref))
77+
78+
(tag (export "t") (param eqref))
79+
(import "env" "t" (tag (param eqref)))
80+
81+
(import "env" "t" (tag (param anyref)))
82+
(import "env" "t" (tag (param i31ref)))
83+
)

0 commit comments

Comments
 (0)