full test params passing of exported functions

This commit is contained in:
Li Jie
2025-09-12 10:08:01 +08:00
parent 50d1d2e19a
commit cc65ee18b5
9 changed files with 565 additions and 42 deletions

View File

@@ -113,6 +113,9 @@ func (hw *cheaderWriter) processDependentTypes(t types.Type, visiting map[string
// For named types, handle the underlying type dependencies
underlying := typ.Underlying()
if structType, ok := underlying.(*types.Struct); ok {
if ssa.IsClosure(structType) {
return fmt.Errorf("closure type %s can't export to C header", typ.Obj().Name())
}
// For named struct types, handle field dependencies directly
for i := 0; i < structType.NumFields(); i++ {
field := structType.Field(i)
@@ -216,6 +219,9 @@ func (hw *cheaderWriter) goCTypeName(t types.Type) string {
case *types.Interface:
return "GoInterface"
case *types.Struct:
if ssa.IsClosure(typ) {
panic("closure type can't export to C header")
}
// For anonymous structs, generate a descriptive name
var fields []string
for i := 0; i < typ.NumFields(); i++ {
@@ -230,12 +236,41 @@ func (hw *cheaderWriter) goCTypeName(t types.Type) string {
return fmt.Sprintf("%s_%s", pkg.Name(), typ.Obj().Name())
case *types.Signature:
// Function types are represented as function pointers in C
// For simplicity, we use void* to represent function pointers
return "void*"
// Generate proper function pointer syntax
return hw.generateFunctionPointerType(typ)
}
panic(fmt.Errorf("unsupported type: %v", t))
}
// generateFunctionPointerType generates C function pointer type for Go function signatures
func (hw *cheaderWriter) generateFunctionPointerType(sig *types.Signature) string {
// Generate return type
var returnType string
results := sig.Results()
if results == nil || results.Len() == 0 {
returnType = "void"
} else if results.Len() == 1 {
returnType = hw.goCTypeName(results.At(0).Type())
} else {
panic("multiple return values can't export to C header")
}
// Generate parameter types
var paramTypes []string
params := sig.Params()
if params == nil || params.Len() == 0 {
paramTypes = []string{"void"}
} else {
for i := 0; i < params.Len(); i++ {
paramType := hw.goCTypeName(params.At(i).Type())
paramTypes = append(paramTypes, paramType)
}
}
// Return function pointer type: returnType (*)(paramType1, paramType2, ...)
return fmt.Sprintf("%s (*)(%s)", returnType, strings.Join(paramTypes, ", "))
}
// generateTypedef generates C typedef declaration for complex types
func (hw *cheaderWriter) generateTypedef(t types.Type) string {
switch typ := t.(type) {
@@ -248,10 +283,41 @@ func (hw *cheaderWriter) generateTypedef(t types.Type) string {
// For named struct types, generate the typedef directly
return hw.generateNamedStructTypedef(typ, structType)
}
cTypeName := hw.goCTypeName(typ)
// Special handling for function types
if sig, ok := underlying.(*types.Signature); ok {
// Generate return type
var returnType string
results := sig.Results()
if results == nil || results.Len() == 0 {
returnType = "void"
} else if results.Len() == 1 {
returnType = hw.goCTypeName(results.At(0).Type())
} else {
panic("multiple return values can't export to C header")
}
// Generate parameter types
var paramTypes []string
params := sig.Params()
if params == nil || params.Len() == 0 {
paramTypes = []string{"void"}
} else {
for i := 0; i < params.Len(); i++ {
paramType := hw.goCTypeName(params.At(i).Type())
paramTypes = append(paramTypes, paramType)
}
}
// Generate proper function pointer typedef: typedef returnType (*typeName)(params);
return fmt.Sprintf("typedef %s (*%s)(%s);", returnType, cTypeName, strings.Join(paramTypes, ", "))
}
// For other named types, create a typedef to the underlying type
underlyingCType := hw.goCTypeName(underlying)
if underlyingCType != "" {
cTypeName := hw.goCTypeName(typ)
return fmt.Sprintf("typedef %s %s;", underlyingCType, cTypeName)
}
}
@@ -312,6 +378,75 @@ func (hw *cheaderWriter) ensureArrayStruct(arr *types.Array) string {
return structName
}
// generateParameterDeclaration generates C parameter declaration for function parameters
func (hw *cheaderWriter) generateParameterDeclaration(paramType types.Type, paramName string) string {
var cType string
switch typ := paramType.(type) {
case *types.Array:
// Handle multidimensional arrays by collecting all dimensions
var dimensions []int64
baseType := types.Type(typ)
// Traverse all array dimensions
for {
if arr, ok := baseType.(*types.Array); ok {
dimensions = append(dimensions, arr.Len())
baseType = arr.Elem()
} else {
break
}
}
// Get base element type
elemType := hw.goCTypeName(baseType)
// For parameters, preserve all array dimensions
// In C, array parameters need special handling for syntax
cType = elemType
// Store dimensions for later use with parameter name
var dimStr strings.Builder
for _, dim := range dimensions {
dimStr.WriteString(fmt.Sprintf("[%d]", dim))
}
// For single dimension, we can use pointer syntax
if len(dimensions) == 1 {
cType = elemType + "*"
} else {
// For multi-dimensional, we need to handle it when adding parameter name
// Store the dimension info in a special way
cType = elemType + "ARRAY_DIMS" + dimStr.String()
}
case *types.Pointer:
pointeeType := hw.goCTypeName(typ.Elem())
cType = pointeeType + "*"
default:
// Regular types
cType = hw.goCTypeName(paramType)
}
// Handle special array dimension syntax
if strings.Contains(cType, "ARRAY_DIMS") {
parts := strings.Split(cType, "ARRAY_DIMS")
elemType := parts[0]
dimStr := parts[1]
if paramName == "" {
// For unnamed parameters, keep dimension info: type[dim1][dim2]
return elemType + dimStr
}
// For named parameters, use proper array syntax: type name[dim1][dim2]
return elemType + " " + paramName + dimStr
}
if paramName == "" {
return cType
}
return cType + " " + paramName
}
// generateFieldDeclaration generates C field declaration with correct array syntax
func (hw *cheaderWriter) generateFieldDeclaration(fieldType types.Type, fieldName string) string {
switch fieldType.(type) {
@@ -461,15 +596,9 @@ func (hw *cheaderWriter) writeFunctionDecl(fullName, linkName string, fn ssa.Fun
}
paramName := param.Name()
if paramName == "" {
paramName = fmt.Sprintf("param%d", i)
}
// Use generateFieldDeclaration logic for consistent parameter syntax
paramDecl := hw.generateFieldDeclaration(paramType, paramName)
// Remove the leading spaces and semicolon to get just the declaration
paramDecl = strings.TrimSpace(paramDecl)
paramDecl = strings.TrimSuffix(paramDecl, ";")
// Generate parameter declaration
paramDecl := hw.generateParameterDeclaration(paramType, paramName)
params = append(params, paramDecl)
}
@@ -571,7 +700,7 @@ func genHeader(p ssa.Program, pkgs []ssa.Package, w io.Writer) error {
link := exports[name] // link is cName
fn := pkg.FuncOf(link)
if fn == nil {
continue
return fmt.Errorf("function %s not found", link)
}
// Write function declaration with proper C types

View File

@@ -235,7 +235,7 @@ func TestGoCTypeName(t *testing.T) {
{
name: "signature type",
goType: types.NewSignature(nil, nil, nil, false),
expected: "void*",
expected: "void (*)(void)",
},
}
@@ -504,6 +504,153 @@ func TestProcessDependentTypesEdgeCases(t *testing.T) {
if err != nil {
t.Errorf("processSignatureTypes(no results) error = %v", err)
}
// Test function type (callback) parameters - IntCallback
intCallbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int]))
intCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
intCallbackSig := types.NewSignatureType(nil, nil, nil, intCallbackParams, intCallbackResults, false)
err = hw.writeTypedefRecursive(intCallbackSig, make(map[string]bool))
if err != nil {
t.Errorf("writeTypedefRecursive(IntCallback) error = %v", err)
}
// Test function type (callback) parameters - StringCallback
stringCallbackParams := types.NewTuple(types.NewVar(0, nil, "s", types.Typ[types.String]))
stringCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.String]))
stringCallbackSig := types.NewSignatureType(nil, nil, nil, stringCallbackParams, stringCallbackResults, false)
err = hw.writeTypedefRecursive(stringCallbackSig, make(map[string]bool))
if err != nil {
t.Errorf("writeTypedefRecursive(StringCallback) error = %v", err)
}
// Test function type (callback) parameters - VoidCallback
voidCallbackSig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
err = hw.writeTypedefRecursive(voidCallbackSig, make(map[string]bool))
if err != nil {
t.Errorf("writeTypedefRecursive(VoidCallback) error = %v", err)
}
// Test Named function type - this should trigger the function typedef generation
pkg := types.NewPackage("test", "test")
callbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int]))
callbackSig := types.NewSignatureType(nil, nil, nil, callbackParams, nil, false)
callbackTypeName := types.NewTypeName(0, pkg, "Callback", nil)
namedCallback := types.NewNamed(callbackTypeName, callbackSig, nil)
// Test Named function type with no parameters - NoParamCallback func() int
noParamCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
noParamCallbackSig := types.NewSignatureType(nil, nil, nil, nil, noParamCallbackResults, false)
noParamCallbackTypeName := types.NewTypeName(0, pkg, "NoParamCallback", nil)
namedNoParamCallback := types.NewNamed(noParamCallbackTypeName, noParamCallbackSig, nil)
err = hw.writeTypedef(namedCallback)
if err != nil {
t.Errorf("writeTypedef(named function) error = %v", err)
}
err = hw.writeTypedef(namedNoParamCallback)
if err != nil {
t.Errorf("writeTypedef(no param callback) error = %v", err)
}
// Verify the generated typedef contains function pointer syntax
output := hw.typeBuf.String()
if !strings.Contains(output, "test_Callback") {
t.Errorf("Expected named function typedef in output")
}
if !strings.Contains(output, "(*test_Callback)") {
t.Errorf("Expected function pointer syntax in typedef: %s", output)
}
if !strings.Contains(output, "test_NoParamCallback") {
t.Errorf("Expected no-param callback typedef in output")
}
if !strings.Contains(output, "(*test_NoParamCallback)(void)") {
t.Errorf("Expected no-param function pointer syntax in typedef: %s", output)
}
// Test function signature with unnamed parameters (like //export ProcessThreeUnnamedParams)
unnamedParams := types.NewTuple(
types.NewVar(0, nil, "", types.Typ[types.Int]),
types.NewVar(0, nil, "", types.Typ[types.String]),
types.NewVar(0, nil, "", types.Typ[types.Bool]),
)
unnamedResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Float64]))
unnamedSig := types.NewSignatureType(nil, nil, nil, unnamedParams, unnamedResults, false)
err = hw.writeTypedefRecursive(unnamedSig, make(map[string]bool))
if err != nil {
t.Errorf("writeTypedefRecursive(unnamed params) error = %v", err)
}
}
// Test generateParameterDeclaration function
func TestGenerateParameterDeclaration(t *testing.T) {
prog := ssa.NewProgram(nil)
hw := newCHeaderWriter(prog)
tests := []struct {
name string
paramType types.Type
paramName string
expected string
}{
{
name: "basic type with name",
paramType: types.Typ[types.Int],
paramName: "x",
expected: "intptr_t x",
},
{
name: "basic type without name",
paramType: types.Typ[types.Int],
paramName: "",
expected: "intptr_t",
},
{
name: "array type with name",
paramType: types.NewArray(types.Typ[types.Int], 5),
paramName: "arr",
expected: "intptr_t* arr",
},
{
name: "array type without name",
paramType: types.NewArray(types.Typ[types.Int], 5),
paramName: "",
expected: "intptr_t*",
},
{
name: "multidimensional array with name",
paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3),
paramName: "matrix",
expected: "intptr_t matrix[3][4]",
},
{
name: "multidimensional array without name",
paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3),
paramName: "",
expected: "intptr_t[3][4]",
},
{
name: "pointer type with name",
paramType: types.NewPointer(types.Typ[types.Int]),
paramName: "ptr",
expected: "intptr_t* ptr",
},
{
name: "pointer type without name",
paramType: types.NewPointer(types.Typ[types.Int]),
paramName: "",
expected: "intptr_t*",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := hw.generateParameterDeclaration(tt.paramType, tt.paramName)
if got != tt.expected {
t.Errorf("generateParameterDeclaration() = %q, want %q", got, tt.expected)
}
})
}
}
// Test generateNamedStructTypedef with forward declaration