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
|
||||
// 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 = `
|
||||
import "unsafe"
|
||||
|
||||
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
|
||||
|
@ -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.
|
||||
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.
|
||||
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
|
||||
|
||||
//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.int32_t = int32
|
||||
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
|
||||
|
||||
//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.foo = 3
|
||||
|
||||
|
|
20
cgo/testdata/errors.out.go
предоставленный
20
cgo/testdata/errors.out.go
предоставленный
|
@ -18,6 +18,26 @@ import "unsafe"
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
//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.FOO_H = 1
|
||||
|
||||
|
|
20
cgo/testdata/symbols.out.go
предоставленный
20
cgo/testdata/symbols.out.go
предоставленный
|
@ -4,6 +4,26 @@ import "unsafe"
|
|||
|
||||
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
|
||||
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
|
||||
|
||||
//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.optionA = 0
|
||||
const C.optionB = 1
|
||||
|
|
|
@ -42,6 +42,9 @@ func memzero(ptr unsafe.Pointer, size uintptr)
|
|||
//export strlen
|
||||
func strlen(ptr unsafe.Pointer) uintptr
|
||||
|
||||
//export malloc
|
||||
func malloc(size uintptr) unsafe.Pointer
|
||||
|
||||
// Compare two same-size buffers for equality.
|
||||
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
|
|
|
@ -13,9 +13,6 @@ func libc_write(fd int32, buf unsafe.Pointer, count uint) int
|
|||
//export usleep
|
||||
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);
|
||||
// Note: off_t is defined as int64 because:
|
||||
// - 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.
|
||||
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);
|
||||
|
||||
char globalChars[] = {2, 0, 4, 8};
|
||||
|
||||
int fortytwo() {
|
||||
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]);
|
||||
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.
|
||||
buf1 := []byte("foobar\x00")
|
||||
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 char globalChars[4];
|
||||
|
||||
// test duplicate definitions
|
||||
int add(int a, int b);
|
||||
extern int global;
|
||||
|
|
7
testdata/cgo/out.txt
предоставленный
7
testdata/cgo/out.txt
предоставленный
|
@ -61,5 +61,12 @@ option 2A: 20
|
|||
option 3A: 21
|
||||
enum width matches: true
|
||||
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
|
||||
line written using C puts
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче