mirror of
https://github.com/golang/go
synced 2024-11-26 06:17:57 -07:00
misc/cgo/errors: consolidate test work
Build a single binary containing all the TestPointerChecks instead of building many small binaries, each with its own cgo+compile+link invocation. This cuts 'go test -run=TestPointerChecks' from 6.7r 35.5u 26.1s to 2.1r 2.1u 1.4s. Move as many cgo checks as possible into fewer test files for TestReportsTypeErrors too. This cuts 'go test -run=TestReportsTypeErrors' from 2.1r 6.7u 6.7s to 1.5r 2.5u 2.5s. After this change, all.bash runs in ~4:30 on my laptop. For #26473. Change-Id: I3787448b03689a1f62dd810957ab6013bb75582f Reviewed-on: https://go-review.googlesource.com/c/go/+/177599 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
b864818494
commit
798e0b38ed
@ -63,7 +63,7 @@ func expect(t *testing.T, file string, errors []*regexp.Regexp) {
|
|||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
dst := filepath.Join(dir, strings.TrimSuffix(file, ".go"))
|
dst := filepath.Join(dir, strings.TrimSuffix(file, ".go"))
|
||||||
cmd := exec.Command("go", "build", "-gcflags=-L", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted
|
cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected cgo to fail but it succeeded")
|
t.Errorf("expected cgo to fail but it succeeded")
|
||||||
@ -107,21 +107,10 @@ func TestReportsTypeErrors(t *testing.T) {
|
|||||||
for _, file := range []string{
|
for _, file := range []string{
|
||||||
"err1.go",
|
"err1.go",
|
||||||
"err2.go",
|
"err2.go",
|
||||||
"err3.go",
|
|
||||||
"issue7757.go",
|
|
||||||
"issue8442.go",
|
|
||||||
"issue11097a.go",
|
"issue11097a.go",
|
||||||
"issue11097b.go",
|
"issue11097b.go",
|
||||||
"issue13129.go",
|
|
||||||
"issue13423.go",
|
|
||||||
"issue13467.go",
|
|
||||||
"issue13635.go",
|
|
||||||
"issue13830.go",
|
|
||||||
"issue16116.go",
|
|
||||||
"issue16591.go",
|
|
||||||
"issue18452.go",
|
"issue18452.go",
|
||||||
"issue18889.go",
|
"issue18889.go",
|
||||||
"issue26745.go",
|
|
||||||
"issue28721.go",
|
"issue28721.go",
|
||||||
} {
|
} {
|
||||||
check(t, file)
|
check(t, file)
|
||||||
|
@ -7,21 +7,25 @@
|
|||||||
package errorstest
|
package errorstest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
|
||||||
|
|
||||||
// ptrTest is the tests without the boilerplate.
|
// ptrTest is the tests without the boilerplate.
|
||||||
type ptrTest struct {
|
type ptrTest struct {
|
||||||
name string // for reporting
|
name string // for reporting
|
||||||
c string // the cgo comment
|
c string // the cgo comment
|
||||||
|
c1 string // cgo comment forced into non-export cgo file
|
||||||
imports []string // a list of imports
|
imports []string // a list of imports
|
||||||
support string // supporting functions
|
support string // supporting functions
|
||||||
body string // the body of the main function
|
body string // the body of the main function
|
||||||
@ -39,253 +43,248 @@ var ptrTests = []ptrTest{
|
|||||||
{
|
{
|
||||||
// Passing a pointer to a struct that contains a Go pointer.
|
// Passing a pointer to a struct that contains a Go pointer.
|
||||||
name: "ptr1",
|
name: "ptr1",
|
||||||
c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
|
c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
|
||||||
body: `C.f(&C.s{new(C.int)})`,
|
body: `C.f1(&C.s1{new(C.int)})`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a pointer to a struct that contains a Go pointer.
|
// Passing a pointer to a struct that contains a Go pointer.
|
||||||
name: "ptr2",
|
name: "ptr2",
|
||||||
c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
|
c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
|
||||||
body: `p := &C.s{new(C.int)}; C.f(p)`,
|
body: `p := &C.s2{new(C.int)}; C.f2(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a pointer to an int field of a Go struct
|
// Passing a pointer to an int field of a Go struct
|
||||||
// that (irrelevantly) contains a Go pointer.
|
// that (irrelevantly) contains a Go pointer.
|
||||||
name: "ok1",
|
name: "ok1",
|
||||||
c: `struct s { int i; int *p; }; void f(int *p) {}`,
|
c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
|
||||||
body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
|
body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a pointer to a pointer field of a Go struct.
|
// Passing a pointer to a pointer field of a Go struct.
|
||||||
name: "ptr-field",
|
name: "ptrfield",
|
||||||
c: `struct s { int i; int *p; }; void f(int **p) {}`,
|
c: `struct s4 { int i; int *p; }; void f4(int **p) {}`,
|
||||||
body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
|
body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a pointer to a pointer field of a Go
|
// Passing a pointer to a pointer field of a Go
|
||||||
// struct, where the field does not contain a Go
|
// struct, where the field does not contain a Go
|
||||||
// pointer, but another field (irrelevantly) does.
|
// pointer, but another field (irrelevantly) does.
|
||||||
name: "ptr-field-ok",
|
name: "ptrfieldok",
|
||||||
c: `struct s { int *p1; int *p2; }; void f(int **p) {}`,
|
c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
|
||||||
body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
|
body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice with no Go pointers.
|
// Passing the address of a slice with no Go pointers.
|
||||||
name: "slice-ok-1",
|
name: "sliceok1",
|
||||||
c: `void f(void **p) {}`,
|
c: `void f6(void **p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
|
body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice with a Go pointer.
|
// Passing the address of a slice with a Go pointer.
|
||||||
name: "slice-ptr-1",
|
name: "sliceptr1",
|
||||||
c: `void f(void **p) {}`,
|
c: `void f7(void **p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
|
body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice with a Go pointer,
|
// Passing the address of a slice with a Go pointer,
|
||||||
// where we are passing the address of an element that
|
// where we are passing the address of an element that
|
||||||
// is not a Go pointer.
|
// is not a Go pointer.
|
||||||
name: "slice-ptr-2",
|
name: "sliceptr2",
|
||||||
c: `void f(void **p) {}`,
|
c: `void f8(void **p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
|
body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice that is an element
|
// Passing the address of a slice that is an element
|
||||||
// in a struct only looks at the slice.
|
// in a struct only looks at the slice.
|
||||||
name: "slice-ok-2",
|
name: "sliceok2",
|
||||||
c: `void f(void **p) {}`,
|
c: `void f9(void **p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; s []unsafe.Pointer }`,
|
support: `type S9 struct { p *int; s []unsafe.Pointer }`,
|
||||||
body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
|
body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice of an array that is
|
// Passing the address of a slice of an array that is
|
||||||
// an element in a struct, with a type conversion.
|
// an element in a struct, with a type conversion.
|
||||||
name: "slice-ok-3",
|
name: "sliceok3",
|
||||||
c: `void f(void* p) {}`,
|
c: `void f10(void* p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; a [4]byte }`,
|
support: `type S10 struct { p *int; a [4]byte }`,
|
||||||
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
|
body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a slice of an array that is
|
// Passing the address of a slice of an array that is
|
||||||
// an element in a struct, with a type conversion.
|
// an element in a struct, with a type conversion.
|
||||||
name: "slice-ok-4",
|
name: "sliceok4",
|
||||||
c: `typedef void* PV; void f(PV p) {}`,
|
c: `typedef void* PV11; void f11(PV11 p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; a [4]byte }`,
|
support: `type S11 struct { p *int; a [4]byte }`,
|
||||||
body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
|
body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a static variable with no
|
// Passing the address of a static variable with no
|
||||||
// pointers doesn't matter.
|
// pointers doesn't matter.
|
||||||
name: "varok",
|
name: "varok",
|
||||||
c: `void f(char** parg) {}`,
|
c: `void f12(char** parg) {}`,
|
||||||
support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
|
support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
|
||||||
body: `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
|
body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing the address of a static variable with
|
// Passing the address of a static variable with
|
||||||
// pointers does matter.
|
// pointers does matter.
|
||||||
name: "var",
|
name: "var1",
|
||||||
c: `void f(char*** parg) {}`,
|
c: `void f13(char*** parg) {}`,
|
||||||
support: `var hello = [...]*C.char{new(C.char)}`,
|
support: `var hello13 = [...]*C.char{new(C.char)}`,
|
||||||
body: `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
|
body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Storing a Go pointer into C memory should fail.
|
// Storing a Go pointer into C memory should fail.
|
||||||
name: "barrier",
|
name: "barrier",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
char **f1() { return malloc(sizeof(char*)); }
|
char **f14a() { return malloc(sizeof(char*)); }
|
||||||
void f2(char **p) {}`,
|
void f14b(char **p) {}`,
|
||||||
body: `p := C.f1(); *p = new(C.char); C.f2(p)`,
|
body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Storing a Go pointer into C memory by assigning a
|
// Storing a Go pointer into C memory by assigning a
|
||||||
// large value should fail.
|
// large value should fail.
|
||||||
name: "barrier-struct",
|
name: "barrierstruct",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[10]; };
|
struct s15 { char *a[10]; };
|
||||||
struct s *f1() { return malloc(sizeof(struct s)); }
|
struct s15 *f15() { return malloc(sizeof(struct s15)); }
|
||||||
void f2(struct s *p) {}`,
|
void f15b(struct s15 *p) {}`,
|
||||||
body: `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
|
body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Storing a Go pointer into C memory using a slice
|
// Storing a Go pointer into C memory using a slice
|
||||||
// copy should fail.
|
// copy should fail.
|
||||||
name: "barrier-slice",
|
name: "barrierslice",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[10]; };
|
struct s16 { char *a[10]; };
|
||||||
struct s *f1() { return malloc(sizeof(struct s)); }
|
struct s16 *f16() { return malloc(sizeof(struct s16)); }
|
||||||
void f2(struct s *p) {}`,
|
void f16b(struct s16 *p) {}`,
|
||||||
body: `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
|
body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// A very large value uses a GC program, which is a
|
// A very large value uses a GC program, which is a
|
||||||
// different code path.
|
// different code path.
|
||||||
name: "barrier-gcprog-array",
|
name: "barriergcprogarray",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[32769]; };
|
struct s17 { char *a[32769]; };
|
||||||
struct s *f1() { return malloc(sizeof(struct s)); }
|
struct s17 *f17() { return malloc(sizeof(struct s17)); }
|
||||||
void f2(struct s *p) {}`,
|
void f17b(struct s17 *p) {}`,
|
||||||
body: `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
|
body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Similar case, with a source on the heap.
|
// Similar case, with a source on the heap.
|
||||||
name: "barrier-gcprog-array-heap",
|
name: "barriergcprogarrayheap",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[32769]; };
|
struct s18 { char *a[32769]; };
|
||||||
struct s *f1() { return malloc(sizeof(struct s)); }
|
struct s18 *f18() { return malloc(sizeof(struct s18)); }
|
||||||
void f2(struct s *p) {}
|
void f18b(struct s18 *p) {}
|
||||||
void f3(void *p) {}`,
|
void f18c(void *p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `p := C.f1(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f2(p); n[0] = nil; C.f3(unsafe.Pointer(n))`,
|
body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// A GC program with a struct.
|
// A GC program with a struct.
|
||||||
name: "barrier-gcprog-struct",
|
name: "barriergcprogstruct",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[32769]; };
|
struct s19a { char *a[32769]; };
|
||||||
struct s2 { struct s f; };
|
struct s19b { struct s19a f; };
|
||||||
struct s2 *f1() { return malloc(sizeof(struct s2)); }
|
struct s19b *f19() { return malloc(sizeof(struct s19b)); }
|
||||||
void f2(struct s2 *p) {}`,
|
void f19b(struct s19b *p) {}`,
|
||||||
body: `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
|
body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Similar case, with a source on the heap.
|
// Similar case, with a source on the heap.
|
||||||
name: "barrier-gcprog-struct-heap",
|
name: "barriergcprogstructheap",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
struct s { char *a[32769]; };
|
struct s20a { char *a[32769]; };
|
||||||
struct s2 { struct s f; };
|
struct s20b { struct s20a f; };
|
||||||
struct s2 *f1() { return malloc(sizeof(struct s2)); }
|
struct s20b *f20() { return malloc(sizeof(struct s20b)); }
|
||||||
void f2(struct s2 *p) {}
|
void f20b(struct s20b *p) {}
|
||||||
void f3(void *p) {}`,
|
void f20c(void *p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `p := C.f1(); n := &C.struct_s{[32769]*C.char{new(C.char)}}; p.f = *n; C.f2(p); n.a[0] = nil; C.f3(unsafe.Pointer(n))`,
|
body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
|
||||||
fail: true,
|
fail: true,
|
||||||
expensive: true,
|
expensive: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Exported functions may not return Go pointers.
|
// Exported functions may not return Go pointers.
|
||||||
name: "export1",
|
name: "export1",
|
||||||
c: `extern unsigned char *GoFn();`,
|
c: `extern unsigned char *GoFn21();`,
|
||||||
support: `//export GoFn
|
support: `//export GoFn21
|
||||||
func GoFn() *byte { return new(byte) }`,
|
func GoFn21() *byte { return new(byte) }`,
|
||||||
body: `C.GoFn()`,
|
body: `C.GoFn21()`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Returning a C pointer is fine.
|
// Returning a C pointer is fine.
|
||||||
name: "exportok",
|
name: "exportok",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
extern unsigned char *GoFn();`,
|
extern unsigned char *GoFn22();`,
|
||||||
support: `//export GoFn
|
support: `//export GoFn22
|
||||||
func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
|
func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
|
||||||
body: `C.GoFn()`,
|
body: `C.GoFn22()`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a Go string is fine.
|
// Passing a Go string is fine.
|
||||||
name: "pass-string",
|
name: "passstring",
|
||||||
c: `#include <stddef.h>
|
c: `#include <stddef.h>
|
||||||
typedef struct { const char *p; ptrdiff_t n; } gostring;
|
typedef struct { const char *p; ptrdiff_t n; } gostring23;
|
||||||
gostring f(gostring s) { return s; }`,
|
gostring23 f23(gostring23 s) { return s; }`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
|
body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Passing a slice of Go strings fails.
|
// Passing a slice of Go strings fails.
|
||||||
name: "pass-string-slice",
|
name: "passstringslice",
|
||||||
c: `void f(void *p) {}`,
|
c: `void f24(void *p) {}`,
|
||||||
imports: []string{"strings", "unsafe"},
|
imports: []string{"strings", "unsafe"},
|
||||||
support: `type S struct { a [1]string }`,
|
support: `type S24 struct { a [1]string }`,
|
||||||
body: `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`,
|
body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Exported functions may not return strings.
|
// Exported functions may not return strings.
|
||||||
name: "ret-string",
|
name: "retstring",
|
||||||
c: `extern void f();`,
|
c: `extern void f25();`,
|
||||||
imports: []string{"strings"},
|
imports: []string{"strings"},
|
||||||
support: `//export GoStr
|
support: `//export GoStr25
|
||||||
func GoStr() string { return strings.Repeat("a", 2) }`,
|
func GoStr25() string { return strings.Repeat("a", 2) }`,
|
||||||
body: `C.f()`,
|
body: `C.f25()`,
|
||||||
extra: []extra{
|
c1: `#include <stddef.h>
|
||||||
{
|
typedef struct { const char *p; ptrdiff_t n; } gostring25;
|
||||||
"call.c",
|
extern gostring25 GoStr25();
|
||||||
`#include <stddef.h>
|
void f25() { GoStr25(); }`,
|
||||||
typedef struct { const char *p; ptrdiff_t n; } gostring;
|
|
||||||
extern gostring GoStr();
|
|
||||||
void f() { GoStr(); }`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -296,37 +295,37 @@ var ptrTests = []ptrTest{
|
|||||||
// that is, we are testing something that is not unsafe.
|
// that is, we are testing something that is not unsafe.
|
||||||
name: "ptrdata1",
|
name: "ptrdata1",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
void f(void* p) {}`,
|
void f26(void* p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; a [8*8]byte; u uintptr }`,
|
support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
|
||||||
body: `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`,
|
body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Like ptrdata1, but with a type that uses a GC program.
|
// Like ptrdata1, but with a type that uses a GC program.
|
||||||
name: "ptrdata2",
|
name: "ptrdata2",
|
||||||
c: `#include <stdlib.h>
|
c: `#include <stdlib.h>
|
||||||
void f(void* p) {}`,
|
void f27(void* p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
|
support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
|
||||||
body: `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`,
|
body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Check deferred pointers when they are used, not
|
// Check deferred pointers when they are used, not
|
||||||
// when the defer statement is run.
|
// when the defer statement is run.
|
||||||
name: "defer",
|
name: "defer1",
|
||||||
c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
|
c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
|
||||||
body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`,
|
body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Check a pointer to a union if the union has any
|
// Check a pointer to a union if the union has any
|
||||||
// pointer fields.
|
// pointer fields.
|
||||||
name: "union1",
|
name: "union1",
|
||||||
c: `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`,
|
c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
|
body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -336,55 +335,55 @@ var ptrTests = []ptrTest{
|
|||||||
// integer that happens to have the same
|
// integer that happens to have the same
|
||||||
// representation as a pointer.
|
// representation as a pointer.
|
||||||
name: "union2",
|
name: "union2",
|
||||||
c: `typedef union { unsigned long i; } u; void f(u *pu) {}`,
|
c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
|
body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test preemption while entering a cgo call. Issue #21306.
|
// Test preemption while entering a cgo call. Issue #21306.
|
||||||
name: "preempt-during-call",
|
name: "preemptduringcall",
|
||||||
c: `void f() {}`,
|
c: `void f30() {}`,
|
||||||
imports: []string{"runtime", "sync"},
|
imports: []string{"runtime", "sync"},
|
||||||
body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
|
body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test poller deadline with cgocheck=2. Issue #23435.
|
// Test poller deadline with cgocheck=2. Issue #23435.
|
||||||
name: "deadline",
|
name: "deadline",
|
||||||
c: `#define US 10`,
|
c: `#define US31 10`,
|
||||||
imports: []string{"os", "time"},
|
imports: []string{"os", "time"},
|
||||||
body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US * time.Microsecond))`,
|
body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test for double evaluation of channel receive.
|
// Test for double evaluation of channel receive.
|
||||||
name: "chan-recv",
|
name: "chanrecv",
|
||||||
c: `void f(char** p) {}`,
|
c: `void f32(char** p) {}`,
|
||||||
imports: []string{"time"},
|
imports: []string{"time"},
|
||||||
body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f(&(<-c)[0]);`,
|
body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test that converting the address of a struct field
|
// Test that converting the address of a struct field
|
||||||
// to unsafe.Pointer still just checks that field.
|
// to unsafe.Pointer still just checks that field.
|
||||||
// Issue #25941.
|
// Issue #25941.
|
||||||
name: "struct-field",
|
name: "structfield",
|
||||||
c: `void f(void* p) {}`,
|
c: `void f33(void* p) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { p *int; a [8]byte; u uintptr }`,
|
support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
|
||||||
body: `s := &S{p: new(int)}; C.f(unsafe.Pointer(&s.a))`,
|
body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test that converting multiple struct field
|
// Test that converting multiple struct field
|
||||||
// addresses to unsafe.Pointer still just checks those
|
// addresses to unsafe.Pointer still just checks those
|
||||||
// fields. Issue #25941.
|
// fields. Issue #25941.
|
||||||
name: "struct-field-2",
|
name: "structfield2",
|
||||||
c: `void f(void* p, int r, void* s) {}`,
|
c: `void f34(void* p, int r, void* s) {}`,
|
||||||
imports: []string{"unsafe"},
|
imports: []string{"unsafe"},
|
||||||
support: `type S struct { a [8]byte; p *int; b int64; }`,
|
support: `type S34 struct { a [8]byte; p *int; b int64; }`,
|
||||||
body: `s := &S{p: new(int)}; C.f(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
|
body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -392,18 +391,18 @@ var ptrTests = []ptrTest{
|
|||||||
// evaluated when a deferred function is deferred, not
|
// evaluated when a deferred function is deferred, not
|
||||||
// when it is run.
|
// when it is run.
|
||||||
name: "defer2",
|
name: "defer2",
|
||||||
c: `void f(char **pc) {}`,
|
c: `void f35(char **pc) {}`,
|
||||||
support: `type S1 struct { s []*C.char }; type S2 struct { ps *S1 }`,
|
support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
|
||||||
body: `p := &S2{&S1{[]*C.char{nil}}}; defer C.f(&p.ps.s[0]); p.ps = nil`,
|
body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test that indexing into a function call still
|
// Test that indexing into a function call still
|
||||||
// examines only the slice being indexed.
|
// examines only the slice being indexed.
|
||||||
name: "buffer",
|
name: "buffer",
|
||||||
c: `void f(void *p) {}`,
|
c: `void f36(void *p) {}`,
|
||||||
imports: []string{"bytes", "unsafe"},
|
imports: []string{"bytes", "unsafe"},
|
||||||
body: `var b bytes.Buffer; b.WriteString("a"); C.f(unsafe.Pointer(&b.Bytes()[0]))`,
|
body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -411,8 +410,8 @@ var ptrTests = []ptrTest{
|
|||||||
name: "finalizer",
|
name: "finalizer",
|
||||||
c: `// Nothing to declare.`,
|
c: `// Nothing to declare.`,
|
||||||
imports: []string{"os"},
|
imports: []string{"os"},
|
||||||
support: `func open() { os.Open(os.Args[0]) }; var G [][]byte`,
|
support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
|
||||||
body: `for i := 0; i < 10000; i++ { G = append(G, make([]byte, 4096)); if i % 100 == 0 { G = nil; open() } }`,
|
body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -420,103 +419,155 @@ var ptrTests = []ptrTest{
|
|||||||
name: "structof",
|
name: "structof",
|
||||||
c: `// Nothing to declare.`,
|
c: `// Nothing to declare.`,
|
||||||
imports: []string{"reflect"},
|
imports: []string{"reflect"},
|
||||||
support: `type MyInt int; func (i MyInt) Get() int { return int(i) }; type Getter interface { Get() int }`,
|
support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
|
||||||
body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt", Type: reflect.TypeOf(MyInt(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter).Get()`,
|
body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPointerChecks(t *testing.T) {
|
func TestPointerChecks(t *testing.T) {
|
||||||
|
dir, exe := buildPtrTests(t)
|
||||||
|
|
||||||
|
// We (TestPointerChecks) return before the parallel subtest functions do,
|
||||||
|
// so we can't just defer os.RemoveAll(dir). Instead we have to wait for
|
||||||
|
// the parallel subtests to finish. This code looks racy but is not:
|
||||||
|
// the add +1 run in serial before testOne blocks. The -1 run in parallel
|
||||||
|
// after testOne finishes.
|
||||||
|
var pending int32
|
||||||
for _, pt := range ptrTests {
|
for _, pt := range ptrTests {
|
||||||
pt := pt
|
pt := pt
|
||||||
t.Run(pt.name, func(t *testing.T) {
|
t.Run(pt.name, func(t *testing.T) {
|
||||||
testOne(t, pt)
|
atomic.AddInt32(&pending, +1)
|
||||||
|
defer func() {
|
||||||
|
if atomic.AddInt32(&pending, -1) == 0 {
|
||||||
|
println("removing", dir)
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
testOne(t, pt, exe)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOne(t *testing.T, pt ptrTest) {
|
func buildPtrTests(t *testing.T) (dir, exe string) {
|
||||||
t.Parallel()
|
var gopath string
|
||||||
|
if *tmp != "" {
|
||||||
gopath, err := ioutil.TempDir("", filepath.Base(t.Name()))
|
gopath = *tmp
|
||||||
if err != nil {
|
dir = ""
|
||||||
t.Fatal(err)
|
} else {
|
||||||
|
d, err := ioutil.TempDir("", filepath.Base(t.Name()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dir = d
|
||||||
|
gopath = d
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(gopath)
|
|
||||||
|
|
||||||
src := filepath.Join(gopath, "src", "ptrtest")
|
src := filepath.Join(gopath, "src", "ptrtest")
|
||||||
if err := os.MkdirAll(src, 0777); err != nil {
|
if err := os.MkdirAll(src, 0777); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name())))
|
// Prepare two cgo inputs: one for standard cgo and one for //export cgo.
|
||||||
f, err := os.Create(name)
|
// (The latter cannot have C definitions, only declarations.)
|
||||||
if err != nil {
|
var cgo1, cgo2 bytes.Buffer
|
||||||
t.Fatal(err)
|
fmt.Fprintf(&cgo1, "package main\n\n/*\n")
|
||||||
}
|
fmt.Fprintf(&cgo2, "package main\n\n/*\n")
|
||||||
|
|
||||||
b := bufio.NewWriter(f)
|
// C code
|
||||||
fmt.Fprintln(b, `package main`)
|
for _, pt := range ptrTests {
|
||||||
fmt.Fprintln(b)
|
cgo := &cgo1
|
||||||
fmt.Fprintln(b, `/*`)
|
if strings.Contains(pt.support, "//export") {
|
||||||
fmt.Fprintln(b, pt.c)
|
cgo = &cgo2
|
||||||
fmt.Fprintln(b, `*/`)
|
}
|
||||||
fmt.Fprintln(b, `import "C"`)
|
fmt.Fprintf(cgo, "%s\n", pt.c)
|
||||||
fmt.Fprintln(b)
|
fmt.Fprintf(&cgo1, "%s\n", pt.c1)
|
||||||
for _, imp := range pt.imports {
|
|
||||||
fmt.Fprintln(b, `import "`+imp+`"`)
|
|
||||||
}
|
}
|
||||||
if len(pt.imports) > 0 {
|
fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
|
||||||
fmt.Fprintln(b)
|
fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
|
||||||
}
|
|
||||||
if len(pt.support) > 0 {
|
|
||||||
fmt.Fprintln(b, pt.support)
|
|
||||||
fmt.Fprintln(b)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(b, `func main() {`)
|
|
||||||
fmt.Fprintln(b, pt.body)
|
|
||||||
fmt.Fprintln(b, `}`)
|
|
||||||
|
|
||||||
if err := b.Flush(); err != nil {
|
// Imports
|
||||||
t.Fatalf("flushing %s: %v", name, err)
|
did1 := make(map[string]bool)
|
||||||
}
|
did2 := make(map[string]bool)
|
||||||
if err := f.Close(); err != nil {
|
did1["os"] = true // for ptrTestMain
|
||||||
t.Fatalf("closing %s: %v", name, err)
|
fmt.Fprintf(&cgo1, "import \"os\"\n")
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range pt.extra {
|
for _, pt := range ptrTests {
|
||||||
if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil {
|
did := did1
|
||||||
t.Fatalf("writing %s: %v", e.name, err)
|
cgo := &cgo1
|
||||||
|
if strings.Contains(pt.support, "//export") {
|
||||||
|
did = did2
|
||||||
|
cgo = &cgo2
|
||||||
|
}
|
||||||
|
for _, imp := range pt.imports {
|
||||||
|
if !did[imp] {
|
||||||
|
did[imp] = true
|
||||||
|
fmt.Fprintf(cgo, "import %q\n", imp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gomod := fmt.Sprintf("module %s\n", filepath.Base(src))
|
// Func support and bodies.
|
||||||
if err := ioutil.WriteFile(filepath.Join(src, "go.mod"), []byte(gomod), 0666); err != nil {
|
for _, pt := range ptrTests {
|
||||||
t.Fatalf("writing go.mod: %v", err)
|
cgo := &cgo1
|
||||||
|
if strings.Contains(pt.support, "//export") {
|
||||||
|
cgo = &cgo2
|
||||||
|
}
|
||||||
|
fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := func(cmd *exec.Cmd) string {
|
// Func list and main dispatch.
|
||||||
return strings.Join(cmd.Args, " ")
|
fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
|
||||||
|
for _, pt := range ptrTests {
|
||||||
|
fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&cgo1, "}\n\n")
|
||||||
|
fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("go", "build")
|
cmd := exec.Command("go", "build", "-o", "ptrtest.exe")
|
||||||
cmd.Dir = src
|
cmd.Dir = src
|
||||||
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
|
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
|
||||||
buf, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Fatalf("go build: %v\n%s", err, out)
|
||||||
t.Fatalf("failed to build: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exe := filepath.Join(src, filepath.Base(src))
|
return dir, filepath.Join(src, "ptrtest.exe")
|
||||||
cmd = exec.Command(exe)
|
}
|
||||||
cmd.Dir = src
|
|
||||||
|
const ptrTestMain = `
|
||||||
|
func main() {
|
||||||
|
for _, arg := range os.Args[1:] {
|
||||||
|
f := funcs[arg]
|
||||||
|
if f == nil {
|
||||||
|
panic("missing func "+arg)
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func testOne(t *testing.T, pt ptrTest, exe string) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
newcmd := func(cgocheck string) *exec.Cmd {
|
||||||
|
cmd := exec.Command(exe, pt.name)
|
||||||
|
cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
if pt.expensive {
|
if pt.expensive {
|
||||||
cmd.Env = cgocheckEnv("1")
|
cmd := newcmd("1")
|
||||||
buf, err := cmd.CombinedOutput()
|
buf, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
if pt.fail {
|
if pt.fail {
|
||||||
t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
|
t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -524,54 +575,45 @@ func testOne(t *testing.T, pt ptrTest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = exec.Command(exe)
|
|
||||||
cmd.Dir = src
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd := newcmd("")
|
||||||
if pt.expensive {
|
if pt.expensive {
|
||||||
cmd.Env = cgocheckEnv("2")
|
cmd = newcmd("2")
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err = cmd.CombinedOutput()
|
buf, err := cmd.CombinedOutput()
|
||||||
if pt.fail {
|
if pt.fail {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
t.Fatalf("did not fail as expected")
|
t.Fatalf("did not fail as expected")
|
||||||
} else if !bytes.Contains(buf, []byte("Go pointer")) {
|
} else if !bytes.Contains(buf, []byte("Go pointer")) {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
t.Fatalf("did not print expected error (failed with %v)", err)
|
t.Fatalf("did not print expected error (failed with %v)", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
t.Fatalf("failed unexpectedly: %v", err)
|
t.Fatalf("failed unexpectedly: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pt.expensive {
|
if !pt.expensive {
|
||||||
// Make sure it passes with the expensive checks.
|
// Make sure it passes with the expensive checks.
|
||||||
cmd := exec.Command(exe)
|
cmd := newcmd("2")
|
||||||
cmd.Dir = src
|
|
||||||
cmd.Env = cgocheckEnv("2")
|
|
||||||
buf, err := cmd.CombinedOutput()
|
buf, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
t.Fatalf("failed unexpectedly with expensive checks: %v", err)
|
t.Fatalf("failed unexpectedly with expensive checks: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pt.fail {
|
if pt.fail {
|
||||||
cmd = exec.Command(exe)
|
cmd := newcmd("0")
|
||||||
cmd.Dir = src
|
|
||||||
cmd.Env = cgocheckEnv("0")
|
|
||||||
buf, err := cmd.CombinedOutput()
|
buf, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%#q:\n%s", args(cmd), buf)
|
t.Logf("%s", buf)
|
||||||
t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
|
t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cgocheckEnv(val string) []string {
|
|
||||||
return append(os.Environ(), "GODEBUG=cgocheck="+val)
|
|
||||||
}
|
|
||||||
|
4
misc/cgo/errors/testdata/err1.go
vendored
4
misc/cgo/errors/testdata/err1.go
vendored
@ -10,6 +10,10 @@ package main
|
|||||||
void test() {
|
void test() {
|
||||||
xxx; // ERROR HERE
|
xxx; // ERROR HERE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 8442. Cgo output unhelpful error messages for
|
||||||
|
// invalid C preambles.
|
||||||
|
void issue8442foo(UNDEF*); // ERROR HERE
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
89
misc/cgo/errors/testdata/err2.go
vendored
89
misc/cgo/errors/testdata/err2.go
vendored
@ -4,10 +4,99 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct foo foo_t;
|
||||||
|
typedef struct bar bar_t;
|
||||||
|
|
||||||
|
foo_t *foop;
|
||||||
|
|
||||||
|
long double x = 0;
|
||||||
|
|
||||||
|
static int transform(int x) { return x; }
|
||||||
|
|
||||||
|
typedef void v;
|
||||||
|
void F(v** p) {}
|
||||||
|
|
||||||
|
void fvi(void *p, int x) {}
|
||||||
|
|
||||||
|
void fppi(int** p) {}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
void fi(int i) {}
|
||||||
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := ""
|
s := ""
|
||||||
_ = s
|
_ = s
|
||||||
C.malloc(s) // ERROR HERE
|
C.malloc(s) // ERROR HERE
|
||||||
|
|
||||||
|
x := (*C.bar_t)(nil)
|
||||||
|
C.foop = x // ERROR HERE
|
||||||
|
|
||||||
|
// issue 13129: used to output error about C.unsignedshort with CC=clang
|
||||||
|
var x C.ushort
|
||||||
|
x = int(0) // ERROR HERE: C\.ushort
|
||||||
|
|
||||||
|
// issue 13423
|
||||||
|
_ = C.fopen() // ERROR HERE
|
||||||
|
|
||||||
|
// issue 13467
|
||||||
|
var x rune = '✈'
|
||||||
|
var _ rune = C.transform(x) // ERROR HERE: C\.int
|
||||||
|
|
||||||
|
// issue 13635: used to output error about C.unsignedchar.
|
||||||
|
// This test tests all such types.
|
||||||
|
var (
|
||||||
|
_ C.uchar = "uc" // ERROR HERE: C\.uchar
|
||||||
|
_ C.schar = "sc" // ERROR HERE: C\.schar
|
||||||
|
_ C.ushort = "us" // ERROR HERE: C\.ushort
|
||||||
|
_ C.uint = "ui" // ERROR HERE: C\.uint
|
||||||
|
_ C.ulong = "ul" // ERROR HERE: C\.ulong
|
||||||
|
_ C.longlong = "ll" // ERROR HERE: C\.longlong
|
||||||
|
_ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong
|
||||||
|
_ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat
|
||||||
|
_ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble
|
||||||
|
)
|
||||||
|
|
||||||
|
// issue 13830
|
||||||
|
// cgo converts C void* to Go unsafe.Pointer, so despite appearances C
|
||||||
|
// void** is Go *unsafe.Pointer. This test verifies that we detect the
|
||||||
|
// problem at build time.
|
||||||
|
{
|
||||||
|
type v [0]byte
|
||||||
|
|
||||||
|
f := func(p **v) {
|
||||||
|
C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE
|
||||||
|
}
|
||||||
|
var p *v
|
||||||
|
f(&p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue 16116
|
||||||
|
_ = C.fvi(1) // ERROR HERE
|
||||||
|
|
||||||
|
// Issue 16591: Test that we detect an invalid call that was being
|
||||||
|
// hidden by a type conversion inserted by cgo checking.
|
||||||
|
{
|
||||||
|
type x *C.int
|
||||||
|
var p *x
|
||||||
|
C.fppi(p) // ERROR HERE
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue 26745
|
||||||
|
_ = func(i int) int {
|
||||||
|
return C.i + 1 // ERROR HERE: :13
|
||||||
|
}
|
||||||
|
_ = func(i int) {
|
||||||
|
C.fi(i) // ERROR HERE: :6
|
||||||
|
}
|
||||||
|
|
||||||
|
C.fi = C.fi // ERROR HERE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
18
misc/cgo/errors/testdata/err3.go
vendored
18
misc/cgo/errors/testdata/err3.go
vendored
@ -1,18 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
typedef struct foo foo_t;
|
|
||||||
typedef struct bar bar_t;
|
|
||||||
|
|
||||||
foo_t *foop;
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
x := (*C.bar_t)(nil)
|
|
||||||
C.foop = x // ERROR HERE
|
|
||||||
}
|
|
14
misc/cgo/errors/testdata/issue13129.go
vendored
14
misc/cgo/errors/testdata/issue13129.go
vendored
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2015 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.
|
|
||||||
|
|
||||||
// issue 13129: used to output error about C.unsignedshort with CC=clang
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var x C.ushort
|
|
||||||
x = int(0) // ERROR HERE: C\.ushort
|
|
||||||
}
|
|
12
misc/cgo/errors/testdata/issue13423.go
vendored
12
misc/cgo/errors/testdata/issue13423.go
vendored
@ -1,12 +0,0 @@
|
|||||||
// Copyright 2015 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// #include <stdio.h>
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
_ = C.fopen() // ERROR HERE
|
|
||||||
}
|
|
15
misc/cgo/errors/testdata/issue13467.go
vendored
15
misc/cgo/errors/testdata/issue13467.go
vendored
@ -1,15 +0,0 @@
|
|||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
package p
|
|
||||||
|
|
||||||
/*
|
|
||||||
static int transform(int x) { return x; }
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func F() {
|
|
||||||
var x rune = '✈'
|
|
||||||
var _ rune = C.transform(x) // ERROR HERE: C\.int
|
|
||||||
}
|
|
24
misc/cgo/errors/testdata/issue13635.go
vendored
24
misc/cgo/errors/testdata/issue13635.go
vendored
@ -1,24 +0,0 @@
|
|||||||
// Copyright 2015 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.
|
|
||||||
|
|
||||||
// issue 13635: used to output error about C.unsignedchar.
|
|
||||||
// This test tests all such types.
|
|
||||||
|
|
||||||
package pkg
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var (
|
|
||||||
_ C.uchar = "uc" // ERROR HERE: C\.uchar
|
|
||||||
_ C.schar = "sc" // ERROR HERE: C\.schar
|
|
||||||
_ C.ushort = "us" // ERROR HERE: C\.ushort
|
|
||||||
_ C.uint = "ui" // ERROR HERE: C\.uint
|
|
||||||
_ C.ulong = "ul" // ERROR HERE: C\.ulong
|
|
||||||
_ C.longlong = "ll" // ERROR HERE: C\.longlong
|
|
||||||
_ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong
|
|
||||||
_ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat
|
|
||||||
_ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble
|
|
||||||
)
|
|
||||||
}
|
|
26
misc/cgo/errors/testdata/issue13830.go
vendored
26
misc/cgo/errors/testdata/issue13830.go
vendored
@ -1,26 +0,0 @@
|
|||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// cgo converts C void* to Go unsafe.Pointer, so despite appearances C
|
|
||||||
// void** is Go *unsafe.Pointer. This test verifies that we detect the
|
|
||||||
// problem at build time.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// typedef void v;
|
|
||||||
// void F(v** p) {}
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
type v [0]byte
|
|
||||||
|
|
||||||
func f(p **v) {
|
|
||||||
C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var p *v
|
|
||||||
f(&p)
|
|
||||||
}
|
|
12
misc/cgo/errors/testdata/issue16116.go
vendored
12
misc/cgo/errors/testdata/issue16116.go
vendored
@ -1,12 +0,0 @@
|
|||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// void f(void *p, int x) {}
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
_ = C.f(1) // ERROR HERE
|
|
||||||
}
|
|
17
misc/cgo/errors/testdata/issue16591.go
vendored
17
misc/cgo/errors/testdata/issue16591.go
vendored
@ -1,17 +0,0 @@
|
|||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// Issue 16591: Test that we detect an invalid call that was being
|
|
||||||
// hidden by a type conversion inserted by cgo checking.
|
|
||||||
|
|
||||||
package p
|
|
||||||
|
|
||||||
// void f(int** p) { }
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type x *C.int
|
|
||||||
|
|
||||||
func F(p *x) {
|
|
||||||
C.f(p) // ERROR HERE
|
|
||||||
}
|
|
17
misc/cgo/errors/testdata/issue26745.go
vendored
17
misc/cgo/errors/testdata/issue26745.go
vendored
@ -1,17 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// int a;
|
|
||||||
// void CF(int i) {}
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func F1(i int) int {
|
|
||||||
return C.a + 1 // ERROR HERE: :13
|
|
||||||
}
|
|
||||||
|
|
||||||
func F2(i int) {
|
|
||||||
C.CF(i) // ERROR HERE: :6
|
|
||||||
}
|
|
14
misc/cgo/errors/testdata/issue7757.go
vendored
14
misc/cgo/errors/testdata/issue7757.go
vendored
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
void foo() {}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
C.foo = C.foo // ERROR HERE
|
|
||||||
}
|
|
17
misc/cgo/errors/testdata/issue8442.go
vendored
17
misc/cgo/errors/testdata/issue8442.go
vendored
@ -1,17 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// Issue 8442. Cgo output unhelpful error messages for
|
|
||||||
// invalid C preambles.
|
|
||||||
|
|
||||||
/*
|
|
||||||
void issue8442foo(UNDEF*); // ERROR HERE
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
C.issue8442foo(nil)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user