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