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