github.com/goplus/igop@v0.25.0/load/embed_go116.go (about)

     1  //go:build go1.16
     2  // +build go1.16
     3  
     4  /*
     5   * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package load
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"go/ast"
    26  	"go/build"
    27  	"go/parser"
    28  	"go/token"
    29  	"strconv"
    30  	_ "unsafe"
    31  
    32  	"github.com/visualfc/goembed"
    33  	embedparser "github.com/visualfc/goembed/parser"
    34  )
    35  
    36  func buildIdent(name string) string {
    37  	return fmt.Sprintf("__igop_embed_%x__", name)
    38  }
    39  
    40  var embed_head = `package %v
    41  
    42  import (
    43  	"embed"
    44  	"unsafe"
    45  )
    46  
    47  func __igop_embed_buildFS__(list []struct {
    48  	name string
    49  	data string
    50  	hash [16]byte
    51  }) (f embed.FS) {
    52  	fs := struct {
    53  		files *[]struct {
    54  			name string
    55  			data string
    56  			hash [16]byte
    57  		}
    58  	}{&list}
    59  	return *(*embed.FS)(unsafe.Pointer(&fs))
    60  }
    61  `
    62  
    63  // Embed check package embed data
    64  func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) (*ast.File, error) {
    65  	var pkgName string
    66  	var err error
    67  	var ems []*goembed.Embed
    68  	if xtest {
    69  		pkgName = bp.Name + "_test"
    70  		ems, err = goembed.CheckEmbed(bp.XTestEmbedPatternPos, fset, files)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  	} else {
    75  		pkgName = bp.Name
    76  		ems, err = goembed.CheckEmbed(bp.EmbedPatternPos, fset, files)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		if test {
    81  			tems, err := goembed.CheckEmbed(bp.TestEmbedPatternPos, fset, files)
    82  			if err != nil {
    83  				return nil, err
    84  			}
    85  			if len(tems) > 0 {
    86  				ems = append(ems, tems...)
    87  			}
    88  		}
    89  	}
    90  	if len(ems) == 0 {
    91  		return nil, nil
    92  	}
    93  	r := goembed.NewResolve()
    94  	for _, v := range ems {
    95  		fs, err := r.Load(bp.Dir, fset, v)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		switch v.Kind {
   100  		case goembed.EmbedMaybeAlias:
   101  			// value = Type(data)
   102  			// valid alias string or []byte type used by types.check
   103  			v.Spec.Values = []ast.Expr{
   104  				&ast.CallExpr{
   105  					Fun: v.Spec.Type,
   106  					Args: []ast.Expr{
   107  						&ast.Ident{Name: buildIdent(fs[0].Name),
   108  							NamePos: v.Spec.Names[0].NamePos},
   109  					},
   110  				}}
   111  		case goembed.EmbedBytes:
   112  			// value = []byte(data)
   113  			v.Spec.Values = []ast.Expr{
   114  				&ast.CallExpr{
   115  					Fun:  v.Spec.Type,
   116  					Args: []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))},
   117  				}}
   118  		case goembed.EmbedString:
   119  			// value = data
   120  			v.Spec.Values = []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))}
   121  		case goembed.EmbedFiles:
   122  			// value = __igop_embed_buildFS__([]struct{name string; data string; hash [16]byte}{...})
   123  			fs = goembed.BuildFS(fs)
   124  			elts := make([]ast.Expr, len(fs), len(fs))
   125  			for i, f := range fs {
   126  				if len(f.Data) == 0 {
   127  					elts[i] = &ast.CompositeLit{
   128  						Elts: []ast.Expr{
   129  							&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)},
   130  							&ast.BasicLit{Kind: token.STRING, Value: `""`},
   131  							&ast.CompositeLit{
   132  								Type: &ast.ArrayType{
   133  									Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
   134  									Elt: ast.NewIdent("byte"),
   135  								},
   136  							},
   137  						},
   138  					}
   139  				} else {
   140  					var hash [16]ast.Expr
   141  					for j, v := range f.Hash {
   142  						hash[j] = &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(v))}
   143  					}
   144  					elts[i] = &ast.CompositeLit{
   145  						Elts: []ast.Expr{
   146  							&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)},
   147  							ast.NewIdent(buildIdent(f.Name)),
   148  							&ast.CompositeLit{
   149  								Type: &ast.ArrayType{
   150  									Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
   151  									Elt: ast.NewIdent("byte"),
   152  								},
   153  								Elts: hash[:],
   154  							},
   155  						},
   156  					}
   157  				}
   158  			}
   159  			call := &ast.CallExpr{
   160  				Fun: ast.NewIdent("__igop_embed_buildFS__"),
   161  				Args: []ast.Expr{
   162  					&ast.CompositeLit{
   163  						Type: &ast.ArrayType{
   164  							Elt: &ast.StructType{
   165  								Fields: &ast.FieldList{
   166  									List: []*ast.Field{
   167  										&ast.Field{
   168  											Names: []*ast.Ident{ast.NewIdent("name")},
   169  											Type:  ast.NewIdent("string"),
   170  										},
   171  										&ast.Field{
   172  											Names: []*ast.Ident{ast.NewIdent("data")},
   173  											Type:  ast.NewIdent("string"),
   174  										},
   175  										&ast.Field{
   176  											Names: []*ast.Ident{ast.NewIdent("hash")},
   177  											Type: &ast.ArrayType{
   178  												Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
   179  												Elt: ast.NewIdent("byte"),
   180  											},
   181  										},
   182  									},
   183  								},
   184  							},
   185  						},
   186  						Elts: elts,
   187  					},
   188  				},
   189  			}
   190  			v.Spec.Values = []ast.Expr{call}
   191  		}
   192  	}
   193  	var buf bytes.Buffer
   194  	fmt.Fprintf(&buf, embed_head, pkgName)
   195  	buf.WriteString("\nconst (\n")
   196  	for _, f := range r.Files() {
   197  		if len(f.Data) == 0 {
   198  			fmt.Fprintf(&buf, "\t%v = \"\"\n", buildIdent(f.Name))
   199  		} else {
   200  			fmt.Fprintf(&buf, "\t%v = \"%v\"\n", buildIdent(f.Name), goembed.BytesToHex(f.Data))
   201  		}
   202  	}
   203  	buf.WriteString(")\n\n")
   204  	return parser.ParseFile(fset, "_igop_embed_data.go", buf.Bytes(), parser.ParseComments)
   205  }
   206  
   207  func EmbedFiles(pkgName string, dir string, fset *token.FileSet, files []*ast.File) (*ast.File, error) {
   208  	embed, err := embedparser.ParseEmbed(fset, files)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	if embed == nil {
   213  		return nil, nil
   214  	}
   215  	bp := &build.Package{
   216  		Name:            pkgName,
   217  		Dir:             dir,
   218  		EmbedPatterns:   embed.Patterns,
   219  		EmbedPatternPos: embed.PatternPos,
   220  	}
   221  	return Embed(bp, fset, files, false, false)
   222  }