Skip to content

Commit e970966

Browse files
runtime: handle kindString in cgoCheckArg
It's awkward to get a string value in cgoCheckArg, but SWIG testing revealed that it is possible. The new handling of extra files in the ptr.go test emulates what SWIG does with an exported function that returns a string. Change-Id: I453717f867b8a49499576c28550e7c93053a0cf8 Reviewed-on: https://go-review.googlesource.com/19020 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent e7ce1ba commit e970966

File tree

2 files changed

+98
-14
lines changed

2 files changed

+98
-14
lines changed

misc/cgo/errors/ptr.go

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,16 @@ type ptrTest struct {
2727
imports []string // a list of imports
2828
support string // supporting functions
2929
body string // the body of the main function
30+
extra []extra // extra files
3031
fail bool // whether the test should fail
3132
expensive bool // whether the test requires the expensive check
3233
}
3334

35+
type extra struct {
36+
name string
37+
contents string
38+
}
39+
3440
var ptrTests = []ptrTest{
3541
{
3642
// Passing a pointer to a struct that contains a Go pointer.
@@ -237,19 +243,61 @@ var ptrTests = []ptrTest{
237243
func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
238244
body: `C.GoFn()`,
239245
},
246+
{
247+
// Passing a Go string is fine.
248+
name: "pass-string",
249+
c: `#include <stddef.h>
250+
typedef struct { const char *p; ptrdiff_t n; } gostring;
251+
gostring f(gostring s) { return s; }`,
252+
imports: []string{"unsafe"},
253+
body: `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
254+
},
255+
{
256+
// Passing a slice of Go strings fails.
257+
name: "pass-string-slice",
258+
c: `void f(void *p) {}`,
259+
imports: []string{"strings", "unsafe"},
260+
support: `type S struct { a [1]string }`,
261+
body: `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`,
262+
fail: true,
263+
},
264+
{
265+
// Exported functions may not return strings.
266+
name: "ret-string",
267+
c: `extern void f();`,
268+
imports: []string{"strings"},
269+
support: `//export GoStr
270+
func GoStr() string { return strings.Repeat("a", 2) }`,
271+
body: `C.f()`,
272+
extra: []extra{
273+
{
274+
"call.c",
275+
`#include <stddef.h>
276+
typedef struct { const char *p; ptrdiff_t n; } gostring;
277+
extern gostring GoStr();
278+
void f() { GoStr(); }`,
279+
},
280+
},
281+
fail: true,
282+
},
240283
}
241284

242285
func main() {
243286
os.Exit(doTests())
244287
}
245288

246289
func doTests() int {
247-
dir, err := ioutil.TempDir("", "cgoerrors")
290+
gopath, err := ioutil.TempDir("", "cgoerrors")
248291
if err != nil {
249292
fmt.Fprintln(os.Stderr, err)
250293
return 2
251294
}
252-
defer os.RemoveAll(dir)
295+
defer os.RemoveAll(gopath)
296+
297+
if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil {
298+
fmt.Fprintln(os.Stderr, err)
299+
return 2
300+
}
253301

254302
workers := runtime.NumCPU() + 1
255303

@@ -259,7 +307,7 @@ func doTests() int {
259307
for i := 0; i < workers; i++ {
260308
wg.Add(1)
261309
go func() {
262-
worker(dir, c, errs)
310+
worker(gopath, c, errs)
263311
wg.Done()
264312
}()
265313
}
@@ -281,10 +329,10 @@ func doTests() int {
281329
return tot
282330
}
283331

284-
func worker(dir string, c, errs chan int) {
332+
func worker(gopath string, c, errs chan int) {
285333
e := 0
286334
for i := range c {
287-
if !doOne(dir, i) {
335+
if !doOne(gopath, i) {
288336
e++
289337
}
290338
}
@@ -293,9 +341,15 @@ func worker(dir string, c, errs chan int) {
293341
}
294342
}
295343

296-
func doOne(dir string, i int) bool {
344+
func doOne(gopath string, i int) bool {
297345
t := &ptrTests[i]
298346

347+
dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i))
348+
if err := os.Mkdir(dir, 0777); err != nil {
349+
fmt.Fprintln(os.Stderr, err)
350+
return false
351+
}
352+
299353
name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
300354
f, err := os.Create(name)
301355
if err != nil {
@@ -330,13 +384,30 @@ func doOne(dir string, i int) bool {
330384
return false
331385
}
332386
if err := f.Close(); err != nil {
333-
fmt.Fprintln(os.Stderr, "closing %s: %v\n", name, err)
387+
fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err)
334388
return false
335389
}
336390

391+
for _, e := range t.extra {
392+
if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil {
393+
fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err)
394+
return false
395+
}
396+
}
397+
337398
ok := true
338399

339-
cmd := exec.Command("go", "run", name)
400+
cmd := exec.Command("go", "build")
401+
cmd.Dir = dir
402+
cmd.Env = addEnv("GOPATH", gopath)
403+
buf, err := cmd.CombinedOutput()
404+
if err != nil {
405+
fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf)
406+
return false
407+
}
408+
409+
exe := filepath.Join(dir, filepath.Base(dir))
410+
cmd = exec.Command(exe)
340411
cmd.Dir = dir
341412

342413
if t.expensive {
@@ -354,15 +425,15 @@ func doOne(dir string, i int) bool {
354425
ok = false
355426
}
356427

357-
cmd = exec.Command("go", "run", name)
428+
cmd = exec.Command(exe)
358429
cmd.Dir = dir
359430
}
360431

361432
if t.expensive {
362433
cmd.Env = cgocheckEnv("2")
363434
}
364435

365-
buf, err := cmd.CombinedOutput()
436+
buf, err = cmd.CombinedOutput()
366437

367438
if t.fail {
368439
if err == nil {
@@ -389,7 +460,7 @@ func doOne(dir string, i int) bool {
389460

390461
if !t.expensive && ok {
391462
// Make sure it passes with the expensive checks.
392-
cmd := exec.Command("go", "run", name)
463+
cmd := exec.Command(exe)
393464
cmd.Dir = dir
394465
cmd.Env = cgocheckEnv("2")
395466
buf, err := cmd.CombinedOutput()
@@ -404,7 +475,7 @@ func doOne(dir string, i int) bool {
404475
}
405476

406477
if t.fail && ok {
407-
cmd = exec.Command("go", "run", name)
478+
cmd = exec.Command(exe)
408479
cmd.Dir = dir
409480
cmd.Env = cgocheckEnv("0")
410481
buf, err := cmd.CombinedOutput()
@@ -427,9 +498,14 @@ func reportTestOutput(w io.Writer, name string, buf []byte) {
427498
}
428499

429500
func cgocheckEnv(val string) []string {
430-
env := []string{"GODEBUG=cgocheck=" + val}
501+
return addEnv("GODEBUG", "cgocheck="+val)
502+
}
503+
504+
func addEnv(key, val string) []string {
505+
env := []string{key + "=" + val}
506+
look := key + "="
431507
for _, e := range os.Environ() {
432-
if !strings.HasPrefix(e, "GODEBUG=") {
508+
if !strings.HasPrefix(e, look) {
433509
env = append(env, e)
434510
}
435511
}

src/runtime/cgocall.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,14 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
467467
cgoCheckArg(st.elem, p, true, false, msg)
468468
p = add(p, st.elem.size)
469469
}
470+
case kindString:
471+
ss := (*stringStruct)(p)
472+
if !cgoIsGoPointer(ss.str) {
473+
return
474+
}
475+
if !top {
476+
panic(errorString(msg))
477+
}
470478
case kindStruct:
471479
st := (*structtype)(unsafe.Pointer(t))
472480
if !indir {

0 commit comments

Comments
 (0)