Skip to content

Commit c066fcd

Browse files
committed
Type imports
Implementation of the Type Imports and Exports proposal, which allows Wasm module to export and import types: (type $File (export "File") (struct ...)) (import "file" "File" (type $File (sub any))) wasm-merge can be used to combine several modules using this proposal, connecting type imports to corresponding type exports. To produce an output compatible with existing tools, an option --strip-type-exports can be use to omit any type export from the output. From an implementation point of view, for imports, a new kind of heap type is used: (import "module" "base" (sub absheaptype)) Including the module and base names in the type definition works well with type canonicalisation. For exports, we separate type exports from other exports, since they don't have an internal name but a heap type: class TypeExport { Name name; HeapType heaptype; // exported type }; This is somewhat error-prone. Typically, to check for repeated exports, we need to check two tables. But the alternatives do not seem much better. If we put all export together, then we have to make sure that we are never trying to access the internal name of a type import.
1 parent 5f767b7 commit c066fcd

39 files changed

+901
-70
lines changed

src/binaryen-c.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4959,6 +4959,37 @@ BinaryenExportRef BinaryenGetExportByIndex(BinaryenModuleRef module,
49594959
return exports[index].get();
49604960
}
49614961

4962+
// Type Exports
4963+
4964+
BinaryenTypeExportRef BinaryenAddTypeExport(BinaryenModuleRef module,
4965+
BinaryenHeapType type,
4966+
const char* externalName) {
4967+
auto* ret = new TypeExport();
4968+
ret->heaptype = HeapType(type);
4969+
ret->name = externalName;
4970+
((Module*)module)->addTypeExport(ret);
4971+
return ret;
4972+
}
4973+
BinaryenTypeExportRef BinaryenGetTypeExport(BinaryenModuleRef module,
4974+
const char* externalName) {
4975+
return ((Module*)module)->getTypeExportOrNull(externalName);
4976+
}
4977+
void BinaryenRemoveTypeExport(BinaryenModuleRef module,
4978+
const char* externalName) {
4979+
((Module*)module)->removeTypeExport(externalName);
4980+
}
4981+
BinaryenIndex BinaryenGetNumTypeExports(BinaryenModuleRef module) {
4982+
return ((Module*)module)->typeExports.size();
4983+
}
4984+
BinaryenTypeExportRef BinaryenGetTypeExportByIndex(BinaryenModuleRef module,
4985+
BinaryenIndex index) {
4986+
const auto& exports = ((Module*)module)->typeExports;
4987+
if (exports.size() <= index) {
4988+
Fatal() << "invalid export index.";
4989+
}
4990+
return exports[index].get();
4991+
}
4992+
49624993
BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
49634994
const char* name,
49644995
BinaryenIndex initial,
@@ -5928,6 +5959,17 @@ const char* BinaryenExportGetValue(BinaryenExportRef export_) {
59285959
return ((Export*)export_)->value.str.data();
59295960
}
59305961

5962+
//
5963+
// =========== Type export operations ===========
5964+
//
5965+
5966+
const char* BinaryenTypeExportGetName(BinaryenTypeExportRef export_) {
5967+
return ((TypeExport*)export_)->name.str.data();
5968+
}
5969+
BinaryenHeapType BinaryenTypeExportGetHeapType(BinaryenTypeExportRef export_) {
5970+
return ((TypeExport*)export_)->heaptype.getID();
5971+
}
5972+
59315973
//
59325974
// ========= Custom sections =========
59335975
//

src/binaryen-c.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,26 @@ BINARYEN_API BinaryenIndex BinaryenGetNumExports(BinaryenModuleRef module);
27252725
BINARYEN_API BinaryenExportRef
27262726
BinaryenGetExportByIndex(BinaryenModuleRef module, BinaryenIndex index);
27272727

2728+
// Type exports
2729+
2730+
BINARYEN_REF(TypeExport);
2731+
2732+
// Adds a type export to the module.
2733+
BINARYEN_API BinaryenTypeExportRef BinaryenAddTypeExport(
2734+
BinaryenModuleRef module, BinaryenHeapType type, const char* externalName);
2735+
// Gets a type export reference by external name. Returns NULL if the export
2736+
// does not exist.
2737+
BINARYEN_API BinaryenTypeExportRef
2738+
BinaryenGetTypeExport(BinaryenModuleRef module, const char* externalName);
2739+
// Removes a type export by external name.
2740+
BINARYEN_API void BinaryenRemoveTypeExport(BinaryenModuleRef module,
2741+
const char* externalName);
2742+
// Gets the number of type exports in the module.
2743+
BINARYEN_API BinaryenIndex BinaryenGetNumTypeExports(BinaryenModuleRef module);
2744+
// Gets the type export at the specified index.
2745+
BINARYEN_API BinaryenTypeExportRef
2746+
BinaryenGetTypeExportByIndex(BinaryenModuleRef module, BinaryenIndex index);
2747+
27282748
// Globals
27292749

