Skip to content

Implement type imports and exports #7330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
5 changes: 4 additions & 1 deletion scripts/test/fuzzing.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@
'stack_switching_suspend.wast',
'stack_switching_resume.wast',
'stack_switching_resume_throw.wast',
'stack_switching_switch.wast'
'stack_switching_switch.wast',
# TODO: fuzzer support for type imports
'type-imports.wast',
'type-imports.wat'
]


Expand Down
42 changes: 42 additions & 0 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4959,6 +4959,37 @@ BinaryenExportRef BinaryenGetExportByIndex(BinaryenModuleRef module,
return exports[index].get();
}

// Type Exports

BinaryenTypeExportRef BinaryenAddTypeExport(BinaryenModuleRef module,
BinaryenHeapType type,
const char* externalName) {
auto* ret = new TypeExport();
ret->heaptype = HeapType(type);
ret->name = externalName;
((Module*)module)->addTypeExport(ret);
return ret;
}
BinaryenTypeExportRef BinaryenGetTypeExport(BinaryenModuleRef module,
const char* externalName) {
return ((Module*)module)->getTypeExportOrNull(externalName);
}
void BinaryenRemoveTypeExport(BinaryenModuleRef module,
const char* externalName) {
((Module*)module)->removeTypeExport(externalName);
}
BinaryenIndex BinaryenGetNumTypeExports(BinaryenModuleRef module) {
return ((Module*)module)->typeExports.size();
}
BinaryenTypeExportRef BinaryenGetTypeExportByIndex(BinaryenModuleRef module,
BinaryenIndex index) {
const auto& exports = ((Module*)module)->typeExports;
if (exports.size() <= index) {
Fatal() << "invalid export index.";
}
return exports[index].get();
}

BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
const char* name,
BinaryenIndex initial,
Expand Down Expand Up @@ -5928,6 +5959,17 @@ const char* BinaryenExportGetValue(BinaryenExportRef export_) {
return ((Export*)export_)->value.str.data();
}

//
// =========== Type export operations ===========
//

const char* BinaryenTypeExportGetName(BinaryenTypeExportRef export_) {
return ((TypeExport*)export_)->name.str.data();
}
BinaryenHeapType BinaryenTypeExportGetHeapType(BinaryenTypeExportRef export_) {
return ((TypeExport*)export_)->heaptype.getID();
}

//
// ========= Custom sections =========
//
Expand Down
31 changes: 31 additions & 0 deletions src/binaryen-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -2725,6 +2725,26 @@ BINARYEN_API BinaryenIndex BinaryenGetNumExports(BinaryenModuleRef module);
BINARYEN_API BinaryenExportRef
BinaryenGetExportByIndex(BinaryenModuleRef module, BinaryenIndex index);

// Type exports

BINARYEN_REF(TypeExport);

// Adds a type export to the module.
BINARYEN_API BinaryenTypeExportRef BinaryenAddTypeExport(
BinaryenModuleRef module, BinaryenHeapType type, const char* externalName);
// Gets a type export reference by external name. Returns NULL if the export
// does not exist.
BINARYEN_API BinaryenTypeExportRef
BinaryenGetTypeExport(BinaryenModuleRef module, const char* externalName);
// Removes a type export by external name.
BINARYEN_API void BinaryenRemoveTypeExport(BinaryenModuleRef module,
const char* externalName);
// Gets the number of type exports in the module.
BINARYEN_API BinaryenIndex BinaryenGetNumTypeExports(BinaryenModuleRef module);
// Gets the type export at the specified index.
BINARYEN_API BinaryenTypeExportRef
BinaryenGetTypeExportByIndex(BinaryenModuleRef module, BinaryenIndex index);

// Globals

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

//
// ========== Type Export Operations ==========
//

// Gets the external name of the specified export.
BINARYEN_API const char*
BinaryenTypeExportGetName(BinaryenTypeExportRef export_);
// Gets the internal name of the specified export.
BINARYEN_API BinaryenHeapType
BinaryenTypeExportGetHeapType(BinaryenTypeExportRef export_);

