diff --git a/cmd/gravity/src/codegen/func.rs b/cmd/gravity/src/codegen/func.rs index b0fedd2..419656f 100644 --- a/cmd/gravity/src/codegen/func.rs +++ b/cmd/gravity/src/codegen/func.rs @@ -10,9 +10,9 @@ use crate::{ go::{ comment, imports::{ - ERRORS_NEW, WAZERO_API_DECODE_F32, WAZERO_API_DECODE_F64, WAZERO_API_DECODE_I32, - WAZERO_API_DECODE_U32, WAZERO_API_ENCODE_F32, WAZERO_API_ENCODE_F64, - WAZERO_API_ENCODE_I32, + ERRORS_NEW, UTF8_VALID_RUNE, WAZERO_API_DECODE_F32, WAZERO_API_DECODE_F64, + WAZERO_API_DECODE_I32, WAZERO_API_DECODE_U32, WAZERO_API_ENCODE_F32, + WAZERO_API_ENCODE_F64, }, GoIdentifier, GoResult, GoType, Operand, }, @@ -1183,7 +1183,16 @@ impl Bindgen for Func<'_> { $module_handle.Memory().WriteUint64Le($ptr+$offset, $tag) } } - Instruction::I32FromChar => todo!("implement instruction: {inst:?}"), + Instruction::I32FromChar => { + let tmp = self.tmp(); + let value = format!("value{tmp}"); + let operand = &operands[0]; + quote_in! { self.body => + $['\r'] + $(&value) := uint32($operand) + } + results.push(Operand::SingleValue(value)) + } Instruction::I64FromU64 => { // I64FromU64 is a no-op reinterpretation (same 64-bit value, // different signedness). Use uint64() identity cast — int64() @@ -1199,27 +1208,24 @@ impl Bindgen for Func<'_> { results.push(Operand::SingleValue(value.into())); } Instruction::I64FromS64 => { + // Go's int64 -> uint64 conversion keeps the two's-complement + // bits, which is the i64 the canonical ABI expects. let tmp = self.tmp(); let value = format!("value{tmp}"); let operand = &operands[0]; quote_in! { self.body => $['\r'] - $(&value) := $operand + $(&value) := uint64($operand) } results.push(Operand::SingleValue(value.into())); } - Instruction::I32FromS32 => { - let tmp = self.tmp(); - let value = format!("value{tmp}"); - let operand = &operands[0]; - quote_in! { self.body => - $['\r'] - $(&value) := $WAZERO_API_ENCODE_I32($operand) - } - results.push(Operand::SingleValue(value)) - } - // All of these values should fit in Go's `int32` type which allows a safe cast - Instruction::I32FromU16 + // Go's integer conversion to uint32 sign-extends the signed types + // and zero-extends the unsigned ones, which is how the canonical + // ABI widens them to an i32. The result is the uint32 a host + // function returns, and uint64(result) equals api.EncodeI32 when + // CallWasm passes it to an export. + Instruction::I32FromS32 + | Instruction::I32FromU16 | Instruction::I32FromS16 | Instruction::I32FromU8 | Instruction::I32FromS8 => { @@ -1228,17 +1234,25 @@ impl Bindgen for Func<'_> { let operand = &operands[0]; quote_in! { self.body => $['\r'] - $(&value) := $WAZERO_API_ENCODE_I32(int32($operand)) + $(&value) := uint32($operand) } results.push(Operand::SingleValue(value)) } + // A host function returns float32 and float64 directly, while + // CallWasm needs the IEEE bits in a uint64. Instruction::CoreF32FromF32 => { let tmp = self.tmp(); let result = &format!("result{tmp}"); let operand = &operands[0]; - quote_in! { self.body => - $['\r'] - $result := $WAZERO_API_ENCODE_F32($operand) + match self.direction { + Direction::Export => quote_in! { self.body => + $['\r'] + $result := $WAZERO_API_ENCODE_F32($operand) + }, + Direction::Import { .. } => quote_in! { self.body => + $['\r'] + $result := $operand + }, }; results.push(Operand::SingleValue(result.into())); } @@ -1246,9 +1260,15 @@ impl Bindgen for Func<'_> { let tmp = self.tmp(); let result = &format!("result{tmp}"); let operand = &operands[0]; - quote_in! { self.body => - $['\r'] - $result := $WAZERO_API_ENCODE_F64($operand) + match self.direction { + Direction::Export => quote_in! { self.body => + $['\r'] + $result := $WAZERO_API_ENCODE_F64($operand) + }, + Direction::Import { .. } => quote_in! { self.body => + $['\r'] + $result := $operand + }, }; results.push(Operand::SingleValue(result.into())); } @@ -1306,7 +1326,16 @@ impl Bindgen for Func<'_> { }; results.push(Operand::SingleValue(result.into())); } - Instruction::S64FromI64 => todo!("implement instruction: {inst:?}"), + Instruction::S64FromI64 => { + let tmp = self.tmp(); + let result = &format!("result{tmp}"); + let operand = &operands[0]; + quote_in! { self.body => + $['\r'] + $result := int64($operand) + }; + results.push(Operand::SingleValue(result.into())); + } Instruction::U64FromI64 => { let tmp = self.tmp(); let value = format!("value{tmp}"); @@ -1317,7 +1346,22 @@ impl Bindgen for Func<'_> { } results.push(Operand::SingleValue(value.into())); } - Instruction::CharFromI32 => todo!("implement instruction: {inst:?}"), + Instruction::CharFromI32 => { + // The canonical ABI rejects surrogates and values above + // U+10FFFF when lifting a char. + let tmp = self.tmp(); + let result = &format!("result{tmp}"); + let operand = &operands[0]; + quote_in! { self.body => + $['\r'] + $result := rune($operand) + if !$UTF8_VALID_RUNE($result) { + $(comment(["TODO(#8): Return an error if the return type allows it"])) + panic($ERRORS_NEW("invalid char value encountered")) + } + }; + results.push(Operand::SingleValue(result.into())); + } Instruction::F32FromCoreF32 => { let tmp = self.tmp(); let result = &format!("result{tmp}"); diff --git a/cmd/gravity/src/go/imports.rs b/cmd/gravity/src/go/imports.rs index 53ea3f2..94522d5 100644 --- a/cmd/gravity/src/go/imports.rs +++ b/cmd/gravity/src/go/imports.rs @@ -37,3 +37,4 @@ pub static WAZERO_API_ENCODE_F64: GoImport = pub static WAZERO_API_DECODE_F64: GoImport = GoImport("github.com/tetratelabs/wazero/api", "DecodeF64"); pub static REFLECT_VALUE_OF: GoImport = GoImport("reflect", "ValueOf"); +pub static UTF8_VALID_RUNE: GoImport = GoImport("unicode/utf8", "ValidRune"); diff --git a/cmd/gravity/src/go/type.rs b/cmd/gravity/src/go/type.rs index 24e47e3..c52005a 100644 --- a/cmd/gravity/src/go/type.rs +++ b/cmd/gravity/src/go/type.rs @@ -30,6 +30,8 @@ pub enum GoType { Float32, /// 64-bit floating point Float64, + /// Unicode code point (`rune`, an alias of `int32`) + Rune, /// String type String, /// Error type (represents Result) @@ -90,7 +92,8 @@ impl GoType { | GoType::Int32 | GoType::Int64 | GoType::Float32 - | GoType::Float64 => false, + | GoType::Float64 + | GoType::Rune => false, // String and slices allocate memory and need cleanup GoType::String | GoType::Slice(_) => true, @@ -150,6 +153,7 @@ impl FormatInto for &GoType { GoType::Int64 => tokens.append(static_literal("int64")), GoType::Float32 => tokens.append(static_literal("float32")), GoType::Float64 => tokens.append(static_literal("float64")), + GoType::Rune => tokens.append(static_literal("rune")), GoType::String => tokens.append(static_literal("string")), GoType::Error => tokens.append(static_literal("error")), GoType::Interface => tokens.append(static_literal("interface{}")), @@ -211,6 +215,7 @@ mod tests { (GoType::Int64, "int64"), (GoType::Float32, "float32"), (GoType::Float64, "float64"), + (GoType::Rune, "rune"), (GoType::String, "string"), (GoType::Error, "error"), (GoType::Interface, "interface{}"), diff --git a/cmd/gravity/src/lib.rs b/cmd/gravity/src/lib.rs index 0b4654c..58d095c 100644 --- a/cmd/gravity/src/lib.rs +++ b/cmd/gravity/src/lib.rs @@ -127,10 +127,7 @@ pub fn resolve_type(typ: &Type, resolve: &Resolve) -> GoType { Type::S64 => GoType::Int64, Type::F32 => GoType::Float32, Type::F64 => GoType::Float64, - Type::Char => { - // Is this a Go "rune"? - todo!("TODO(#6): resolve char type") - } + Type::Char => GoType::Rune, Type::String => GoType::String, Type::ErrorContext => todo!("TODO(#4): implement error context conversion"), diff --git a/cmd/gravity/tests/cmd/iface-method-returns-string.stdout b/cmd/gravity/tests/cmd/iface-method-returns-string.stdout index f0c8a04..e88fafd 100644 --- a/cmd/gravity/tests/cmd/iface-method-returns-string.stdout +++ b/cmd/gravity/tests/cmd/iface-method-returns-string.stdout @@ -6,6 +6,7 @@ import "context" import "errors" import "github.com/tetratelabs/wazero" import "github.com/tetratelabs/wazero/api" +import "unicode/utf8" import _ "embed" @@ -19,6 +20,39 @@ type IExampleRuntime interface { Arch( ctx context.Context, ) string + GetU32( + ctx context.Context, + ) uint32 + GetU8( + ctx context.Context, + ) uint8 + GetS8( + ctx context.Context, + ) int8 + GetU16( + ctx context.Context, + ) uint16 + GetS16( + ctx context.Context, + ) int16 + GetS32( + ctx context.Context, + ) int32 + GetU64( + ctx context.Context, + ) uint64 + GetS64( + ctx context.Context, + ) int64 + GetF32( + ctx context.Context, + ) float32 + GetF64( + ctx context.Context, + ) float64 + GetChar( + ctx context.Context, + ) rune Puts( ctx context.Context, msg string, @@ -72,6 +106,116 @@ func NewExampleFactory( }). Export("arch"). NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetU32(ctx, ) + result1 := uint32(value0) + return result1 + }). + Export("get-u32"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetU8(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-u8"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetS8(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-s8"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetU16(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-u16"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetS16(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-s16"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetS32(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-s32"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint64{ + value0 := runtime.GetU64(ctx, ) + value1 := uint64(value0) + return value1 + }). + Export("get-u64"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint64{ + value0 := runtime.GetS64(ctx, ) + value1 := uint64(value0) + return value1 + }). + Export("get-s64"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) float32{ + value0 := runtime.GetF32(ctx, ) + result1 := value0 + return result1 + }). + Export("get-f32"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) float64{ + value0 := runtime.GetF64(ctx, ) + result1 := value0 + return result1 + }). + Export("get-f64"). + NewFunctionBuilder(). + WithFunc(func( + ctx context.Context, + mod api.Module, + ) uint32{ + value0 := runtime.GetChar(ctx, ) + value1 := uint32(value0) + return value1 + }). + Export("get-char"). + NewFunctionBuilder(). WithFunc(func( ctx context.Context, mod api.Module, @@ -225,3 +369,161 @@ func (i *ExampleInstance) Hello( return value8, err8 } +func (i *ExampleInstance) CallGetU32( + ctx context.Context, +) uint32 { + raw0, err0 := i.module.ExportedFunction("call-get-u32").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := uint32(results0) + return result1 +} + +func (i *ExampleInstance) CallGetU8( + ctx context.Context, +) uint8 { + raw0, err0 := i.module.ExportedFunction("call-get-u8").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := uint8(api.DecodeU32(results0)) + return result1 +} + +func (i *ExampleInstance) CallGetS8( + ctx context.Context, +) int8 { + raw0, err0 := i.module.ExportedFunction("call-get-s8").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := int8(api.DecodeI32(results0)) + return result1 +} + +func (i *ExampleInstance) CallGetU16( + ctx context.Context, +) uint16 { + raw0, err0 := i.module.ExportedFunction("call-get-u16").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := uint16(api.DecodeU32(results0)) + return result1 +} + +func (i *ExampleInstance) CallGetS16( + ctx context.Context, +) int16 { + raw0, err0 := i.module.ExportedFunction("call-get-s16").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := int16(api.DecodeI32(results0)) + return result1 +} + +func (i *ExampleInstance) CallGetS32( + ctx context.Context, +) int32 { + raw0, err0 := i.module.ExportedFunction("call-get-s32").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := api.DecodeI32(results0) + return result1 +} + +func (i *ExampleInstance) CallGetU64( + ctx context.Context, +) uint64 { + raw0, err0 := i.module.ExportedFunction("call-get-u64").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + value1 := uint64(results0) + return value1 +} + +func (i *ExampleInstance) CallGetS64( + ctx context.Context, +) int64 { + raw0, err0 := i.module.ExportedFunction("call-get-s64").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := int64(results0) + return result1 +} + +func (i *ExampleInstance) CallGetF32( + ctx context.Context, +) float32 { + raw0, err0 := i.module.ExportedFunction("call-get-f32").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := api.DecodeF32(results0) + return result1 +} + +func (i *ExampleInstance) CallGetF64( + ctx context.Context, +) float64 { + raw0, err0 := i.module.ExportedFunction("call-get-f64").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := api.DecodeF64(results0) + return result1 +} + +func (i *ExampleInstance) CallGetChar( + ctx context.Context, +) rune { + raw0, err0 := i.module.ExportedFunction("call-get-char").Call(ctx, ) + // The return type doesn't contain an error so we panic if one is encountered + if err0 != nil { + panic(err0) + } + + results0 := raw0[0] + result1 := rune(results0) + if !utf8.ValidRune(result1) { + // TODO(#8): Return an error if the return type allows it + panic(errors.New("invalid char value encountered")) + } + return result1 +} + diff --git a/cmd/gravity/tests/cmd/instructions.stdout b/cmd/gravity/tests/cmd/instructions.stdout index 3766664..6cbe15c 100644 --- a/cmd/gravity/tests/cmd/instructions.stdout +++ b/cmd/gravity/tests/cmd/instructions.stdout @@ -120,7 +120,7 @@ func (i *InstructionsInstance) S8Roundtrip( val int8, ) int8 { arg0 := val - value0 := api.EncodeI32(int32(arg0)) + value0 := uint32(arg0) raw1, err1 := i.module.ExportedFunction("s8-roundtrip").Call(ctx, uint64(value0)) // The return type doesn't contain an error so we panic if one is encountered if err1 != nil { @@ -137,7 +137,7 @@ func (i *InstructionsInstance) U8Roundtrip( val uint8, ) uint8 { arg0 := val - value0 := api.EncodeI32(int32(arg0)) + value0 := uint32(arg0) raw1, err1 := i.module.ExportedFunction("u8-roundtrip").Call(ctx, uint64(value0)) // The return type doesn't contain an error so we panic if one is encountered if err1 != nil { @@ -154,7 +154,7 @@ func (i *InstructionsInstance) S16Roundtrip( val int16, ) int16 { arg0 := val - value0 := api.EncodeI32(int32(arg0)) + value0 := uint32(arg0) raw1, err1 := i.module.ExportedFunction("s16-roundtrip").Call(ctx, uint64(value0)) // The return type doesn't contain an error so we panic if one is encountered if err1 != nil { @@ -171,7 +171,7 @@ func (i *InstructionsInstance) U16Roundtrip( val uint16, ) uint16 { arg0 := val - value0 := api.EncodeI32(int32(arg0)) + value0 := uint32(arg0) raw1, err1 := i.module.ExportedFunction("u16-roundtrip").Call(ctx, uint64(value0)) // The return type doesn't contain an error so we panic if one is encountered if err1 != nil { @@ -188,7 +188,7 @@ func (i *InstructionsInstance) S32Roundtrip( val int32, ) int32 { arg0 := val - value0 := api.EncodeI32(arg0) + value0 := uint32(arg0) raw1, err1 := i.module.ExportedFunction("s32-roundtrip").Call(ctx, uint64(value0)) // The return type doesn't contain an error so we panic if one is encountered if err1 != nil { diff --git a/examples/iface-method-returns-string/example_test.go b/examples/iface-method-returns-string/example_test.go index 6e6a337..cd66ea2 100644 --- a/examples/iface-method-returns-string/example_test.go +++ b/examples/iface-method-returns-string/example_test.go @@ -3,16 +3,39 @@ package example import ( "context" "fmt" + "math" "runtime" "testing" ) type Runtime struct { msg string + + u8 uint8 + s8 int8 + u16 uint16 + s16 int16 + s32 int32 + u64 uint64 + s64 int64 + f32 float32 + f64 float64 + char rune } func (Runtime) Os(context.Context) string { return runtime.GOOS } func (Runtime) Arch(context.Context) string { return runtime.GOARCH } +func (Runtime) GetU32(context.Context) uint32 { return 42 } +func (r *Runtime) GetU8(context.Context) uint8 { return r.u8 } +func (r *Runtime) GetS8(context.Context) int8 { return r.s8 } +func (r *Runtime) GetU16(context.Context) uint16 { return r.u16 } +func (r *Runtime) GetS16(context.Context) int16 { return r.s16 } +func (r *Runtime) GetS32(context.Context) int32 { return r.s32 } +func (r *Runtime) GetU64(context.Context) uint64 { return r.u64 } +func (r *Runtime) GetS64(context.Context) int64 { return r.s64 } +func (r *Runtime) GetF32(context.Context) float32 { return r.f32 } +func (r *Runtime) GetF64(context.Context) float64 { return r.f64 } +func (r *Runtime) GetChar(context.Context) rune { return r.char } func (r *Runtime) Puts(_ context.Context, msg string) { r.msg = msg } func TestBasic(t *testing.T) { @@ -44,3 +67,101 @@ func TestBasic(t *testing.T) { t.Errorf("wanted: %s, but got: %s", wantPutsMsg, r.msg) } } + +func TestCallGetU32(t *testing.T) { + r := &Runtime{} + fac, err := NewExampleFactory(t.Context(), r) + if err != nil { + t.Fatal(err) + } + defer fac.Close(t.Context()) + + ins, err := fac.Instantiate(t.Context()) + if err != nil { + t.Fatal(err) + } + defer ins.Close(t.Context()) + + value := ins.CallGetU32(t.Context()) + + var want uint32 = 42 + if value != want { + t.Errorf("wanted: %d, but got: %d", want, value) + } +} + +// roundTrip sets each value as the host import's return value, calls the +// export that returns the import's result, and checks the value survives the +// trip through the guest. +func roundTrip[T comparable](t *testing.T, values []T, set func(*Runtime, T), call func(*ExampleInstance, context.Context) T) { + t.Helper() + r := &Runtime{} + fac, err := NewExampleFactory(t.Context(), r) + if err != nil { + t.Fatal(err) + } + defer fac.Close(t.Context()) + + ins, err := fac.Instantiate(t.Context()) + if err != nil { + t.Fatal(err) + } + defer ins.Close(t.Context()) + + for _, want := range values { + set(r, want) + if got := call(ins, t.Context()); got != want { + t.Errorf("wanted: %v, but got: %v", want, got) + } + } +} + +func TestImportReturnsU8(t *testing.T) { + roundTrip(t, []uint8{0, 1, 200, math.MaxUint8}, + func(r *Runtime, v uint8) { r.u8 = v }, (*ExampleInstance).CallGetU8) +} + +func TestImportReturnsS8(t *testing.T) { + roundTrip(t, []int8{math.MinInt8, -1, 0, 1, math.MaxInt8}, + func(r *Runtime, v int8) { r.s8 = v }, (*ExampleInstance).CallGetS8) +} + +func TestImportReturnsU16(t *testing.T) { + roundTrip(t, []uint16{0, 1, 65000, math.MaxUint16}, + func(r *Runtime, v uint16) { r.u16 = v }, (*ExampleInstance).CallGetU16) +} + +func TestImportReturnsS16(t *testing.T) { + roundTrip(t, []int16{math.MinInt16, -1, 0, 1, math.MaxInt16}, + func(r *Runtime, v int16) { r.s16 = v }, (*ExampleInstance).CallGetS16) +} + +func TestImportReturnsS32(t *testing.T) { + roundTrip(t, []int32{math.MinInt32, -42, -1, 0, 1, math.MaxInt32}, + func(r *Runtime, v int32) { r.s32 = v }, (*ExampleInstance).CallGetS32) +} + +func TestImportReturnsU64(t *testing.T) { + roundTrip(t, []uint64{0, 1, 1 << 40, math.MaxUint64}, + func(r *Runtime, v uint64) { r.u64 = v }, (*ExampleInstance).CallGetU64) +} + +func TestImportReturnsS64(t *testing.T) { + roundTrip(t, []int64{math.MinInt64, -(1 << 40), -1, 0, 1, math.MaxInt64}, + func(r *Runtime, v int64) { r.s64 = v }, (*ExampleInstance).CallGetS64) +} + +func TestImportReturnsF32(t *testing.T) { + roundTrip(t, []float32{0, 1.5, -2.25, math.SmallestNonzeroFloat32, math.MaxFloat32, float32(math.Inf(1)), float32(math.Inf(-1))}, + func(r *Runtime, v float32) { r.f32 = v }, (*ExampleInstance).CallGetF32) +} + +func TestImportReturnsF64(t *testing.T) { + roundTrip(t, []float64{0, 1.5, -2.25, math.SmallestNonzeroFloat64, math.MaxFloat64, math.Inf(1), math.Inf(-1)}, + func(r *Runtime, v float64) { r.f64 = v }, (*ExampleInstance).CallGetF64) +} + +func TestImportReturnsChar(t *testing.T) { + roundTrip(t, []rune{0, 'a', 'λ', '😀', 0xD7FF, 0xE000, 0x10FFFF}, + func(r *Runtime, v rune) { r.char = v }, (*ExampleInstance).CallGetChar) +} diff --git a/examples/iface-method-returns-string/src/lib.rs b/examples/iface-method-returns-string/src/lib.rs index b50bf52..96614b9 100644 --- a/examples/iface-method-returns-string/src/lib.rs +++ b/examples/iface-method-returns-string/src/lib.rs @@ -14,4 +14,48 @@ impl Guest for ExampleWorld { Ok("Hello, world!".into()) } + + fn call_get_u32() -> u32 { + runtime::get_u32() + } + + fn call_get_u8() -> u8 { + runtime::get_u8() + } + + fn call_get_s8() -> i8 { + runtime::get_s8() + } + + fn call_get_u16() -> u16 { + runtime::get_u16() + } + + fn call_get_s16() -> i16 { + runtime::get_s16() + } + + fn call_get_s32() -> i32 { + runtime::get_s32() + } + + fn call_get_u64() -> u64 { + runtime::get_u64() + } + + fn call_get_s64() -> i64 { + runtime::get_s64() + } + + fn call_get_f32() -> f32 { + runtime::get_f32() + } + + fn call_get_f64() -> f64 { + runtime::get_f64() + } + + fn call_get_char() -> char { + runtime::get_char() + } } diff --git a/examples/iface-method-returns-string/wit/example.wit b/examples/iface-method-returns-string/wit/example.wit index 5e82f65..bdf5e5c 100644 --- a/examples/iface-method-returns-string/wit/example.wit +++ b/examples/iface-method-returns-string/wit/example.wit @@ -4,6 +4,18 @@ interface runtime { os: func() -> string; arch: func() -> string; + get-u32: func() -> u32; + get-u8: func() -> u8; + get-s8: func() -> s8; + get-u16: func() -> u16; + get-s16: func() -> s16; + get-s32: func() -> s32; + get-u64: func() -> u64; + get-s64: func() -> s64; + get-f32: func() -> f32; + get-f64: func() -> f64; + get-char: func() -> char; + puts: func(msg: string); } @@ -11,4 +23,15 @@ world example { import runtime; export hello: func() -> result; + export call-get-u32: func() -> u32; + export call-get-u8: func() -> u8; + export call-get-s8: func() -> s8; + export call-get-u16: func() -> u16; + export call-get-s16: func() -> s16; + export call-get-s32: func() -> s32; + export call-get-u64: func() -> u64; + export call-get-s64: func() -> s64; + export call-get-f32: func() -> f32; + export call-get-f64: func() -> f64; + export call-get-char: func() -> char; }