27302750
BINARYEN_REF(Global);
@@ -3315,6 +3335,17 @@ BINARYEN_API const char* BinaryenExportGetName(BinaryenExportRef export_);
33153335
// Gets the internal name of the specified export.
33163336
BINARYEN_API const char* BinaryenExportGetValue(BinaryenExportRef export_);
33173337

3338+
//
3339+
// ========== Type Export Operations ==========
3340+
//
3341+
3342+
// Gets the external name of the specified export.
3343+
BINARYEN_API const char*
3344+
BinaryenTypeExportGetName(BinaryenTypeExportRef export_);
3345+
// Gets the internal name of the specified export.
3346+
BINARYEN_API BinaryenHeapType
3347+
BinaryenTypeExportGetHeapType(BinaryenTypeExportRef export_);
3348+
33183349
//
33193350
// ========= Custom sections =========
33203351
//

src/ir/gc-type-utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ inline std::optional<Field> getField(HeapType type, Index index = 0) {
156156
return type.getArray().element;
157157
case HeapTypeKind::Func:
158158
case HeapTypeKind::Cont:
159+
case HeapTypeKind::Import:
159160
case HeapTypeKind::Basic:
160161
break;
161162
}

src/ir/module-utils.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,9 @@ InsertOrderedMap<HeapType, HeapTypeInfo> collectHeapTypeInfo(
489489
for (auto& curr : wasm.elementSegments) {
490490
info.note(curr->type);
491491
}
492+
for (auto& curr : wasm.typeExports) {
493+
info.note(curr->heaptype);
494+
}
492495

493496
// Collect info from functions in parallel.
494497
ModuleUtils::ParallelFunctionAnalysis<TypeInfos, Immutable, InsertOrderedMap>
@@ -655,11 +658,15 @@ void classifyTypeVisibility(Module& wasm,
655658
case ExternalKind::Tag:
656659
notePublic(wasm.getTag(ex->value)->type);
657660
continue;
661+
case ExternalKind::Type:
658662
case ExternalKind::Invalid:
659663
break;
660664
}
661665
WASM_UNREACHABLE("unexpected export kind");
662666
}
667+
for (auto& ex : wasm.typeExports) {
668+
notePublic(ex->heaptype);
669+
}
663670

664671
// Ignorable public types are public.
665672
for (auto type : getIgnorablePublicTypes()) {
@@ -793,6 +800,11 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
793800
}
794801
}
795802

803+
std::vector<bool> isImport(groups.size());
804+
for (size_t i = 0; i < groups.size(); ++i) {
805+
isImport[i] = groups[i][0].isImport();
806+
}
807+
796808
// If we've preserved the input type order on the module, we have to respect
797809
// that first. Use the index of the first type from each group. In principle
798810
// we could try to do something more robust like take the minimum index of all
@@ -812,7 +824,11 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
812824
}
813825
}
814826

