1
2
3
4
5 package staticdata
6
7 import (
8 "path"
9 "sort"
10 "strings"
11
12 "cmd/compile/internal/base"
13 "cmd/compile/internal/ir"
14 "cmd/compile/internal/objw"
15 "cmd/compile/internal/types"
16 "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
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, "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, "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(), "invalid go:embed: multiple files for type %v", v.Type())
61 return nil
62 }
63 }
64
65 return list
66 }
67
68
69 func embedKind(typ *types.Type) int {
70 if typ.Sym() != nil && typ.Sym().Name == "FS" && (typ.Sym().Pkg.Path == "embed" || (typ.Sym().Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "embed")) {
71 return embedFiles
72 }
73 if typ.Kind() == types.TSTRING {
74 return embedString
75 }
76 if typ.Sym() == nil && 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
98
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
106
107 func WriteEmbed(v *ir.Name) {
108
109
110 commentPos := (*v.Embed)[0].Pos
111 if !types.AllowsGoVersion(types.LocalPkg, 1, 16) {
112 prevPos := base.Pos
113 base.Pos = commentPos
114 base.ErrorfVers("go1.16", "go:embed")
115 base.Pos = prevPos
116 return
117 }
118 if base.Flag.Cfg.Embed.Patterns == nil {
119 base.ErrorfAt(commentPos, "invalid go:embed: build system did not supply embed configuration")
120 return
121 }
122 kind := embedKind(v.Type())
123 if kind == embedUnknown {
124 base.ErrorfAt(v.Pos(), "go:embed cannot apply to var of type %v", v.Type())
125 return
126 }
127
128 files := embedFileList(v, kind)
129 switch kind {
130 case embedString, embedBytes:
131 file := files[0]
132 fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], kind == embedString, nil)
133 if err != nil {
134 base.ErrorfAt(v.Pos(), "embed %s: %v", file, err)
135 }
136 sym := v.Linksym()
137 off := 0
138 off = objw.SymPtr(sym, off, fsym, 0)
139 off = objw.Uintptr(sym, off, uint64(size))
140 if kind == embedBytes {
141 objw.Uintptr(sym, off, uint64(size))
142 }
143
144 case embedFiles:
145 slicedata := base.Ctxt.Lookup(`"".` + v.Sym().Name + `.files`)
146 off := 0
147
148 off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize)
149 off = objw.Uintptr(slicedata, off, uint64(len(files)))
150 off = objw.Uintptr(slicedata, off, uint64(len(files)))
151
152
153
154
155
156
157 const hashSize = 16
158 hash := make([]byte, hashSize)
159 for _, file := range files {
160 off = objw.SymPtr(slicedata, off, StringSym(v.Pos(), file), 0)
161 off = objw.Uintptr(slicedata, off, uint64(len(file)))
162 if strings.HasSuffix(file, "/") {
163
164 off = objw.Uintptr(slicedata, off, 0)
165 off = objw.Uintptr(slicedata, off, 0)
166 off += hashSize
167 } else {
168 fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], true, hash)
169 if err != nil {
170 base.ErrorfAt(v.Pos(), "embed %s: %v", file, err)
171 }
172 off = objw.SymPtr(slicedata, off, fsym, 0)
173 off = objw.Uintptr(slicedata, off, uint64(size))
174 off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash))
175 }
176 }
177 objw.Global(slicedata, int32(off), obj.RODATA|obj.LOCAL)
178 sym := v.Linksym()
179 objw.SymPtr(sym, 0, slicedata, 0)
180 }
181 }
182
View as plain text