//
// ========= Custom sections =========
//
Expand Down
1 change: 1 addition & 0 deletions src/ir/gc-type-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ inline std::optional<Field> getField(HeapType type, Index index = 0) {
return type.getArray().element;
case HeapTypeKind::Func:
case HeapTypeKind::Cont:
case HeapTypeKind::Import:
case HeapTypeKind::Basic:
break;
}
Expand Down
18 changes: 17 additions & 1 deletion src/ir/module-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,9 @@ InsertOrderedMap<HeapType, HeapTypeInfo> collectHeapTypeInfo(
for (auto& curr : wasm.elementSegments) {
info.note(curr->type);
}
for (auto& curr : wasm.typeExports) {
info.note(curr->heaptype);
}

// Collect info from functions in parallel.
ModuleUtils::ParallelFunctionAnalysis<TypeInfos, Immutable, InsertOrderedMap>
Expand Down Expand Up @@ -655,11 +658,15 @@ void classifyTypeVisibility(Module& wasm,
case ExternalKind::Tag:
notePublic(wasm.getTag(ex->value)->type);
continue;
case ExternalKind::Type:
case ExternalKind::Invalid:
break;
}
WASM_UNREACHABLE("unexpected export kind");
}
for (auto& ex : wasm.typeExports) {
notePublic(ex->heaptype);
}

// Ignorable public types are public.
for (auto type : getIgnorablePublicTypes()) {
Expand Down Expand Up @@ -793,6 +800,11 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
}
}

std::vector<bool> isImport(groups.size());
for (size_t i = 0; i < groups.size(); ++i) {
isImport[i] = groups[i][0].isImport();
}

// If we've preserved the input type order on the module, we have to respect
// that first. Use the index of the first type from each group. In principle
// we could try to do something more robust like take the minimum index of all
Expand All @@ -812,7 +824,11 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
}
}

