1
0
mirror of https://github.com/golang/go synced 2024-11-26 21:51:44 -07:00
go/misc/cgo/test/testx.go
Russ Cox 19470dc535 misc/cgo/test: do not redeclare exported Go functions
An exported Go function like

	//export F
	func F() {}

gets declared in _cgo_export.h as something like

	extern void F(void);

The exact declaration varies by operating system.
In particular, Windows adds __declspec(dllimport).

Clang on Windows/ARM64 rejects code that contains
conflicting declarations for F, like:

	extern void F(void);
	extern void __declspec(dllimport) F(void);

This means that F must not be declared separately from _cgo_export.h:
any code that wants to refer to F must use #include "_cgo_export.h".

Unfortunately, the cgo prologue itself (the commented code before import "C")
cannot include "_cgo_export.h", because that file is itself produced from the
cgo Go sources and therefore cannot be a dependency of the cgo Go sources.

This CL rewrites misc/cgo/test to avoid redeclaring exported functions.
Most of the time, this is not a significant problem: just move the code
that needs the header into a .c file, perhaps with a wrapper exposed
to the cgo Go sources.

The one case that is potentially problematic is f7665, which is part of
the test for golang.org/issue/7665. That bug report explicitly identified
a bug in referring to the C name for an exported function in the same
Go source file as it was exported function. That is now impossible,
at least on Windows/ARM64, so the test is modified a bit and possibly
does not test what the original bug was. But the original bug should
be long gone: that part of the compiler has been rewritten.

Change-Id: I0d14d9336632f0e5e3db4273d9d32ef2cca0298d
Reviewed-on: https://go-review.googlesource.com/c/go/+/312029
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-04-23 21:42:52 +00:00

557 lines
10 KiB
Go

