Commit 8f6ae337 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile, cmd/link: encode cgo directives using JSON

The standard library has plenty of polished encoder/decoder
implementations. No need for another ad-hoc one.

I considered using encoding/gob instead, but these strings go into the
package data part of the object file, so it's important they don't
contain "\n$$\n". Package json escapes newlines in strings, so it's
safe to use here.

Change-Id: I998655524ccee7365c2c8e9a843e6975e95a3e62
Reviewed-on: https://go-review.googlesource.com/106463
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 044d2d5a
......@@ -602,6 +602,7 @@ var knownFormats = map[string]string{
"[]*cmd/compile/internal/gc.Node %v": "",
"[]*cmd/compile/internal/ssa.Block %v": "",
"[]*cmd/compile/internal/ssa.Value %v": "",
"[][]string %q": "",
"[]byte %s": "",
"[]byte %x": "",
"[]cmd/compile/internal/ssa.Edge %v": "",
......
......@@ -78,7 +78,7 @@ var sizeof_Array int // runtime sizeof(Array)
// } String;
var sizeof_String int // runtime sizeof(String)
var pragcgobuf string
var pragcgobuf [][]string
var outfile string
var linkobj string
......
......@@ -28,18 +28,6 @@ func isQuoted(s string) bool {
return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
}
func plan9quote(s string) string {
if s == "" {
return "''"
}
for _, c := range s {
if c <= ' ' || c == '\'' {
return "'" + strings.Replace(s, "'", "''", -1) + "'"
}
}
return s
}
const (
// Func pragmas.
Nointerface syntax.Pragma = 1 << iota
......@@ -105,74 +93,58 @@ func pragmaValue(verb string) syntax.Pragma {
}
// pragcgo is called concurrently if files are parsed concurrently.
func (p *noder) pragcgo(pos syntax.Pos, text string) string {
func (p *noder) pragcgo(pos syntax.Pos, text string) {
f := pragmaFields(text)
verb := f[0][3:] // skip "go:"
verb := strings.TrimPrefix(f[0][3:], "go:")
f[0] = verb
switch verb {
case "cgo_export_static", "cgo_export_dynamic":
switch {
case len(f) == 2 && !isQuoted(f[1]):
local := plan9quote(f[1])
return fmt.Sprintln(verb, local)
case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
local := plan9quote(f[1])
remote := plan9quote(f[2])
return fmt.Sprintln(verb, local, remote)
default:
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)})
return
}
case "cgo_import_dynamic":
switch {
case len(f) == 2 && !isQuoted(f[1]):
local := plan9quote(f[1])
return fmt.Sprintln(verb, local)
case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
local := plan9quote(f[1])
remote := plan9quote(f[2])
return fmt.Sprintln(verb, local, remote)
case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
local := plan9quote(f[1])
remote := plan9quote(f[2])
library := plan9quote(strings.Trim(f[3], `"`))
return fmt.Sprintln(verb, local, remote, library)
f[3] = strings.Trim(f[3], `"`)
default:
p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`})
return
}
case "cgo_import_static":
switch {
case len(f) == 2 && !isQuoted(f[1]):
local := plan9quote(f[1])
return fmt.Sprintln(verb, local)
default:
p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`})
return
}
case "cgo_dynamic_linker":
switch {
case len(f) == 2 && isQuoted(f[1]):
path := plan9quote(strings.Trim(f[1], `"`))
return fmt.Sprintln(verb, path)
f[1] = strings.Trim(f[1], `"`)
default:
p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`})
return
}
case "cgo_ldflag":
switch {
case len(f) == 2 && isQuoted(f[1]):
arg := plan9quote(strings.Trim(f[1], `"`))
return fmt.Sprintln(verb, arg)
f[1] = strings.Trim(f[1], `"`)
default:
p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`})
return
}
default:
return
}
return ""
p.pragcgobuf = append(p.pragcgobuf, f)
}
// pragmaFields is similar to strings.FieldsFunc(s, isSpace)
......
......@@ -6,6 +6,7 @@ package gc
import (
"cmd/compile/internal/syntax"
"reflect"
"testing"
)
......@@ -50,32 +51,36 @@ func TestPragmaFields(t *testing.T) {
func TestPragcgo(t *testing.T) {
var tests = []struct {
in string
want string
want []string
}{
{`go:cgo_export_dynamic local`, "cgo_export_dynamic local\n"},
{`go:cgo_export_dynamic local remote`, "cgo_export_dynamic local remote\n"},
{`go:cgo_export_dynamic local' remote'`, "cgo_export_dynamic 'local''' 'remote'''\n"},
{`go:cgo_export_static local`, "cgo_export_static local\n"},
{`go:cgo_export_static local remote`, "cgo_export_static local remote\n"},
{`go:cgo_export_static local' remote'`, "cgo_export_static 'local''' 'remote'''\n"},
{`go:cgo_import_dynamic local`, "cgo_import_dynamic local\n"},
{`go:cgo_import_dynamic local remote`, "cgo_import_dynamic local remote\n"},
{`go:cgo_import_dynamic local remote "library"`, "cgo_import_dynamic local remote library\n"},
{`go:cgo_import_dynamic local' remote' "lib rary"`, "cgo_import_dynamic 'local''' 'remote''' 'lib rary'\n"},
{`go:cgo_import_static local`, "cgo_import_static local\n"},
{`go:cgo_import_static local'`, "cgo_import_static 'local'''\n"},
{`go:cgo_dynamic_linker "/path/"`, "cgo_dynamic_linker /path/\n"},
{`go:cgo_dynamic_linker "/p ath/"`, "cgo_dynamic_linker '/p ath/'\n"},
{`go:cgo_ldflag "arg"`, "cgo_ldflag arg\n"},
{`go:cgo_ldflag "a rg"`, "cgo_ldflag 'a rg'\n"},
{`go:cgo_export_dynamic local`, []string{`cgo_export_dynamic`, `local`}},
{`go:cgo_export_dynamic local remote`, []string{`cgo_export_dynamic`, `local`, `remote`}},
{`go:cgo_export_dynamic local' remote'`, []string{`cgo_export_dynamic`, `local'`, `remote'`}},
{`go:cgo_export_static local`, []string{`cgo_export_static`, `local`}},
{`go:cgo_export_static local remote`, []string{`cgo_export_static`, `local`, `remote`}},
{`go:cgo_export_static local' remote'`, []string{`cgo_export_static`, `local'`, `remote'`}},
{`go:cgo_import_dynamic local`, []string{`cgo_import_dynamic`, `local`}},
{`go:cgo_import_dynamic local remote`, []string{`cgo_import_dynamic`, `local`, `remote`}},
{`go:cgo_import_dynamic local remote "library"`, []string{`cgo_import_dynamic`, `local`, `remote`, `library`}},
{`go:cgo_import_dynamic local' remote' "lib rary"`, []string{`cgo_import_dynamic`, `local'`, `remote'`, `lib rary`}},
{`go:cgo_import_static local`, []string{`cgo_import_static`, `local`}},
{`go:cgo_import_static local'`, []string{`cgo_import_static`, `local'`}},
{`go:cgo_dynamic_linker "/path/"`, []string{`cgo_dynamic_linker`, `/path/`}},
{`go:cgo_dynamic_linker "/p ath/"`, []string{`cgo_dynamic_linker`, `/p ath/`}},
{`go:cgo_ldflag "arg"`, []string{`cgo_ldflag`, `arg`}},
{`go:cgo_ldflag "a rg"`, []string{`cgo_ldflag`, `a rg`}},
}
var p noder
var nopos syntax.Pos
for _, tt := range tests {
got := p.pragcgo(nopos, tt.in)
if got != tt.want {
t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, tt.want)
p.pragcgobuf = nil
p.pragcgo(nopos, tt.in)
got := p.pragcgobuf
want := [][]string{tt.want}
if !reflect.DeepEqual(got, want) {
t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, want)
continue
}
}
......
......@@ -127,7 +127,7 @@ type noder struct {
file *syntax.File
linknames []linkname
pragcgobuf string
pragcgobuf [][]string
err chan syntax.Error
scope ScopeID
......@@ -246,7 +246,7 @@ func (p *noder) node() {
}
}
pragcgobuf += p.pragcgobuf
pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
lineno = src.NoXPos
clearImports()
}
......@@ -1417,7 +1417,7 @@ func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
}
p.pragcgobuf += p.pragcgo(pos, text)
p.pragcgo(pos, text)
return pragmaValue("go:cgo_import_dynamic")
}
fallthrough
......@@ -1428,7 +1428,7 @@ func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
if !isCgoGeneratedFile(pos) && !compiling_std {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
}
p.pragcgobuf += p.pragcgo(pos, text)
p.pragcgo(pos, text)
fallthrough // because of //go:cgo_unsafe_args
default:
verb := text
......
......@@ -11,6 +11,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"strconv"
......@@ -121,11 +122,14 @@ func dumpCompilerObj(bout *bio.Writer) {
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if pragcgobuf != "" {
if len(pragcgobuf) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf)
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
......
......@@ -11,6 +11,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/link/internal/sym"
"encoding/json"
"fmt"
"io"
"os"
......@@ -104,28 +105,18 @@ func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename s
}
func loadcgo(ctxt *Link, file string, pkg string, p string) {
var next string
var q string
var lib string
var s *sym.Symbol
p0 := ""
for ; p != ""; p = next {
if i := strings.Index(p, "\n"); i >= 0 {
p, next = p[:i], p[i+1:]
} else {
next = ""
}
p0 = p // save for error message
f := tokenize(p)
if len(f) == 0 {
continue
}
var directives [][]string
if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
nerrors++
return
}
if f[0] == "cgo_import_dynamic" {
for _, f := range directives {
switch f[0] {
case "cgo_import_dynamic":
if len(f) < 2 || len(f) > 4 {
goto err
break
}
local := f[1]
......@@ -133,7 +124,7 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
if len(f) > 2 {
remote = f[2]
}
lib = ""
lib := ""
if len(f) > 3 {
lib = f[3]
}
......@@ -158,11 +149,11 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
}
local = expandpkg(local, pkg)
q = ""
q := ""
if i := strings.Index(remote, "#"); i >= 0 {
remote, q = remote[:i], remote[i+1:]
}
s = ctxt.Syms.Lookup(local, 0)
s := ctxt.Syms.Lookup(local, 0)
if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ {
s.Dynimplib = lib
s.Extname = remote
......@@ -172,34 +163,31 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
}
havedynamic = 1
}
continue
}
if f[0] == "cgo_import_static" {
case "cgo_import_static":
if len(f) != 2 {
goto err
break
}
local := f[1]
s = ctxt.Syms.Lookup(local, 0)
s := ctxt.Syms.Lookup(local, 0)
s.Type = sym.SHOSTOBJ
s.Size = 0
continue
}
if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
case "cgo_export_static", "cgo_export_dynamic":
if len(f) < 2 || len(f) > 3 {
goto err
break
}
local := f[1]
var remote string
remote := local
if len(f) > 2 {
remote = f[2]
} else {
remote = local
}
local = expandpkg(local, pkg)
s = ctxt.Syms.Lookup(local, 0)
s := ctxt.Syms.Lookup(local, 0)
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
......@@ -232,11 +220,10 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
s.Attr |= sym.AttrCgoExportDynamic
}
continue
}
if f[0] == "cgo_dynamic_linker" {
case "cgo_dynamic_linker":
if len(f) != 2 {
goto err
break
}
if *flagInterpreter == "" {
......@@ -248,24 +235,19 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
interpreter = f[1]
}
continue
}
if f[0] == "cgo_ldflag" {
case "cgo_ldflag":
if len(f) != 2 {
goto err
break
}
ldflag = append(ldflag, f[1])
continue
}
}
return
err:
fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0)
nerrors++
fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
nerrors++
}
}
var seenlib = make(map[string]bool)
......
......@@ -9,7 +9,6 @@ import (
"encoding/binary"
"fmt"
"os"
"strings"
"time"
)
......@@ -23,50 +22,6 @@ func Cputime() float64 {
return time.Since(startTime).Seconds()
}
func tokenize(s string) []string {
var f []string
for {
s = strings.TrimLeft(s, " \t\r\n")
if s == "" {
break
}
quote := false
i := 0
for ; i < len(s); i++ {
if s[i] == '\'' {
if quote && i+1 < len(s) && s[i+1] == '\'' {
i++
continue
}
quote = !quote
}
if !quote && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') {
break
}
}
next := s[:i]
s = s[i:]
if strings.Contains(next, "'") {
var buf []byte
quote := false
for i := 0; i < len(next); i++ {
if next[i] == '\'' {
if quote && i+1 < len(next) && next[i+1] == '\'' {
i++
buf = append(buf, '\'')
}
quote = !quote
continue
}
buf = append(buf, next[i])
}
next = string(buf)
}
f = append(f, next)
}
return f
}
var atExitFuncs []func()
func AtExit(f func()) {
......
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