Skip to content

Commit 3738327

Browse files
Add export stub generation
1 parent 0629cd5 commit 3738327

2 files changed

Lines changed: 220 additions & 5 deletions

File tree

crates/d/src/lib.rs

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ struct DSig {
2626
#[derive(Default)]
2727
struct D {
2828
used_interfaces: HashSet<(WorldKey, InterfaceId)>,
29+
export_stubs: Vec<String>,
2930

3031
interface_imports: Vec<String>,
3132
interface_exports: Vec<String>,
3233
type_imports_src: Source,
3334
function_imports_src: Source,
3435
function_exports_src: Source,
36+
export_stubs_src: Source,
3537

3638
opts: Opts,
3739

@@ -57,6 +59,11 @@ pub struct Opts {
5759
/// Where to place output files
5860
#[cfg_attr(feature = "clap", arg(skip))]
5961
out_dir: Option<PathBuf>,
62+
63+
#[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
64+
/// Whether stubs/declarations for exports should be emitted
65+
/// Only for testing purposes.
66+
emit_export_stubs: bool,
6067
}
6168

6269
impl Opts {
@@ -313,6 +320,8 @@ impl D {
313320

314321
DInterfaceGenerator {
315322
src: Source::default(),
323+
stub_src: Source::default(),
324+
stubs: Vec::default(),
316325
fqn: "",
317326
r#gen: self,
318327
resolve,
@@ -487,7 +496,10 @@ impl WorldGenerator for D {
487496
types: &[(&str, TypeId)],
488497
_files: &mut Files,
489498
) {
499+
let fqn = self.world_fqn.clone();
490500
let mut r#gen = self.interface(resolve, Some(Direction::Import), None, Some("$root"));
501+
r#gen.fqn = &fqn;
502+
491503
for (name, id) in types.iter() {
492504
r#gen.define_type(name, *id);
493505
}
@@ -502,7 +514,10 @@ impl WorldGenerator for D {
502514
funcs: &[(&str, &Function)],
503515
_files: &mut Files,
504516
) {
517+
let fqn = self.world_fqn.clone();
505518
let mut r#gen = self.interface(resolve, Some(Direction::Import), None, Some("$root"));
519+
r#gen.fqn = &fqn;
520+
506521
for (_name, func) in funcs {
507522
r#gen.import_func(func);
508523
}
@@ -604,12 +619,31 @@ impl WorldGenerator for D {
604619
let ret_area_decl = r#gen.emit_ret_area_if_needed();
605620

606621
r#gen.src.push_str(&ret_area_decl);
607-
r#gen.src.push_str("}\n");
622+
r#gen.src.push_str("}\n\n");
623+
624+
let DInterfaceGenerator {
625+
mut src,
626+
stub_src,
627+
stubs,
628+
..
629+
} = r#gen;
630+
631+
if self.opts.emit_export_stubs {
632+
src.append_src(&stub_src);
633+
634+
src.push_str("alias STUBS = AliasSeq!(\n");
635+
src.indent(1);
636+
src.push_str(&stubs.join(",\n"));
637+
src.deindent(1);
638+
src.push_str("\n);\n");
639+
640+
self.export_stubs.push(format!("{fqn}.STUBS"));
641+
}
608642

609643
let mut interface_filepath = PathBuf::from_iter(fqn.split("."));
610644
interface_filepath.add_extension("d");
611645

612-
files.push(interface_filepath.to_str().unwrap(), r#gen.src.as_bytes());
646+
files.push(interface_filepath.to_str().unwrap(), src.as_bytes());
613647

614648
self.cur_interface = None;
615649
Ok(())
@@ -622,7 +656,10 @@ impl WorldGenerator for D {
622656
funcs: &[(&str, &Function)],
623657
_files: &mut Files,
624658
) -> Result<()> {
659+
let fqn = self.world_fqn.clone();
625660
let mut r#gen = self.interface(resolve, Some(Direction::Export), None, Some("$root"));
661+
r#gen.fqn = &fqn;
662+
626663
for (_name, func) in funcs {
627664
match func.kind {
628665
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
@@ -634,9 +671,21 @@ impl WorldGenerator for D {
634671

635672
let ret_area_decl = r#gen.emit_ret_area_if_needed();
636673

637-
self.function_exports_src = take(&mut r#gen.src);
674+
let DInterfaceGenerator {
675+
src,
676+
stub_src,
677+
mut stubs,
678+
..
679+
} = r#gen;
680+
681+
self.function_exports_src = src;
638682
self.function_exports_src.push_str(&ret_area_decl);
639683

684+
if self.opts.emit_export_stubs {
685+
self.export_stubs_src.append_src(&stub_src);
686+
self.export_stubs.append(&mut stubs);
687+
}
688+
640689
Ok(())
641690
}
642691

@@ -714,6 +763,20 @@ impl WorldGenerator for D {
714763
world_src.push_str(&self.function_exports_src.as_str());
715764
world_src.push_str("}\n");
716765

766+
if self.opts.emit_export_stubs {
767+
self.export_stubs_src.push_str("alias STUBS = AliasSeq!(\n");
768+
self.export_stubs_src.indent(1);
769+
self.export_stubs_src
770+
.push_str(&self.export_stubs.join(",\n"));
771+
self.export_stubs_src.deindent(1);
772+
self.export_stubs_src.push_str("\n);\n");
773+
774+
self.export_stubs_src
775+
.push_str("alias Exports_STUB_INVOKE = Exports!(STUBS);\n");
776+
777+
world_src.append_src(&self.export_stubs_src);
778+
}
779+
717780
let mut world_filepath = PathBuf::from_iter(get_world_fqn(world_id, resolve).split("."));
718781
world_filepath.push("package.d");
719782

@@ -726,6 +789,8 @@ impl WorldGenerator for D {
726789

727790
struct DInterfaceGenerator<'a> {
728791
src: Source,
792+
stub_src: Source,
793+
stubs: Vec<String>,
729794
direction: Option<Direction>,
730795
r#gen: &'a mut D,
731796
resolve: &'a Resolve,
@@ -1190,6 +1255,24 @@ impl<'a> DInterfaceGenerator<'a> {
11901255
}
11911256
));
11921257

1258+
if self.r#gen.opts.emit_export_stubs {
1259+
self.stub_src.push_str(&format!(
1260+
"@witExport(\"{}\", \"{}\")\n{} {}_STUB({});\n",
1261+
self.wasm_import_module.unwrap(),
1262+
func.name,
1263+
d_sig.result,
1264+
d_sig.name,
1265+
d_sig
1266+
.arguments
1267+
.iter()
1268+
.map(|(name, ty)| "in ".to_owned() + ty + " " + name)
1269+
.collect::<Vec<String>>()
1270+
.join(", ")
1271+
));
1272+
1273+
self.stubs.push(d_sig.name.clone() + "_STUB");
1274+
}
1275+
11931276
self.src.push_str("/// ditto\n");
11941277
self.src.push_str(&format!(
11951278
"@wasmExport!(\"{}#{}\")\n",
@@ -1991,7 +2074,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
19912074
fn perform_cast(op: &str, cast: &Bitcast) -> String {
19922075
match cast {
19932076
Bitcast::I32ToF32 | Bitcast::I64ToF32 => {
1994-
format!("cast(uint)({op}).reinterpretCast!float")
2077+
format!("(cast(uint){op}).reinterpretCast!float")
19952078
}
19962079
Bitcast::F32ToI32 | Bitcast::F32ToI64 => {
19972080
format!("({op}).reinterpretCast!uint")
@@ -2260,13 +2343,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
22602343
base,
22612344
} = self.blocks.pop().unwrap();
22622345
let arr_src = &operands[0];
2346+
let arr_dst = &operands[1];
22632347
let size_str = self.r#gen.sizes.size(element).format("size_t.sizeof");
22642348

22652349
self.push_str(&format!(
22662350
"foreach ({block_element}_idx, const ref {block_element}; {arr_src}) {{\n"
22672351
));
22682352
self.push_str(&format!(
2269-
"const auto {base} = {arr_src} + {block_element}_idx * {size_str};\n"
2353+
"const auto {base} = {arr_dst} + {block_element}_idx * {size_str};\n"
22702354
));
22712355
self.push_str(&body);
22722356
self.push_str("\n}\n");

crates/test/src/d.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use crate::config::StringList;
2+
use crate::{Compile, LanguageMethods, Runner, Verify};
3+
use anyhow::{Context, Result};
4+
use clap::Parser;
5+
use heck::ToSnakeCase;
6+
use serde::Deserialize;
7+
use std::env;
8+
use std::fs;
9+
use std::path::{Path, PathBuf};
10+
use std::process::Command;
11+
12+
#[derive(Default, Debug, Clone, Parser)]
13+
pub struct DOpts {}
14+
15+
pub struct D;
16+
17+
fn ldc2(runner: &Runner) -> PathBuf {
18+
format!("ldc2").into()
19+
}
20+
21+
impl LanguageMethods for D {
22+
fn display(&self) -> &str {
23+
"d"
24+
}
25+
26+
fn comment_prefix_for_test_config(&self) -> Option<&str> {
27+
Some("//@")
28+
}
29+
30+
fn should_fail_verify(
31+
&self,
32+
name: &str,
33+
config: &crate::config::WitConfig,
34+
_args: &[String],
35+
) -> bool {
36+
config.async_ || config.error_context
37+
}
38+
39+
fn default_bindgen_args_for_codegen(&self) -> &[&str] {
40+
&["--emit-export-stubs"]
41+
}
42+
43+
fn prepare(&self, runner: &mut Runner) -> Result<()> {
44+
prepare(runner, ldc2(runner))
45+
}
46+
47+
fn compile(&self, runner: &Runner, c: &Compile<'_>) -> Result<()> {
48+
compile(runner, c, ldc2(runner))
49+
}
50+
51+
fn verify(&self, runner: &Runner, v: &Verify<'_>) -> Result<()> {
52+
verify(runner, v, ldc2(runner))
53+
}
54+
}
55+
56+
fn prepare(runner: &mut Runner, compiler: PathBuf) -> Result<()> {
57+
let cwd = env::current_dir()?;
58+
let dir = cwd.join(&runner.opts.artifacts).join("d");
59+
60+
super::write_if_different(&dir.join("test.d"), "extern(C) void _start() {}")?;
61+
62+
println!("Testing if `{}` works...", compiler.display());
63+
runner
64+
.run_command(
65+
Command::new(&compiler)
66+
.current_dir(&dir)
67+
.arg("-mtriple=wasm32-unknown-unknown")
68+
.arg("-betterC")
69+
.arg("test.d"),
70+
)
71+
.inspect_err(|_| {
72+
eprintln!("Error: failed to find `{}`.", compiler.display());
73+
})?;
74+
75+
Ok(())
76+
}
77+
78+
fn search_for_world_package(bindings_root: &Path) -> Option<PathBuf> {
79+
// Look for a package.d generated from a world nested at wit/*/*/*/package.d
80+
81+
// TODO: If we had access to the full package+version of the world being
82+
// generated, we wouldn't need to search.
83+
84+
// ./wit/*
85+
fs::read_dir(bindings_root.join("wit"))
86+
.ok()?
87+
.flatten()
88+
.map(|e| e.path())
89+
.filter(|p| p.is_dir())
90+
// ./wit/*/*
91+
.filter_map(|p| fs::read_dir(p).ok())
92+
.flatten()
93+
.flatten()
94+
.map(|e| e.path())
95+
.filter(|p| p.is_dir())
96+
// ./wit/*/*/*
97+
.filter_map(|p| fs::read_dir(p).ok())
98+
.flatten()
99+
.flatten()
100+
.map(|e| e.path())
101+
.filter(|p| p.is_dir())
102+
// ./wit/*/*/*/package.d
103+
.filter_map(|p| fs::read_dir(p).ok())
104+
.flatten()
105+
.flatten()
106+
.map(|e| e.path())
107+
.find(|p| p.is_file() && p.file_name().unwrap() == "package.d")
108+
}
109+
110+
fn compile(runner: &Runner, compile: &Compile<'_>, compiler: PathBuf) -> Result<()> {
111+
todo!();
112+
}
113+
114+
fn verify(runner: &Runner, verify: &Verify<'_>, compiler: PathBuf) -> Result<()> {
115+
let mut cmd = Command::new(compiler);
116+
117+
let world_path = search_for_world_package(verify.bindings_dir).unwrap();
118+
119+
cmd.arg(world_path)
120+
.arg("-betterC")
121+
.arg("-mtriple=wasm32-unknown-unknown")
122+
.arg("-I")
123+
.arg(&verify.bindings_dir)
124+
.arg("-i") // compile included dependencies
125+
.arg("-c") // compile only
126+
.arg("--de") // deperecations are errors
127+
.arg("-w") // warnigns are errors
128+
.arg("-of")
129+
.arg(verify.artifacts_dir.join("tmp.o"));
130+
runner.run_command(&mut cmd)
131+
}

0 commit comments

Comments
 (0)