// Copyright 2011 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.
// Test cases for cgo.
// Both the import "C" prologue and the main file are sorted by issue number.
// This file contains //export directives on Go functions
// and so it must NOT contain C definitions (only declarations).
// See test.go for C definitions.
package cgotest
import (
"runtime"
"runtime/cgo"
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"unsafe"
)
/*
// threads
extern void doAdd(int, int);
// issue 1328
void IntoC(void);
// issue 1560
// mysleep returns the absolute start time in ms.
long long mysleep(int seconds);
// twoSleep returns the absolute start time of the first sleep
// in ms.
long long twoSleep(int);
// issue 3775
void lockOSThreadC(void);
int usleep(unsigned usec);
// issue 4054 part 2 - part 1 in test.go
typedef enum {
A = 0,
B,
C,
D,
E,
F,
G,
H,
II,
J,
} issue4054b;
// issue 5548
extern int issue5548_in_c(void);
// issue 6833
extern unsigned long long issue6833Func(unsigned int, unsigned long long);
// issue 6907
extern int CheckIssue6907C(_GoString_);
// issue 7665
extern void f7665(void);
// issue 7978
// Stack tracing didn't work during cgo code after calling a Go
// callback. Make sure GC works and the stack trace is correct.
#include <stdint.h>
// use ugly atomic variable sync since that doesn't require calling back into
// Go code or OS dependencies
void issue7978c(uint32_t *sync);
// issue 8331 part 2 - part 1 in test.go
// A typedef of an unnamed struct is the same struct when
// #include'd twice. No runtime test; just make sure it compiles.
#include "issue8331.h"
// issue 8945
typedef void (*PFunc8945)();
extern PFunc8945 func8945; // definition is in test.go
// issue 20910
void callMulti(void);
// issue 28772 part 2 - part 1 in issuex.go
#define issue28772Constant2 2
// issue 31891
typedef struct {
long obj;
} Issue31891A;
typedef struct {
long obj;
} Issue31891B;
void callIssue31891(void);
typedef struct {
int i;
} Issue38408, *PIssue38408;
*/
import "C"
// exports
//export ReturnIntLong
func ReturnIntLong() (int, C.long) {
return 1, 2
}
//export gc
func gc() {
runtime.GC()
}
// threads
var sum struct {
sync.Mutex
i int
}
//export Add
func Add(x int) {
defer func() {
recover()
}()
sum.Lock()
sum.i += x
sum.Unlock()
var p *int
*p = 2
}
func testCthread(t *testing.T) {
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
t.Skip("the iOS exec wrapper is unable to properly handle the panic from Add")
}
sum.i = 0
C.doAdd(10, 6)
want := 10 * (10 - 1) / 2 * 6
if sum.i != want {
t.Fatalf("sum=%d, want %d", sum.i, want)
}
}
// issue 1328
//export BackIntoGo
func BackIntoGo() {
x := 1
for i := 0; i < 10000; i++ {
xvariadic(x)
if x != 1 {
panic("x is not 1?")
}
}
}
func xvariadic(x ...interface{}) {
}
func test1328(t *testing.T) {
C.IntoC()
}
// issue 1560
var sleepDone = make(chan int64)
// parallelSleep returns the absolute difference between the start time
// of the two sleeps.
func parallelSleep(n int) int64 {
t := int64(C.twoSleep(C.int(n))) - <-sleepDone
if t < 0 {
return -t
}
return t
}
//export BackgroundSleep
func BackgroundSleep(n int32) {
go func() {
sleepDone <- int64(C.mysleep(C.int(n)))
}()
}
func testParallelSleep(t *testing.T) {
sleepSec := 1
dt := time.Duration(parallelSleep(sleepSec)) * time.Millisecond
t.Logf("difference in start time for two sleep(%d) is %v", sleepSec, dt)
// bug used to run sleeps in serial, producing a 2*sleepSec-second delay.
// we detect if the start times of those sleeps are > 0.5*sleepSec-second.
if dt >= time.Duration(sleepSec)*time.Second/2 {
t.Fatalf("parallel %d-second sleeps slept for %f seconds", sleepSec, dt.Seconds())
}
}
// issue 2462
//export exportbyte
func exportbyte() byte {
return 0
}
//export exportbool
func exportbool() bool {
return false
}
//export exportrune
func exportrune() rune {
return 0
}
//export exporterror
func exporterror() error {
return nil
}
//export exportint
func exportint() int {
return 0
}
//export exportuint
func exportuint() uint {
return 0
}
//export exportuintptr
func exportuintptr() uintptr {
return (uintptr)(0)
}
//export exportint8
func exportint8() int8 {
return 0
}
//export exportuint8
func exportuint8() uint8 {
return 0
}
//export exportint16
func exportint16() int16 {
return 0
}
//export exportuint16
func exportuint16() uint16 {
return 0
}
//export exportint32
func exportint32() int32 {
return 0
}
//export exportuint32
func exportuint32() uint32 {
return 0
}
//export exportint64
func exportint64() int64 {
return 0
}
//export exportuint64
func exportuint64() uint64 {
return 0
}
//export exportfloat32
func exportfloat32() float32 {
return 0
}
//export exportfloat64
func exportfloat64() float64 {
return 0
}
//export exportcomplex64
func exportcomplex64() complex64 {
return 0
}
//export exportcomplex128
func exportcomplex128() complex128 {
return 0
}
// issue 3741
//export exportSliceIn
func exportSliceIn(s []byte) bool {
return len(s) == cap(s)
}
//export exportSliceOut
func exportSliceOut() []byte {
return []byte{1}
}
//export exportSliceInOut
func exportSliceInOut(s []byte) []byte {
return s
}
// issue 3775
func init() {
if runtime.GOOS == "android" {
return
}
// Same as test3775 but run during init so that
// there are two levels of internal runtime lock
// (1 for init, 1 for cgo).
// This would have been broken by CL 11663043.
C.lockOSThreadC()
}
func test3775(t *testing.T) {
if runtime.GOOS == "android" {
return
}
// Used to panic because of the UnlockOSThread below.
C.lockOSThreadC()
}
//export lockOSThreadCallback
func lockOSThreadCallback() {
runtime.LockOSThread()
runtime.UnlockOSThread()
go C.usleep(10000)
runtime.Gosched()
}
// issue 4054 part 2 - part 1 in test.go
var issue4054b = []int{C.A, C.B, C.C, C.D, C.E, C.F, C.G, C.H, C.II, C.J}
//export issue5548FromC
func issue5548FromC(s string, i int) int {
if len(s) == 4 && s == "test" && i == 42 {
return 12345
}
println("got", len(s), i)
return 9876
}
func test5548(t *testing.T) {
if x := C.issue5548_in_c(); x != 12345 {
t.Errorf("issue5548_in_c = %d, want %d", x, 12345)
}
}
// issue 6833
//export GoIssue6833Func
func GoIssue6833Func(aui uint, aui64 uint64) uint64 {
return aui64 + uint64(aui)
}
func test6833(t *testing.T) {
ui := 7
ull := uint64(0x4000300020001000)
v := uint64(C.issue6833Func(C.uint(ui), C.ulonglong(ull)))
exp := uint64(ui) + ull
if v != exp {
t.Errorf("issue6833Func() returns %x, expected %x", v, exp)
}
}
// issue 6907
const CString = "C string"
//export CheckIssue6907Go
func CheckIssue6907Go(s string) C.int {
if s == CString {
return 1
}
return 0
}
func test6907Go(t *testing.T) {
if got := C.CheckIssue6907C(CString); got != 1 {
t.Errorf("C.CheckIssue6907C() == %d, want %d", got, 1)
}
}
// issue 7665
var bad7665 unsafe.Pointer = C.f7665
var good7665 uintptr = uintptr(C.f7665)
func test7665(t *testing.T) {
if bad7665 == nil || uintptr(bad7665) != good7665 {
t.Errorf("ptrs = %p, %#x, want same non-nil pointer", bad7665, good7665)
}
}
// issue 7978
var issue7978sync uint32
func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
runtime.GC()
buf := make([]byte, 65536)
trace := string(buf[:runtime.Stack(buf, true)])
for _, goroutine := range strings.Split(trace, "\n\n") {
if strings.Contains(goroutine, "test.issue7978go") {
trace := strings.Split(goroutine, "\n")
// look for the expected function in the stack
for i := 0; i < depth; i++ {
if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) {
t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine)
return
}
if strings.Contains(trace[1+2*i], wantFunc) {
return
}
}
t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine)
return
}
}
t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace)
}
func issue7978wait(store uint32, wait uint32) {
if store != 0 {
atomic.StoreUint32(&issue7978sync, store)
}
for atomic.LoadUint32(&issue7978sync) != wait {
runtime.Gosched()
}
}
//export issue7978cb
func issue7978cb() {
// Force a stack growth from the callback to put extra
// pressure on the runtime. See issue #17785.
growStack(64)
issue7978wait(3, 4)
}
func growStack(n int) int {
var buf [128]int
if n == 0 {
return 0
}
return buf[growStack(n-1)]
}
func issue7978go() {
C.issue7978c((*C.uint32_t)(&issue7978sync))
issue7978wait(7, 8)
}
func test7978(t *testing.T) {
if runtime.Compiler == "gccgo" {
t.Skip("gccgo can not do stack traces of C code")
}
debug.SetTraceback("2")
issue7978sync = 0
go issue7978go()
// test in c code, before callback
issue7978wait(0, 1)
issue7978check(t, "_Cfunc_issue7978c(", "", 1)
// test in go code, during callback
issue7978wait(2, 3)
issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
// test in c code, after callback
issue7978wait(4, 5)
issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
// test in go code, after return from cgo
issue7978wait(6, 7)
issue7978check(t, "test.issue7978go(", "", 3)
atomic.StoreUint32(&issue7978sync, 8)
}
// issue 8331 part 2
var issue8331Var C.issue8331
// issue 8945
//export Test8945
func Test8945() {
_ = C.func8945
}
// issue 20910
//export multi
func multi() (*C.char, C.int) {
return C.CString("multi"), 0
}
func test20910(t *testing.T) {
C.callMulti()
}
// issue 28772 part 2
const issue28772Constant2 = C.issue28772Constant2
// issue 31891
//export useIssue31891A
func useIssue31891A(c *C.Issue31891A) {}
//export useIssue31891B
func useIssue31891B(c *C.Issue31891B) {}
func test31891(t *testing.T) {
C.callIssue31891()
}
// issue 37033, check if cgo.Handle works properly
var issue37033 = 42
//export GoFunc37033
func GoFunc37033(handle C.uintptr_t) {
h := cgo.Handle(handle)
ch := h.Value().(chan int)
ch <- issue37033
}
// issue 38408
// A typedef pointer can be used as the element type.
// No runtime test; just make sure it compiles.
var _ C.PIssue38408 = &C.Issue38408{i: 1}