Skip to content

Commit 8c082d0

Browse files
feat(d): Improve export marking & naming (#1702)
* Factor out `@witInterface` into separate UDA. * Rearrange export discovery and name extraction * Make resource method naming more concise, and remove need for `@witInterface` * Allow omitting explicit export name and infer from kebab-case of identifier. * Fix clippy
1 parent ecbbf34 commit 8c082d0

69 files changed

Lines changed: 1256 additions & 989 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/d/src/lib.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,11 @@ impl WorldGenerator for D {
650650
));
651651

652652
if emit_exports_stubs {
653+
r#gen
654+
.stub_src
655+
.push_str(&format!("@witInterface(\"{wasm_import_module}\")"));
653656
r#gen.stub_src.push_str(&format!(
654-
"@witExport(\"{}\", \"{}\")\nstruct {escaped_name}_STUB {{\n",
655-
wasm_import_module,
657+
"@witExport(\"{}\")\nstruct {escaped_name}_STUB {{\n",
656658
ty.name.as_ref().unwrap()
657659
));
658660

@@ -842,13 +844,19 @@ impl WorldGenerator for D {
842844

843845
world_src.push_str("\n\nprivate alias AliasSeq(T...) = T;\n");
844846
world_src.push_str("template Exports(Impl...) {\n");
847+
848+
world_src.push_str(&format!(
849+
"\nalias FilteredImpl = {}.findWitExports!Impl;\n",
850+
self.common_module
851+
));
852+
845853
world_src.push_str("alias InterfaceExports = AliasSeq!(\n");
846854
world_src.indent(1);
847855
world_src.push_str(
848856
&self
849857
.interface_exports
850858
.iter()
851-
.map(|fqn| format!("{fqn}.Exports!Impl"))
859+
.map(|fqn| format!("{fqn}.Exports!FilteredImpl"))
852860
.collect::<Vec<String>>()
853861
.join(",\n"),
854862
);
@@ -1430,26 +1438,44 @@ impl<'a> DInterfaceGenerator<'a> {
14301438
.join(", ")
14311439
));
14321440

1433-
self.src.push_str(&format!(
1434-
"/// ditto\nalias {}_Impl = findWitExportFunc!(\"{}\", \"{}\", {0}_Sig, {}, {});\n",
1435-
d_sig.name,
1436-
self.wasm_import_module.unwrap(),
1437-
func.name,
1438-
d_sig.implicit_self,
1439-
match &func.kind {
1440-
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => "Impl",
1441-
_ => {
1442-
"witExportsIn!_Resource_Impl"
1443-
}
1441+
match &func.kind {
1442+
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
1443+
self.src.push_str(&format!(
1444+
"/// ditto\nalias {}_Impl = findWitExportFunc!(\"{}\", \"{}\", {0}_Sig, Impl);\n",
1445+
d_sig.name,
1446+
self.wasm_import_module.unwrap(),
1447+
func.name
1448+
));
14441449
}
1445-
));
1450+
_ => {
1451+
self.src.push_str(&format!(
1452+
"/// ditto\nalias {}_Impl = findWitExportMethod!(_Resource_Impl, \"{}\", {0}_Sig, {});\n",
1453+
d_sig.name,
1454+
func.name,
1455+
!d_sig.implicit_self,
1456+
));
1457+
}
1458+
}
14461459

14471460
if self.r#gen.opts.emit_export_stubs {
1448-
self.stub_src.push_str(&format!(
1449-
"@witExport(\"{}\", \"{}\")\n",
1450-
self.wasm_import_module.unwrap(),
1451-
func.name
1452-
));
1461+
if matches!(
1462+
&func.kind,
1463+
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding
1464+
) {
1465+
self.stub_src.push_str(&format!(
1466+
"@witInterface(\"{}\")",
1467+
self.wasm_import_module.unwrap(),
1468+
));
1469+
}
1470+
1471+
let name = &func.name;
1472+
let name = match &func.kind {
1473+
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => name,
1474+
FunctionKind::Constructor(_) => "[constructor]",
1475+
_ => name.split(".").skip(1).next().unwrap(),
1476+
};
1477+
1478+
self.stub_src.push_str(&format!("@witExport(\"{name}\")\n"));
14531479
if d_sig.static_member {
14541480
self.stub_src.push_str("static ");
14551481
}

