File size: 4,938 Bytes
e36aeda | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | // 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 staticdata
import (
"path"
"sort"
"strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
)
const (
embedUnknown = iota
embedBytes
embedString
embedFiles
)
func embedFileList(v *ir.Name, kind int) []string {
// Build list of files to store.
have := make(map[string]bool)
var list []string
for _, e := range *v.Embed {
for _, pattern := range e.Patterns {
files, ok := base.Flag.Cfg.Embed.Patterns[pattern]
if !ok {
base.ErrorfAt(e.Pos, 0, "invalid go:embed: build system did not map pattern: %s", pattern)
}
for _, file := range files {
if base.Flag.Cfg.Embed.Files[file] == "" {
base.ErrorfAt(e.Pos, 0, "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 {
base.ErrorfAt(v.Pos(), 0, "invalid go:embed: multiple files for type %v", v.Type())
return nil
}
}
return list
}
// 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" {
return embedFiles
}
if typ.Kind() == types.TSTRING {
return embedString
}
if typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
return embedBytes
}
return embedUnknown
}
func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
name, isDir = strings.CutSuffix(name, "/")
i := strings.LastIndexByte(name, '/')
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
}
// WriteEmbed emits the init data for a //go:embed variable,
// which is either a string, a []byte, or an embed.FS.
func WriteEmbed(v *ir.Name) {
// TODO(mdempsky): User errors should be reported by the frontend.
commentPos := (*v.Embed)[0].Pos
if base.Flag.Cfg.Embed.Patterns == nil {
base.ErrorfAt(commentPos, 0, "invalid go:embed: build system did not supply embed configuration")
return
}
kind := embedKind(v.Type())
if kind == embedUnknown {
base.ErrorfAt(v.Pos(), 0, "go:embed cannot apply to var of type %v", v.Type())
return
}
files := embedFileList(v, kind)
if base.Errors() > 0 {
return
}
switch kind {
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(), 0, "embed %s: %v", file, err)
}
sym := v.Linksym()
off := 0
off = objw.SymPtr(sym, off, fsym, 0) // data string
off = objw.Uintptr(sym, off, uint64(size)) // len
if kind == embedBytes {
objw.Uintptr(sym, off, uint64(size)) // cap for slice
}
case embedFiles:
slicedata := v.Sym().Pkg.Lookup(v.Sym().Name + `.files`).Linksym()
off := 0
// []files pointed at by Files
off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize) // []file, pointing just past slice
off = objw.Uintptr(slicedata, off, uint64(len(files)))
off = objw.Uintptr(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 = objw.SymPtr(slicedata, off, StringSym(v.Pos(), file), 0) // file string
off = objw.Uintptr(slicedata, off, uint64(len(file)))
if strings.HasSuffix(file, "/") {
// entry for directory - no data
off = objw.Uintptr(slicedata, off, 0)
off = objw.Uintptr(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(), 0, "embed %s: %v", file, err)
}
off = objw.SymPtr(slicedata, off, fsym, 0) // data string
off = objw.Uintptr(slicedata, off, uint64(size))
off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash))
}
}
objw.Global(slicedata, int32(off), obj.RODATA|obj.LOCAL)
sym := v.Linksym()
objw.SymPtr(sym, 0, slicedata, 0)
}
}
|