Commit 07669d27 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Matthew Dempsky

cmd/compile: cleanup pragcgo

Removes dynimport, dynexport, dynlinker cases since they can not
be reached due to prefix check for "go:cgo_" in getlinepragma.

Replaces the if chains for verb distinction by a switch statement.
Replaces fmt.Sprintf by fmt.Sprintln for string concatenation.

Removes the more, getimpsym and getquoted functions by introducing a
pragmaFields function that partitions a pragma into its components.

Adds tests for cgo pragmas.

Change-Id: I43c7b9550feb3ddccaff7fb02198a3f994444123
Reviewed-on: https://go-review.googlesource.com/21607Reviewed-by: 's avatarMatthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent e569b10e
...@@ -44,6 +44,10 @@ func isDigit(c rune) bool { ...@@ -44,6 +44,10 @@ func isDigit(c rune) bool {
return '0' <= c && c <= '9' return '0' <= c && c <= '9'
} }
func isQuoted(s string) bool {
return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
}
func plan9quote(s string) string { func plan9quote(s string) string {
if s == "" { if s == "" {
return "''" return "''"
...@@ -853,15 +857,6 @@ func internString(b []byte) string { ...@@ -853,15 +857,6 @@ func internString(b []byte) string {
return s return s
} }
func more(pp *string) bool {
p := *pp
for p != "" && isSpace(rune(p[0])) {
p = p[1:]
}
*pp = p
return p != ""
}
// read and interpret syntax that looks like // read and interpret syntax that looks like
// //line parse.y:15 // //line parse.y:15
// as a discontinuity in sequential line numbers. // as a discontinuity in sequential line numbers.
...@@ -887,7 +882,7 @@ func (l *lexer) getlinepragma() rune { ...@@ -887,7 +882,7 @@ func (l *lexer) getlinepragma() rune {
text := strings.TrimSuffix(lexbuf.String(), "\r") text := strings.TrimSuffix(lexbuf.String(), "\r")
if strings.HasPrefix(text, "go:cgo_") { if strings.HasPrefix(text, "go:cgo_") {
pragcgo(text) pragcgobuf += pragcgo(text)
} }
verb := text verb := text
...@@ -991,139 +986,114 @@ func (l *lexer) getlinepragma() rune { ...@@ -991,139 +986,114 @@ func (l *lexer) getlinepragma() rune {
return c return c
} }
func getimpsym(pp *string) string { func pragcgo(text string) string {
more(pp) // skip spaces f := pragmaFields(text)
p := *pp
if p == "" || p[0] == '"' {
return ""
}
i := 0
for i < len(p) && !isSpace(rune(p[i])) && p[i] != '"' {
i++
}
sym := p[:i]
*pp = p[i:]
return sym
}
func getquoted(pp *string) (string, bool) {
more(pp) // skip spaces
p := *pp
if p == "" || p[0] != '"' {
return "", false
}
p = p[1:]
i := strings.Index(p, `"`)
if i < 0 {
return "", false
}
*pp = p[i+1:]
return p[:i], true
}
// Copied nearly verbatim from the C compiler's #pragma parser.
// TODO: Rewrite more cleanly once the compiler is written in Go.
func pragcgo(text string) {
var q string
if i := strings.Index(text, " "); i >= 0 { verb := f[0][3:] // skip "go:"
text, q = text[:i], text[i:] 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)
verb := text[3:] // skip "go:" case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
local := plan9quote(f[1])
remote := plan9quote(f[2])
return fmt.Sprintln(verb, local, remote)
if verb == "cgo_dynamic_linker" || verb == "dynlinker" { default:
p, ok := getquoted(&q) Yyerror(`usage: //go:%s local [remote]`, verb)
if !ok {
Yyerror("usage: //go:cgo_dynamic_linker \"path\"")
return
} }
pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p)) case "cgo_import_dynamic":
return 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)
if verb == "dynexport" { case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
verb = "cgo_export_dynamic" local := plan9quote(f[1])
} remote := plan9quote(f[2])
if verb == "cgo_export_static" || verb == "cgo_export_dynamic" { library := plan9quote(strings.Trim(f[3], `"`))
local := getimpsym(&q) return fmt.Sprintln(verb, local, remote, library)
var remote string
if local == "" {
goto err2
}
if !more(&q) {
pragcgobuf += fmt.Sprintf("%s %v\n", verb, plan9quote(local))
return
}
remote = getimpsym(&q) default:
if remote == "" { Yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
goto err2
} }
pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote)) case "cgo_import_static":
return switch {
case len(f) == 2 && !isQuoted(f[1]):
err2: local := plan9quote(f[1])
Yyerror("usage: //go:%s local [remote]", verb) return fmt.Sprintln(verb, local)
return
}
if verb == "cgo_import_dynamic" || verb == "dynimport" { default:
var ok bool Yyerror(`usage: //go:cgo_import_static local`)
local := getimpsym(&q)
var p string
var remote string
if local == "" {
goto err3
}
if !more(&q) {
pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v\n", plan9quote(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)
remote = getimpsym(&q) default:
if remote == "" { Yyerror(`usage: //go:cgo_dynamic_linker "path"`)
goto err3
}
if !more(&q) {
pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote))
return
} }
case "cgo_ldflag":
switch {
case len(f) == 2 && isQuoted(f[1]):
arg := plan9quote(strings.Trim(f[1], `"`))
return fmt.Sprintln(verb, arg)
p, ok = getquoted(&q) default:
if !ok { Yyerror(`usage: //go:cgo_ldflag "arg"`)
goto err3
} }
pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v %v\n", plan9quote(local), plan9quote(remote), plan9quote(p))
return
err3:
Yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]")
return
} }
return ""
}
if verb == "cgo_import_static" { // pragmaFields is similar to strings.FieldsFunc(s, isSpace)
local := getimpsym(&q) // but does not split when inside double quoted regions and always
if local == "" || more(&q) { // splits before the start and after the end of a double quoted region.
Yyerror("usage: //go:cgo_import_static local") // pragmaFields does not recognize escaped quotes. If a quote in s is not
return // closed the part after the opening quote will not be returned as a field.
func pragmaFields(s string) []string {
var a []string
inQuote := false
fieldStart := -1 // Set to -1 when looking for start of field.
for i, c := range s {
switch {
case c == '"':
if inQuote {
inQuote = false
a = append(a, s[fieldStart:i+1])
fieldStart = -1
} else {
inQuote = true
if fieldStart >= 0 {
a = append(a, s[fieldStart:i])
}
fieldStart = i
}
case !inQuote && isSpace(c):
if fieldStart >= 0 {
a = append(a, s[fieldStart:i])
fieldStart = -1
}
default:
if fieldStart == -1 {
fieldStart = i
}
} }
pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local))
return
} }
if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
if verb == "cgo_ldflag" { a = append(a, s[fieldStart:])
p, ok := getquoted(&q)
if !ok {
Yyerror("usage: //go:cgo_ldflag \"arg\"")
return
}
pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p))
return
} }
return a
} }
func (l *lexer) getr() rune { func (l *lexer) getr() rune {
......
// 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 gc
import "testing"
func eq(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
func TestPragmaFields(t *testing.T) {
var tests = []struct {
in string
want []string
}{
{"", []string{}},
{" \t ", []string{}},
{`""""`, []string{`""`, `""`}},
{" a'b'c ", []string{"a'b'c"}},
{"1 2 3 4", []string{"1", "2", "3", "4"}},
{"\n\t\n", []string{"☺", "☹"}},
{`"1 2 " 3 " 4 5"`, []string{`"1 2 "`, `3`, `" 4 5"`}},
{`"1""2 3""4"`, []string{`"1"`, `"2 3"`, `"4"`}},
{`12"34"`, []string{`12`, `"34"`}},
{`12"34 `, []string{`12`}},
}
for _, tt := range tests {
got := pragmaFields(tt.in)
if !eq(got, tt.want) {
t.Errorf("pragmaFields(%q) = %v; want %v", tt.in, got, tt.want)
continue
}
}
}
func TestPragcgo(t *testing.T) {
var tests = []struct {
in 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"},
}
for _, tt := range tests {
got := pragcgo(tt.in)
if got != tt.want {
t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, tt.want)
continue
}
}
}
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