This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch main
in repository ego.

View the commit online.

commit 1a13614ef083d831e58000527ef3ca6947099444
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 30 21:50:29 2026 -0600

    feat(ego-gen): add function pointer introspection and callback data model
    
    Add Typedecl wrapper, TypedeclFunctionPointer constant, and
    Type.IsFunctionPointer() for Eolian introspection. Add
    CallbackTypeData/CallbackParamData structs and buildCallbackTypeData
    for extracting callback signatures from Eolian function pointer types.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 cmd/ego-gen/callback.go       | 73 +++++++++++++++++++++++++++++++++++++
 cmd/ego-gen/eolian.go         | 49 +++++++++++++++++++++++++
 cmd/ego-gen/eolian_test.go    |  6 ++++
 cmd/ego-gen/generator.go      | 35 ++++++++++++++----
 cmd/ego-gen/generator_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 241 insertions(+), 6 deletions(-)

diff --git a/cmd/ego-gen/callback.go b/cmd/ego-gen/callback.go
new file mode 100644
index 0000000..6d4f63a
--- /dev/null
+++ b/cmd/ego-gen/callback.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+// isCallbackParam reports whether a method parameter is a function pointer type.
+func isCallbackParam(p *Parameter) bool {
+	pt := p.Type()
+	if pt == nil {
+		return false
+	}
+	return pt.IsFunctionPointer()
+}
+
+// buildCallbackTypeData extracts a CallbackTypeData from a function pointer parameter.
+func buildCallbackTypeData(p *Parameter) (*CallbackTypeData, error) {
+	pt := p.Type()
+	if pt == nil {
+		return nil, fmt.Errorf("parameter %q has nil type", p.Name())
+	}
+	td := pt.Typedecl()
+	if td == nil {
+		return nil, fmt.Errorf("parameter %q type has no typedecl", p.Name())
+	}
+	fp := td.FunctionPointer()
+	if fp == nil {
+		return nil, fmt.Errorf("parameter %q typedecl is not a function pointer", p.Name())
+	}
+
+	eolianName := td.Name()
+	goTrampoline := "egoCallback_" + eolianName
+	cTypedef := eolianName
+
+	var cbParams []ParamData
+	for _, cp := range fp.Parameters() {
+		cbParams = append(cbParams, buildParamData(cp))
+	}
+
+	var returnType, returnCType string
+	if rt := fp.ReturnType(FunctionMethod); rt != nil {
+		returnCType = rt.CType()
+		returnType = CTypeToGo(returnCType)
+		if returnCType == "void" || returnType == "" {
+			returnType = ""
+			returnCType = ""
+		}
+	}
+
+	var paramTypes []string
+	for _, cp := range cbParams {
+		if cp.IsEo {
+			paramTypes = append(paramTypes, "unsafe.Pointer")
+		} else {
+			paramTypes = append(paramTypes, cp.GoType)
+		}
+	}
+	goFuncType := "func(" + strings.Join(paramTypes, ", ") + ")"
+	if returnType != "" {
+		goFuncType += " " + returnType
+	}
+
+	return &CallbackTypeData{
+		EolianName:       eolianName,
+		GoTrampolineName: goTrampoline,
+		CTypedef:         cTypedef,
+		Params:           cbParams,
+		ReturnType:       returnType,
+		ReturnCType:      returnCType,
+		GoFuncType:       goFuncType,
+	}, nil
+}
diff --git a/cmd/ego-gen/eolian.go b/cmd/ego-gen/eolian.go
index 3aa1e3e..3e9843c 100644
--- a/cmd/ego-gen/eolian.go
+++ b/cmd/ego-gen/eolian.go
@@ -64,6 +64,13 @@ const (
 	BuiltinTypeIterator BuiltinType = C.EOLIAN_TYPE_BUILTIN_ITERATOR
 )
 
