cgo: add support for C.CString and related functions
Этот коммит содержится в:
родитель
6bd18af5ef
коммит
c31aef06ba
14 изменённых файлов: 238 добавлений и 3 удалений
42
cgo/cgo.go
42
cgo/cgo.go
|
@ -165,10 +165,35 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||||
|
|
||||||
// First part of the generated Go file. Written here as Go because that's much
|
// First part of the generated Go file. Written here as Go because that's much
|
||||||
// easier than constructing the entire AST in memory.
|
// easier than constructing the entire AST in memory.
|
||||||
|
// The string/bytes functions below implement C.CString etc. To make sure the
|
||||||
|
// runtime doesn't need to know the C int type, lengths are converted to uintptr
|
||||||
|
// first.
|
||||||
|
// These functions will be modified to get a "C." prefix, so the source below
|
||||||
|
// doesn't reflect the final AST.
|
||||||
const generatedGoFilePrefix = `
|
const generatedGoFilePrefix = `
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func __GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func __GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
|
@ -219,6 +244,23 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||||
// This is always a bug in the cgo package.
|
// This is always a bug in the cgo package.
|
||||||
panic("unexpected error: " + err.Error())
|
panic("unexpected error: " + err.Error())
|
||||||
}
|
}
|
||||||
|
// If the Comments field is not set to nil, the fmt package will get
|
||||||
|
// confused about where comments should go.
|
||||||
|
p.generated.Comments = nil
|
||||||
|
// Adjust some of the functions in there.
|
||||||
|
for _, decl := range p.generated.Decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
switch decl.Name.Name {
|
||||||
|
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes":
|
||||||
|
// Adjust the name to have a "C." prefix so it is correctly
|
||||||
|
// resolved.
|
||||||
|
decl.Name.Name = "C." + decl.Name.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Patch some types, for example *C.char in C.CString.
|
||||||
|
astutil.Apply(p.generated, p.walker, nil)
|
||||||
|
|
||||||
// Find all C.* symbols.
|
// Find all C.* symbols.
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
|
20
cgo/testdata/basic.out.go
предоставленный
20
cgo/testdata/basic.out.go
предоставленный
|
@ -4,6 +4,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
type C.int16_t = int16
|
type C.int16_t = int16
|
||||||
type C.int32_t = int32
|
type C.int32_t = int32
|
||||||
type C.int64_t = int64
|
type C.int64_t = int64
|
||||||
|
|
20
cgo/testdata/const.out.go
предоставленный
20
cgo/testdata/const.out.go
предоставленный
|
@ -4,6 +4,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
const C.bar = C.foo
|
const C.bar = C.foo
|
||||||
const C.foo = 3
|
const C.foo = 3
|
||||||
|
|
||||||
|
|
20
cgo/testdata/errors.out.go
предоставленный
20
cgo/testdata/errors.out.go
предоставленный
|
@ -18,6 +18,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|
||||||
type C.int16_t = int16
|
type C.int16_t = int16
|
||||||
|
|
20
cgo/testdata/flags.out.go
предоставленный
20
cgo/testdata/flags.out.go
предоставленный
|
@ -9,6 +9,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
const C.BAR = 3
|
const C.BAR = 3
|
||||||
const C.FOO_H = 1
|
const C.FOO_H = 1
|
||||||
|
|
||||||
|
|
20
cgo/testdata/symbols.out.go
предоставленный
20
cgo/testdata/symbols.out.go
предоставленный
|
@ -4,6 +4,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
//export foo
|
//export foo
|
||||||
func C.foo(a C.int, b C.int) C.int
|
func C.foo(a C.int, b C.int) C.int
|
||||||
|
|
||||||
|
|
20
cgo/testdata/types.out.go
предоставленный
20
cgo/testdata/types.out.go
предоставленный
|
@ -4,6 +4,26 @@ import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname C.CString runtime.cgo_CString
|
||||||
|
func C.CString(string) *C.char
|
||||||
|
|
||||||
|
//go:linkname C.GoString runtime.cgo_GoString
|
||||||
|
func C.GoString(*C.char) string
|
||||||
|
|
||||||
|
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||||
|
func C.__GoStringN(*C.char, uintptr) string
|
||||||
|
|
||||||
|
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||||
|
return C.__GoStringN(cstr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||||
|
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||||
|
|
||||||
|
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||||
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
|
||||||
const C.option2A = 20
|
const C.option2A = 20
|
||||||
const C.optionA = 0
|
const C.optionA = 0
|
||||||
const C.optionB = 1
|
const C.optionB = 1
|
||||||
|
|
|
@ -42,6 +42,9 @@ func memzero(ptr unsafe.Pointer, size uintptr)
|
||||||
//export strlen
|
//export strlen
|
||||||
func strlen(ptr unsafe.Pointer) uintptr
|
func strlen(ptr unsafe.Pointer) uintptr
|
||||||
|
|
||||||
|
//export malloc
|
||||||
|
func malloc(size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
// Compare two same-size buffers for equality.
|
// Compare two same-size buffers for equality.
|
||||||
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
for i := uintptr(0); i < n; i++ {
|
for i := uintptr(0); i < n; i++ {
|
||||||
|
|
|
@ -13,9 +13,6 @@ func libc_write(fd int32, buf unsafe.Pointer, count uint) int
|
||||||
//export usleep
|
//export usleep
|
||||||
func usleep(usec uint) int
|
func usleep(usec uint) int
|
||||||
|
|
||||||
//export malloc
|
|
||||||
func malloc(size uintptr) unsafe.Pointer
|
|
||||||
|
|
||||||
// void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
// void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||||
// Note: off_t is defined as int64 because:
|
// Note: off_t is defined as int64 because:
|
||||||
// - musl (used on Linux) always defines it as int64
|
// - musl (used on Linux) always defines it as int64
|
||||||
|
|
|
@ -233,3 +233,50 @@ func isContinuation(b byte) bool {
|
||||||
// Continuation bytes have their topmost bits set to 0b10.
|
// Continuation bytes have their topmost bits set to 0b10.
|
||||||
return b&0xc0 == 0x80
|
return b&0xc0 == 0x80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions used in CGo.
|
||||||
|
|
||||||
|
// Convert a Go string to a C string.
|
||||||
|
func cgo_CString(s _string) unsafe.Pointer {
|
||||||
|
buf := malloc(s.length + 1)
|
||||||
|
memcpy(buf, unsafe.Pointer(s.ptr), s.length)
|
||||||
|
*(*byte)(unsafe.Pointer(uintptr(buf) + s.length)) = 0 // trailing 0 byte
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a C string to a Go string.
|
||||||
|
func cgo_GoString(cstr unsafe.Pointer) _string {
|
||||||
|
if cstr == nil {
|
||||||
|
return _string{}
|
||||||
|
}
|
||||||
|
return makeGoString(cstr, strlen(cstr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a C data buffer to a Go string (that possibly contains 0 bytes).
|
||||||
|
func cgo_GoStringN(cstr unsafe.Pointer, length uintptr) _string {
|
||||||
|
return makeGoString(cstr, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a Go string given a source buffer and a length.
|
||||||
|
func makeGoString(cstr unsafe.Pointer, length uintptr) _string {
|
||||||
|
s := _string{
|
||||||
|
length: length,
|
||||||
|
}
|
||||||
|
if s.length != 0 {
|
||||||
|
buf := make([]byte, s.length)
|
||||||
|
s.ptr = &buf[0]
|
||||||
|
memcpy(unsafe.Pointer(s.ptr), cstr, s.length)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a C data buffer to a Go byte slice.
|
||||||
|
func cgo_GoBytes(ptr unsafe.Pointer, length uintptr) []byte {
|
||||||
|
// Note: don't return nil if length is 0, to match the behavior of C.GoBytes
|
||||||
|
// of upstream Go.
|
||||||
|
buf := make([]byte, length)
|
||||||
|
if length != 0 {
|
||||||
|
memcpy(unsafe.Pointer(&buf[0]), ptr, uintptr(length))
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
2
testdata/cgo/main.c
предоставленный
2
testdata/cgo/main.c
предоставленный
|
@ -24,6 +24,8 @@ int cflagsConstant = SOME_CONSTANT;
|
||||||
|
|
||||||
int smallEnumWidth = sizeof(option2_t);
|
int smallEnumWidth = sizeof(option2_t);
|
||||||
|
|
||||||
|
char globalChars[] = {2, 0, 4, 8};
|
||||||
|
|
||||||
int fortytwo() {
|
int fortytwo() {
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
15
testdata/cgo/main.go
предоставленный
15
testdata/cgo/main.go
предоставленный
|
@ -135,6 +135,21 @@ func main() {
|
||||||
// void arraydecay(int *buf1, int *buf2[8], int *buf3[7][2]);
|
// void arraydecay(int *buf1, int *buf2[8], int *buf3[7][2]);
|
||||||
C.arraydecay((*C.int)(nil), (*[8]C.int)(nil), (*[7][2]C.int)(nil))
|
C.arraydecay((*C.int)(nil), (*[8]C.int)(nil), (*[7][2]C.int)(nil))
|
||||||
|
|
||||||
|
// Test CGo builtins like C.CString.
|
||||||
|
cstr := C.CString("string passed to C")
|
||||||
|
println("cstr length:", C.strlen(cstr))
|
||||||
|
gostr := C.GoString(cstr)
|
||||||
|
println("C.CString:", gostr)
|
||||||
|
charBuf := C.GoBytes(unsafe.Pointer(&C.globalChars[0]), 4)
|
||||||
|
println("C.charBuf:", charBuf[0], charBuf[1], charBuf[2], charBuf[3])
|
||||||
|
binaryString := C.GoStringN(&C.globalChars[0], 4)
|
||||||
|
println("C.CStringN:", len(binaryString), binaryString[0], binaryString[1], binaryString[2], binaryString[3])
|
||||||
|
|
||||||
|
// Test whether those builtins also work with zero length data.
|
||||||
|
println("C.GoString(nil):", C.GoString(nil))
|
||||||
|
println("len(C.GoStringN(nil, 0)):", len(C.GoStringN(nil, 0)))
|
||||||
|
println("len(C.GoBytes(nil, 0)):", len(C.GoBytes(nil, 0)))
|
||||||
|
|
||||||
// libc: test whether C functions work at all.
|
// libc: test whether C functions work at all.
|
||||||
buf1 := []byte("foobar\x00")
|
buf1 := []byte("foobar\x00")
|
||||||
buf2 := make([]byte, len(buf1))
|
buf2 := make([]byte, len(buf1))
|
||||||
|
|
2
testdata/cgo/main.h
предоставленный
2
testdata/cgo/main.h
предоставленный
|
@ -141,6 +141,8 @@ extern int smallEnumWidth;
|
||||||
|
|
||||||
extern int cflagsConstant;
|
extern int cflagsConstant;
|
||||||
|
|
||||||
|
extern char globalChars[4];
|
||||||
|
|
||||||
// test duplicate definitions
|
// test duplicate definitions
|
||||||
int add(int a, int b);
|
int add(int a, int b);
|
||||||
extern int global;
|
extern int global;
|
||||||
|
|
7
testdata/cgo/out.txt
предоставленный
7
testdata/cgo/out.txt
предоставленный
|
@ -61,5 +61,12 @@ option 2A: 20
|
||||||
option 3A: 21
|
option 3A: 21
|
||||||
enum width matches: true
|
enum width matches: true
|
||||||
CFLAGS value: 17
|
CFLAGS value: 17
|
||||||
|
cstr length: 18
|
||||||
|
C.CString: string passed to C
|
||||||
|
C.charBuf: 2 0 4 8
|
||||||
|
C.CStringN: 4 2 0 4 8
|
||||||
|
C.GoString(nil):
|
||||||
|
len(C.GoStringN(nil, 0)): 0
|
||||||
|
len(C.GoBytes(nil, 0)): 0
|
||||||
copied string: foobar
|
copied string: foobar
|
||||||
line written using C puts
|
line written using C puts
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче