Skip to content

Commit

Permalink
wasm-merge: check that the types of imports and exports match.
Browse files Browse the repository at this point in the history
  • Loading branch information
vouillon committed Mar 25, 2024
1 parent c853560 commit 60a2fc4
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 0 deletions.
107 changes: 107 additions & 0 deletions src/tools/wasm-merge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,37 @@ void copyModuleContents(Module& input, Name inputName) {
// TODO: type names, features, debug info, custom sections, dylink info, etc.
}

void reportTypeMismatch(bool& valid, const char* kind, Importable* import) {
valid = false;
std::cerr << "Type mismatch when importing " << kind << " " << import->base
<< " from module " << import->module << ": ";
}

// Check that the export and import limits match.
template<typename T>
void checkLimit(bool& valid, const char* kind, T* export_, T* import) {
if (export_->initial < import->initial) {
reportTypeMismatch(valid, kind, import);
std::cerr << "minimal size " << export_->initial
<< " is smaller than expected minimal size " << import->initial
<< ".\n";
}
if (import->hasMax()) {
if (!export_->hasMax()) {
reportTypeMismatch(valid, kind, import);
std::cerr << "expecting a bounded " << kind
<< " but the "
"imported "
<< kind << " is unbounded.\n";
} else if (export_->max > import->max) {
reportTypeMismatch(valid, kind, import);
std::cerr << "maximal size " << export_->max
<< " is larger than expected maximal size " << import->max
<< ".\n";
}
}
}

// Find pairs of matching imports and exports, and make uses of the import refer
// to the exported item (which has been merged into the module).
void fuseImportsAndExports() {
Expand Down Expand Up @@ -428,6 +459,82 @@ void fuseImportsAndExports() {
}
});

// Make sure that the export types match the import types.
bool valid = true;
ModuleUtils::iterImportedFunctions(merged, [&](Function* import) {
auto internalName = kindModuleExportMaps[ExternalKind::Function]
[import->module][import->base];
if (internalName.is()) {
auto* export_ = merged.getFunction(internalName);
if (!HeapType::isSubType(export_->type, import->type)) {
reportTypeMismatch(valid, "function", import);
std::cerr << "type " << export_->type << " is not a subtype of "
<< import->type << ".\n";
}
}
});
ModuleUtils::iterImportedTables(merged, [&](Table* import) {
auto internalName =
kindModuleExportMaps[ExternalKind::Table][import->module][import->base];
if (internalName.is()) {
auto* export_ = merged.getTable(internalName);
checkLimit(valid, "table", export_, import);
if (export_->type != import->type) {
reportTypeMismatch(valid, "table", import);
std::cerr << "export type " << export_->type
<< " is different from import type " << import->type << ".\n";
}
}
});
ModuleUtils::iterImportedMemories(merged, [&](Memory* import) {
auto internalName =
kindModuleExportMaps[ExternalKind::Memory][import->module][import->base];
if (internalName.is()) {
auto* export_ = merged.getMemory(internalName);
if (export_->is64() != import->is64()) {
reportTypeMismatch(valid, "memory", import);
std::cerr << "index type should match.\n";
}
checkLimit(valid, "memory", export_, import);
}
});
ModuleUtils::iterImportedGlobals(merged, [&](Global* import) {
auto internalName =
kindModuleExportMaps[ExternalKind::Global][import->module][import->base];
if (internalName.is()) {
auto* export_ = merged.getGlobal(internalName);
if (export_->mutable_ != import->mutable_) {
reportTypeMismatch(valid, "global", import);
std::cerr << "mutability should match.\n";
}
if (export_->mutable_ && export_->type != import->type) {
reportTypeMismatch(valid, "global", import);
std::cerr << "export type " << export_->type
<< " is different from import type " << import->type << ".\n";
}
if (!export_->mutable_ && !Type::isSubType(export_->type, import->type)) {
reportTypeMismatch(valid, "global", import);
std::cerr << "type " << export_->type << " is not a subtype of "
<< import->type << ".\n";
}
}
});
ModuleUtils::iterImportedTags(merged, [&](Tag* import) {
auto internalName =
kindModuleExportMaps[ExternalKind::Tag][import->module][import->base];
if (internalName.is()) {
auto* export_ = merged.getTag(internalName);
if (HeapType(export_->sig) != HeapType(import->sig)) {
reportTypeMismatch(valid, "tag", import);
std::cerr << "export type " << export_->sig
<< " is different from import type " << import->sig << ".\n";
}
}
});
if (!valid) {
Fatal() << "import/export mismatches";
}

