Commit 918396b3 authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/cgo: permit passing string values directly between Go and C

Permit the C preamble to use the _GoString_ type. Permit Go code to
pass string values directly to those C types. Add accessors for C
code to retrieve sizes and pointers.

Fixes #6907

Change-Id: I190c88319ec88a3ef0ddb99f342a843ba69fcaa3
Reviewed-on: https://go-review.googlesource.com/70890
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: 's avatarAustin Clements <austin@google.com>
parent 707a4d3f
......@@ -83,5 +83,7 @@ func Test20129(t *testing.T) { test20129(t) }
func Test20910(t *testing.T) { test20910(t) }
func Test21708(t *testing.T) { test21708(t) }
func Test21809(t *testing.T) { test21809(t) }
func Test6907(t *testing.T) { test6907(t) }
func Test6907Go(t *testing.T) { test6907Go(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
// 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 cgotest
/*
#include <stdlib.h>
#include <string.h>
char* Issue6907CopyString(_GoString_ s) {
size_t n;
const char *p;
char *r;
n = _GoStringLen(s);
p = _GoStringPtr(s);
r = malloc(n + 1);
memmove(r, p, n);
r[n] = '\0';
return r;
}
*/
import "C"
import "testing"
func test6907(t *testing.T) {
want := "yarn"
if got := C.GoString(C.Issue6907CopyString(want)); got != want {
t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want)
}
}
// 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 cgotest
/*
extern int CheckIssue6907C(_GoString_);
*/
import "C"
import (
"testing"
)
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)
}
}
// 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.
#include <string.h>
#include "_cgo_export.h"
int CheckIssue6907C(_GoString_ s) {
return CheckIssue6907Go(s);
}
......@@ -134,6 +134,19 @@ struct_, union_, or enum_, as in C.struct_stat.
The size of any C type T is available as C.sizeof_T, as in
C.sizeof_struct_stat.
A C function may be declared in the Go file with a parameter type of
the special name _GoString_. This function may be called with an
ordinary Go string value. The string length, and a pointer to the
string contents, may be accessed by calling the C functions
size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);
These functions are only available in the preamble, not in other C
files. The C code must not modify the contents of the pointer returned
by _GoStringPtr. Note that the string contents may not have a trailing
NUL byte.
As Go doesn't have support for C's union type in the general case,
C's union types are represented as a Go byte array with the same length.
......@@ -248,6 +261,12 @@ Not all Go types can be mapped to C types in a useful way.
Go struct types are not supported; use a C struct type.
Go array types are not supported; use a C pointer.
Go functions that take arguments of type string may be called with the
C type _GoString_, described above. The _GoString_ type will be
automatically defined in the preamble. Note that there is no way for C
code to create a value of this type; this is only useful for passing
string values from Go to C and back to Go.
Using //export in a file places a restriction on the preamble:
since it is copied into two different C output files, it must not
contain any definitions, only declarations. If a file contains both
......@@ -269,6 +288,14 @@ pointer is a Go pointer or a C pointer is a dynamic property
determined by how the memory was allocated; it has nothing to do with
the type of the pointer.
Note that values of some Go types, other than the type's zero value,
always include Go pointers. This is true of string, slice, interface,
channel, map, and function types. A pointer type may hold a Go pointer
or a C pointer. Array and struct types may or may not include Go
pointers, depending on the element types. All the discussion below
about Go pointers applies not just to pointer types, but also to other
types that include Go pointers.
Go code may pass a Go pointer to C provided the Go memory to which it
points does not contain any Go pointers. The C code must preserve
this property: it must not store any Go pointers in Go memory, even
......@@ -279,14 +306,17 @@ the Go memory in question is the entire array or the entire backing
array of the slice.
C code may not keep a copy of a Go pointer after the call returns.
A Go function called by C code may not return a Go pointer. A Go
function called by C code may take C pointers as arguments, and it may
store non-pointer or C pointer data through those pointers, but it may
not store a Go pointer in memory pointed to by a C pointer. A Go
function called by C code may take a Go pointer as an argument, but it
must preserve the property that the Go memory to which it points does
not contain any Go pointers.
This includes the _GoString_ type, which, as noted above, includes a
Go pointer; _GoString_ values may not be retained by C code.
A Go function called by C code may not return a Go pointer (which
implies that it may not return a string, slice, channel, and so
forth). A Go function called by C code may take C pointers as
arguments, and it may store non-pointer or C pointer data through
those pointers, but it may not store a Go pointer in memory pointed to
by a C pointer. A Go function called by C code may take a Go pointer
as an argument, but it must preserve the property that the Go memory
to which it points does not contain any Go pointers.
Go code may not store a Go pointer in C memory. C code may store Go
pointers in C memory, subject to the rule above: it must stop storing
......
......@@ -192,8 +192,8 @@ func (p *Package) Translate(f *File) {
// in the file f and saves relevant renamings in f.Name[name].Define.
func (p *Package) loadDefines(f *File) {
var b bytes.Buffer
b.WriteString(f.Preamble)
b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
stdout := p.gccDefines(b.Bytes())
for _, line := range strings.Split(stdout, "\n") {
......@@ -312,8 +312,8 @@ func (p *Package) guessKinds(f *File) []*Name {
// whether name denotes a type or an expression.
var b bytes.Buffer
b.WriteString(f.Preamble)
b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
for i, n := range names {
fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+
......@@ -461,8 +461,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// for each entry in names and then dereference the type we
// learn for __cgo__i.
var b bytes.Buffer
b.WriteString(f.Preamble)
b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
for i, n := range names {
fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
......
......@@ -539,6 +539,7 @@ func (p *Package) writeOutput(f *File, srcfile string) {
// While we process the vars and funcs, also write gcc output.
// Gcc output starts with the preamble.
fmt.Fprintf(fgcc, "%s\n", builtinProlog)
fmt.Fprintf(fgcc, "%s\n", f.Preamble)
fmt.Fprintf(fgcc, "%s\n", gccProlog)
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
......@@ -1145,6 +1146,7 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
pkg = p.PackagePath
}
fmt.Fprintf(fgcch, "/* package %s */\n\n", pkg)
fmt.Fprintf(fgcch, "%s\n", builtinExportProlog)
fmt.Fprintf(fgcch, "/* Start of preamble from import \"C\" comments. */\n\n")
fmt.Fprintf(fgcch, "%s\n", p.Preamble)
......@@ -1389,7 +1391,7 @@ const builtinProlog = `
/* Define intgo when compiling with GCC. */
typedef ptrdiff_t intgo;
typedef struct { char *p; intgo n; } _GoString_;
typedef struct { const char *p; intgo n; } _GoString_;
typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
_GoString_ GoString(char *p);
_GoString_ GoStringN(char *p, int l);
......@@ -1397,6 +1399,12 @@ _GoBytes_ GoBytes(void *p, int n);
char *CString(_GoString_);
void *CBytes(_GoBytes_);
void *_CMalloc(size_t);
__attribute__ ((unused))
static size_t _GoStringLen(_GoString_ s) { return s.n; }
__attribute__ ((unused))
static const char *_GoStringPtr(_GoString_ s) { return s.p; }
`
const goProlog = `
......@@ -1628,6 +1636,27 @@ void localCgoCheckResult(Eface val) {
}
`
// builtinExportProlog is a shorter version of builtinProlog,
// to be put into the _cgo_export.h file.
// For historical reasons we can't use builtinProlog in _cgo_export.h,
// because _cgo_export.h defines GoString as a struct while builtinProlog
// defines it as a function. We don't change this to avoid unnecessarily
// breaking existing code.
const builtinExportProlog = `
#line 1 "cgo-builtin-prolog"
#include <stddef.h> /* for ptrdiff_t below */
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
typedef ptrdiff_t intgo;
typedef struct { const char *p; intgo n; } _GoString_;
#endif
`
func (p *Package) gccExportHeaderProlog() string {
return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1)
}
......@@ -1661,7 +1690,7 @@ typedef double _Complex GoComplex128;
*/
typedef char _check_for_GOINTBITS_bit_pointer_matching_GoInt[sizeof(void*)==GOINTBITS/8 ? 1:-1];
typedef struct { const char *p; GoInt n; } GoString;
typedef _GoString_ GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment