libgo: update to Go1.10rc2
Reviewed-on: https://go-review.googlesource.com/92736 From-SVN: r257493
This commit is contained in:
parent
b5ec4de777
commit
9adab5dd16
39 changed files with 1035 additions and 345 deletions
|
@ -1,4 +1,4 @@
|
|||
cdc28627b7abfd73f5d552813db8eb4293b823b0
|
||||
2aa95f1499cf931ef8e95c7958463829276a0f2c
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
5348aed83e39bd1d450d92d7f627e994c2db6ebf
|
||||
20e228f2fdb44350c858de941dff4aea9f3127b8
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
|
|
@ -1 +1 @@
|
|||
go1.10rc1
|
||||
go1.10rc2
|
||||
|
|
|
@ -45,8 +45,8 @@ For example:
|
|||
// #include <png.h>
|
||||
import "C"
|
||||
|
||||
Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config
|
||||
tool using a '#cgo pkg-config:' directive followed by the package names.
|
||||
Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool
|
||||
using a '#cgo pkg-config:' directive followed by the package names.
|
||||
For example:
|
||||
|
||||
// #cgo pkg-config: png cairo
|
||||
|
@ -55,11 +55,21 @@ For example:
|
|||
|
||||
The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable.
|
||||
|
||||
For security reasons, only a limited set of flags are allowed, notably -D, -I, and -l.
|
||||
To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression
|
||||
matching the new flags. To disallow flags that would otherwise be allowed,
|
||||
set CGO_CFLAGS_DISALLOW to a regular expression matching arguments
|
||||
that must be disallowed. In both cases the regular expression must match
|
||||
a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*',
|
||||
not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control
|
||||
the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS.
|
||||
|
||||
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
|
||||
CGO_LDFLAGS environment variables are added to the flags derived from
|
||||
these directives. Package-specific flags should be set using the
|
||||
directives, not the environment variables, so that builds work in
|
||||
unmodified environments.
|
||||
unmodified environments. Flags obtained from environment variables
|
||||
are not subject to the security limitations described above.
|
||||
|
||||
All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and
|
||||
used to compile C files in that package. All the CPPFLAGS and CXXFLAGS
|
||||
|
|
|
@ -2345,12 +2345,6 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
|
|||
break
|
||||
}
|
||||
|
||||
// If we already know the typedef for t just use that.
|
||||
// See issue 19832.
|
||||
if def := typedef[t.Go.(*ast.Ident).Name]; def != nil {
|
||||
break
|
||||
}
|
||||
|
||||
t = c.Type(ptr, pos)
|
||||
if t == nil {
|
||||
return nil
|
||||
|
|
|
@ -1227,17 +1227,26 @@
|
|||
// CGO_CFLAGS
|
||||
// Flags that cgo will pass to the compiler when compiling
|
||||
// C code.
|
||||
// CGO_CPPFLAGS
|
||||
// Flags that cgo will pass to the compiler when compiling
|
||||
// C or C++ code.
|
||||
// CGO_CXXFLAGS
|
||||
// Flags that cgo will pass to the compiler when compiling
|
||||
// C++ code.
|
||||
// CGO_FFLAGS
|
||||
// Flags that cgo will pass to the compiler when compiling
|
||||
// Fortran code.
|
||||
// CGO_LDFLAGS
|
||||
// Flags that cgo will pass to the compiler when linking.
|
||||
// CGO_CFLAGS_ALLOW
|
||||
// A regular expression specifying additional flags to allow
|
||||
// to appear in #cgo CFLAGS source code directives.
|
||||
// Does not apply to the CGO_CFLAGS environment variable.
|
||||
// CGO_CFLAGS_DISALLOW
|
||||
// A regular expression specifying flags that must be disallowed
|
||||
// from appearing in #cgo CFLAGS source code directives.
|
||||
// Does not apply to the CGO_CFLAGS environment variable.
|
||||
// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW
|
||||
// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
// but for the C preprocessor.
|
||||
// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW
|
||||
// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
// but for the C++ compiler.
|
||||
// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW
|
||||
// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
// but for the Fortran compiler.
|
||||
// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW
|
||||
// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
// but for the linker.
|
||||
// CXX
|
||||
// The command to use to compile C++ code.
|
||||
// PKG_CONFIG
|
||||
|
|
|
@ -81,6 +81,13 @@ func init() {
|
|||
skipExternal = true
|
||||
canRun = false
|
||||
}
|
||||
case "plan9":
|
||||
switch runtime.GOARCH {
|
||||
case "arm":
|
||||
// many plan9/arm machines are too slow to run
|
||||
// the full set of external tests.
|
||||
skipExternal = true
|
||||
}
|
||||
case "windows":
|
||||
exeSuffix = ".exe"
|
||||
}
|
||||
|
@ -2815,7 +2822,7 @@ func TestCgoHandlesWlORIGIN(t *testing.T) {
|
|||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("src/origin/origin.go", `package origin
|
||||
// #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
|
||||
// #cgo !darwin LDFLAGS: -Wl,-rpath,$ORIGIN
|
||||
// void f(void) {}
|
||||
import "C"
|
||||
func f() { C.f() }`)
|
||||
|
@ -5421,6 +5428,30 @@ func TestTestCacheInputs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNoCache(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
t.Skipf("no unwritable directories on %s", runtime.GOOS)
|
||||
}
|
||||
if os.Getuid() == 0 {
|
||||
t.Skip("skipping test because running as root")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("triv.go", `package main; func main() {}`)
|
||||
tg.must(os.MkdirAll(tg.path("unwritable"), 0555))
|
||||
home := "HOME"
|
||||
if runtime.GOOS == "plan9" {
|
||||
home = "home"
|
||||
}
|
||||
tg.setenv(home, tg.path(filepath.Join("unwritable", "home")))
|
||||
tg.unsetenv("GOCACHE")
|
||||
tg.run("build", "-o", tg.path("triv"), tg.path("triv.go"))
|
||||
tg.grepStderr("disabling cache", "did not disable cache")
|
||||
}
|
||||
|
||||
func TestTestVet(t *testing.T) {
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
|
@ -5463,6 +5494,44 @@ func TestTestVet(t *testing.T) {
|
|||
tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2")
|
||||
}
|
||||
|
||||
func TestTestRebuild(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
|
||||
// golang.org/issue/23701.
|
||||
// b_test imports b with augmented method from export_test.go.
|
||||
// b_test also imports a, which imports b.
|
||||
// Must not accidentally see un-augmented b propagate through a to b_test.
|
||||
tg.tempFile("src/a/a.go", `package a
|
||||
import "b"
|
||||
type Type struct{}
|
||||
func (*Type) M() b.T {return 0}
|
||||
`)
|
||||
tg.tempFile("src/b/b.go", `package b
|
||||
type T int
|
||||
type I interface {M() T}
|
||||
`)
|
||||
tg.tempFile("src/b/export_test.go", `package b
|
||||
func (*T) Method() *T { return nil }
|
||||
`)
|
||||
tg.tempFile("src/b/b_test.go", `package b_test
|
||||
import (
|
||||
"testing"
|
||||
"a"
|
||||
. "b"
|
||||
)
|
||||
func TestBroken(t *testing.T) {
|
||||
x := new(T)
|
||||
x.Method()
|
||||
_ = new(a.Type)
|
||||
}
|
||||
`)
|
||||
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("test", "b")
|
||||
}
|
||||
|
||||
func TestInstallDeps(t *testing.T) {
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
|
@ -5712,3 +5781,177 @@ func TestCpuprofileTwice(t *testing.T) {
|
|||
tg.run("test", "-o="+bin, "-cpuprofile="+out, "x")
|
||||
tg.mustExist(out)
|
||||
}
|
||||
|
||||
// Issue 23694.
|
||||
func TestAtomicCoverpkgAll(t *testing.T) {
|
||||
skipIfGccgo(t, "gccgo has no cover tool")
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
|
||||
tg.tempFile("src/x/x.go", `package x; import _ "sync/atomic"; func F() {}`)
|
||||
tg.tempFile("src/x/x_test.go", `package x; import "testing"; func TestF(t *testing.T) { F() }`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("test", "-coverpkg=all", "-covermode=atomic", "x")
|
||||
if canRace {
|
||||
tg.run("test", "-coverpkg=all", "-race", "x")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadCommandLines(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
tg.tempFile("src/x/x.go", "package x\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
|
||||
tg.run("build", "x")
|
||||
|
||||
tg.tempFile("src/x/@y.go", "package x\n")
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid input file name \"@y.go\"", "did not reject @y.go")
|
||||
tg.must(os.Remove(tg.path("src/x/@y.go")))
|
||||
|
||||
tg.tempFile("src/x/-y.go", "package x\n")
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid input file name \"-y.go\"", "did not reject -y.go")
|
||||
tg.must(os.Remove(tg.path("src/x/-y.go")))
|
||||
|
||||
if runtime.Compiler == "gccgo" {
|
||||
tg.runFail("build", "-gccgoflags=all=@x", "x")
|
||||
} else {
|
||||
tg.runFail("build", "-gcflags=all=@x", "x")
|
||||
}
|
||||
tg.grepStderr("invalid command-line argument @x in command", "did not reject @x during exec")
|
||||
|
||||
tg.tempFile("src/@x/x.go", "package x\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.runFail("build", "@x")
|
||||
tg.grepStderr("invalid input directory name \"@x\"", "did not reject @x directory")
|
||||
|
||||
tg.tempFile("src/@x/y/y.go", "package y\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.runFail("build", "@x/y")
|
||||
tg.grepStderr("invalid import path \"@x/y\"", "did not reject @x/y import path")
|
||||
|
||||
tg.tempFile("src/-x/x.go", "package x\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.runFail("build", "--", "-x")
|
||||
tg.grepStderr("invalid input directory name \"-x\"", "did not reject -x directory")
|
||||
|
||||
tg.tempFile("src/-x/y/y.go", "package y\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.runFail("build", "--", "-x/y")
|
||||
tg.grepStderr("invalid import path \"-x/y\"", "did not reject -x/y import path")
|
||||
}
|
||||
|
||||
func TestBadCgoDirectives(t *testing.T) {
|
||||
if !canCgo {
|
||||
t.Skip("no cgo")
|
||||
}
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
tg.tempFile("src/x/x.go", "package x\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
|
||||
if runtime.Compiler == "gc" {
|
||||
tg.tempFile("src/x/x.go", `package x
|
||||
|
||||
//go:cgo_ldflag "-fplugin=foo.so"
|
||||
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("//go:cgo_ldflag .* only allowed in cgo-generated code", "did not reject //go:cgo_ldflag directive")
|
||||
}
|
||||
|
||||
tg.must(os.Remove(tg.path("src/x/x.go")))
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("no Go files", "did not report missing source code")
|
||||
tg.tempFile("src/x/_cgo_yy.go", `package x
|
||||
|
||||
//go:cgo_ldflag "-fplugin=foo.so"
|
||||
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("no Go files", "did not report missing source code") // _* files are ignored...
|
||||
|
||||
if runtime.Compiler == "gc" {
|
||||
tg.runFail("build", tg.path("src/x/_cgo_yy.go")) // ... but if forced, the comment is rejected
|
||||
// Actually, today there is a separate issue that _ files named
|
||||
// on the command-line are ignored. Once that is fixed,
|
||||
// we want to see the cgo_ldflag error.
|
||||
tg.grepStderr("//go:cgo_ldflag only allowed in cgo-generated code|no Go files", "did not reject //go:cgo_ldflag directive")
|
||||
}
|
||||
|
||||
tg.must(os.Remove(tg.path("src/x/_cgo_yy.go")))
|
||||
|
||||
tg.tempFile("src/x/x.go", "package x\n")
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: -fplugin=foo.so
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: -fplugin=foo.so", "did not reject -fplugin")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: -Ibar -fplugin=foo.so
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: -fplugin=foo.so", "did not reject -fplugin")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo pkg-config: -foo
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid pkg-config package name: -foo", "did not reject pkg-config: -foo")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo pkg-config: @foo
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid pkg-config package name: @foo", "did not reject pkg-config: -foo")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: @foo
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: @foo", "did not reject @foo flag")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: -D
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: -D without argument", "did not reject trailing -I flag")
|
||||
|
||||
// Note that -I @foo is allowed because we rewrite it into -I /path/to/src/@foo
|
||||
// before the check is applied. There's no such rewrite for -D.
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: -D @foo
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: -D @foo", "did not reject -D @foo flag")
|
||||
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
// #cgo CFLAGS: -D@foo
|
||||
import "C"
|
||||
`)
|
||||
tg.runFail("build", "x")
|
||||
tg.grepStderr("invalid flag in #cgo CFLAGS: -D@foo", "did not reject -D@foo flag")
|
||||
|
||||
tg.setenv("CGO_CFLAGS", "-D@foo")
|
||||
tg.tempFile("src/x/y.go", `package x
|
||||
import "C"
|
||||
`)
|
||||
tg.run("build", "-n", "x")
|
||||
tg.grepStderr("-D@foo", "did not find -D@foo in commands")
|
||||
}
|
||||
|
|
8
libgo/go/cmd/go/internal/cache/default.go
vendored
8
libgo/go/cmd/go/internal/cache/default.go
vendored
|
@ -5,7 +5,7 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -40,7 +40,8 @@ func initDefaultCache() {
|
|||
return
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
base.Fatalf("initializing cache in $GOCACHE: %s", err)
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
|
||||
// Best effort.
|
||||
|
@ -49,7 +50,8 @@ func initDefaultCache() {
|
|||
|
||||
c, err := Open(dir)
|
||||
if err != nil {
|
||||
base.Fatalf("initializing cache in $GOCACHE: %s", err)
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
return
|
||||
}
|
||||
defaultCache = c
|
||||
}
|
||||
|
|
|
@ -113,7 +113,12 @@ func findEnv(env []cfg.EnvVar, name string) string {
|
|||
func ExtraEnvVars() []cfg.EnvVar {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
cppflags, cflags, cxxflags, fflags, ldflags := b.CFlags(&load.Package{})
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
|
||||
if err != nil {
|
||||
// Should not happen - b.CFlags was given an empty package.
|
||||
fmt.Fprintf(os.Stderr, "go: invalid cflags: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
cmd := b.GccCmd(".", "")
|
||||
return []cfg.EnvVar{
|
||||
// Note: Update the switch in runEnv below when adding to this list.
|
||||
|
|
|
@ -487,17 +487,26 @@ Environment variables for use with cgo:
|
|||
CGO_CFLAGS
|
||||
Flags that cgo will pass to the compiler when compiling
|
||||
C code.
|
||||
CGO_CPPFLAGS
|
||||
Flags that cgo will pass to the compiler when compiling
|
||||
C or C++ code.
|
||||
CGO_CXXFLAGS
|
||||
Flags that cgo will pass to the compiler when compiling
|
||||
C++ code.
|
||||
CGO_FFLAGS
|
||||
Flags that cgo will pass to the compiler when compiling
|
||||
Fortran code.
|
||||
CGO_LDFLAGS
|
||||
Flags that cgo will pass to the compiler when linking.
|
||||
CGO_CFLAGS_ALLOW
|
||||
A regular expression specifying additional flags to allow
|
||||
to appear in #cgo CFLAGS source code directives.
|
||||
Does not apply to the CGO_CFLAGS environment variable.
|
||||
CGO_CFLAGS_DISALLOW
|
||||
A regular expression specifying flags that must be disallowed
|
||||
from appearing in #cgo CFLAGS source code directives.
|
||||
Does not apply to the CGO_CFLAGS environment variable.
|
||||
CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW
|
||||
Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
but for the C preprocessor.
|
||||
CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW
|
||||
Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
but for the C++ compiler.
|
||||
CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW
|
||||
Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
but for the Fortran compiler.
|
||||
CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW
|
||||
Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
|
||||
but for the linker.
|
||||
CXX
|
||||
The command to use to compile C++ code.
|
||||
PKG_CONFIG
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
|
@ -55,6 +56,8 @@ type PackagePublic struct {
|
|||
StaleReason string `json:",omitempty"` // why is Stale true?
|
||||
|
||||
// Source files
|
||||
// If you add to this list you MUST add to p.AllFiles (below) too.
|
||||
// Otherwise file name security lists will not apply to any new additions.
|
||||
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
|
||||
IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints
|
||||
|
@ -86,12 +89,38 @@ type PackagePublic struct {
|
|||
DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies
|
||||
|
||||
// Test information
|
||||
// If you add to this list you MUST add to p.AllFiles (below) too.
|
||||
// Otherwise file name security lists will not apply to any new additions.
|
||||
TestGoFiles []string `json:",omitempty"` // _test.go files in package
|
||||
TestImports []string `json:",omitempty"` // imports from TestGoFiles
|
||||
XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
|
||||
XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
|
||||
}
|
||||
|
||||
// AllFiles returns the names of all the files considered for the package.
|
||||
// This is used for sanity and security checks, so we include all files,
|
||||
// even IgnoredGoFiles, because some subcommands consider them.
|
||||
// The go/build package filtered others out (like foo_wrongGOARCH.s)
|
||||
// and that's OK.
|
||||
func (p *Package) AllFiles() []string {
|
||||
return str.StringList(
|
||||
p.GoFiles,
|
||||
p.CgoFiles,
|
||||
p.IgnoredGoFiles,
|
||||
p.CFiles,
|
||||
p.CXXFiles,
|
||||
p.MFiles,
|
||||
p.HFiles,
|
||||
p.FFiles,
|
||||
p.SFiles,
|
||||
p.SwigFiles,
|
||||
p.SwigCXXFiles,
|
||||
p.SysoFiles,
|
||||
p.TestGoFiles,
|
||||
p.XTestGoFiles,
|
||||
)
|
||||
}
|
||||
|
||||
type PackageInternal struct {
|
||||
// Unexported fields are not part of the public API.
|
||||
Build *build.Package
|
||||
|
@ -420,6 +449,9 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
|
|||
var err error
|
||||
if debugDeprecatedImportcfgDir != "" {
|
||||
bp, err = cfg.BuildContext.ImportDir(debugDeprecatedImportcfgDir, 0)
|
||||
} else if DebugDeprecatedImportcfg.enabled {
|
||||
bp = new(build.Package)
|
||||
err = fmt.Errorf("unknown import path %q: not in import cfg", importPath)
|
||||
} else {
|
||||
buildMode := build.ImportComment
|
||||
if mode&UseVendor == 0 || path != origPath {
|
||||
|
@ -518,6 +550,13 @@ func isDir(path string) bool {
|
|||
// x/vendor/path, vendor/path, or else stay path if none of those exist.
|
||||
// VendoredImportPath returns the expanded path or, if no expansion is found, the original.
|
||||
func VendoredImportPath(parent *Package, path string) (found string) {
|
||||
if DebugDeprecatedImportcfg.enabled {
|
||||
if d, i := DebugDeprecatedImportcfg.lookup(parent, path); d != "" {
|
||||
return i
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
if parent == nil || parent.Root == "" {
|
||||
return path
|
||||
}
|
||||
|
@ -1010,22 +1049,8 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
|
|||
// To avoid problems on case-insensitive files, we reject any package
|
||||
// where two different input files have equal names under a case-insensitive
|
||||
// comparison.
|
||||
f1, f2 := str.FoldDup(str.StringList(
|
||||
p.GoFiles,
|
||||
p.CgoFiles,
|
||||
p.IgnoredGoFiles,
|
||||
p.CFiles,
|
||||
p.CXXFiles,
|
||||
p.MFiles,
|
||||
p.HFiles,
|
||||
p.FFiles,
|
||||
p.SFiles,
|
||||
p.SysoFiles,
|
||||
p.SwigFiles,
|
||||
p.SwigCXXFiles,
|
||||
p.TestGoFiles,
|
||||
p.XTestGoFiles,
|
||||
))
|
||||
inputs := p.AllFiles()
|
||||
f1, f2 := str.FoldDup(inputs)
|
||||
if f1 != "" {
|
||||
p.Error = &PackageError{
|
||||
ImportStack: stk.Copy(),
|
||||
|
@ -1034,6 +1059,37 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// If first letter of input file is ASCII, it must be alphanumeric.
|
||||
// This avoids files turning into flags when invoking commands,
|
||||
// and other problems we haven't thought of yet.
|
||||
// Also, _cgo_ files must be generated by us, not supplied.
|
||||
// They are allowed to have //go:cgo_ldflag directives.
|
||||
// The directory scan ignores files beginning with _,
|
||||
// so we shouldn't see any _cgo_ files anyway, but just be safe.
|
||||
for _, file := range inputs {
|
||||
if !SafeArg(file) || strings.HasPrefix(file, "_cgo_") {
|
||||
p.Error = &PackageError{
|
||||
ImportStack: stk.Copy(),
|
||||
Err: fmt.Sprintf("invalid input file name %q", file),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if name := pathpkg.Base(p.ImportPath); !SafeArg(name) {
|
||||
p.Error = &PackageError{
|
||||
ImportStack: stk.Copy(),
|
||||
Err: fmt.Sprintf("invalid input directory name %q", name),
|
||||
}
|
||||
return
|
||||
}
|
||||
if !SafeArg(p.ImportPath) {
|
||||
p.Error = &PackageError{
|
||||
ImportStack: stk.Copy(),
|
||||
Err: fmt.Sprintf("invalid import path %q", p.ImportPath),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Build list of imported packages and full dependency list.
|
||||
imports := make([]*Package, 0, len(p.Imports))
|
||||
for i, path := range importPaths {
|
||||
|
@ -1160,6 +1216,22 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// SafeArg reports whether arg is a "safe" command-line argument,
|
||||
// meaning that when it appears in a command-line, it probably
|
||||
// doesn't have some special meaning other than its own name.
|
||||
// Obviously args beginning with - are not safe (they look like flags).
|
||||
// Less obviously, args beginning with @ are not safe (they look like
|
||||
// GNU binutils flagfile specifiers, sometimes called "response files").
|
||||
// To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII.
|
||||
// We accept leading . _ and / as likely in file system paths.
|
||||
func SafeArg(name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
c := name[0]
|
||||
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
|
||||
}
|
||||
|
||||
// LinkerDeps returns the list of linker-induced dependencies for main package p.
|
||||
func LinkerDeps(p *Package) []string {
|
||||
// Everything links runtime.
|
||||
|
|
|
@ -662,6 +662,15 @@ func runTest(cmd *base.Command, args []string) {
|
|||
haveMatch = true
|
||||
}
|
||||
}
|
||||
|
||||
// Silently ignore attempts to run coverage on
|
||||
// sync/atomic when using atomic coverage mode.
|
||||
// Atomic coverage mode uses sync/atomic, so
|
||||
// we can't also do coverage on it.
|
||||
if testCoverMode == "atomic" && p.Standard && p.ImportPath == "sync/atomic" {
|
||||
continue
|
||||
}
|
||||
|
||||
if haveMatch {
|
||||
testCoverPkgs = append(testCoverPkgs, p)
|
||||
}
|
||||
|
@ -894,7 +903,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
|
|||
t.ImportXtest = true
|
||||
}
|
||||
|
||||
if ptest != p && localCover {
|
||||
if ptest != p {
|
||||
// We have made modifications to the package p being tested
|
||||
// and are rebuilding p (as ptest).
|
||||
// Arrange to rebuild all packages q such that
|
||||
|
@ -903,13 +912,6 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
|
|||
// Strictly speaking, the rebuild is only necessary if the
|
||||
// modifications to p change its export metadata, but
|
||||
// determining that is a bit tricky, so we rebuild always.
|
||||
// TODO(rsc): Once we get export metadata changes
|
||||
// handled properly, look into the expense of dropping
|
||||
// "&& localCover" above.
|
||||
//
|
||||
// This will cause extra compilation, so for now we only do it
|
||||
// when testCover is set. The conditions are more general, though,
|
||||
// and we may find that we need to do it always in the future.
|
||||
recompileForTest(pmain, p, ptest, pxtest)
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
|||
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)
|
||||
if len(p.CgoFiles)+len(p.SwigFiles) > 0 {
|
||||
fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo"))
|
||||
cppflags, cflags, cxxflags, fflags, _ := b.CFlags(p)
|
||||
cppflags, cflags, cxxflags, fflags, _, _ := b.CFlags(p)
|
||||
fmt.Fprintf(h, "CC=%q %q %q\n", b.ccExe(), cppflags, cflags)
|
||||
if len(p.CXXFiles)+len(p.SwigFiles) > 0 {
|
||||
fmt.Fprintf(h, "CXX=%q %q\n", b.cxxExe(), cxxflags)
|
||||
|
@ -521,7 +521,14 @@ func (b *Builder) build(a *Action) (err error) {
|
|||
}
|
||||
|
||||
// Prepare Go import config.
|
||||
// We start it off with a comment so it can't be empty, so icfg.Bytes() below is never nil.
|
||||
// It should never be empty anyway, but there have been bugs in the past that resulted
|
||||
// in empty configs, which then unfortunately turn into "no config passed to compiler",
|
||||
// and the compiler falls back to looking in pkg itself, which mostly works,
|
||||
// except when it doesn't.
|
||||
var icfg bytes.Buffer
|
||||
fmt.Fprintf(&icfg, "# import config\n")
|
||||
|
||||
for i, raw := range a.Package.Internal.RawImports {
|
||||
final := a.Package.Imports[i]
|
||||
if final != raw {
|
||||
|
@ -938,28 +945,38 @@ func splitPkgConfigOutput(out []byte) []string {
|
|||
// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
|
||||
func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, err error) {
|
||||
if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
|
||||
for _, pkg := range pkgs {
|
||||
if !load.SafeArg(pkg) {
|
||||
return nil, nil, fmt.Errorf("invalid pkg-config package name: %s", pkg)
|
||||
}
|
||||
}
|
||||
var out []byte
|
||||
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", pkgs)
|
||||
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", "--", pkgs)
|
||||
if err != nil {
|
||||
b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pkgs, " "), string(out))
|
||||
b.Print(err.Error() + "\n")
|
||||
err = errPrintedOutput
|
||||
return
|
||||
return nil, nil, errPrintedOutput
|
||||
}
|
||||
if len(out) > 0 {
|
||||
cflags = splitPkgConfigOutput(out)
|
||||
if err := checkCompilerFlags("CFLAGS", "pkg-config --cflags", cflags); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", pkgs)
|
||||
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", "--", pkgs)
|
||||
if err != nil {
|
||||
b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pkgs, " "), string(out))
|
||||
b.Print(err.Error() + "\n")
|
||||
err = errPrintedOutput
|
||||
return
|
||||
return nil, nil, errPrintedOutput
|
||||
}
|
||||
if len(out) > 0 {
|
||||
ldflags = strings.Fields(string(out))
|
||||
if err := checkLinkerFlags("CFLAGS", "pkg-config --cflags", ldflags); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1464,6 +1481,17 @@ func (b *Builder) processOutput(out []byte) string {
|
|||
// It returns the command output and any errors that occurred.
|
||||
func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) {
|
||||
cmdline := str.StringList(cmdargs...)
|
||||
|
||||
for _, arg := range cmdline {
|
||||
// GNU binutils commands, including gcc and gccgo, interpret an argument
|
||||
// @foo anywhere in the command line (even following --) as meaning
|
||||
// "read and insert arguments from the file named foo."
|
||||
// Don't say anything that might be misinterpreted that way.
|
||||
if strings.HasPrefix(arg, "@") {
|
||||
return nil, fmt.Errorf("invalid command-line argument %s in command: %s", arg, joinUnambiguously(cmdline))
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.BuildN || cfg.BuildX {
|
||||
var envcmdline string
|
||||
for _, e := range env {
|
||||
|
@ -1916,22 +1944,44 @@ func envList(key, def string) []string {
|
|||
}
|
||||
|
||||
// CFlags returns the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
|
||||
func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
|
||||
func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, ldflags []string, err error) {
|
||||
defaults := "-g -O2"
|
||||
|
||||
cppflags = str.StringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
|
||||
cflags = str.StringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
|
||||
cxxflags = str.StringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
|
||||
fflags = str.StringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS)
|
||||
ldflags = str.StringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
|
||||
if cppflags, err = buildFlags("CPPFLAGS", "", p.CgoCPPFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if cflags, err = buildFlags("CFLAGS", defaults, p.CgoCFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if cxxflags, err = buildFlags("CXXFLAGS", defaults, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if fflags, err = buildFlags("FFLAGS", defaults, p.CgoFFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if ldflags, err = buildFlags("LDFLAGS", defaults, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func buildFlags(name, defaults string, fromPackage []string, check func(string, string, []string) error) ([]string, error) {
|
||||
if err := check(name, "#cgo "+name, fromPackage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return str.StringList(envList("CGO_"+name, defaults), fromPackage), nil
|
||||
}
|
||||
|
||||
var cgoRe = regexp.MustCompile(`[/\\:]`)
|
||||
|
||||
func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
|
||||
p := a.Package
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.CFlags(p)
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS, err := b.CFlags(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
|
||||
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
|
||||
// If we are compiling Objective-C code, then we need to link against libobjc
|
||||
|
@ -1981,6 +2031,12 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
|
|||
}
|
||||
|
||||
// Update $CGO_LDFLAGS with p.CgoLDFLAGS.
|
||||
// These flags are recorded in the generated _cgo_gotypes.go file
|
||||
// using //go:cgo_ldflag directives, the compiler records them in the
|
||||
// object file for the package, and then the Go linker passes them
|
||||
// along to the host linker. At this point in the code, cgoLDFLAGS
|
||||
// consists of the original $CGO_LDFLAGS (unchecked) and all the
|
||||
// flags put together from source code (checked).
|
||||
var cgoenv []string
|
||||
if len(cgoLDFLAGS) > 0 {
|
||||
flags := make([]string, len(cgoLDFLAGS))
|
||||
|
@ -2272,7 +2328,11 @@ func (b *Builder) swigIntSize(objdir string) (intsize string, err error) {
|
|||
|
||||
// Run SWIG on one SWIG input file.
|
||||
func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.CFlags(p)
|
||||
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _, err := b.CFlags(p)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var cflags []string
|
||||
if cxx {
|
||||
cflags = str.StringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
|
||||
|
|
160
libgo/go/cmd/go/internal/work/security.go
Normal file
160
libgo/go/cmd/go/internal/work/security.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
// Checking of compiler and linker flags.
|
||||
// We must avoid flags like -fplugin=, which can allow
|
||||
// arbitrary code execution during the build.
|
||||
// Do not make changes here without carefully
|
||||
// considering the implications.
|
||||
// (That's why the code is isolated in a file named security.go.)
|
||||
//
|
||||
// Note that -Wl,foo means split foo on commas and pass to
|
||||
// the linker, so that -Wl,-foo,bar means pass -foo bar to
|
||||
// the linker. Similarly -Wa,foo for the assembler and so on.
|
||||
// If any of these are permitted, the wildcard portion must
|
||||
// disallow commas.
|
||||
//
|
||||
// Note also that GNU binutils accept any argument @foo
|
||||
// as meaning "read more flags from the file foo", so we must
|
||||
// guard against any command-line argument beginning with @,
|
||||
// even things like "-I @foo".
|
||||
// We use load.SafeArg (which is even more conservative)
|
||||
// to reject these.
|
||||
//
|
||||
// Even worse, gcc -I@foo (one arg) turns into cc1 -I @foo (two args),
|
||||
// so although gcc doesn't expand the @foo, cc1 will.
|
||||
// So out of paranoia, we reject @ at the beginning of every
|
||||
// flag argument that might be split into its own argument.
|
||||
|
||||
package work
|
||||
|
||||
import (
|
||||
"cmd/go/internal/load"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile
|
||||
|
||||
var validCompilerFlags = []*regexp.Regexp{
|
||||
re(`-D([A-Za-z_].*)`),
|
||||
re(`-I([^@\-].*)`),
|
||||
re(`-O`),
|
||||
re(`-O([^@\-].*)`),
|
||||
re(`-W`),
|
||||
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
|
||||
re(`-f(no-)?objc-arc`),
|
||||
re(`-f(no-)?omit-frame-pointer`),
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?split-stack`),
|
||||
re(`-f(no-)?stack-(.+)`),
|
||||
re(`-f(no-)?strict-aliasing`),
|
||||
re(`-fsanitize=(.+)`),
|
||||
re(`-g([^@\-].*)?`),
|
||||
re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`),
|
||||
re(`-m(no-)?stack-(.+)`),
|
||||
re(`-mmacosx-(.+)`),
|
||||
re(`-mnop-fun-dllimport`),
|
||||
re(`-pthread`),
|
||||
re(`-std=([^@\-].*)`),
|
||||
re(`-x([^@\-].*)`),
|
||||
}
|
||||
|
||||
var validCompilerFlagsWithNextArg = []string{
|
||||
"-D",
|
||||
"-I",
|
||||
"-framework",
|
||||
"-x",
|
||||
}
|
||||
|
||||
var validLinkerFlags = []*regexp.Regexp{
|
||||
re(`-F([^@\-].*)`),
|
||||
re(`-l([^@\-].*)`),
|
||||
re(`-L([^@\-].*)`),
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-fsanitize=([^@\-].*)`),
|
||||
re(`-g([^@\-].*)?`),
|
||||
re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`),
|
||||
re(`-(pic|PIC|pie|PIE)`),
|
||||
re(`-pthread`),
|
||||
|
||||
// Note that any wildcards in -Wl need to exclude comma,
|
||||
// since -Wl splits its argument at commas and passes
|
||||
// them all to the linker uninterpreted. Allowing comma
|
||||
// in a wildcard would allow tunnelling arbitrary additional
|
||||
// linker arguments through one of these.
|
||||
re(`-Wl,-rpath,([^,@\-][^,]+)`),
|
||||
re(`-Wl,--(no-)?warn-([^,]+)`),
|
||||
|
||||
re(`[a-zA-Z0-9_].*\.(o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o)
|
||||
}
|
||||
|
||||
var validLinkerFlagsWithNextArg = []string{
|
||||
"-F",
|
||||
"-l",
|
||||
"-L",
|
||||
"-framework",
|
||||
}
|
||||
|
||||
func checkCompilerFlags(name, source string, list []string) error {
|
||||
return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg)
|
||||
}
|
||||
|
||||
func checkLinkerFlags(name, source string, list []string) error {
|
||||
return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg)
|
||||
}
|
||||
|
||||
func checkFlags(name, source string, list []string, valid []*regexp.Regexp, validNext []string) error {
|
||||
// Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
|
||||
var (
|
||||
allow *regexp.Regexp
|
||||
disallow *regexp.Regexp
|
||||
)
|
||||
if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" {
|
||||
r, err := regexp.Compile(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
|
||||
}
|
||||
allow = r
|
||||
}
|
||||
if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
|
||||
r, err := regexp.Compile(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)
|
||||
}
|
||||
disallow = r
|
||||
}
|
||||
|
||||
Args:
|
||||
for i := 0; i < len(list); i++ {
|
||||
arg := list[i]
|
||||
if disallow != nil && disallow.FindString(arg) == arg {
|
||||
goto Bad
|
||||
}
|
||||
if allow != nil && allow.FindString(arg) == arg {
|
||||
continue Args
|
||||
}
|
||||
for _, re := range valid {
|
||||
if re.FindString(arg) == arg { // must be complete match
|
||||
continue Args
|
||||
}
|
||||
}
|
||||
for _, x := range validNext {
|
||||
if arg == x {
|
||||
if i+1 < len(list) && load.SafeArg(list[i+1]) {
|
||||
i++
|
||||
continue Args
|
||||
}
|
||||
if i+1 < len(list) {
|
||||
return fmt.Errorf("invalid flag in %s: %s %s", source, arg, list[i+1])
|
||||
}
|
||||
return fmt.Errorf("invalid flag in %s: %s without argument", source, arg)
|
||||
}
|
||||
}
|
||||
Bad:
|
||||
return fmt.Errorf("invalid flag in %s: %s", source, arg)
|
||||
}
|
||||
return nil
|
||||
}
|
240
libgo/go/cmd/go/internal/work/security_test.go
Normal file
240
libgo/go/cmd/go/internal/work/security_test.go
Normal file
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2018 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 work
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var goodCompilerFlags = [][]string{
|
||||
{"-DFOO"},
|
||||
{"-Dfoo=bar"},
|
||||
{"-I/"},
|
||||
{"-I/etc/passwd"},
|
||||
{"-I."},
|
||||
{"-O"},
|
||||
{"-O2"},
|
||||
{"-Osmall"},
|
||||
{"-W"},
|
||||
{"-Wall"},
|
||||
{"-fobjc-arc"},
|
||||
{"-fno-objc-arc"},
|
||||
{"-fomit-frame-pointer"},
|
||||
{"-fno-omit-frame-pointer"},
|
||||
{"-fpic"},
|
||||
{"-fno-pic"},
|
||||
{"-fPIC"},
|
||||
{"-fno-PIC"},
|
||||
{"-fpie"},
|
||||
{"-fno-pie"},
|
||||
{"-fPIE"},
|
||||
{"-fno-PIE"},
|
||||
{"-fsplit-stack"},
|
||||
{"-fno-split-stack"},
|
||||
{"-fstack-xxx"},
|
||||
{"-fno-stack-xxx"},
|
||||
{"-fsanitize=hands"},
|
||||
{"-g"},
|
||||
{"-ggdb"},
|
||||
{"-march=souza"},
|
||||
{"-mcpu=123"},
|
||||
{"-mfpu=123"},
|
||||
{"-mtune=happybirthday"},
|
||||
{"-mstack-overflow"},
|
||||
{"-mno-stack-overflow"},
|
||||
{"-mmacosx-version"},
|
||||
{"-mnop-fun-dllimport"},
|
||||
{"-pthread"},
|
||||
{"-std=c99"},
|
||||
{"-xc"},
|
||||
{"-D", "FOO"},
|
||||
{"-D", "foo=bar"},
|
||||
{"-I", "."},
|
||||
{"-I", "/etc/passwd"},
|
||||
{"-I", "世界"},
|
||||
{"-framework", "Chocolate"},
|
||||
{"-x", "c"},
|
||||
}
|
||||
|
||||
var badCompilerFlags = [][]string{
|
||||
{"-D@X"},
|
||||
{"-D-X"},
|
||||
{"-I@dir"},
|
||||
{"-I-dir"},
|
||||
{"-O@1"},
|
||||
{"-Wa,-foo"},
|
||||
{"-W@foo"},
|
||||
{"-g@gdb"},
|
||||
{"-g-gdb"},
|
||||
{"-march=@dawn"},
|
||||
{"-march=-dawn"},
|
||||
{"-std=@c99"},
|
||||
{"-std=-c99"},
|
||||
{"-x@c"},
|
||||
{"-x-c"},
|
||||
{"-D", "@foo"},
|
||||
{"-D", "-foo"},
|
||||
{"-I", "@foo"},
|
||||
{"-I", "-foo"},
|
||||
{"-framework", "-Caffeine"},
|
||||
{"-framework", "@Home"},
|
||||
{"-x", "--c"},
|
||||
{"-x", "@obj"},
|
||||
}
|
||||
|
||||
func TestCheckCompilerFlags(t *testing.T) {
|
||||
for _, f := range goodCompilerFlags {
|
||||
if err := checkCompilerFlags("test", "test", f); err != nil {
|
||||
t.Errorf("unexpected error for %q: %v", f, err)
|
||||
}
|
||||
}
|
||||
for _, f := range badCompilerFlags {
|
||||
if err := checkCompilerFlags("test", "test", f); err == nil {
|
||||
t.Errorf("missing error for %q", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var goodLinkerFlags = [][]string{
|
||||
{"-Fbar"},
|
||||
{"-lbar"},
|
||||
{"-Lbar"},
|
||||
{"-fpic"},
|
||||
{"-fno-pic"},
|
||||
{"-fPIC"},
|
||||
{"-fno-PIC"},
|
||||
{"-fpie"},
|
||||
{"-fno-pie"},
|
||||
{"-fPIE"},
|
||||
{"-fno-PIE"},
|
||||
{"-fsanitize=hands"},
|
||||
{"-g"},
|
||||
{"-ggdb"},
|
||||
{"-march=souza"},
|
||||
{"-mcpu=123"},
|
||||
{"-mfpu=123"},
|
||||
{"-mtune=happybirthday"},
|
||||
{"-pic"},
|
||||
{"-pthread"},
|
||||
{"-Wl,-rpath,foo"},
|
||||
{"-Wl,-rpath,$ORIGIN/foo"},
|
||||
{"-Wl,--warn-error"},
|
||||
{"-Wl,--no-warn-error"},
|
||||
{"foo.so"},
|
||||
{"_世界.dll"},
|
||||
{"libcgosotest.dylib"},
|
||||
{"-F", "framework"},
|
||||
{"-l", "."},
|
||||
{"-l", "/etc/passwd"},
|
||||
{"-l", "世界"},
|
||||
{"-L", "framework"},
|
||||
{"-framework", "Chocolate"},
|
||||
}
|
||||
|
||||
var badLinkerFlags = [][]string{
|
||||
{"-DFOO"},
|
||||
{"-Dfoo=bar"},
|
||||
{"-O"},
|
||||
{"-O2"},
|
||||
{"-Osmall"},
|
||||
{"-W"},
|
||||
{"-Wall"},
|
||||
{"-fobjc-arc"},
|
||||
{"-fno-objc-arc"},
|
||||
{"-fomit-frame-pointer"},
|
||||
{"-fno-omit-frame-pointer"},
|
||||
{"-fsplit-stack"},
|
||||
{"-fno-split-stack"},
|
||||
{"-fstack-xxx"},
|
||||
{"-fno-stack-xxx"},
|
||||
{"-mstack-overflow"},
|
||||
{"-mno-stack-overflow"},
|
||||
{"-mmacosx-version"},
|
||||
{"-mnop-fun-dllimport"},
|
||||
{"-std=c99"},
|
||||
{"-xc"},
|
||||
{"-D", "FOO"},
|
||||
{"-D", "foo=bar"},
|
||||
{"-I", "FOO"},
|
||||
{"-L", "@foo"},
|
||||
{"-L", "-foo"},
|
||||
{"-x", "c"},
|
||||
{"-D@X"},
|
||||
{"-D-X"},
|
||||
{"-I@dir"},
|
||||
{"-I-dir"},
|
||||
{"-O@1"},
|
||||
{"-Wa,-foo"},
|
||||
{"-W@foo"},
|
||||
{"-g@gdb"},
|
||||
{"-g-gdb"},
|
||||
{"-march=@dawn"},
|
||||
{"-march=-dawn"},
|
||||
{"-std=@c99"},
|
||||
{"-std=-c99"},
|
||||
{"-x@c"},
|
||||
{"-x-c"},
|
||||
{"-D", "@foo"},
|
||||
{"-D", "-foo"},
|
||||
{"-I", "@foo"},
|
||||
{"-I", "-foo"},
|
||||
{"-l", "@foo"},
|
||||
{"-l", "-foo"},
|
||||
{"-framework", "-Caffeine"},
|
||||
{"-framework", "@Home"},
|
||||
{"-x", "--c"},
|
||||
{"-x", "@obj"},
|
||||
{"-Wl,-rpath,@foo"},
|
||||
}
|
||||
|
||||
func TestCheckLinkerFlags(t *testing.T) {
|
||||
for _, f := range goodLinkerFlags {
|
||||
if err := checkLinkerFlags("test", "test", f); err != nil {
|
||||
t.Errorf("unexpected error for %q: %v", f, err)
|
||||
}
|
||||
}
|
||||
for _, f := range badLinkerFlags {
|
||||
if err := checkLinkerFlags("test", "test", f); err == nil {
|
||||
t.Errorf("missing error for %q", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckFlagAllowDisallow(t *testing.T) {
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil {
|
||||
t.Fatalf("missing error for -disallow")
|
||||
}
|
||||
os.Setenv("CGO_TEST_ALLOW", "-disallo")
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil {
|
||||
t.Fatalf("missing error for -disallow with CGO_TEST_ALLOW=-disallo")
|
||||
}
|
||||
os.Setenv("CGO_TEST_ALLOW", "-disallow")
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err != nil {
|
||||
t.Fatalf("unexpected error for -disallow with CGO_TEST_ALLOW=-disallow: %v", err)
|
||||
}
|
||||
os.Unsetenv("CGO_TEST_ALLOW")
|
||||
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err != nil {
|
||||
t.Fatalf("unexpected error for -Wall: %v", err)
|
||||
}
|
||||
os.Setenv("CGO_TEST_DISALLOW", "-Wall")
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil {
|
||||
t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall")
|
||||
}
|
||||
os.Setenv("CGO_TEST_ALLOW", "-Wall") // disallow wins
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil {
|
||||
t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall and CGO_TEST_ALLOW=-Wall")
|
||||
}
|
||||
|
||||
os.Setenv("CGO_TEST_ALLOW", "-fplugin.*")
|
||||
os.Setenv("CGO_TEST_DISALLOW", "-fplugin=lint.so")
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=faster.so"}); err != nil {
|
||||
t.Fatalf("unexpected error for -fplugin=faster.so: %v", err)
|
||||
}
|
||||
if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=lint.so"}); err == nil {
|
||||
t.Fatalf("missing error for -fplugin=lint.so: %v", err)
|
||||
}
|
||||
}
|
|
@ -379,6 +379,10 @@ type Rows interface {
|
|||
// size as the Columns() are wide.
|
||||
//
|
||||
// Next should return io.EOF when there are no more rows.
|
||||
//
|
||||
// The dest should not be written to outside of Next. Care
|
||||
// should be taken when closing Rows not to modify
|
||||
// a buffer held in dest.
|
||||
Next(dest []Value) error
|
||||
}
|
||||
|
||||
|
|
|
@ -1020,11 +1020,6 @@ func (rc *rowsCursor) touchMem() {
|
|||
}
|
||||
|
||||
func (rc *rowsCursor) Close() error {
|
||||
if !rc.closed {
|
||||
for _, bs := range rc.bytesClone {
|
||||
bs[0] = 255 // first byte corrupted
|
||||
}
|
||||
}
|
||||
rc.touchMem()
|
||||
rc.parentMem.touchMem()
|
||||
rc.closed = true
|
||||
|
|
|
@ -663,43 +663,6 @@ func TestPoolExhaustOnCancel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestByteOwnership(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
rows, err := db.Query("SELECT|people|name,photo|")
|
||||
if err != nil {
|
||||
t.Fatalf("Query: %v", err)
|
||||
}
|
||||
type row struct {
|
||||
name []byte
|
||||
photo RawBytes
|
||||
}
|
||||
got := []row{}
|
||||
for rows.Next() {
|
||||
var r row
|
||||
err = rows.Scan(&r.name, &r.photo)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan: %v", err)
|
||||
}
|
||||
got = append(got, r)
|
||||
}
|
||||
corruptMemory := []byte("\xffPHOTO")
|
||||
want := []row{
|
||||
{name: []byte("Alice"), photo: corruptMemory},
|
||||
{name: []byte("Bob"), photo: corruptMemory},
|
||||
{name: []byte("Chris"), photo: corruptMemory},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
|
||||
var photo RawBytes
|
||||
err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo)
|
||||
if err == nil {
|
||||
t.Error("want error scanning into RawBytes from QueryRow")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRowsColumns(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
@ -3192,8 +3155,11 @@ func TestIssue18429(t *testing.T) {
|
|||
// reported.
|
||||
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
|
||||
if rows != nil {
|
||||
var name string
|
||||
// Call Next to test Issue 21117 and check for races.
|
||||
for rows.Next() {
|
||||
// Scan the buffer so it is read and checked for races.
|
||||
rows.Scan(&name)
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ var pkgDeps = map[string][]string{
|
|||
"os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
|
||||
|
||||
// Internal package used only for testing.
|
||||
"os/signal/internal/pty": {"CGO", "fmt", "os"},
|
||||
"os/signal/internal/pty": {"CGO", "fmt", "os", "syscall"},
|
||||
|
||||
// Basic networking.
|
||||
// Because net must be used by any package that wants to
|
||||
|
|
|
@ -62,8 +62,6 @@ var importablePackages = [...]string{
|
|||
"encoding/pem",
|
||||
"encoding/xml",
|
||||
"errors",
|
||||
"exp/proxy",
|
||||
"exp/terminal",
|
||||
"expvar",
|
||||
"flag",
|
||||
"fmt",
|
||||
|
@ -114,8 +112,6 @@ var importablePackages = [...]string{
|
|||
"net/smtp",
|
||||
"net/textproto",
|
||||
"net/url",
|
||||
"old/regexp",
|
||||
"old/template",
|
||||
"os/exec",
|
||||
"os",
|
||||
"os/signal",
|
||||
|
|
|
@ -20,7 +20,7 @@ func maxListenerBacklog() int {
|
|||
case "darwin":
|
||||
n, err = syscall.SysctlUint32("kern.ipc.somaxconn")
|
||||
case "freebsd":
|
||||
n, err = syscall.SysctlUint32("kern.ipc.acceptqueue")
|
||||
n, err = syscall.SysctlUint32("kern.ipc.soacceptqueue")
|
||||
case "netbsd":
|
||||
// NOTE: NetBSD has no somaxconn-like kernel state so far
|
||||
case "openbsd":
|
||||
|
|
|
@ -88,6 +88,11 @@ func FindProcess(pid int) (*Process, error) {
|
|||
// specified by name, argv and attr. The argv slice will become os.Args in the
|
||||
// new process, so it normally starts with the program name.
|
||||
//
|
||||
// If the calling goroutine has locked the operating system thread
|
||||
// with runtime.LockOSThread and modified any inheritable OS-level
|
||||
// thread state (for example, Linux or Plan 9 name spaces), the new
|
||||
// process will inherit the caller's thread state.
|
||||
//
|
||||
// StartProcess is a low-level interface. The os/exec package provides
|
||||
// higher-level interfaces.
|
||||
//
|
||||
|
|
|
@ -293,6 +293,11 @@ func (c *Cmd) closeDescriptors(closers []io.Closer) {
|
|||
//
|
||||
// If the command starts but does not complete successfully, the error is of
|
||||
// type *ExitError. Other error types may be returned for other situations.
|
||||
//
|
||||
// If the calling goroutine has locked the operating system thread
|
||||
// with runtime.LockOSThread and modified any inheritable OS-level
|
||||
// thread state (for example, Linux or Plan 9 name spaces), the new
|
||||
// process will inherit the caller's thread state.
|
||||
func (c *Cmd) Run() error {
|
||||
if err := c.Start(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -33,21 +33,35 @@ func close(int32) int32
|
|||
|
||||
const _O_RDWR = 2
|
||||
|
||||
type PtyError struct {
|
||||
FuncName string
|
||||
ErrorString string
|
||||
Errno syscall.Errno
|
||||
}
|
||||
|
||||
func ptyError(name string, err error) *PtyError {
|
||||
return &PtyError{name, err.Error(), err.(syscall.Errno)}
|
||||
}
|
||||
|
||||
func (e *PtyError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.FuncName, e.ErrorString)
|
||||
}
|
||||
|
||||
// Open returns a master pty and the name of the linked slave tty.
|
||||
func Open() (master *os.File, slave string, err error) {
|
||||
m := posix_openpt(_O_RDWR)
|
||||
if m < 0 {
|
||||
return nil, "", fmt.Errorf("posix_openpt: %v", syscall.GetErrno())
|
||||
return nil, "", ptyError("posix_openpt", syscall.GetErrno())
|
||||
}
|
||||
if grantpt(m) < 0 {
|
||||
errno := syscall.GetErrno()
|
||||
close(m)
|
||||
return nil, "", fmt.Errorf("grantpt: %v", errno)
|
||||
return nil, "", ptyError("grantpt", errno)
|
||||
}
|
||||
if unlockpt(m) < 0 {
|
||||
errno := syscall.GetErrno()
|
||||
close(m)
|
||||
return nil, "", fmt.Errorf("unlockpt: %v", errno)
|
||||
return nil, "", ptyError("unlockpt", errno)
|
||||
}
|
||||
p := ptsname(m)
|
||||
s := (*[32000]byte)(unsafe.Pointer(p))[:]
|
||||
|
|
|
@ -72,6 +72,10 @@ func TestTerminalSignal(t *testing.T) {
|
|||
|
||||
master, sname, err := pty.Open()
|
||||
if err != nil {
|
||||
ptyErr := err.(*pty.PtyError)
|
||||
if ptyErr.FuncName == "posix_openpt" && ptyErr.Errno == syscall.EACCES {
|
||||
t.Skip("posix_openpt failed with EACCES, assuming chroot and skipping")
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer master.Close()
|
||||
|
|
|
@ -493,3 +493,24 @@ func TestSigStackSwapping(t *testing.T) {
|
|||
t.Errorf("expected %q got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCgoTracebackSigpanic(t *testing.T) {
|
||||
// Test unwinding over a sigpanic in C code without a C
|
||||
// symbolizer. See issue #23576.
|
||||
if runtime.GOOS == "windows" {
|
||||
// On Windows if we get an exception in C code, we let
|
||||
// the Windows exception handler unwind it, rather
|
||||
// than injecting a sigpanic.
|
||||
t.Skip("no sigpanic in C on windows")
|
||||
}
|
||||
t.Parallel()
|
||||
got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
|
||||
want := "runtime.sigpanic"
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
|
||||
}
|
||||
nowant := "unexpected return pc"
|
||||
if strings.Contains(got, nowant) {
|
||||
t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got)
|
||||
}
|
||||
}
|
||||
|
|
28
libgo/go/runtime/testdata/testprogcgo/sigpanic.go
vendored
Normal file
28
libgo/go/runtime/testdata/testprogcgo/sigpanic.go
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2018 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 main
|
||||
|
||||
// This program will crash.
|
||||
// We want to test unwinding from sigpanic into C code (without a C symbolizer).
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -O0
|
||||
|
||||
char *pnil;
|
||||
|
||||
static int f1(void) {
|
||||
*pnil = 0;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func init() {
|
||||
register("TracebackSigpanic", TracebackSigpanic)
|
||||
}
|
||||
|
||||
func TracebackSigpanic() {
|
||||
C.f1()
|
||||
}
|
|
@ -110,12 +110,6 @@ data, defined in detail in the corresponding sections that follow.
|
|||
T0 is executed; otherwise, dot is set to the successive elements
|
||||
of the array, slice, or map and T1 is executed.
|
||||
|
||||
{{break}}
|
||||
Break out of the surrounding range loop.
|
||||
|
||||
{{continue}}
|
||||
Begin the next iteration of the surrounding range loop.
|
||||
|
||||
{{template "name"}}
|
||||
The template with the specified name is executed with nil data.
|
||||
|
||||
|
|
|
@ -27,12 +27,11 @@ const maxExecDepth = 1000
|
|||
// template so that multiple executions of the same template
|
||||
// can execute in parallel.
|
||||
type state struct {
|
||||
tmpl *Template
|
||||
wr io.Writer
|
||||
node parse.Node // current node, for errors.
|
||||
vars []variable // push-down stack of variable values.
|
||||
depth int // the height of the stack of executing templates.
|
||||
rangeDepth int // nesting level of range loops.
|
||||
tmpl *Template
|
||||
wr io.Writer
|
||||
node parse.Node // current node, for errors
|
||||
vars []variable // push-down stack of variable values.
|
||||
depth int // the height of the stack of executing templates.
|
||||
}
|
||||
|
||||
// variable holds the dynamic value of a variable such as $, $x etc.
|
||||
|
@ -223,17 +222,9 @@ func (t *Template) DefinedTemplates() string {
|
|||
return s
|
||||
}
|
||||
|
||||
type rangeControl int8
|
||||
|
||||
const (
|
||||
rangeNone rangeControl = iota // no action.
|
||||
rangeBreak // break out of range.
|
||||
rangeContinue // continues next range iteration.
|
||||
)
|
||||
|
||||
// Walk functions step through the major pieces of the template structure,
|
||||
// generating output as they go.
|
||||
func (s *state) walk(dot reflect.Value, node parse.Node) rangeControl {
|
||||
func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
s.at(node)
|
||||
switch node := node.(type) {
|
||||
case *parse.ActionNode:
|
||||
|
@ -244,15 +235,13 @@ func (s *state) walk(dot reflect.Value, node parse.Node) rangeControl {
|
|||
s.printValue(node, val)
|
||||
}
|
||||
case *parse.IfNode:
|
||||
return s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
||||
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
||||
case *parse.ListNode:
|
||||
for _, node := range node.Nodes {
|
||||
if c := s.walk(dot, node); c != rangeNone {
|
||||
return c
|
||||
}
|
||||
s.walk(dot, node)
|
||||
}
|
||||
case *parse.RangeNode:
|
||||
return s.walkRange(dot, node)
|
||||
s.walkRange(dot, node)
|
||||
case *parse.TemplateNode:
|
||||
s.walkTemplate(dot, node)
|
||||
case *parse.TextNode:
|
||||
|
@ -260,26 +249,15 @@ func (s *state) walk(dot reflect.Value, node parse.Node) rangeControl {
|
|||
s.writeError(err)
|
||||
}
|
||||
case *parse.WithNode:
|
||||
return s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
|
||||
case *parse.BreakNode:
|
||||
if s.rangeDepth == 0 {
|
||||
s.errorf("invalid break outside of range")
|
||||
}
|
||||
return rangeBreak
|
||||
case *parse.ContinueNode:
|
||||
if s.rangeDepth == 0 {
|
||||
s.errorf("invalid continue outside of range")
|
||||
}
|
||||
return rangeContinue
|
||||
s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
|
||||
default:
|
||||
s.errorf("unknown node: %s", node)
|
||||
}
|
||||
return rangeNone
|
||||
}
|
||||
|
||||
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
|
||||
// are identical in behavior except that 'with' sets dot.
|
||||
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) rangeControl {
|
||||
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
|
||||
defer s.pop(s.mark())
|
||||
val := s.evalPipeline(dot, pipe)
|
||||
truth, ok := isTrue(val)
|
||||
|
@ -288,14 +266,13 @@ func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.
|
|||
}
|
||||
if truth {
|
||||
if typ == parse.NodeWith {
|
||||
return s.walk(val, list)
|
||||
s.walk(val, list)
|
||||
} else {
|
||||
return s.walk(dot, list)
|
||||
s.walk(dot, list)
|
||||
}
|
||||
} else if elseList != nil {
|
||||
return s.walk(dot, elseList)
|
||||
s.walk(dot, elseList)
|
||||
}
|
||||
return rangeNone
|
||||
}
|
||||
|
||||
// IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
|
||||
|
@ -333,14 +310,13 @@ func isTrue(val reflect.Value) (truth, ok bool) {
|
|||
return truth, true
|
||||
}
|
||||
|
||||
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
|
||||
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
s.at(r)
|
||||
defer s.pop(s.mark())
|
||||
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
|
||||
// mark top of stack before any variables in the body are pushed.
|
||||
mark := s.mark()
|
||||
s.rangeDepth++
|
||||
oneIteration := func(index, elem reflect.Value) rangeControl {
|
||||
oneIteration := func(index, elem reflect.Value) {
|
||||
// Set top var (lexically the second if there are two) to the element.
|
||||
if len(r.Pipe.Decl) > 0 {
|
||||
s.setVar(1, elem)
|
||||
|
@ -349,9 +325,8 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
|
|||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setVar(2, index)
|
||||
}
|
||||
ctrl := s.walk(elem, r.List)
|
||||
s.walk(elem, r.List)
|
||||
s.pop(mark)
|
||||
return ctrl
|
||||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
|
@ -359,23 +334,17 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
|
|||
break
|
||||
}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
if ctrl := oneIteration(reflect.ValueOf(i), val.Index(i)); ctrl == rangeBreak {
|
||||
break
|
||||
}
|
||||
oneIteration(reflect.ValueOf(i), val.Index(i))
|
||||
}
|
||||
s.rangeDepth--
|
||||
return rangeNone
|
||||
return
|
||||
case reflect.Map:
|
||||
if val.Len() == 0 {
|
||||
break
|
||||
}
|
||||
for _, key := range sortKeys(val.MapKeys()) {
|
||||
if ctrl := oneIteration(key, val.MapIndex(key)); ctrl == rangeBreak {
|
||||
break
|
||||
}
|
||||
oneIteration(key, val.MapIndex(key))
|
||||
}
|
||||
s.rangeDepth--
|
||||
return rangeNone
|
||||
return
|
||||
case reflect.Chan:
|
||||
if val.IsNil() {
|
||||
break
|
||||
|
@ -386,25 +355,20 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
|
|||
if !ok {
|
||||
break
|
||||
}
|
||||
if ctrl := oneIteration(reflect.ValueOf(i), elem); ctrl == rangeBreak {
|
||||
break
|
||||
}
|
||||
oneIteration(reflect.ValueOf(i), elem)
|
||||
}
|
||||
if i == 0 {
|
||||
break
|
||||
}
|
||||
s.rangeDepth--
|
||||
return rangeNone
|
||||
return
|
||||
case reflect.Invalid:
|
||||
break // An invalid value is likely a nil map, etc. and acts like an empty map.
|
||||
default:
|
||||
s.errorf("range can't iterate over %v", val)
|
||||
}
|
||||
s.rangeDepth--
|
||||
if r.ElseList != nil {
|
||||
return s.walk(dot, r.ElseList)
|
||||
s.walk(dot, r.ElseList)
|
||||
}
|
||||
return rangeNone
|
||||
}
|
||||
|
||||
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
|
||||
|
|
|
@ -513,10 +513,6 @@ var execTests = []execTest{
|
|||
{"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
|
||||
{"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
|
||||
{"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
|
||||
{"range quick break", `{{range .SI}}{{break}}{{.}}{{end}}`, "", tVal, true},
|
||||
{"range break after two", `{{range $i, $x := .SI}}{{if ge $i 2}}{{break}}{{end}}{{.}}{{end}}`, "34", tVal, true},
|
||||
{"range continue", `{{range .SI}}{{continue}}{{.}}{{end}}`, "", tVal, true},
|
||||
{"range continue condition", `{{range .SI}}{{if eq . 3 }}{{continue}}{{end}}{{.}}{{end}}`, "45", tVal, true},
|
||||
|
||||
// Cute examples.
|
||||
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
|
||||
|
|
|
@ -60,8 +60,6 @@ const (
|
|||
// Keywords appear after all the rest.
|
||||
itemKeyword // used only to delimit the keywords
|
||||
itemBlock // block keyword
|
||||
itemBreak // break keyword
|
||||
itemContinue // continue keyword
|
||||
itemDot // the cursor, spelled '.'
|
||||
itemDefine // define keyword
|
||||
itemElse // else keyword
|
||||
|
@ -76,8 +74,6 @@ const (
|
|||
var key = map[string]itemType{
|
||||
".": itemDot,
|
||||
"block": itemBlock,
|
||||
"break": itemBreak,
|
||||
"continue": itemContinue,
|
||||
"define": itemDefine,
|
||||
"else": itemElse,
|
||||
"end": itemEnd,
|
||||
|
|
|
@ -192,7 +192,7 @@ var lexTests = []lexTest{
|
|||
tRight,
|
||||
tEOF,
|
||||
}},
|
||||
{"keywords", "{{range if else end with break continue}}", []item{
|
||||
{"keywords", "{{range if else end with}}", []item{
|
||||
tLeft,
|
||||
mkItem(itemRange, "range"),
|
||||
tSpace,
|
||||
|
@ -203,10 +203,6 @@ var lexTests = []lexTest{
|
|||
mkItem(itemEnd, "end"),
|
||||
tSpace,
|
||||
mkItem(itemWith, "with"),
|
||||
tSpace,
|
||||
mkItem(itemBreak, "break"),
|
||||
tSpace,
|
||||
mkItem(itemContinue, "continue"),
|
||||
tRight,
|
||||
tEOF,
|
||||
}},
|
||||
|
|
|
@ -69,8 +69,6 @@ const (
|
|||
NodeTemplate // A template invocation action.
|
||||
NodeVariable // A $ variable.
|
||||
NodeWith // A with action.
|
||||
NodeBreak // A break action.
|
||||
NodeContinue // A continue action.
|
||||
)
|
||||
|
||||
// Nodes.
|
||||
|
@ -798,68 +796,6 @@ func (r *RangeNode) Copy() Node {
|
|||
return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
|
||||
}
|
||||
|
||||
// BreakNode represents a {{break}} action.
|
||||
type BreakNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
tr *Tree
|
||||
}
|
||||
|
||||
func (t *Tree) newBreak(pos Pos) *BreakNode {
|
||||
return &BreakNode{NodeType: NodeBreak, Pos: pos, tr: t}
|
||||
}
|
||||
|
||||
func (b *BreakNode) Type() NodeType {
|
||||
return b.NodeType
|
||||
}
|
||||
|
||||
func (b *BreakNode) String() string {
|
||||
return "{{break}}"
|
||||
}
|
||||
|
||||
func (b *BreakNode) Copy() Node {
|
||||
return b.tr.newBreak(b.Pos)
|
||||
}
|
||||
|
||||
func (b *BreakNode) Position() Pos {
|
||||
return b.Pos
|
||||
}
|
||||
|
||||
func (b *BreakNode) tree() *Tree {
|
||||
return b.tr
|
||||
}
|
||||
|
||||
// ContinueNode represents a {{continue}} action.
|
||||
type ContinueNode struct {
|
||||
NodeType
|
||||
Pos
|
||||
tr *Tree
|
||||
}
|
||||
|
||||
func (t *Tree) newContinue(pos Pos) *ContinueNode {
|
||||
return &ContinueNode{NodeType: NodeContinue, Pos: pos, tr: t}
|
||||
}
|
||||
|
||||
func (c *ContinueNode) Type() NodeType {
|
||||
return c.NodeType
|
||||
}
|
||||
|
||||
func (c *ContinueNode) String() string {
|
||||
return "{{continue}}"
|
||||
}
|
||||
|
||||
func (c *ContinueNode) Copy() Node {
|
||||
return c.tr.newContinue(c.Pos)
|
||||
}
|
||||
|
||||
func (c *ContinueNode) Position() Pos {
|
||||
return c.Pos
|
||||
}
|
||||
|
||||
func (c *ContinueNode) tree() *Tree {
|
||||
return c.tr
|
||||
}
|
||||
|
||||
// WithNode represents a {{with}} action and its commands.
|
||||
type WithNode struct {
|
||||
BranchNode
|
||||
|
|
|
@ -23,13 +23,12 @@ type Tree struct {
|
|||
Root *ListNode // top-level root of the tree.
|
||||
text string // text parsed to create the template (or its parent)
|
||||
// Parsing only; cleared after parse.
|
||||
funcs []map[string]interface{}
|
||||
lex *lexer
|
||||
token [3]item // three-token lookahead for parser.
|
||||
peekCount int
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
rangeDepth int // nesting level of range loops.
|
||||
funcs []map[string]interface{}
|
||||
lex *lexer
|
||||
token [3]item // three-token lookahead for parser.
|
||||
peekCount int
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
}
|
||||
|
||||
// Copy returns a copy of the Tree. Any parsing state is discarded.
|
||||
|
@ -220,7 +219,6 @@ func (t *Tree) stopParse() {
|
|||
t.vars = nil
|
||||
t.funcs = nil
|
||||
t.treeSet = nil
|
||||
t.rangeDepth = 0
|
||||
}
|
||||
|
||||
// Parse parses the template definition string to construct a representation of
|
||||
|
@ -375,10 +373,6 @@ func (t *Tree) action() (n Node) {
|
|||
return t.templateControl()
|
||||
case itemWith:
|
||||
return t.withControl()
|
||||
case itemBreak:
|
||||
return t.breakControl()
|
||||
case itemContinue:
|
||||
return t.continueControl()
|
||||
}
|
||||
t.backup()
|
||||
token := t.peek()
|
||||
|
@ -459,13 +453,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
|
|||
defer t.popVars(len(t.vars))
|
||||
pipe = t.pipeline(context)
|
||||
var next Node
|
||||
if context == "range" {
|
||||
t.rangeDepth++
|
||||
}
|
||||
list, next = t.itemList()
|
||||
if context == "range" {
|
||||
t.rangeDepth--
|
||||
}
|
||||
switch next.Type() {
|
||||
case nodeEnd: //done
|
||||
case nodeElse:
|
||||
|
@ -510,26 +498,6 @@ func (t *Tree) rangeControl() Node {
|
|||
return t.newRange(t.parseControl(false, "range"))
|
||||
}
|
||||
|
||||
// Break:
|
||||
// {{break}}
|
||||
// Break keyword is past.
|
||||
func (t *Tree) breakControl() Node {
|
||||
if t.rangeDepth == 0 {
|
||||
t.errorf("unexpected break outside of range")
|
||||
}
|
||||
return t.newBreak(t.expect(itemRightDelim, "break").pos)
|
||||
}
|
||||
|
||||
// Continue:
|
||||
// {{continue}}
|
||||
// Continue keyword is past.
|
||||
func (t *Tree) continueControl() Node {
|
||||
if t.rangeDepth == 0 {
|
||||
t.errorf("unexpected continue outside of range")
|
||||
}
|
||||
return t.newContinue(t.expect(itemRightDelim, "continue").pos)
|
||||
}
|
||||
|
||||
// With:
|
||||
// {{with pipeline}} itemList {{end}}
|
||||
// {{with pipeline}} itemList {{else}} itemList {{end}}
|
||||
|
|
|
@ -218,12 +218,6 @@ var parseTests = []parseTest{
|
|||
`{{range $x := .SI}}{{.}}{{end}}`},
|
||||
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
|
||||
`{{range $x, $y := .SI}}{{.}}{{end}}`},
|
||||
{"range []int with break", "{{range .SI}}{{break}}{{.}}{{end}}", noError,
|
||||
`{{range .SI}}{{break}}{{.}}{{end}}`},
|
||||
{"range []int with break in else", "{{range .SI}}{{range .SI}}{{.}}{{else}}{{break}}{{end}}{{end}}", noError,
|
||||
`{{range .SI}}{{range .SI}}{{.}}{{else}}{{break}}{{end}}{{end}}`},
|
||||
{"range []int with continue", "{{range .SI}}{{continue}}{{.}}{{end}}", noError,
|
||||
`{{range .SI}}{{continue}}{{.}}{{end}}`},
|
||||
{"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
|
||||
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
|
||||
{"template", "{{template `x`}}", noError,
|
||||
|
@ -294,12 +288,6 @@ var parseTests = []parseTest{
|
|||
{"empty pipeline", `{{printf "%d" ( ) }}`, hasError, ""},
|
||||
// Missing pipeline in block
|
||||
{"block definition", `{{block "foo"}}hello{{end}}`, hasError, ""},
|
||||
// Invalid range control
|
||||
{"break outside of range", `{{break}}`, hasError, ""},
|
||||
{"break in range else, outside of range", `{{range .}}{{.}}{{else}}{{break}}{{end}}`, hasError, ""},
|
||||
{"continue outside of range", `{{continue}}`, hasError, ""},
|
||||
{"continue in range else, outside of range", `{{range .}}{{.}}{{else}}{{continue}}{{end}}`, hasError, ""},
|
||||
{"additional break data", `{{range .}}{{break label}}{{end}}`, hasError, ""},
|
||||
}
|
||||
|
||||
var builtins = map[string]interface{}{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -c
|
||||
#cgo LDFLAGS: -L/nonexist
|
||||
|
||||
void test() {
|
||||
xxx; // ERROR HERE
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// Issue 19832. Functions taking a pointer typedef were being expanded and triggering a compiler error.
|
||||
|
||||
package cgotest
|
||||
|
||||
// typedef struct { int i; } *PS;
|
||||
// void T19832(PS p) {}
|
||||
import "C"
|
||||
import "testing"
|
||||
|
||||
func test19832(t *testing.T) {
|
||||
C.T19832(nil)
|
||||
}
|
|
@ -4,6 +4,25 @@
|
|||
|
||||
// +build !windows
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
// Write our own versions of dlopen/dlsym/dlclose so that we represent
|
||||
// the opaque handle as a Go uintptr rather than a Go pointer to avoid
|
||||
// garbage collector confusion. See issue 23663.
|
||||
|
||||
uintptr_t dlopen4029(char* name, int flags) {
|
||||
return (uintptr_t)(dlopen(name, flags));
|
||||
}
|
||||
|
||||
uintptr_t dlsym4029(uintptr_t handle, char* name) {
|
||||
return (uintptr_t)(dlsym((void*)(handle), name));
|
||||
}
|
||||
|
||||
int dlclose4029(uintptr_t handle) {
|
||||
return dlclose((void*)(handle));
|
||||
}
|
||||
|
||||
void call4029(void *arg) {
|
||||
void (*fn)(void) = arg;
|
||||
fn();
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
package cgotest
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
#cgo linux LDFLAGS: -ldl
|
||||
|
||||
extern void call4029(void *arg);
|
||||
extern uintptr_t dlopen4029(char*, int);
|
||||
extern uintptr_t dlsym4029(uintptr_t, char*);
|
||||
extern int dlclose4029(uintptr_t);
|
||||
|
||||
extern void call4029(uintptr_t arg);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
@ -51,15 +56,15 @@ func test4029(t *testing.T) {
|
|||
}
|
||||
|
||||
func loadThySelf(t *testing.T, symbol string) {
|
||||
this_process := C.dlopen(nil, C.RTLD_NOW)
|
||||
if this_process == nil {
|
||||
this_process := C.dlopen4029(nil, C.RTLD_NOW)
|
||||
if this_process == 0 {
|
||||
t.Error("dlopen:", C.GoString(C.dlerror()))
|
||||
return
|
||||
}
|
||||
defer C.dlclose(this_process)
|
||||
defer C.dlclose4029(this_process)
|
||||
|
||||
symbol_address := C.dlsym(this_process, C.CString(symbol))
|
||||
if symbol_address == nil {
|
||||
symbol_address := C.dlsym4029(this_process, C.CString(symbol))
|
||||
if symbol_address == 0 {
|
||||
t.Error("dlsym:", C.GoString(C.dlerror()))
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue