From 05e49d731ee950b61fea30bbc76ce843bce7298a Mon Sep 17 00:00:00 2001 From: iyear Date: Tue, 28 Mar 2023 16:13:23 +0800 Subject: [PATCH] ports/go: add more type conversions and tests --- source/ports/go_port/source/go_port.go | 63 +++++++++++++++++- source/ports/go_port/source/go_port_test.go | 73 +++++++++++++++++++-- 2 files changed, 129 insertions(+), 7 deletions(-) diff --git a/source/ports/go_port/source/go_port.go b/source/ports/go_port/source/go_port.go index 70cea447d..2d71dd645 100644 --- a/source/ports/go_port/source/go_port.go +++ b/source/ports/go_port/source/go_port.go @@ -44,6 +44,7 @@ import "C" import ( "errors" "fmt" + "reflect" "runtime" "sync" "unsafe" @@ -362,11 +363,40 @@ func getFunction(function string) (unsafe.Pointer, error) { } func goToValue(arg interface{}, ptr *unsafe.Pointer) { + // Create null + if arg == nil { + *ptr = C.metacall_value_create_null() + } + + // Create bool + if i, ok := arg.(bool); ok { + if i { + *ptr = C.metacall_value_create_bool(C.uchar(1)) + } else { + *ptr = C.metacall_value_create_bool(C.uchar(0)) + } + } + + // Create char + if i, ok := arg.(byte); ok { + *ptr = C.metacall_value_create_char((C.char)(i)) + } + + // Create short + if i, ok := arg.(int16); ok { + *ptr = C.metacall_value_create_short((C.short)(i)) + } + // Create int if i, ok := arg.(int); ok { *ptr = C.metacall_value_create_int((C.int)(i)) } + // Create long + if i, ok := arg.(int64); ok { + *ptr = C.metacall_value_create_long((C.long)(i)) + } + // Create float32 if i, ok := arg.(float32); ok { *ptr = C.metacall_value_create_float((C.float)(i)) @@ -384,16 +414,45 @@ func goToValue(arg interface{}, ptr *unsafe.Pointer) { *ptr = C.metacall_value_create_string(cStr, (C.size_t)(len(str))) } + // Create array + if v := reflect.ValueOf(arg); v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + length := v.Len() + cArgs := C.malloc(C.size_t(length) * C.size_t(unsafe.Sizeof(uintptr(0)))) + for index := 0; index < length; index++ { + goToValue(v.Index(index).Interface(), (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(cArgs))+uintptr(index)*PtrSizeInBytes))) + } + *ptr = C.metacall_value_create_array((*unsafe.Pointer)(cArgs), (C.size_t)(length)) + } + // TODO: Add more types } func valueToGo(value unsafe.Pointer) interface{} { switch C.metacall_value_id(value) { + case C.METACALL_NULL: + { + return nil + } + case C.METACALL_BOOL: + { + return C.metacall_value_to_bool(value) != C.uchar(0) + } + case C.METACALL_CHAR: + { + return byte(C.metacall_value_to_char(value)) + } + case C.METACALL_SHORT: + { + return int16(C.metacall_value_to_short(value)) + } case C.METACALL_INT: { return int(C.metacall_value_to_int(value)) } - + case C.METACALL_LONG: + { + return int64(C.metacall_value_to_long(value)) + } case C.METACALL_FLOAT: { return float32(C.metacall_value_to_float(value)) @@ -415,7 +474,7 @@ func valueToGo(value unsafe.Pointer) interface{} { array := make([]interface{}, arraySize) for iterator := C.size_t(0); iterator < arraySize; iterator++ { - currentValue := (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(arrayValue))+uintptr(iterator*PtrSizeInBytes))) + currentValue := (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(arrayValue)) + uintptr(iterator*PtrSizeInBytes))) array[iterator] = valueToGo(*currentValue) } diff --git a/source/ports/go_port/source/go_port_test.go b/source/ports/go_port/source/go_port_test.go index 5c27260a1..35cb76830 100644 --- a/source/ports/go_port/source/go_port_test.go +++ b/source/ports/go_port/source/go_port_test.go @@ -3,8 +3,10 @@ package metacall import ( "log" "os" + "reflect" "sync" "testing" + "unsafe" ) func TestMain(m *testing.M) { @@ -13,12 +15,12 @@ func TestMain(m *testing.M) { } // if benchmark { - buffer := "module.exports = { benchmark: async x => x }" + buffer := "module.exports = { benchmark: async x => x }" - if err := LoadFromMemory("node", buffer); err != nil { - log.Fatal(err) - return - } + if err := LoadFromMemory("node", buffer); err != nil { + log.Fatal(err) + return + } // } code := m.Run() @@ -123,6 +125,67 @@ func TestNodeJSAwait(t *testing.T) { wg.Wait() } +func TestValues(t *testing.T) { + tests := []struct { + name string + input interface{} + want interface{} + }{ + {"null", nil, nil}, + {"bool_true", true, true}, + {"bool_false", false, false}, + {"char", byte('H'), byte('H')}, + {"char_min", byte(127), byte(127)}, + {"char_max", byte(128), byte(128)}, + {"short", int16(1), int16(1)}, + {"short_min", int16(-32768), int16(-32768)}, + {"short_max", int16(32767), int16(32767)}, + {"int", int(1), int(1)}, + {"int_min", int(-2147483648), int(-2147483648)}, + {"int_max", int(2147483647), int(2147483647)}, + {"long", int64(3), int64(3)}, + {"long_min", int64(-9223372036854775808), int64(-9223372036854775808)}, + {"long_max", int64(9223372036854775807), int64(9223372036854775807)}, + {"float", float32(1.0), float32(1.0)}, + {"float_min", float32(1.2e-38), float32(1.2e-38)}, + {"float_max", float32(3.4e+38), float32(3.4e+38)}, + {"double", float64(1.0), float64(1.0)}, + {"double_min", float64(2.3e-308), float64(2.3e-308)}, + {"double_max", float64(1.7e+308), float64(1.7e+308)}, + {"string", "hello", "hello"}, + {"array", [3]interface{}{1, 2, 3}, []interface{}{1, 2, 3}}, + {"array_bool", [3]bool{true, false, true}, []interface{}{true, false, true}}, + {"array_char", [3]byte{'1', '2', '3'}, []interface{}{byte('1'), byte('2'), byte('3')}}, + {"array_short", [3]int16{1, 2, 3}, []interface{}{int16(1), int16(2), int16(3)}}, + {"array_int", [3]int{1, 2, 3}, []interface{}{int(1), int(2), int(3)}}, + {"array_float", [3]float32{1.0, 2.0, 3.0}, []interface{}{float32(1.0), float32(2.0), float32(3.0)}}, + {"array_double", [3]float64{1.0, 2.0, 3.0}, []interface{}{float64(1.0), float64(2.0), float64(3.0)}}, + {"array_string", [3]string{"1", "2", "3"}, []interface{}{"1", "2", "3"}}, + {"slice", []interface{}{1, 2, 3}, []interface{}{1, 2, 3}}, + {"slice_bool", []bool{true, false, true}, []interface{}{true, false, true}}, + {"slice_char", []byte{'1', '2', '3'}, []interface{}{byte('1'), byte('2'), byte('3')}}, + {"slice_short", []int16{1, 2, 3}, []interface{}{int16(1), int16(2), int16(3)}}, + {"slice_int", []int{1, 2, 3}, []interface{}{int(1), int(2), int(3)}}, + {"slice_float", []float32{1.0, 2.0, 3.0}, []interface{}{float32(1.0), float32(2.0), float32(3.0)}}, + {"slice_double", []float64{1.0, 2.0, 3.0}, []interface{}{float64(1.0), float64(2.0), float64(3.0)}}, + {"slice_string", []string{"1", "2", "3"}, []interface{}{"1", "2", "3"}}, + } + + for _, tt := range tests { + var ptr unsafe.Pointer + goToValue(tt.input, &ptr) + + if ptr == nil { + t.Errorf("invalid pointer: %s", tt.name) + return + } + + if v := valueToGo(ptr); !reflect.DeepEqual(v, tt.want) { + t.Errorf("name: %s, input: %T,%v, want: %T,%v, got: %T,%v", tt.name, tt.input, tt.input, tt.want, tt.want, v, v) + } + } +} + func benchmarkNodeJS(b *testing.B, n int) { var wg sync.WaitGroup