// Update the things we found.
updateNames(merged, kindNameUpdates);
}
Expand Down
83 changes: 83 additions & 0 deletions test/lit/merge/types.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
;; RUN: not wasm-merge %s env -all 2>&1 | filecheck %s

;; Test of exports / imports type matching

;; 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)))).
;; 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)))).
;; CHECK-NEXT: Type mismatch when importing table t1 from module env: minimal size 10 is smaller than expected minimal size 11.
;; CHECK-NEXT: Type mismatch when importing table t1 from module env: maximal size 100 is larger than expected maximal size 99.
;; CHECK-NEXT: Type mismatch when importing table t2 from module env: expecting a bounded table but the imported table is unbounded.
;; CHECK-NEXT: Type mismatch when importing table t3 from module env: export type anyref is different from import type funcref.
;; CHECK-NEXT: Type mismatch when importing memory m1 from module env: minimal size 10 is smaller than expected minimal size 11.
;; CHECK-NEXT: Type mismatch when importing memory m1 from module env: maximal size 100 is larger than expected maximal size 99.
;; CHECK-NEXT: Type mismatch when importing memory m2 from module env: expecting a bounded memory but the imported memory is unbounded.
;; CHECK-NEXT: Type mismatch when importing memory m3 from module env: index type should match.
;; CHECK-NEXT: Type mismatch when importing global g1 from module env: mutability should match.
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: mutability should match.
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: export type eqref is different from import type i31ref.
;; CHECK-NEXT: Type mismatch when importing global g2 from module env: export type eqref is different from import type anyref.
;; CHECK-NEXT: Type mismatch when importing global g1 from module env: type eqref is not a subtype of i31ref.
;; CHECK-NEXT: Type mismatch when importing tag t from module env: export type (func (param eqref)) is different from import type (func (param anyref)).
;; CHECK-NEXT: Type mismatch when importing tag t from module env: export type (func (param eqref)) is different from import type (func (param i31ref)).
;; CHECK-NEXT: Fatal: import/export mismatches

(module
(type $f (sub (func (result anyref))))
(type $g (sub $f (func (result eqref))))

(func (export "f1"))
(func (export "f2") (type $g) (ref.null eq))
(func (export "f3") (type $f) (ref.null eq))

(import "env" "f1" (func))
(import "env" "f2" (func (type $f)))

(import "env" "f1" (func (param (ref eq))))
(import "env" "f3" (func (type $g)))

(table (export "t1") 10 100 funcref)
(table (export "t2") 10 funcref)
(table (export "t3") 10 anyref)

(import "env" "t1" (table 10 funcref))
(import "env" "t1" (table 10 100 funcref))
(import "env" "t2" (table 10 funcref))
(import "env" "t3" (table 10 anyref))

(import "env" "t1" (table 11 funcref))
(import "env" "t1" (table 10 99 funcref))
(import "env" "t2" (table 10 100 funcref))
(import "env" "t3" (table 10 funcref))

(memory (export "m1") 10 100)
(memory (export "m2") 10)
(memory (export "m3") i64 10)

(import "env" "m1" (memory 10))
(import "env" "m1" (memory 10 100))
(import "env" "m2" (memory 10))
(import "env" "m3" (memory i64 10))

(import "env" "m1" (memory 11))
(import "env" "m1" (memory 10 99))
(import "env" "m2" (memory 10 100))
(import "env" "m3" (memory 10))

(global (export "g1") eqref (ref.null eq))
(global (export "g2") (mut eqref) (ref.null eq))

(import "env" "g1" (global anyref))
(import "env" "g2" (global (mut eqref)))

(import "env" "g1" (global (mut eqref)))
(import "env" "g2" (global eqref))
(import "env" "g2" (global (mut i31ref)))
(import "env" "g2" (global (mut anyref)))
(import "env" "g1" (global i31ref))

(tag (export "t") (param eqref))
(import "env" "t" (tag (param eqref)))

(import "env" "t" (tag (param anyref)))
(import "env" "t" (tag (param i31ref)))
)

0 comments on commit 60a2fc4

Please sign in to comment.