Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

458 lines
14 KiB
Go

package languages
import (
"strconv"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
sitter "github.com/zzet/gortex/internal/parser/tsitter"
)
// C/C++ function-pointer dispatch binding. A command-table / ops-struct /
// vtable pattern registers concrete functions into a struct's
// function-pointer fields (`static struct cmd cmds[] = {{"add", cmd_add}}`)
// and dispatches them indirectly (`cmds[i].fn(argc, argv)`). The static
// call graph cannot connect the dispatch to the concrete function because
// the field holds a runtime pointer. This pass records the (struct, field)
// registrations and the dispatch sites; the resolver pairs them by slot and
// fans out a call edge to every registered function.
// fnPtrDispatchViaTag marks a dispatch placeholder; fnPtrRegViaTag marks a
// registration carrier. Both must match the resolver's constants.
const (
fnPtrDispatchViaTag = "fn-pointer-dispatch"
fnPtrRegViaTag = "fn-pointer-reg"
)
// captureCFnPointerDispatch runs the extractor passes for the C/C++
// function-pointer dispatch synthesizer. Shared by the C and C++ extractors.
func captureCFnPointerDispatch(result *parser.ExtractionResult, root *sitter.Node, filePath string, src []byte) {
if root == nil || result == nil {
return
}
// Pass 1: fn-pointer typedefs.
typedefs := cFnPtrTypedefs(root, src)
// Pass 2: struct fn-pointer fields (ordered fields + fn-ptr set).
fields := cStructFnPtrFields(root, src, typedefs)
if len(fields) == 0 {
return
}
// Variable → struct type (file scope), for dispatch receiver inference.
varTypes := cVarStructTypes(root, src)
// Pass 3: registrations (initializers + assignments).
cEmitRegistrations(result, root, filePath, src, fields, varTypes)
// Pass 4: dispatch sites.
cEmitDispatch(result, root, filePath, src, fields, varTypes)
}
// structFields holds a struct's ordered field names and its fn-pointer set.
type structFields struct {
order []string
fnptr map[string]bool
}
// cFnPtrTypedefs collects the names of `typedef RET (*NAME)(...)` fn-pointer
// typedefs.
func cFnPtrTypedefs(root *sitter.Node, src []byte) map[string]bool {
out := map[string]bool{}
cFnPtrWalk(root, func(n *sitter.Node) {
if n.Type() != "type_definition" {
return
}
decl := n.ChildByFieldName("declarator")
if decl == nil || decl.Type() != "function_declarator" {
return
}
inner := decl.ChildByFieldName("declarator")
if inner == nil || inner.Type() != "parenthesized_declarator" {
return
}
if name := cTypeIdentifierIn(inner, src); name != "" {
out[name] = true
}
})
return out
}
// cStructFnPtrFields maps a struct type name to its ordered fields and the
// subset that are function pointers.
func cStructFnPtrFields(root *sitter.Node, src []byte, typedefs map[string]bool) map[string]*structFields {
out := map[string]*structFields{}
cFnPtrWalk(root, func(n *sitter.Node) {
if n.Type() != "struct_specifier" && n.Type() != "union_specifier" {
return
}
nameNode := n.ChildByFieldName("name")
body := n.ChildByFieldName("body")
if nameNode == nil || body == nil {
return
}
structName := nameNode.Content(src)
sf := &structFields{fnptr: map[string]bool{}}
for i, _nc := 0, int(body.NamedChildCount()); i < _nc; i++ {
fd := body.NamedChild(i)
if fd == nil || fd.Type() != "field_declaration" {
continue
}
name := cFieldName(fd, src)
if name == "" {
continue
}
sf.order = append(sf.order, name)
if cFieldIsFnPtr(fd, src, typedefs) {
sf.fnptr[name] = true
}
}
if len(sf.fnptr) > 0 {
out[structName] = sf
}
})
return out
}
// cVarStructTypes maps a variable name to its struct type from declarations
// and parameters (`struct cmd cmds[]`, `struct cmd *c`).
func cVarStructTypes(root *sitter.Node, src []byte) map[string]string {
out := map[string]string{}
cFnPtrWalk(root, func(n *sitter.Node) {
switch n.Type() {
case "declaration", "parameter_declaration", "field_declaration":
default:
return
}
st := cStructTypeOf(n, src)
if st == "" {
return
}
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
c := n.NamedChild(i)
if c == nil {
continue
}
switch c.Type() {
case "init_declarator", "array_declarator", "pointer_declarator", "identifier":
if name := cDeclName(c, src); name != "" {
out[name] = st
}
}
}
})
return out
}
// cEmitRegistrations records concrete functions bound to (struct, field)
// slots, from struct initializers and `x.field = fn` assignments.
func cEmitRegistrations(result *parser.ExtractionResult, root *sitter.Node, filePath string, src []byte, fields map[string]*structFields, varTypes map[string]string) {
cFnPtrWalk(root, func(n *sitter.Node) {
switch n.Type() {
case "declaration":
st := cStructTypeOf(n, src)
sf := fields[st]
if sf == nil {
return
}
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
id := n.NamedChild(i)
if id == nil || id.Type() != "init_declarator" {
continue
}
val := id.ChildByFieldName("value")
if val == nil || val.Type() != "initializer_list" {
continue
}
declTy := id.ChildByFieldName("declarator")
if declTy != nil && declTy.Type() == "array_declarator" {
// Array of structs: each element is a struct initializer.
for j, _nc := 0, int(val.NamedChildCount()); j < _nc; j++ {
if el := val.NamedChild(j); el != nil && el.Type() == "initializer_list" {
cEmitStructInit(result, filePath, src, st, sf, el)
}
}
} else {
cEmitStructInit(result, filePath, src, st, sf, val)
}
}
case "assignment_expression":
cEmitFieldAssignment(result, filePath, src, fields, varTypes, n)
}
})
}
// cEmitStructInit emits registrations for one struct initializer, mapping
// positional values by field order and designated values by field name.
func cEmitStructInit(result *parser.ExtractionResult, filePath string, src []byte, st string, sf *structFields, init *sitter.Node) {
pos := 0
for i, _nc := 0, int(init.NamedChildCount()); i < _nc; i++ {
el := init.NamedChild(i)
if el == nil {
continue
}
if el.Type() == "initializer_pair" {
field := cDesignatorField(el, src)
val := el.ChildByFieldName("value")
if field != "" && sf.fnptr[field] {
cEmitReg(result, filePath, src, st, field, val, init)
}
continue
}
// Positional element → the field at this position.
if pos < len(sf.order) {
field := sf.order[pos]
if sf.fnptr[field] {
cEmitReg(result, filePath, src, st, field, el, init)
}
}
pos++
}
}
// cEmitFieldAssignment handles `recv.field = fn` / `recv->field = fn` and
// the field-copy `a.field = b.field`.
func cEmitFieldAssignment(result *parser.ExtractionResult, filePath string, src []byte, fields map[string]*structFields, varTypes map[string]string, assign *sitter.Node) {
left := assign.ChildByFieldName("left")
right := assign.ChildByFieldName("right")
if left == nil || right == nil || left.Type() != "field_expression" {
return
}
st, field := cFieldExprSlot(left, src, varTypes, fields)
if st == "" || field == "" {
return
}
if right.Type() == "field_expression" {
// Field copy: a.field ← b.field.
fromSt, fromField := cFieldExprSlot(right, src, varTypes, fields)
if fromSt == "" || fromField == "" {
return
}
result.Edges = append(result.Edges, &graph.Edge{
From: filePath, To: "unresolved::*." + fromField, Kind: graph.EdgeReferences,
FilePath: filePath, Line: int(assign.StartPoint().Row) + 1,
Meta: map[string]any{
"via": fnPtrRegViaTag, "fnptr_struct": st, "fnptr_field": field,
"fnptr_copy_struct": fromSt, "fnptr_copy_field": fromField,
},
})
return
}
cEmitReg(result, filePath, src, st, field, right, assign)
}
// cEmitReg emits a registration carrier edge for a concrete function value
// bound to a (struct, field) slot.
func cEmitReg(result *parser.ExtractionResult, filePath string, src []byte, st, field string, val, site *sitter.Node) {
fn := cFnValueName(val, src)
if fn == "" {
return
}
result.Edges = append(result.Edges, &graph.Edge{
From: filePath, To: "unresolved::*." + fn, Kind: graph.EdgeReferences,
FilePath: filePath, Line: int(site.StartPoint().Row) + 1,
Meta: map[string]any{"via": fnPtrRegViaTag, "fnptr_struct": st, "fnptr_field": field, "fnptr_fn": fn},
})
}
// cEmitDispatch stamps a placeholder per indirect dispatch through a known
// fn-pointer field.
func cEmitDispatch(result *parser.ExtractionResult, root *sitter.Node, filePath string, src []byte, fields map[string]*structFields, varTypes map[string]string) {
funcRanges := buildFuncRanges(result)
seen := map[string]bool{}
cFnPtrWalk(root, func(call *sitter.Node) {
if call.Type() != "call_expression" {
return
}
fn := call.ChildByFieldName("function")
if fn == nil || fn.Type() != "field_expression" {
return
}
st, field := cFieldExprSlot(fn, src, varTypes, fields)
if st == "" || field == "" {
return
}
line := int(call.StartPoint().Row) + 1
from := findEnclosingFunc(funcRanges, line)
if from == "" {
return
}
k := from + "\x00" + st + "\x00" + field + "\x00" + strconv.Itoa(line)
if seen[k] {
return
}
seen[k] = true
result.Edges = append(result.Edges, &graph.Edge{
From: from, To: "unresolved::*." + field, Kind: graph.EdgeCalls,
FilePath: filePath, Line: line,
Meta: map[string]any{"via": fnPtrDispatchViaTag, "fnptr_struct": st, "fnptr_field": field},
})
})
}
// cFieldExprSlot resolves a field_expression (`recv.field` / `recv->field` /
// `table[i].field`) to a (struct type, field) slot when field is a known
// fn-pointer field of recv's struct type.
func cFieldExprSlot(fe *sitter.Node, src []byte, varTypes map[string]string, fields map[string]*structFields) (string, string) {
fieldNode := fe.ChildByFieldName("field")
if fieldNode == nil {
return "", ""
}
field := fieldNode.Content(src)
base := cBaseVar(fe.ChildByFieldName("argument"), src)
if base == "" {
return "", ""
}
st := varTypes[base]
if st == "" {
return "", ""
}
sf := fields[st]
if sf == nil || !sf.fnptr[field] {
return "", ""
}
return st, field
}
// cBaseVar returns the base variable name of a receiver expression:
// `cmds` for `cmds[i]`, `c` for `c`, unwrapping `(*c)` and subscripts.
func cBaseVar(recv *sitter.Node, src []byte) string {
for recv != nil {
switch recv.Type() {
case "identifier":
return recv.Content(src)
case "subscript_expression":
arg := recv.ChildByFieldName("argument")
if arg == nil && recv.NamedChildCount() > 0 {
arg = recv.NamedChild(0)
}
recv = arg
case "parenthesized_expression", "pointer_expression":
if recv.NamedChildCount() == 0 {
return ""
}
recv = recv.NamedChild(0)
case "field_expression":
recv = recv.ChildByFieldName("argument")
default:
return ""
}
}
return ""
}
// cFnValueName returns the function name of a value identifier (`cmd_add`),
// unwrapping a leading `&`.
func cFnValueName(val *sitter.Node, src []byte) string {
if val == nil {
return ""
}
if val.Type() == "pointer_expression" && val.NamedChildCount() > 0 {
val = val.NamedChild(0)
}
if val.Type() == "identifier" {
return val.Content(src)
}
return ""
}
// cDesignatorField returns the field name of an `initializer_pair`'s
// `.field =` designator.
func cDesignatorField(pair *sitter.Node, src []byte) string {
for i, _nc := 0, int(pair.NamedChildCount()); i < _nc; i++ {
d := pair.NamedChild(i)
if d != nil && d.Type() == "field_designator" && d.NamedChildCount() > 0 {
return d.NamedChild(0).Content(src)
}
}
return ""
}
// cStructTypeOf returns the `struct X` type name declared by a declaration /
// parameter / field node, or "".
func cStructTypeOf(n *sitter.Node, src []byte) string {
t := n.ChildByFieldName("type")
if t == nil {
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
if c := n.NamedChild(i); c != nil && (c.Type() == "struct_specifier" || c.Type() == "union_specifier") {
t = c
break
}
}
}
if t == nil {
return ""
}
if t.Type() == "struct_specifier" || t.Type() == "union_specifier" {
if nm := t.ChildByFieldName("name"); nm != nil {
return nm.Content(src)
}
}
return ""
}
// cFieldName returns the declared field name of a field_declaration.
func cFieldName(fd *sitter.Node, src []byte) string {
d := fd.ChildByFieldName("declarator")
if d == nil {
return ""
}
return cDeclName(d, src)
}
// cFieldIsFnPtr reports whether a field_declaration declares a function
// pointer — inline `RET (*f)(...)` or a fn-pointer typedef'd type.
func cFieldIsFnPtr(fd *sitter.Node, src []byte, typedefs map[string]bool) bool {
if d := fd.ChildByFieldName("declarator"); d != nil && d.Type() == "function_declarator" {
return true
}
if t := fd.ChildByFieldName("type"); t != nil && t.Type() == "type_identifier" && typedefs[t.Content(src)] {
return true
}
return false
}
// cDeclName walks a declarator chain to its innermost identifier name.
func cDeclName(decl *sitter.Node, src []byte) string {
for decl != nil {
switch decl.Type() {
case "identifier", "field_identifier":
return decl.Content(src)
case "function_declarator", "array_declarator", "pointer_declarator", "init_declarator":
decl = decl.ChildByFieldName("declarator")
case "parenthesized_declarator":
if decl.NamedChildCount() == 0 {
return ""
}
decl = decl.NamedChild(0)
default:
return ""
}
}
return ""
}
// cTypeIdentifierIn returns the first type_identifier descendant of n.
func cTypeIdentifierIn(n *sitter.Node, src []byte) string {
if n == nil {
return ""
}
if n.Type() == "type_identifier" {
return n.Content(src)
}
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
if x := cTypeIdentifierIn(n.NamedChild(i), src); x != "" {
return x
}
}
return ""
}
// cFnPtrWalk visits n and all its named descendants.
func cFnPtrWalk(n *sitter.Node, fn func(*sitter.Node)) {
if n == nil {
return
}
fn(n)
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
cFnPtrWalk(n.NamedChild(i), fn)
}
}