815-
auto order = TopologicalSort::minSort(deps, [&](size_t a, size_t b) {
827+
auto order = TopologicalSort::minSort(deps, [&](size_t a, size_t b) -> bool {
828+
// Imports should be first
829+
if (isImport[a] != isImport[b]) {
830+
return isImport[a];
831+
}
816832
auto indexA = groupTypeIndices[a];
817833
auto indexB = groupTypeIndices[b];
818834
// Groups with indices must be sorted before groups without indices to

src/ir/names.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ inline Name getValidName(Name root,
7070
inline Name getValidExportName(Module& module, Name root) {
7171
return getValidName(
7272
root,
73-
[&](Name test) { return !module.getExportOrNull(test); },
74-
module.exports.size());
73+
[&](Name test) {
74+
return !module.getTypeExportOrNull(test) && !module.getExportOrNull(test);
75+
},
76+
module.typeExports.size() + module.exports.size());
7577
}
7678
inline Name getValidGlobalName(Module& module, Name root) {
7779
return getValidName(

src/ir/subtypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct SubTypes {
127127
break;
128128
case HeapTypeKind::Cont:
129129
WASM_UNREACHABLE("TODO: cont");
130+
case HeapTypeKind::Import:
130131
case HeapTypeKind::Basic:
131132
WASM_UNREACHABLE("unexpected kind");
132133
}

src/ir/type-updating.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
152152
}
153153
case HeapTypeKind::Cont:
154154
WASM_UNREACHABLE("TODO: cont");
155+
case HeapTypeKind::Import: {
156+
break;
157+
}
155158
case HeapTypeKind::Basic:
156159
WASM_UNREACHABLE("unexpected kind");
157160
}
@@ -302,6 +305,9 @@ void GlobalTypeRewriter::mapTypes(const TypeMap& oldToNewTypes) {
302305
for (auto& tag : wasm.tags) {
303306
tag->type = updater.getNew(tag->type);
304307
}
308+
for (auto& exp : wasm.typeExports) {
309+
exp->heaptype = updater.getNew(exp->heaptype);
310+
}
305311
}
306312

307313
void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) {

src/parser/context-decls.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Result<> addExports(Lexer& in,
3333
const std::vector<Name>& exports,
3434
ExternalKind kind) {
3535
for (auto name : exports) {
36-
if (wasm.getExportOrNull(name)) {
36+
if (wasm.getTypeExportOrNull(name) || wasm.getExportOrNull(name)) {
3737
// TODO: Fix error location
3838
return in.err("repeated export name");
3939
}

src/parser/contexts.h

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,10 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
935935
std::vector<DefPos> dataDefs;
936936
std::vector<DefPos> tagDefs;
937937

938+
// Type imports: name, positions of type and import names.
939+
std::vector<std::tuple<Name, std::vector<Name>, ImportNames, Index>>
940+
typeImports;
941+
938942
// Positions of export definitions.
939943
std::vector<Index> exportDefs;
940944

@@ -957,6 +961,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
957961

958962
// Used to verify that all imports come before all non-imports.
959963
bool hasNonImport = false;
964+
bool hasTypeDefinition = false;
960965

961966
Result<> checkImport(Index pos, ImportNames* import) {
962967
if (import) {
@@ -978,9 +983,10 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
978983
void setOpen() {}
979984
void setShared() {}
980985
Result<> addSubtype(HeapTypeT) { return Ok{}; }
981-
void finishTypeDef(Name name, Index pos) {
986+
void finishTypeDef(Name name, const std::vector<Name>& exports, Index pos) {
982987
// TODO: type annotations
983988
typeDefs.push_back({name, pos, Index(typeDefs.size()), {}});
989+
hasTypeDefinition = true;
984990
}
985991
size_t getRecGroupStartIndex() { return 0; }
986992
void addRecGroup(Index, size_t) {}
@@ -1092,10 +1098,28 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
10921098
TypeUseT type,
10931099
Index pos);
10941100

1101+
Result<> addTypeImport(Name name,
1102+
const std::vector<Name>& exports,
1103+
ImportNames* import,
1104+
Index pos,
1105+
Index typePos) {
1106+
if (hasTypeDefinition) {
1107+
return in.err(pos, "type import after type definitions");
1108+
}
1109+
typeDefs.push_back({name, pos, Index(typeDefs.size()), {}});
1110+
typeImports.push_back({name, exports, *import, typePos});
1111+
return Ok{};
1112+
}
1113+
10951114
Result<> addExport(Index pos, Ok, Name, ExternalKind) {
10961115
exportDefs.push_back(pos);
10971116
return Ok{};
10981117
}
1118+
1119+
Result<> addTypeExport(Index pos, Ok, Name) {
1120+
exportDefs.push_back(pos);
1121+
return Ok{};
1122+
}
10991123
};
11001124

11011125
// Phase 2: Parse type definitions into a TypeBuilder.
@@ -1108,12 +1132,15 @@ struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {
11081132
// Parse the names of types and fields as we go.
11091133
std::vector<TypeNames> names;
11101134

1135+
// Keep track of type exports
1136+
std::vector<std::vector<Name>> typeExports;
1137+
11111138
// The index of the subtype definition we are parsing.
11121139
Index index = 0;
11131140

11141141
ParseTypeDefsCtx(Lexer& in, TypeBuilder& builder, const IndexMap& typeIndices)
11151142
: TypeParserCtx<ParseTypeDefsCtx>(typeIndices), in(in), builder(builder),
1116-
names(builder.size()) {}
1143+
names(builder.size()), typeExports(builder.size()) {}
11171144

11181145
TypeT makeRefType(HeapTypeT ht, Nullability nullability) {
11191146
return builder.getTempRefType(ht, nullability);
@@ -1154,7 +1181,18 @@ struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {
11541181
return Ok{};
11551182
}
11561183

1157-
void finishTypeDef(Name name, Index pos) { names[index++].name = name; }
1184+
void finishTypeDef(Name name, const std::vector<Name>& exports, Index pos) {
1185+
typeExports[index] = exports;
1186+
names[index++].name = name;
1187+
}
1188+
1189+
Result<> addTypeImport(Name name,
1190+
const std::vector<Name>& exports,
1191+
ImportNames* import,
1192+
Index pos,
1193+
Index typePos) {
1194+
return Ok{};
1195+
}
11581196

11591197
size_t getRecGroupStartIndex() { return index; }
11601198

@@ -1403,6 +1441,14 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
14031441
t->type = use.type;
14041442
return Ok{};
14051443
}
1444+
1445+
Result<> addTypeImport(Name name,
1446+
const std::vector<Name>& exports,
1447+
ImportNames* import,
1448+
Index pos,
1449+
Index typePos) {
1450+
return Ok{};
1451+
}
14061452
};
14071453

14081454
// Phase 5: Parse module element definitions, including instructions.
@@ -1757,14 +1803,30 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
17571803
return Ok{};
17581804
}
17591805

1806+
Result<> addTypeImport(Name,
1807+
const std::vector<Name> exports,
1808+
ImportNames* import,
1809+
Index pos,
1810+
Index typePos) {
1811+
return Ok{};
1812+
}
1813+
17601814
Result<> addExport(Index pos, Name value, Name name, ExternalKind kind) {
1761-
if (wasm.getExportOrNull(name)) {
1815+
if (wasm.getTypeExportOrNull(name) || wasm.getExportOrNull(name)) {
17621816
return in.err(pos, "duplicate export");
17631817
}
17641818
wasm.addExport(builder.makeExport(name, value, kind));
17651819
return Ok{};
17661820
}
17671821

1822+
Result<> addTypeExport(Index pos, HeapType heaptype, Name name) {
1823+
if (wasm.getTypeExportOrNull(name) || wasm.getTypeExportOrNull(name)) {
1824+
return in.err(pos, "duplicate export");
1825+
}
1826+
wasm.addTypeExport(builder.makeTypeExport(name, heaptype));
1827+
return Ok{};
1828+
}
1829+
17681830
Result<Index> addScratchLocal(Index pos, Type type) {
17691831
if (!func) {
17701832
return in.err(pos,

src/parser/parse-2-typedefs.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ Result<> parseTypeDefs(
2626
std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) {
2727
TypeBuilder builder(decls.typeDefs.size());
2828
ParseTypeDefsCtx ctx(input, builder, typeIndices);
29+
for (auto& [name, exports, importNames, pos] : decls.typeImports) {
30+
WithPosition with(ctx, pos);
31+
auto heaptype = typetype(ctx);
32+
CHECK_ERR(heaptype);
33+
builder[ctx.index] = Import(importNames.mod, importNames.nm, *heaptype);
34+
ctx.typeExports[ctx.index] = exports;
35+
ctx.names[ctx.index++].name = name;
36+
}
2937
for (auto& recType : decls.recTypeDefs) {
3038
WithPosition with(ctx, recType.pos);
3139
CHECK_ERR(rectype(ctx));
@@ -49,6 +57,17 @@ Result<> parseTypeDefs(
4957
}
5058
}
5159
}
60+
for (size_t i = 0; i < types.size(); ++i) {
61+
for (Name& name : ctx.typeExports[i]) {
62+
if (decls.wasm.getTypeExportOrNull(name) ||
63+
decls.wasm.getExportOrNull(name)) {
64+
// TODO: Fix error location
65+
return ctx.in.err("repeated export name");
66+
}
67+
decls.wasm.addTypeExport(
68+
Builder(decls.wasm).makeTypeExport(name, types[i]));
69+
}
70+
}
5271
return Ok{};
5372
}
5473

0 commit comments

Comments
 (0)