mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Move Flag, Debug, Ctxt, Exit, and error messages to
new package cmd/compile/internal/base.
These are the core functionality that everything in gc uses
and which otherwise prevent splitting any other code
out of gc into different packages.
A minor milestone: the compiler source code
no longer contains the string "yy".
[git-generate]
cd src/cmd/compile/internal/gc
rf '
mv atExit AtExit
mv Ctxt atExitFuncs AtExit Exit base.go
mv lineno Pos
mv linestr FmtPos
mv flusherrors FlushErrors
mv yyerror Errorf
mv yyerrorl ErrorfAt
mv yyerrorv ErrorfVers
mv noder.yyerrorpos noder.errorAt
mv Warnl WarnfAt
mv errorexit ErrorExit
mv base.go debug.go flag.go print.go cmd/compile/internal/base
'
: # update comments
sed -i '' 's/yyerrorl/ErrorfAt/g; s/yyerror/Errorf/g' *.go
: # bootstrap.go is not built by default so invisible to rf
sed -i '' 's/Fatalf/base.Fatalf/' bootstrap.go
goimports -w bootstrap.go
: # update cmd/dist to add internal/base
cd ../../../dist
sed -i '' '/internal.amd64/a\
"cmd/compile/internal/base",
' buildtool.go
gofmt -w buildtool.go
Change-Id: I59903c7084222d6eaee38823fd222159ba24a31a
Reviewed-on: https://go-review.googlesource.com/c/go/+/272250
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
251 lines
7 KiB
Go
251 lines
7 KiB
Go
// Copyright 2020 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 (
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/syntax"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
|
|
"path"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var embedlist []*Node
|
|
|
|
const (
|
|
embedUnknown = iota
|
|
embedBytes
|
|
embedString
|
|
embedFiles
|
|
)
|
|
|
|
var numLocalEmbed int
|
|
|
|
func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) (newExprs []*Node) {
|
|
haveEmbed := false
|
|
for _, decl := range p.file.DeclList {
|
|
imp, ok := decl.(*syntax.ImportDecl)
|
|
if !ok {
|
|
// imports always come first
|
|
break
|
|
}
|
|
path, _ := strconv.Unquote(imp.Path.Value)
|
|
if path == "embed" {
|
|
haveEmbed = true
|
|
break
|
|
}
|
|
}
|
|
|
|
pos := embeds[0].Pos
|
|
if !haveEmbed {
|
|
p.errorAt(pos, "invalid go:embed: missing import \"embed\"")
|
|
return exprs
|
|
}
|
|
if base.Flag.Cfg.Embed.Patterns == nil {
|
|
p.errorAt(pos, "invalid go:embed: build system did not supply embed configuration")
|
|
return exprs
|
|
}
|
|
if len(names) > 1 {
|
|
p.errorAt(pos, "go:embed cannot apply to multiple vars")
|
|
return exprs
|
|
}
|
|
if len(exprs) > 0 {
|
|
p.errorAt(pos, "go:embed cannot apply to var with initializer")
|
|
return exprs
|
|
}
|
|
if typ == nil {
|
|
// Should not happen, since len(exprs) == 0 now.
|
|
p.errorAt(pos, "go:embed cannot apply to var without type")
|
|
return exprs
|
|
}
|
|
|
|
kind := embedKindApprox(typ)
|
|
if kind == embedUnknown {
|
|
p.errorAt(pos, "go:embed cannot apply to var of type %v", typ)
|
|
return exprs
|
|
}
|
|
|
|
// Build list of files to store.
|
|
have := make(map[string]bool)
|
|
var list []string
|
|
for _, e := range embeds {
|
|
for _, pattern := range e.Patterns {
|
|
files, ok := base.Flag.Cfg.Embed.Patterns[pattern]
|
|
if !ok {
|
|
p.errorAt(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
|
|
}
|
|
for _, file := range files {
|
|
if base.Flag.Cfg.Embed.Files[file] == "" {
|
|
p.errorAt(e.Pos, "invalid go:embed: build system did not map file: %s", file)
|
|
continue
|
|
}
|
|
if !have[file] {
|
|
have[file] = true
|
|
list = append(list, file)
|
|
}
|
|
if kind == embedFiles {
|
|
for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
|
|
have[dir] = true
|
|
list = append(list, dir+"/")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sort.Slice(list, func(i, j int) bool {
|
|
return embedFileLess(list[i], list[j])
|
|
})
|
|
|
|
if kind == embedString || kind == embedBytes {
|
|
if len(list) > 1 {
|
|
p.errorAt(pos, "invalid go:embed: multiple files for type %v", typ)
|
|
return exprs
|
|
}
|
|
}
|
|
|
|
v := names[0]
|
|
if dclcontext != PEXTERN {
|
|
numLocalEmbed++
|
|
v = newnamel(v.Pos, lookupN("embed.", numLocalEmbed))
|
|
v.Sym.Def = asTypesNode(v)
|
|
v.Name.Param.Ntype = typ
|
|
v.SetClass(PEXTERN)
|
|
externdcl = append(externdcl, v)
|
|
exprs = []*Node{v}
|
|
}
|
|
|
|
v.Name.Param.SetEmbedFiles(list)
|
|
embedlist = append(embedlist, v)
|
|
return exprs
|
|
}
|
|
|
|
// embedKindApprox determines the kind of embedding variable, approximately.
|
|
// The match is approximate because we haven't done scope resolution yet and
|
|
// can't tell whether "string" and "byte" really mean "string" and "byte".
|
|
// The result must be confirmed later, after type checking, using embedKind.
|
|
func embedKindApprox(typ *Node) int {
|
|
if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && base.Ctxt.Pkgpath == "embed")) {
|
|
return embedFiles
|
|
}
|
|
// These are not guaranteed to match only string and []byte -
|
|
// maybe the local package has redefined one of those words.
|
|
// But it's the best we can do now during the noder.
|
|
// The stricter check happens later, in initEmbed calling embedKind.
|
|
if typ.Sym != nil && typ.Sym.Name == "string" && typ.Sym.Pkg == localpkg {
|
|
return embedString
|
|
}
|
|
if typ.Op == OTARRAY && typ.Left == nil && typ.Right.Sym != nil && typ.Right.Sym.Name == "byte" && typ.Right.Sym.Pkg == localpkg {
|
|
return embedBytes
|
|
}
|
|
return embedUnknown
|
|
}
|
|
|
|
// embedKind determines the kind of embedding variable.
|
|
func embedKind(typ *types.Type) int {
|
|
if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && base.Ctxt.Pkgpath == "embed")) {
|
|
return embedFiles
|
|
}
|
|
if typ == types.Types[TSTRING] {
|
|
return embedString
|
|
}
|
|
if typ.Sym == nil && typ.IsSlice() && typ.Elem() == types.Bytetype {
|
|
return embedBytes
|
|
}
|
|
return embedUnknown
|
|
}
|
|
|
|
func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
|
|
if name[len(name)-1] == '/' {
|
|
isDir = true
|
|
name = name[:len(name)-1]
|
|
}
|
|
i := len(name) - 1
|
|
for i >= 0 && name[i] != '/' {
|
|
i--
|
|
}
|
|
if i < 0 {
|
|
return ".", name, isDir
|
|
}
|
|
return name[:i], name[i+1:], isDir
|
|
}
|
|
|
|
// embedFileLess implements the sort order for a list of embedded files.
|
|
// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
|
|
func embedFileLess(x, y string) bool {
|
|
xdir, xelem, _ := embedFileNameSplit(x)
|
|
ydir, yelem, _ := embedFileNameSplit(y)
|
|
return xdir < ydir || xdir == ydir && xelem < yelem
|
|
}
|
|
|
|
func dumpembeds() {
|
|
for _, v := range embedlist {
|
|
initEmbed(v)
|
|
}
|
|
}
|
|
|
|
// initEmbed emits the init data for a //go:embed variable,
|
|
// which is either a string, a []byte, or an embed.FS.
|
|
func initEmbed(v *Node) {
|
|
files := v.Name.Param.EmbedFiles()
|
|
switch kind := embedKind(v.Type); kind {
|
|
case embedUnknown:
|
|
base.ErrorfAt(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
|
|
|
|
case embedString, embedBytes:
|
|
file := files[0]
|
|
fsym, size, err := fileStringSym(v.Pos, base.Flag.Cfg.Embed.Files[file], kind == embedString, nil)
|
|
if err != nil {
|
|
base.ErrorfAt(v.Pos, "embed %s: %v", file, err)
|
|
}
|
|
sym := v.Sym.Linksym()
|
|
off := 0
|
|
off = dsymptr(sym, off, fsym, 0) // data string
|
|
off = duintptr(sym, off, uint64(size)) // len
|
|
if kind == embedBytes {
|
|
duintptr(sym, off, uint64(size)) // cap for slice
|
|
}
|
|
|
|
case embedFiles:
|
|
slicedata := base.Ctxt.Lookup(`"".` + v.Sym.Name + `.files`)
|
|
off := 0
|
|
// []files pointed at by Files
|
|
off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice
|
|
off = duintptr(slicedata, off, uint64(len(files)))
|
|
off = duintptr(slicedata, off, uint64(len(files)))
|
|
|
|
// embed/embed.go type file is:
|
|
// name string
|
|
// data string
|
|
// hash [16]byte
|
|
// Emit one of these per file in the set.
|
|
const hashSize = 16
|
|
hash := make([]byte, hashSize)
|
|
for _, file := range files {
|
|
off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string
|
|
off = duintptr(slicedata, off, uint64(len(file)))
|
|
if strings.HasSuffix(file, "/") {
|
|
// entry for directory - no data
|
|
off = duintptr(slicedata, off, 0)
|
|
off = duintptr(slicedata, off, 0)
|
|
off += hashSize
|
|
} else {
|
|
fsym, size, err := fileStringSym(v.Pos, base.Flag.Cfg.Embed.Files[file], true, hash)
|
|
if err != nil {
|
|
base.ErrorfAt(v.Pos, "embed %s: %v", file, err)
|
|
}
|
|
off = dsymptr(slicedata, off, fsym, 0) // data string
|
|
off = duintptr(slicedata, off, uint64(size))
|
|
off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash))
|
|
}
|
|
}
|
|
ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL)
|
|
sym := v.Sym.Linksym()
|
|
dsymptr(sym, 0, slicedata, 0)
|
|
}
|
|
}
|