mirror of
https://github.com/golang/go
synced 2024-11-25 07:17:56 -07:00
cmd/cgo: Add support for C function pointers
* Add a new kind of Name, "fpvar" which stands for function pointer variable * When walking the AST, find functions used as expressions and create a new Name object for them * Track functions which are only used in expr contexts, and avoid generating bridge code for them R=golang-dev, minux.ma, fullung, rsc, iant CC=golang-dev https://golang.org/cl/9835047
This commit is contained in:
parent
469250fb77
commit
c18dc11ef2
@ -44,5 +44,6 @@ func Test5548(t *testing.T) { test5548(t) }
|
|||||||
func Test5603(t *testing.T) { test5603(t) }
|
func Test5603(t *testing.T) { test5603(t) }
|
||||||
func Test3250(t *testing.T) { test3250(t) }
|
func Test3250(t *testing.T) { test3250(t) }
|
||||||
func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
|
func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
|
||||||
|
func TestFpVar(t *testing.T) { testFpVar(t) }
|
||||||
|
|
||||||
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
|
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
|
||||||
|
50
misc/cgo/test/fpvar.go
Normal file
50
misc/cgo/test/fpvar.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file contains test cases for cgo with function pointer variables.
|
||||||
|
|
||||||
|
package cgotest
|
||||||
|
|
||||||
|
/*
|
||||||
|
typedef int (*intFunc) ();
|
||||||
|
|
||||||
|
int
|
||||||
|
bridge_int_func(intFunc f)
|
||||||
|
{
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
int fortytwo()
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func callBridge(f C.intFunc) int {
|
||||||
|
return int(C.bridge_int_func(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func callCBridge(f C.intFunc) C.int {
|
||||||
|
return C.bridge_int_func(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFpVar(t *testing.T) {
|
||||||
|
const expected = 42
|
||||||
|
f := C.intFunc(C.fortytwo)
|
||||||
|
res1 := C.bridge_int_func(f)
|
||||||
|
if r1 := int(res1); r1 != expected {
|
||||||
|
t.Errorf("got %d, want %d", r1, expected)
|
||||||
|
}
|
||||||
|
res2 := callCBridge(f)
|
||||||
|
if r2 := int(res2); r2 != expected {
|
||||||
|
t.Errorf("got %d, want %d", r2, expected)
|
||||||
|
}
|
||||||
|
r3 := callBridge(f)
|
||||||
|
if r3 != expected {
|
||||||
|
t.Errorf("got %d, want %d", r3, expected)
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,33 @@ function returns void). For example:
|
|||||||
n, err := C.sqrt(-1)
|
n, err := C.sqrt(-1)
|
||||||
_, err := C.voidFunc()
|
_, err := C.voidFunc()
|
||||||
|
|
||||||
|
Calling C function pointers is currently not supported, however you can
|
||||||
|
declare Go variables which hold C function pointers and pass them
|
||||||
|
back and forth between Go and C. C code may call function pointers
|
||||||
|
received from Go. For example:
|
||||||
|
|
||||||
|
package main
|
||||||
|
// typedef int (*intFunc) ();
|
||||||
|
//
|
||||||
|
// int
|
||||||
|
// bridge_int_func(intFunc f)
|
||||||
|
// {
|
||||||
|
// return f();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int fortytwo()
|
||||||
|
// {
|
||||||
|
// return 42;
|
||||||
|
// }
|
||||||
|
import "C"
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f := C.intFunc(C.fortytwo)
|
||||||
|
fmt.Println(int(C.bridge_int_func(f)))
|
||||||
|
// Output: 42
|
||||||
|
}
|
||||||
|
|
||||||
In C, a function argument written as a fixed size array
|
In C, a function argument written as a fixed size array
|
||||||
actually requires a pointer to the first element of the array.
|
actually requires a pointer to the first element of the array.
|
||||||
C compilers are aware of this calling convention and adjust
|
C compilers are aware of this calling convention and adjust
|
||||||
|
@ -548,25 +548,40 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mangleName does name mangling to translate names
|
||||||
|
// from the original Go source files to the names
|
||||||
|
// used in the final Go files generated by cgo.
|
||||||
|
func (p *Package) mangleName(n *Name) {
|
||||||
|
// When using gccgo variables have to be
|
||||||
|
// exported so that they become global symbols
|
||||||
|
// that the C code can refer to.
|
||||||
|
prefix := "_C"
|
||||||
|
if *gccgo && n.IsVar() {
|
||||||
|
prefix = "C"
|
||||||
|
}
|
||||||
|
n.Mangle = prefix + n.Kind + "_" + n.Go
|
||||||
|
}
|
||||||
|
|
||||||
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
|
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
|
||||||
// Go equivalents, now that we have figured out the meaning of all
|
// Go equivalents, now that we have figured out the meaning of all
|
||||||
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
|
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
|
||||||
// with full definitions instead of mangled names.
|
// with full definitions instead of mangled names.
|
||||||
func (p *Package) rewriteRef(f *File) {
|
func (p *Package) rewriteRef(f *File) {
|
||||||
|
// Keep a list of all the functions, to remove the ones
|
||||||
|
// only used as expressions and avoid generating bridge
|
||||||
|
// code for them.
|
||||||
|
functions := make(map[string]bool)
|
||||||
|
|
||||||
// Assign mangled names.
|
// Assign mangled names.
|
||||||
for _, n := range f.Name {
|
for _, n := range f.Name {
|
||||||
if n.Kind == "not-type" {
|
if n.Kind == "not-type" {
|
||||||
n.Kind = "var"
|
n.Kind = "var"
|
||||||
}
|
}
|
||||||
if n.Mangle == "" {
|
if n.Mangle == "" {
|
||||||
// When using gccgo variables have to be
|
p.mangleName(n)
|
||||||
// exported so that they become global symbols
|
}
|
||||||
// that the C code can refer to.
|
if n.Kind == "func" {
|
||||||
prefix := "_C"
|
functions[n.Go] = false
|
||||||
if *gccgo && n.Kind == "var" {
|
|
||||||
prefix = "C"
|
|
||||||
}
|
|
||||||
n.Mangle = prefix + n.Kind + "_" + n.Go
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,6 +605,7 @@ func (p *Package) rewriteRef(f *File) {
|
|||||||
error_(r.Pos(), "call of non-function C.%s", r.Name.Go)
|
error_(r.Pos(), "call of non-function C.%s", r.Name.Go)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
functions[r.Name.Go] = true
|
||||||
if r.Context == "call2" {
|
if r.Context == "call2" {
|
||||||
// Invent new Name for the two-result function.
|
// Invent new Name for the two-result function.
|
||||||
n := f.Name["2"+r.Name.Go]
|
n := f.Name["2"+r.Name.Go]
|
||||||
@ -606,13 +622,26 @@ func (p *Package) rewriteRef(f *File) {
|
|||||||
}
|
}
|
||||||
case "expr":
|
case "expr":
|
||||||
if r.Name.Kind == "func" {
|
if r.Name.Kind == "func" {
|
||||||
error_(r.Pos(), "must call C.%s", r.Name.Go)
|
// Function is being used in an expression, to e.g. pass around a C function pointer.
|
||||||
}
|
// Create a new Name for this Ref which causes the variable to be declared in Go land.
|
||||||
if r.Name.Kind == "type" {
|
fpName := "fp_" + r.Name.Go
|
||||||
|
name := f.Name[fpName]
|
||||||
|
if name == nil {
|
||||||
|
name = &Name{
|
||||||
|
Go: fpName,
|
||||||
|
C: r.Name.C,
|
||||||
|
Kind: "fpvar",
|
||||||
|
Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
|
||||||
|
}
|
||||||
|
p.mangleName(name)
|
||||||
|
f.Name[fpName] = name
|
||||||
|
}
|
||||||
|
r.Name = name
|
||||||
|
expr = ast.NewIdent(name.Mangle)
|
||||||
|
} else if r.Name.Kind == "type" {
|
||||||
// Okay - might be new(T)
|
// Okay - might be new(T)
|
||||||
expr = r.Name.Type.Go
|
expr = r.Name.Type.Go
|
||||||
}
|
} else if r.Name.Kind == "var" {
|
||||||
if r.Name.Kind == "var" {
|
|
||||||
expr = &ast.StarExpr{X: expr}
|
expr = &ast.StarExpr{X: expr}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,6 +673,14 @@ func (p *Package) rewriteRef(f *File) {
|
|||||||
}
|
}
|
||||||
*r.Expr = expr
|
*r.Expr = expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove functions only used as expressions, so their respective
|
||||||
|
// bridge functions are not generated.
|
||||||
|
for name, used := range functions {
|
||||||
|
if !used {
|
||||||
|
delete(f.Name, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gccBaseCmd returns the start of the compiler command line.
|
// gccBaseCmd returns the start of the compiler command line.
|
||||||
|
@ -80,13 +80,18 @@ type Name struct {
|
|||||||
Mangle string // name used in generated Go
|
Mangle string // name used in generated Go
|
||||||
C string // name used in C
|
C string // name used in C
|
||||||
Define string // #define expansion
|
Define string // #define expansion
|
||||||
Kind string // "const", "type", "var", "func", "not-type"
|
Kind string // "const", "type", "var", "fpvar", "func", "not-type"
|
||||||
Type *Type // the type of xxx
|
Type *Type // the type of xxx
|
||||||
FuncType *FuncType
|
FuncType *FuncType
|
||||||
AddError bool
|
AddError bool
|
||||||
Const string // constant definition
|
Const string // constant definition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsVar returns true if Kind is either "var" or "fpvar"
|
||||||
|
func (n *Name) IsVar() bool {
|
||||||
|
return n.Kind == "var" || n.Kind == "fpvar"
|
||||||
|
}
|
||||||
|
|
||||||
// A ExpFunc is an exported function, callable from C.
|
// A ExpFunc is an exported function, callable from C.
|
||||||
// Such functions are identified in the Go input file
|
// Such functions are identified in the Go input file
|
||||||
// by doc comments containing the line //export ExpName
|
// by doc comments containing the line //export ExpName
|
||||||
|
@ -97,7 +97,7 @@ func (p *Package) writeDefs() {
|
|||||||
cVars := make(map[string]bool)
|
cVars := make(map[string]bool)
|
||||||
for _, key := range nameKeys(p.Name) {
|
for _, key := range nameKeys(p.Name) {
|
||||||
n := p.Name[key]
|
n := p.Name[key]
|
||||||
if n.Kind != "var" {
|
if !n.IsVar() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,17 +113,26 @@ func (p *Package) writeDefs() {
|
|||||||
|
|
||||||
cVars[n.C] = true
|
cVars[n.C] = true
|
||||||
}
|
}
|
||||||
|
var amp string
|
||||||
|
var node ast.Node
|
||||||
|
if n.Kind == "var" {
|
||||||
|
amp = "&"
|
||||||
|
node = &ast.StarExpr{X: n.Type.Go}
|
||||||
|
} else if n.Kind == "fpvar" {
|
||||||
|
node = n.Type.Go
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("invalid var kind %q", n.Kind))
|
||||||
|
}
|
||||||
if *gccgo {
|
if *gccgo {
|
||||||
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
|
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
|
||||||
fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
|
fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
|
fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(fc, "\n")
|
fmt.Fprintf(fc, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
|
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
|
||||||
conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
|
conf.Fprint(fgo2, fset, node)
|
||||||
fmt.Fprintf(fgo2, "\n")
|
fmt.Fprintf(fgo2, "\n")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(fc, "\n")
|
fmt.Fprintf(fc, "\n")
|
||||||
|
Loading…
Reference in New Issue
Block a user