auto order = TopologicalSort::minSort(deps, [&](size_t a, size_t b) {
auto order = TopologicalSort::minSort(deps, [&](size_t a, size_t b) -> bool {
// Imports should be first
if (isImport[a] != isImport[b]) {
return isImport[a];
}
auto indexA = groupTypeIndices[a];
auto indexB = groupTypeIndices[b];
// Groups with indices must be sorted before groups without indices to
Expand Down
6 changes: 4 additions & 2 deletions src/ir/names.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ inline Name getValidName(Name root,
inline Name getValidExportName(Module& module, Name root) {
return getValidName(
root,
[&](Name test) { return !module.getExportOrNull(test); },
module.exports.size());
[&](Name test) {
return !module.getTypeExportOrNull(test) && !module.getExportOrNull(test);
},
module.typeExports.size() + module.exports.size());
}
inline Name getValidGlobalName(Module& module, Name root) {
return getValidName(
Expand Down
1 change: 1 addition & 0 deletions src/ir/subtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ struct SubTypes {
break;
case HeapTypeKind::Cont:
WASM_UNREACHABLE("TODO: cont");
case HeapTypeKind::Import:
case HeapTypeKind::Basic:
WASM_UNREACHABLE("unexpected kind");
}
Expand Down
6 changes: 6 additions & 0 deletions src/ir/type-updating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
}
case HeapTypeKind::Cont:
WASM_UNREACHABLE("TODO: cont");
case HeapTypeKind::Import: {
break;
}
case HeapTypeKind::Basic:
WASM_UNREACHABLE("unexpected kind");
}
Expand Down Expand Up @@ -302,6 +305,9 @@ void GlobalTypeRewriter::mapTypes(const TypeMap& oldToNewTypes) {
for (auto& tag : wasm.tags) {
tag->type = updater.getNew(tag->type);
}
for (auto& exp : wasm.typeExports) {
exp->heaptype = updater.getNew(exp->heaptype);
}
}

void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/context-decls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Result<> addExports(Lexer& in,
const std::vector<Name>& exports,
ExternalKind kind) {
for (auto name : exports) {
if (wasm.getExportOrNull(name)) {
if (wasm.getTypeExportOrNull(name) || wasm.getExportOrNull(name)) {
// TODO: Fix error location
return in.err("repeated export name");
}
Expand Down
70 changes: 66 additions & 4 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,10 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::vector<DefPos> dataDefs;
std::vector<DefPos> tagDefs;

// Type imports: name, positions of type and import names.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Type imports: name, positions of type and import names.
// Type imports: name, export names, import names, and type index.
std::vector<std::tuple<Name, std::vector<Name>, ImportNames, Index>>
typeImports;

// Positions of export definitions.
std::vector<Index> exportDefs;

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

// Used to verify that all imports come before all non-imports.
bool hasNonImport = false;
bool hasTypeDefinition = false;

Result<> checkImport(Index pos, ImportNames* import) {
if (import) {
Expand All @@ -978,9 +983,10 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
void setOpen() {}
void setShared() {}
Result<> addSubtype(HeapTypeT) { return Ok{}; }
void finishTypeDef(Name name, Index pos) {
void finishTypeDef(Name name, const std::vector<Name>& exports, Index pos) {
// TODO: type annotations
typeDefs.push_back({name, pos, Index(typeDefs.size()), {}});
hasTypeDefinition = true;
}
size_t getRecGroupStartIndex() { return 0; }
void addRecGroup(Index, size_t) {}
Expand Down Expand Up @@ -1092,10 +1098,28 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
TypeUseT type,
Index pos);

Result<> addTypeImport(Name name,
const std::vector<Name>& exports,
ImportNames* import,
Index pos,
Index typePos) {
if (hasTypeDefinition) {
return in.err(pos, "type import after type definitions");
}
typeDefs.push_back({name, pos, Index(typeDefs.size()), {}});
typeImports.push_back({name, exports, *import, typePos});
return Ok{};
}

Result<> addExport(Index pos, Ok, Name, ExternalKind) {
exportDefs.push_back(pos);
return Ok{};
}

Result<> addTypeExport(Index pos, Ok, Name) {
exportDefs.push_back(pos);
return Ok{};
}
};

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

// Keep track of type exports
std::vector<std::vector<Name>> typeExports;

// The index of the subtype definition we are parsing.
Index index = 0;

ParseTypeDefsCtx(Lexer& in, TypeBuilder& builder, const IndexMap& typeIndices)
: TypeParserCtx<ParseTypeDefsCtx>(typeIndices), in(in), builder(builder),
names(builder.size()) {}
names(builder.size()), typeExports(builder.size()) {}

TypeT makeRefType(HeapTypeT ht, Nullability nullability) {
return builder.getTempRefType(ht, nullability);
Expand Down Expand Up @@ -1154,7 +1181,18 @@ struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {
return Ok{};
}

void finishTypeDef(Name name, Index pos) { names[index++].name = name; }
void finishTypeDef(Name name, const std::vector<Name>& exports, Index pos) {
typeExports[index] = exports;
names[index++].name = name;
}

Result<> addTypeImport(Name name,
const std::vector<Name>& exports,
ImportNames* import,
Index pos,
Index typePos) {
return Ok{};
}

size_t getRecGroupStartIndex() { return index; }

Expand Down Expand Up @@ -1403,6 +1441,14 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
t->type = use.type;
return Ok{};
}

Result<> addTypeImport(Name name,
const std::vector<Name>& exports,
ImportNames* import,
Index pos,
Index typePos) {
return Ok{};
}
};

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

Result<> addTypeImport(Name,
const std::vector<Name> exports,
ImportNames* import,
Index pos,
Index typePos) {
return Ok{};
}

Result<> addExport(Index pos, Name value, Name name, ExternalKind kind) {
if (wasm.getExportOrNull(name)) {
if (wasm.getTypeExportOrNull(name) || wasm.getExportOrNull(name)) {
return in.err(pos, "duplicate export");
}
wasm.addExport(builder.makeExport(name, value, kind));
return Ok{};
}

Result<> addTypeExport(Index pos, HeapType heaptype, Name name) {
if (wasm.getTypeExportOrNull(name) || wasm.getTypeExportOrNull(name)) {
return in.err(pos, "duplicate export");
}
wasm.addTypeExport(builder.makeTypeExport(name, heaptype));
return Ok{};
}

Result<Index> addScratchLocal(Index pos, Type type) {
if (!func) {
return in.err(pos,
Expand Down
19 changes: 19 additions & 0 deletions src/parser/parse-2-typedefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Result<> parseTypeDefs(
std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) {
TypeBuilder builder(decls.typeDefs.size());
ParseTypeDefsCtx ctx(input, builder, typeIndices);
for (auto& [name, exports, importNames, pos] : decls.typeImports) {
WithPosition with(ctx, pos);
auto heaptype = typetype(ctx);
CHECK_ERR(heaptype);
builder[ctx.index] = Import(importNames.mod, importNames.nm, *heaptype);
ctx.typeExports[ctx.index] = exports;
ctx.names[ctx.index++].name = name;
}
for (auto& recType : decls.recTypeDefs) {
WithPosition with(ctx, recType.pos);
CHECK_ERR(rectype(ctx));
Expand All @@ -49,6 +57,17 @@ Result<> parseTypeDefs(
}
}
}
for (size_t i = 0; i < types.size(); ++i) {
for (Name& name : ctx.typeExports[i]) {
if (decls.wasm.getTypeExportOrNull(name) ||
decls.wasm.getExportOrNull(name)) {
// TODO: Fix error location
return ctx.in.err("repeated export name");
}
decls.wasm.addTypeExport(
Builder(decls.wasm).makeTypeExport(name, types[i]));
}
}
return Ok{};
}

Expand Down
Loading
Loading