+// TypedeclType represents the kind of an Eolian type declaration.
+type TypedeclType int
+
+const (
+	TypedeclFunctionPointer TypedeclType = C.EOLIAN_TYPEDECL_FUNCTION_POINTER
+)
+
 // iterCollect drains an Eina_Iterator into a slice using the provided convert
 // function. It frees the iterator when done. Returns nil if iter is nil.
 func iterCollect[T any](iter *C.Eina_Iterator, convert func(unsafe.Pointer) T) []T {
@@ -330,6 +337,48 @@ func (t *Type) IsIterator() bool {
 	return t.BuiltinType() == BuiltinTypeIterator
 }
 
+// Typedecl returns the type declaration for this type, or nil.
+func (t *Type) Typedecl() *Typedecl {
+	td := C.eolian_type_typedecl_get(t.ptr)
+	if td == nil {
+		return nil
+	}
+	return &Typedecl{ptr: td}
+}
+
+// IsFunctionPointer reports whether this type is a function pointer typedef.
+func (t *Type) IsFunctionPointer() bool {
+	td := t.Typedecl()
+	if td == nil {
+		return false
+	}
+	return td.Type() == TypedeclFunctionPointer
+}
+
+// Typedecl wraps an Eolian_Typedecl pointer.
+type Typedecl struct {
+	ptr *C.Eolian_Typedecl
+}
+
+// Type returns the kind of this type declaration.
+func (td *Typedecl) Type() TypedeclType {
+	return TypedeclType(C.eolian_typedecl_type_get(td.ptr))
+}
+
+// FunctionPointer returns the Function representing this callback typedef.
+func (td *Typedecl) FunctionPointer() *Function {
+	f := C.eolian_typedecl_function_pointer_get(td.ptr)
+	if f == nil {
+		return nil
+	}
+	return &Function{ptr: f}
+}
+
+// Name returns the short name of this type declaration.
+func (td *Typedecl) Name() string {
+	return C.GoString(C.eolian_typedecl_short_name_get(td.ptr))
+}
+
 // Constructor wraps an Eolian_Constructor pointer.
 type Constructor struct {
 	ptr *C.Eolian_Constructor
diff --git a/cmd/ego-gen/eolian_test.go b/cmd/ego-gen/eolian_test.go
index a0a1d6d..3aba06f 100644
--- a/cmd/ego-gen/eolian_test.go
+++ b/cmd/ego-gen/eolian_test.go
@@ -182,6 +182,12 @@ func TestBuiltinTypeIteratorConstant(t *testing.T) {
 	}
 }
 
+func TestTypedeclFunctionPointerConstants(t *testing.T) {
+	if TypedeclFunctionPointer == 0 {
+		t.Error("TypedeclFunctionPointer should be non-zero")
+	}
+}
+
 // TestClassTypes verifies that every parsed class has a type value within the
 // known valid range (i.e. one of the four ClassType constants).
 func TestClassTypes(t *testing.T) {
diff --git a/cmd/ego-gen/generator.go b/cmd/ego-gen/generator.go
index ef5828d..0b0c37d 100644
--- a/cmd/ego-gen/generator.go
+++ b/cmd/ego-gen/generator.go
@@ -37,6 +37,8 @@ type ClassData struct {
 	HasIterators         bool                  // true if IteratorMethods is non-empty (controls import)
 	IteratorStructTypes  []IteratorMethodData  // one entry per distinct struct elem type (for C helpers)
 	GlobalFunctions []GlobalFunctionData // package-level functions with no Eo receiver
+	CallbackTypes   []CallbackTypeData   // distinct callback typedefs used by this class
+	HasCallbacks    bool                 // true if CallbackTypes is non-empty (controls imports/trampolines)
 }
 
 // ConstructorData describes a constructor option for an EFL class. Each entry
@@ -60,12 +62,13 @@ type ConstructorData struct {
 
 // MethodData describes a single method on an EFL class.
 type MethodData struct {
-	GoName      string
-	CName       string
-	Params      []ParamData
-	ReturnType  string         // Go type, empty if void
-	ReturnCType string
-	OutParams   []OutParamData // out-param struct returns
+	GoName         string
+	CName          string
+	Params         []ParamData
+	ReturnType     string             // Go type, empty if void
+	ReturnCType    string
+	OutParams      []OutParamData     // out-param struct returns
+	CallbackParams []CallbackParamData
 }
 
 // ParamData describes a single parameter of a method.
@@ -148,6 +151,26 @@ type IndexedPropertyData struct {
 	MultiSetValues []MultiValueData
 }
 
+// CallbackTypeData describes an Eolian function pointer typedef used as a
+// callback parameter. It holds all information needed to generate the C
+// trampoline declaration and the Go wrapper signature.
+type CallbackTypeData struct {
+	EolianName       string
+	GoTrampolineName string
+	CTypedef         string
+	Params           []ParamData
+	ReturnType       string
+	ReturnCType      string
+	GoFuncType       string
+}
+
+// CallbackParamData describes a single callback (function pointer) parameter
+// of a method, pairing the Go parameter name with its full type metadata.
+type CallbackParamData struct {
+	GoName       string
+	CallbackType *CallbackTypeData
+}
+
 // OutParamData describes a single out-parameter of a method that returns
 // a struct value. The codegen generates a C wrapper shim to decompose the
 // struct into individual fields for cgo consumption.
diff --git a/cmd/ego-gen/generator_test.go b/cmd/ego-gen/generator_test.go
index ae17367..9182629 100644
--- a/cmd/ego-gen/generator_test.go
+++ b/cmd/ego-gen/generator_test.go
@@ -1140,3 +1140,87 @@ func TestGenerateClass_OutParamSlice(t *testing.T) {
 		}
 	}
 }
+
+func TestGenerateClass_CallbackMethod(t *testing.T) {
+	g, outDir := newTestGenerator(t)
+
+	data := ClassData{
+		PackageName:   "layout",
+		GoName:        "Signal",
+		EolianName:    "Efl.Layout.Signal",
+		CClassName:    "EFL_LAYOUT_SIGNAL_CLASS",
+		CClassGetFunc: "efl_layout_signal_class_get",
+		CPrefix:       "efl_layout_signal",
+		EOHeaderFile:  "efl_layout_signal.eo.h",
+		IsAbstract:    true,
+		HasCallbacks:  true,
+		Methods: []MethodData{
+			{
+				GoName:      "SignalCallbackAdd",
+				CName:       "efl_layout_signal_callback_add",
+				ReturnType:  "bool",
+				ReturnCType: "Eina_Bool",
+				Params: []ParamData{
+					{GoName: "emission", GoType: "string", CType: "const char *"},
+					{GoName: "source", GoType: "string", CType: "const char *"},
+				},
+				CallbackParams: []CallbackParamData{
+					{
+						GoName: "fn",
+						CallbackType: &CallbackTypeData{
+							EolianName:       "EflLayoutSignalCb",
+							GoTrampolineName: "egoCallback_EflLayoutSignalCb",
+							CTypedef:         "EflLayoutSignalCb",
+							Params: []ParamData{
+								{GoName: "object", GoType: "unsafe.Pointer", CType: "Eo *", IsEo: true},
+								{GoName: "emission", GoType: "string", CType: "const char *"},
+								{GoName: "source", GoType: "string", CType: "const char *"},
+							},
+							GoFuncType: "func(unsafe.Pointer, string, string)",
+						},
+					},
+				},
+			},
+		},
+		CallbackTypes: []CallbackTypeData{
+			{
+				EolianName:       "EflLayoutSignalCb",
+				GoTrampolineName: "egoCallback_EflLayoutSignalCb",
+				CTypedef:         "EflLayoutSignalCb",
+				Params: []ParamData{
+					{GoName: "object", GoType: "unsafe.Pointer", CType: "Eo *", IsEo: true},
+					{GoName: "emission", GoType: "string", CType: "const char *"},
+					{GoName: "source", GoType: "string", CType: "const char *"},
+				},
+				GoFuncType: "func(unsafe.Pointer, string, string)",
+			},
+		},
+	}
+
+	if err := g.GenerateClass(data); err != nil {
+		t.Fatalf("GenerateClass: %v", err)
+	}
+
+	outFile := filepath.Join(outDir, "layout", "signal.go")
+	content, err := os.ReadFile(outFile)
+	if err != nil {
+		t.Fatalf("output file not found at %s: %v", outFile, err)
+	}
+
+	src := string(content)
+
+	mustContain := []string{
+		"extern void egoCallback_EflLayoutSignalCb(",
+		"extern void egoCallbackFree(",
+		"func (o *Signal) SignalCallbackAdd(emission string, source string, fn func(unsafe.Pointer, string, string)) bool",
+		"efl.CallbackRegister(fn)",
+		"efl_layout_signal_callback_add",
+		"egoCallbackFree",
+	}
+
+	for _, want := range mustContain {
+		if !strings.Contains(src, want) {
+			t.Errorf("output missing expected pattern %q\nfile content:\n%s", want, src)
+		}
+	}
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to