crates/d/src/wit_common.d

Lines changed: 188 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ alias wasmImport(string mod, string name) = AliasSeq!(
88

99
enum wasmExport(string name) = llvmAttr("wasm-export-name", name);
1010

11-
struct witExport { string mod; string name; }
11+
struct witInterface { string name; }
12+
struct witExport { string name; }
1213

1314
/// Thin CABI compliant wrapper over `T[]`
1415
struct WitList(T) {
@@ -443,28 +444,112 @@ T[] mallocSlice(T)(size_t count) @nogc nothrow {
443444
// from std.meta
444445
alias AliasSeq(T...) = T;
445446

447+
template witInterfaceOf(alias Symbol) {
448+
alias udas = AliasSeq!();
449+
static foreach (uda; __traits(getAttributes, Symbol)) {
450+
static if (!is(uda) && is(typeof(uda) == witInterface)) {
451+
udas = AliasSeq!(udas, uda);
452+
}
453+
}
446454

447-
template findWitExportFunc(string mod, string name, Sig, bool implicitSelf, Impl...) {
448-
static foreach(Func; Impl) {
449-
static foreach(uda; __traits(getAttributes, Func)) {
450-
static if (!is(uda) && is(typeof(uda) == witExport) && uda == witExport(mod, name)) {
451-
static assert(
452-
!is(Func) &&
453-
(is(typeof(Func) == function)),
454-
"The implementation of '", mod, "#", name, "' ",
455-
"`", __traits(fullyQualifiedName, findWitExportFunc), "` ",
456-
"must be a function or method."
457-
);
458-
459-
static assert(
460-
!is(typeof(findWitExportFunc) == void) || __traits(isSame, findWitExportFunc, Func),
461-
"There must be only one implementation of '", mod, "#", name, "'. ",
462-
"Found at least `", __traits(fullyQualifiedName, findWitExportFunc),
463-
"` and `", __traits(fullyQualifiedName, Func), "`."
464-
);
465-
alias findWitExportFunc = Func;
455+
static assert(
456+
udas.length <= 1,
457+
"There must be at most one `@witInterface` attached. Found multiple on `",
458+
__traits(fullyQualifiedName, Symbol), "`.",
459+
);
460+
461+
static if (udas.length) {
462+
enum string witInterfaceOf = udas[0].name;
463+
} else {
464+
enum string witInterfaceOf = "";
465+
}
466+
}
467+
468+
alias toKebabCase = (string s) { // lambda to satisfy `betterC` (use of GC)
469+
if (s.length == 0) return "";
470+
471+
char[] buf;
472+
foreach (i, c; s) {
473+
if (i > 0 && c >= 'A' && c <= 'Z') {
474+
char prev = s[i - 1];
475+
char next = ((i + 1) < s.length) ? s[i + 1] : '\0';
476+
if (
477+
(prev >= 'a' && prev <= 'z') ||
478+
(
479+
next != '\0' &&
480+
prev >= 'A' && prev <= 'Z' &&
481+
!(next >= 'A' && next <= 'Z')
482+
)
483+
) {
484+
buf ~= '-';
466485
}
467486
}
487+
buf ~= (c >= 'A' && c <= 'Z') ? (c + 32) : c;
488+
}
489+
return cast(string)buf;
490+
};
491+
492+
template witNameOf(alias Symbol) {
493+
alias udas = AliasSeq!();
494+
static foreach (uda; __traits(getAttributes, Symbol)) {
495+
static if ((!is(uda) && is(typeof(uda) == witExport)) || is(uda == witExport)) {
496+
udas = AliasSeq!(udas, uda);
497+
}
498+
}
499+
500+
static assert(
501+
udas.length <= 1,
502+
"There must be at most one `@witExport` attached. Found multiple on `",
503+
__traits(fullyQualifiedName, Symbol), "`.",
504+
);
505+
506+
static if (udas.length) {
507+
static if (is(udas[0])) {
508+
enum string witNameOf = toKebabCase(__traits(identifier, Symbol));
509+
} else {
510+
static assert(
511+
udas[0].name.length,
512+
"Specifying an empty name for `@witExport(...)` is not allowed. Found empty on `",
513+
__traits(fullyQualifiedName, Symbol), "`. Omit the parenthesis and parameter",
514+
" (i.e. use as `@witExport`) or specify non-empty name.",
515+
);
516+
enum string witNameOf = udas[0].name;
517+
}
518+
} else {
519+
enum string witNameOf = "";
520+
}
521+
}
522+
523+
template witNameInResourceOf(T, alias Func) {
524+
enum resName = witNameOf!T;
525+
enum name = witNameOf!Func;
526+
527+
static if (name == "[constructor]") {
528+
enum witNameInResourceOf = "[constructor]" ~ resName;
529+
} else {
530+
enum witNameInResourceOf = (__traits(isStaticFunction, Func) ? "[static]" : "[method]") ~ resName ~ "." ~ name;
531+
}
532+
}
533+
534+
template findWitExportFunc(string mod, string name, Sig, Impl...) {
535+
static foreach(Func; Impl) {
536+
static if (witNameOf!Func == name && witInterfaceOf!Func == mod) {
537+
static assert(
538+
!is(Func) &&
539+
(is(typeof(Func) == function)),
540+
"The implementation of '", mod, "#", name, "' ",
541+
"`", __traits(fullyQualifiedName, findWitExportFunc), "` ",
542+
"must be a function or method."
543+
);
544+
545+
static assert(
546+
!is(typeof(findWitExportFunc) == void) || __traits(isSame, findWitExportFunc, Func),
547+
"There must be only one implementation of '", mod, "#", name, "'. ",
548+
"Found at least `", __traits(fullyQualifiedName, findWitExportFunc),
549+
"` and `", __traits(fullyQualifiedName, Func), "`."
550+
);
551+
alias findWitExportFunc = Func;
552+
}
468553
}
469554

470555
static assert(
@@ -473,7 +558,14 @@ template findWitExportFunc(string mod, string name, Sig, bool implicitSelf, Impl
473558
);
474559

475560
static assert(
476-
is(typeof(&findWitExportFunc) : Sig) && __traits(isStaticFunction, findWitExportFunc) != implicitSelf,
561+
__traits(isStaticFunction, findWitExportFunc),
562+
"The implementation of '", mod, "#", name, "' ",
563+
"`", __traits(fullyQualifiedName, findWitExportFunc), "` ",
564+
"must be static.",
565+
);
566+
567+
static assert(
568+
is(typeof(&findWitExportFunc) : Sig),
477569
"The implementation of '", mod, "#", name, "' ",
478570
"`", __traits(fullyQualifiedName, findWitExportFunc), "` ",
479571
"must conform to the necessary signature. ",
@@ -482,25 +574,70 @@ template findWitExportFunc(string mod, string name, Sig, bool implicitSelf, Impl
482574
);
483575
}
484576

577+
template findWitExportMethod(T, string name, Sig, bool isStatic) {
578+
alias Impl = witExportsIn!T;
579+
580+
enum mod = witInterfaceOf!T;
581+
582+
static foreach(Func; Impl) {
583+
static if (witNameInResourceOf!(T, Func) == name) {
584+
static assert(
585+
!is(Func) &&
586+
(is(typeof(Func) == function)),
587+
"The implementation of '", mod, "#", name, "' ",
588+
"`", __traits(fullyQualifiedName, findWitExportMethod), "` ",
589+
"must be a function or method."
590+
);
591+
592+
static assert(
593+
!is(typeof(findWitExportMethod) == void) || __traits(isSame, findWitExportMethod, Func),
594+
"There must be only one implementation of '", mod, "#", name, "'. ",
595+
"Found at least `", __traits(fullyQualifiedName, findWitExportMethod),
596+
"` and `", __traits(fullyQualifiedName, Func), "`."
597+
);
598+
alias findWitExportMethod = Func;
599+
}
600+
}
601+
602+
static assert(
603+
!is(typeof(findWitExportMethod) == void),
604+
"Could not find implementation for '", mod, "#", name, "'"
605+
);
606+
607+
static assert(
608+
__traits(isStaticFunction, findWitExportMethod) == isStatic,
609+
"The implementation of '", mod, "#", name, "' ",
610+
"`", __traits(fullyQualifiedName, findWitExportMethod), "` ",
611+
"must " ~ (isSttic ? "be static" : "have implicit `this`") ~ ".",
612+
);
613+
614+
static assert(
615+
is(typeof(&findWitExportMethod) : Sig),
616+
"The implementation of '", mod, "#", name, "' ",
617+
"`", __traits(fullyQualifiedName, findWitExportMethod), "` ",
618+
"must conform to the necessary signature. ",
619+
"Found `", typeof(&findWitExportMethod), "`",
620+
", but expected `", Sig, "`"
621+
);
622+
}
623+
485624
template findWitExportResource(string mod, string name, Impl...) {
486625
static foreach(Resource; Impl) {
487-
static foreach(uda; __traits(getAttributes, Resource)) {
488-
static if (!is(uda) && is(typeof(uda) == witExport) && uda == witExport(mod, name)) {
489-
static assert(
490-
is(Resource == struct),
491-
"The implementation of '", mod, "#", name, "' ",
492-
"`", __traits(fullyQualifiedName, findWitExportResource), "` ",
493-
"must be a struct."
494-
);
495-
496-
static assert(
497-
!is(typeof(findWitExportResource) == void) || __traits(isSame, findWitExportResource, Resource),
498-
"There must be only one implementation of '", mod, "#", name, "'. ",
499-
"Found at least `", __traits(fullyQualifiedName, findWitExportResource),
500-
"` and `", __traits(fullyQualifiedName, Resource), "`."
501-
);
502-
alias findWitExportResource = Resource;
503-
}
626+
static if (witNameOf!Resource == name && witInterfaceOf!Resource == mod) {
627+
static assert(
628+
is(Resource == struct),
629+
"The implementation of '", mod, "#", name, "' ",
630+
"`", __traits(fullyQualifiedName, findWitExportResource), "` ",
631+
"must be a struct."
632+
);
633+
634+
static assert(
635+
!is(typeof(findWitExportResource) == void) || __traits(isSame, findWitExportResource, Resource),
636+
"There must be only one implementation of '", mod, "#", name, "'. ",
637+
"Found at least `", __traits(fullyQualifiedName, findWitExportResource),
638+
"` and `", __traits(fullyQualifiedName, Resource), "`."
639+
);
640+
alias findWitExportResource = Resource;
504641
}
505642
}
506643

@@ -510,16 +647,21 @@ template findWitExportResource(string mod, string name, Impl...) {
510647
);
511648
}
512649

513-
514-
template witExportsIn(T) {
650+
public template witExportsIn(T) {
515651
alias witExportsIn = AliasSeq!();
516652

517-
static foreach(M; __traits(allMembers, T)) {
518-
static foreach(Export; __traits(getOverloads, T, M)) {
519-
static foreach(uda; __traits(getAttributes, Export)) {
520-
static if (!is(uda) && is(typeof(uda) == witExport)) {
521-
witExportsIn = AliasSeq!(witExportsIn, Export);
522-
}
653+
static foreach(member; __traits(allMembers, T)) {
654+
witExportsIn = AliasSeq!(witExportsIn, findWitExports!(__traits(getOverloads, T, member)));
655+
}
656+
}
657+
658+
public template findWitExports(Exports...) {
659+
alias findWitExports = AliasSeq!();
660+
661+
static foreach (elem; Exports) {
662+
static foreach(uda; __traits(getAttributes, elem)) {
663+
static if ((!is(uda) && is(typeof(uda) == witExport)) || is(uda == witExport)) {
664+
findWitExports = AliasSeq!(findWitExports, elem);
523665
}
524666
}
525667
}

0 commit comments

Comments
 (0)