Skip to content

Commit 0bb8697

Browse files
jsturtevantyowldicej
authored
c#: Add cli option to generate Result types (#1115)
* c#: Add cli option to generate Result types Signed-off-by: James Sturtevant <[email protected]> * Apply suggestions from code review Co-authored-by: yowl <[email protected]> Co-authored-by: Joel Dice <[email protected]> * Fix indentation Signed-off-by: James Sturtevant <[email protected]> --------- Signed-off-by: James Sturtevant <[email protected]> Co-authored-by: yowl <[email protected]> Co-authored-by: Joel Dice <[email protected]>
1 parent 341c473 commit 0bb8697

File tree

6 files changed

+266
-121
lines changed

6 files changed

+266
-121
lines changed

crates/csharp/src/function.rs

Lines changed: 144 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,148 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
225225

226226
results.push(lifted);
227227
}
228+
229+
fn handle_result_import(&mut self, operands: &mut Vec<String>) {
230+
if self.interface_gen.csharp_gen.opts.with_wit_results {
231+
uwriteln!(self.src, "return {};", operands[0]);
232+
return;
233+
}
234+
235+
let mut payload_is_void = false;
236+
let mut previous = operands[0].clone();
237+
let mut vars: Vec<(String, Option<String>)> = Vec::with_capacity(self.results.len());
238+
if let Direction::Import = self.interface_gen.direction {
239+
for ty in &self.results {
240+
let tmp = self.locals.tmp("tmp");
241+
uwrite!(
242+
self.src,
243+
"\
244+
if ({previous}.IsOk)
245+
{{
246+
var {tmp} = {previous}.AsOk;
247+
"
248+
);
249+
let TypeDefKind::Result(result) = &self.interface_gen.resolve.types[*ty].kind
250+
else {
251+
unreachable!();
252+
};
253+
let exception_name = result
254+
.err
255+
.map(|ty| self.interface_gen.type_name_with_qualifier(&ty, true));
256+
vars.push((previous.clone(), exception_name));
257+
payload_is_void = result.ok.is_none();
258+
previous = tmp;
259+
}
260+
}
261+
uwriteln!(
262+
self.src,
263+
"return {};",
264+
if payload_is_void { "" } else { &previous }
265+
);
266+
for (level, var) in vars.iter().enumerate().rev() {
267+
self.interface_gen.csharp_gen.needs_wit_exception = true;
268+
let (var_name, exception_name) = var;
269+
let exception_name = match exception_name {
270+
Some(type_name) => &format!("WitException<{}>", type_name),
271+
None => "WitException",
272+
};
273+
uwrite!(
274+
self.src,
275+
"\
276+
}}
277+
else
278+
{{
279+
throw new {exception_name}({var_name}.AsErr!, {level});
280+
}}
281+
"
282+
);
283+
}
284+
}
285+
286+
fn handle_result_call(
287+
&mut self,
288+
func: &&wit_parser::Function,
289+
target: String,
290+
func_name: String,
291+
oper: String,
292+
) -> String {
293+
let ret = self.locals.tmp("ret");
294+
if self.interface_gen.csharp_gen.opts.with_wit_results {
295+
uwriteln!(self.src, "var {ret} = {target}.{func_name}({oper});");
296+
return ret;
297+
}
298+
299+
// otherwise generate exception code
300+
let ty = self
301+
.interface_gen
302+
.type_name_with_qualifier(func.results.iter_types().next().unwrap(), true);
303+
uwriteln!(self.src, "{ty} {ret};");
304+
let mut cases = Vec::with_capacity(self.results.len());
305+
let mut oks = Vec::with_capacity(self.results.len());
306+
let mut payload_is_void = false;
307+
for (index, ty) in self.results.iter().enumerate() {
308+
let TypeDefKind::Result(result) = &self.interface_gen.resolve.types[*ty].kind else {
309+
unreachable!();
310+
};
311+
let err_ty = if let Some(ty) = result.err {
312+
self.interface_gen.type_name_with_qualifier(&ty, true)
313+
} else {
314+
"None".to_owned()
315+
};
316+
let ty = self
317+
.interface_gen
318+
.type_name_with_qualifier(&Type::Id(*ty), true);
319+
let head = oks.concat();
320+
let tail = oks.iter().map(|_| ")").collect::<Vec<_>>().concat();
321+
cases.push(format!(
322+
"\
323+
case {index}:
324+
{{
325+
ret = {head}{ty}.Err(({err_ty}) e.Value){tail};
326+
break;
327+
}}
328+
"
329+
));
330+
oks.push(format!("{ty}.Ok("));
331+
payload_is_void = result.ok.is_none();
332+
}
333+
if !self.results.is_empty() {
334+
self.src.push_str(
335+
"
336+
try
337+
{\n
338+
",
339+
);
340+
}
341+
let head = oks.concat();
342+
let tail = oks.iter().map(|_| ")").collect::<Vec<_>>().concat();
343+
let val = if payload_is_void {
344+
uwriteln!(self.src, "{target}.{func_name}({oper});");
345+
"new None()".to_owned()
346+
} else {
347+
format!("{target}.{func_name}({oper})")
348+
};
349+
uwriteln!(self.src, "{ret} = {head}{val}{tail};");
350+
if !self.results.is_empty() {
351+
self.interface_gen.csharp_gen.needs_wit_exception = true;
352+
let cases = cases.join("\n");
353+
uwriteln!(
354+
self.src,
355+
r#"}}
356+
catch (WitException e)
357+
{{
358+
switch (e.NestingLevel)
359+
{{
360+
{cases}
361+
362+
default: throw new ArgumentException($"invalid nesting level: {{e.NestingLevel}}");
363+
}}
364+
}}
365+
"#
366+
);
367+
}
368+
ret
369+
}
228370
}
229371

