github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/build/embed.go (about) 1 package build 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/token" 9 "strconv" 10 11 "github.com/visualfc/goembed" 12 ) 13 14 func buildIdent(name string) string { 15 return fmt.Sprintf("__gopherjs_embed_%x__", name) 16 } 17 18 var embed_head = `package %v 19 20 import ( 21 "embed" 22 _ "unsafe" 23 ) 24 25 //go:linkname __gopherjs_embed_buildFS__ embed.buildFS 26 func __gopherjs_embed_buildFS__(list []struct { 27 name string 28 data string 29 hash [16]byte 30 }) (f embed.FS) 31 ` 32 33 // embedFiles generates an additional source file, which initializes all variables in the package with a go:embed directive. 34 func embedFiles(pkg *PackageData, fset *token.FileSet, files []*ast.File) (*ast.File, error) { 35 if len(pkg.EmbedPatternPos) == 0 { 36 return nil, nil 37 } 38 39 ems, err := goembed.CheckEmbed(pkg.EmbedPatternPos, fset, files) 40 if err != nil { 41 return nil, err 42 } 43 44 r := goembed.NewResolve() 45 for _, em := range ems { 46 fs, err := r.Load(pkg.Dir, fset, em) 47 if err != nil { 48 return nil, err 49 } 50 switch em.Kind { 51 case goembed.EmbedMaybeAlias: 52 // value = Type(data) 53 // valid alias string or []byte type used by types.check 54 em.Spec.Values = []ast.Expr{ 55 &ast.CallExpr{ 56 Fun: em.Spec.Type, 57 Args: []ast.Expr{ 58 &ast.Ident{ 59 Name: buildIdent(fs[0].Name), 60 NamePos: em.Spec.Names[0].NamePos, 61 }, 62 }, 63 }, 64 } 65 case goembed.EmbedBytes: 66 // value = []byte(data) 67 em.Spec.Values = []ast.Expr{ 68 &ast.CallExpr{ 69 Fun: em.Spec.Type, 70 Args: []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))}, 71 }, 72 } 73 case goembed.EmbedString: 74 // value = data 75 em.Spec.Values = []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))} 76 case goembed.EmbedFiles: 77 // value = __gopherjs_embed_buildFS__([]struct{name string; data string; hash [16]byte}{...}) 78 fs = goembed.BuildFS(fs) 79 elts := make([]ast.Expr, len(fs)) 80 for i, f := range fs { 81 if len(f.Data) == 0 { 82 elts[i] = &ast.CompositeLit{ 83 Elts: []ast.Expr{ 84 &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, 85 &ast.BasicLit{Kind: token.STRING, Value: `""`}, 86 &ast.CompositeLit{ 87 Type: &ast.ArrayType{ 88 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 89 Elt: ast.NewIdent("byte"), 90 }, 91 }, 92 }, 93 } 94 } else { 95 var hash [16]ast.Expr 96 for j, v := range f.Hash { 97 hash[j] = &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(v))} 98 } 99 elts[i] = &ast.CompositeLit{ 100 Elts: []ast.Expr{ 101 &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, 102 ast.NewIdent(buildIdent(f.Name)), 103 &ast.CompositeLit{ 104 Type: &ast.ArrayType{ 105 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 106 Elt: ast.NewIdent("byte"), 107 }, 108 Elts: hash[:], 109 }, 110 }, 111 } 112 } 113 } 114 call := &ast.CallExpr{ 115 Fun: ast.NewIdent("__gopherjs_embed_buildFS__"), 116 Args: []ast.Expr{ 117 &ast.CompositeLit{ 118 Type: &ast.ArrayType{ 119 Elt: &ast.StructType{ 120 Fields: &ast.FieldList{ 121 List: []*ast.Field{ 122 { 123 Names: []*ast.Ident{ast.NewIdent("name")}, 124 Type: ast.NewIdent("string"), 125 }, 126 { 127 Names: []*ast.Ident{ast.NewIdent("data")}, 128 Type: ast.NewIdent("string"), 129 }, 130 { 131 Names: []*ast.Ident{ast.NewIdent("hash")}, 132 Type: &ast.ArrayType{ 133 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 134 Elt: ast.NewIdent("byte"), 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 Elts: elts, 142 }, 143 }, 144 } 145 em.Spec.Values = []ast.Expr{call} 146 } 147 } 148 149 var buf bytes.Buffer 150 fmt.Fprintf(&buf, embed_head, pkg.Name) 151 buf.WriteString("\nconst (\n") 152 for _, f := range r.Files() { 153 if len(f.Data) == 0 { 154 fmt.Fprintf(&buf, "\t%v = \"\"\n", buildIdent(f.Name)) 155 } else { 156 fmt.Fprintf(&buf, "\t%v = \"%v\"\n", buildIdent(f.Name), goembed.BytesToHex(f.Data)) 157 } 158 } 159 buf.WriteString(")\n\n") 160 f, err := parser.ParseFile(fset, "js_embed.go", buf.String(), parser.ParseComments) 161 if err != nil { 162 return nil, err 163 } 164 return f, nil 165 } 166 167 func joinEmbedPatternPos(m1, m2 map[string][]token.Position) map[string][]token.Position { 168 if len(m1) == 0 && len(m2) == 0 { 169 return nil 170 } 171 m := make(map[string][]token.Position) 172 for k, v := range m1 { 173 m[k] = v 174 } 175 for k, v := range m2 { 176 m[k] = append(m[k], v...) 177 } 178 return m 179 }