Skip to content

Commit

Permalink
Handle struct returns with one primitive field (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
ambiguousname authored Dec 9, 2024
1 parent b029af6 commit 8593fda
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 30 deletions.
3 changes: 3 additions & 0 deletions feature_tests/c/include/CyclicStructB.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions feature_tests/cpp/include/CyclicStructB.d.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions feature_tests/cpp/include/CyclicStructB.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions feature_tests/dart/lib/src/CyclicStructB.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions feature_tests/dart/lib/src/lib.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 5 additions & 9 deletions feature_tests/js/api/CyclicStructA.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions feature_tests/js/api/CyclicStructB.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 19 additions & 6 deletions feature_tests/js/api/CyclicStructB.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions feature_tests/js/api/CyclicStructC.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion feature_tests/js/test/struct-ts.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'ava';
import { MyEnum, MyStruct } from "diplomat-wasm-js-feature-tests";
import { MyEnum, MyStruct, CyclicStructB } from "diplomat-wasm-js-feature-tests";
test("Verify invariants of struct", t => {
const s = MyStruct.new_();
t.is(s.a, 17);
Expand All @@ -23,3 +23,11 @@ test("Test struct creation", t => {
});
t.is(s.intoA(), 17);
});
test("Function Returning Nested Struct of One Field", t => {
const a = CyclicStructB.getA();
t.is(a.cyclicOut(), "0");
});
test("Function De-Referencing Nested Struct of One Primitive", t => {
const a = CyclicStructB.getAOption();
t.is(a.cyclicOut(), "0");
});
12 changes: 11 additions & 1 deletion feature_tests/js/test/struct-ts.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'ava';
import { MyEnum, MyStruct } from "diplomat-wasm-js-feature-tests";
import { MyEnum, MyStruct, CyclicStructB } from "diplomat-wasm-js-feature-tests";

test("Verify invariants of struct", t => {
const s = MyStruct.new_();
Expand All @@ -24,4 +24,14 @@ test("Test struct creation", t => {
g: MyEnum.B
});
t.is(s.intoA(), 17);
});

test("Function Returning Nested Struct of One Field", t => {
const a = CyclicStructB.getA();
t.is(a.cyclicOut(), "0");
});

test("Function De-Referencing Nested Struct of One Primitive", t => {
const a = CyclicStructB.getAOption();
t.is(a.cyclicOut(), "0");
});
12 changes: 11 additions & 1 deletion feature_tests/js/test/struct.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'ava';
import { MyEnum, MyStruct, ScalarPairWithPadding, BigStructWithStuff } from "diplomat-wasm-js-feature-tests";
import { MyEnum, MyStruct, ScalarPairWithPadding, BigStructWithStuff, CyclicStructB } from "diplomat-wasm-js-feature-tests";

test("Verify invariants of struct", t => {
const s = MyStruct.new_("hello");
Expand Down Expand Up @@ -46,3 +46,13 @@ test("Test struct layout: complex struct with multiple padding types and contain
s.assertValue(853);
t.is(true, true); // Ava doesn't like tests without assertions
});

test("Function Returning Nested Struct of One Primitive", t => {
const a = CyclicStructB.getA();
t.is(a.cyclicOut(), "0");
});

test("Function De-Referencing Nested Struct of One Primitive", t => {
const a = CyclicStructB.getAOption();
t.is(a.cyclicOut(), "0");
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions feature_tests/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ pub mod ffi {
pub fn get_a() -> CyclicStructA {
Default::default()
}

pub fn get_a_option() -> Option<CyclicStructA> {
Some(Default::default())
}
}

impl CyclicStructC {
Expand Down
36 changes: 30 additions & 6 deletions tool/src/js/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,25 +284,48 @@ impl<'tcx> TyGenContext<'_, 'tcx> {
offset: usize,
) -> Cow<'tcx, str> {
let pointer = if offset == 0 {
variable_name
variable_name.clone()
} else {
format!("{variable_name} + {offset}").into()
};
match *ty {
match ty {
Type::Enum(..) => format!("diplomatRuntime.enumDiscriminant(wasm, {pointer})").into(),
Type::Opaque(..) => format!("diplomatRuntime.ptrRead(wasm, {pointer})").into(),
// Structs always assume they're being passed a pointer, so they handle this in their constructors:
// See NestedBorrowedFields
Type::Struct(..) | Type::Slice(..) | Type::DiplomatOption(..) => pointer,
Type::Primitive(p) => format!(
"(new {ctor}(wasm.memory.buffer, {pointer}, 1))[0]{cmp}",
ctor = self.formatter.fmt_primitive_slice(p),
ctor = self.formatter.fmt_primitive_slice(*p),
cmp = match p {
PrimitiveType::Bool => " === 1",
_ => "",
}
)
.into(),
Type::Struct(st)
if match st.id() {
hir::TypeId::OutStruct(s) => {
self.only_primitive(self.tcx.resolve_out_struct(s))
}
hir::TypeId::Struct(s) => self.only_primitive(self.tcx.resolve_struct(s)),
_ => false,
} =>
{
match st.id() {
hir::TypeId::OutStruct(s) => {
let first = self.tcx.resolve_out_struct(s).fields.first().unwrap();

self.gen_c_to_js_deref_for_type(&first.ty, variable_name, offset)
}
hir::TypeId::Struct(s) => {
let first = self.tcx.resolve_struct(s).fields.first().unwrap();

self.gen_c_to_js_deref_for_type(&first.ty, variable_name, offset)
}
_ => unreachable!("Expected struct, got {:?}", st.id()),
}
}
// Structs (nearly) always assume they're being passed a pointer, so they handle this in their constructors:
// See NestedBorrowedFields
Type::Struct(..) | Type::Slice(..) | Type::DiplomatOption(..) => pointer,
_ => unreachable!("Unknown AST/HIR variant {:?}", ty),
}
}
Expand Down Expand Up @@ -381,6 +404,7 @@ impl<'tcx> TyGenContext<'_, 'tcx> {
ReturnType::Infallible(SuccessType::OutType(ref o)) => {
let mut result = "result";
match o {
Type::Struct(s) if self.wraps_a_primitive(s) => {}
Type::Struct(_) | Type::Slice(_) => {
let layout = crate::js::layout::type_size_alignment(o, self.tcx);
let size = layout.size();
Expand Down
Loading

0 comments on commit 8593fda

Please sign in to comment.