230372
impl Bindgen for FunctionBindgen<'_, '_> {
@@ -814,70 +956,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
814956
match func.results.len() {
815957
0 => uwriteln!(self.src, "{target}.{func_name}({oper});"),
816958
1 => {
817-
let ret = self.locals.tmp("ret");
818-
let ty = self.interface_gen.type_name_with_qualifier(
819-
func.results.iter_types().next().unwrap(),
820-
true
821-
);
822-
uwriteln!(self.src, "{ty} {ret};");
823-
let mut cases = Vec::with_capacity(self.results.len());
824-
let mut oks = Vec::with_capacity(self.results.len());
825-
let mut payload_is_void = false;
826-
for (index, ty) in self.results.iter().enumerate() {
827-
let TypeDefKind::Result(result) = &self.interface_gen.resolve.types[*ty].kind else {
828-
unreachable!();
829-
};
830-
let err_ty = if let Some(ty) = result.err {
831-
self.interface_gen.type_name_with_qualifier(&ty, true)
832-
} else {
833-
"None".to_owned()
834-
};
835-
let ty = self.interface_gen.type_name_with_qualifier(&Type::Id(*ty), true);
836-
let head = oks.concat();
837-
let tail = oks.iter().map(|_| ")").collect::<Vec<_>>().concat();
838-
cases.push(
839-
format!(
840-
"\
841-
case {index}: {{
842-
ret = {head}{ty}.Err(({err_ty}) e.Value){tail};
843-
break;
844-
}}
845-
"
846-
)
847-
);
848-
oks.push(format!("{ty}.Ok("));
849-
payload_is_void = result.ok.is_none();
850-
}
851-
if !self.results.is_empty() {
852-
self.src.push_str("try {\n");
853-
}
854-
let head = oks.concat();
855-
let tail = oks.iter().map(|_| ")").collect::<Vec<_>>().concat();
856-
let val = if payload_is_void {
857-
uwriteln!(self.src, "{target}.{func_name}({oper});");
858-
"new None()".to_owned()
859-
} else {
860-
format!("{target}.{func_name}({oper})")
861-
};
862-
uwriteln!(
863-
self.src,
864-
"{ret} = {head}{val}{tail};"
865-
);
866-
if !self.results.is_empty() {
867-
self.interface_gen.csharp_gen.needs_wit_exception = true;
868-
let cases = cases.join("\n");
869-
uwriteln!(
870-
self.src,
871-
r#"}} catch (WitException e) {{
872-
switch (e.NestingLevel) {{
873-
{cases}
874-
875-
default: throw new ArgumentException($"invalid nesting level: {{e.NestingLevel}}");
876-
}}
877-
}}
878-
"#
879-
);
880-
}
959+
let ret = self.handle_result_call(func, target, func_name, oper);
881960
results.push(ret);
882961
}
883962
_ => {
@@ -927,46 +1006,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
9271006
match func.results.len() {
9281007
0 => (),
9291008
1 => {
930-
let mut payload_is_void = false;
931-
let mut previous = operands[0].clone();
932-
let mut vars: Vec<(String, Option<String>)> = Vec::with_capacity(self.results.len());
933-
if let Direction::Import = self.interface_gen.direction {
934-
for ty in &self.results {
935-
let tmp = self.locals.tmp("tmp");
936-
uwrite!(
937-
self.src,
938-
"\
939-
if ({previous}.IsOk) {{
940-
var {tmp} = {previous}.AsOk;
941-
"
942-
);
943-
let TypeDefKind::Result(result) = &self.interface_gen.resolve.types[*ty].kind else {
944-
unreachable!();
945-
};
946-
let exception_name = result.err
947-
.map(|ty| self.interface_gen.type_name_with_qualifier(&ty, true));
948-
vars.push((previous.clone(), exception_name));
949-
payload_is_void = result.ok.is_none();
950-
previous = tmp;
951-
}
952-
}
953-
uwriteln!(self.src, "return {};", if payload_is_void { "" } else { &previous });
954-
for (level, var) in vars.iter().enumerate().rev() {
955-
self.interface_gen.csharp_gen.needs_wit_exception = true;
956-
let (var_name, exception_name) = var;
957-
let exception_name = match exception_name {
958-
Some(type_name) => &format!("WitException<{}>",type_name),
959-
None => "WitException",
960-
};
961-
uwrite!(
962-
self.src,
963-
"\
964-
}} else {{
965-
throw new {exception_name}({var_name}.AsErr!, {level});
966-
}}
967-
"
968-
);
969-
}
1009+
self.handle_result_import(operands);
9701010
}
9711011
_ => {
9721012
let results = operands.join(", ");

crates/csharp/src/interface.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ impl InterfaceGenerator<'_> {
212212
let (payload, results) = payload_and_results(
213213
self.resolve,
214214
*func.results.iter_types().next().unwrap(),
215+
self.csharp_gen.opts.with_wit_results,
215216
);
216217
(
217218
if let Some(ty) = payload {
@@ -359,6 +360,7 @@ impl InterfaceGenerator<'_> {
359360
let (payload, results) = payload_and_results(
360361
self.resolve,
361362
*func.results.iter_types().next().unwrap(),
363+
self.csharp_gen.opts.with_wit_results,
362364
);
363365
(
364366
if let Some(ty) = payload {
@@ -844,6 +846,7 @@ impl InterfaceGenerator<'_> {
844846
let (payload, _) = payload_and_results(
845847
self.resolve,
846848
*func.results.iter_types().next().unwrap(),
849+
self.csharp_gen.opts.with_wit_results,
847850
);
848851
if let Some(ty) = payload {
849852
self.csharp_gen.needs_result = true;
@@ -1177,7 +1180,15 @@ impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> {
11771180
}
11781181
}
11791182

1180-
fn payload_and_results(resolve: &Resolve, ty: Type) -> (Option<Type>, Vec<TypeId>) {
1183+
fn payload_and_results(
1184+
resolve: &Resolve,
1185+
ty: Type,
1186+
with_wit_results: bool,
1187+
) -> (Option<Type>, Vec<TypeId>) {
1188+
if with_wit_results {
1189+
return (Some(ty), Vec::new());
1190+
}
1191+
11811192
fn recurse(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) -> Option<Type> {
11821193
if let Type::Id(id) = ty {
11831194
if let TypeDefKind::Result(result) = &resolve.types[id].kind {

crates/csharp/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ pub struct Opts {
3030
/// Skip generating `cabi_realloc`, `WasmImportLinkageAttribute`, and component type files
3131
#[cfg_attr(feature = "clap", arg(long))]
3232
pub skip_support_files: bool,
33+
34+
/// Generate code for WIT `Result` types instead of exceptions
35+
#[cfg_attr(feature = "clap", arg(long))]
36+
pub with_wit_results: bool,
3337
}
3438

3539
impl Opts {

crates/csharp/tests/codegen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ macro_rules! codegen_test {
3333
runtime: wit_bindgen_csharp::CSharpRuntime::Mono,
3434
internal: false,
3535
skip_support_files: false,
36+
with_wit_results: false,
3637
}
3738
.build()
3839
.generate(resolve, world, files)

0 commit comments

Comments
 (0)