easyjson生成代码工具的入口位于:
github.com/mailru/easyjson@v0.7.7/easyjson/main.go
代码语言:javascript复制func main() {
for _, fname := range files {
if err := generate(fname); err != nil {
代码语言:javascript复制func generate(fname string) (err error) {
fInfo, err := os.Stat(fname)
p := parser.Parser{AllStructs: *allStructs}
if err := p.Parse(fname, fInfo.IsDir()); err != nil {
outName = filepath.Join(fname, p.PkgName "_easyjson.go")
g := bootstrap.Generator{
BuildTags: trimmedBuildTags,
GenBuildFlags: trimmedGenBuildFlags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
LowerCamelCase: *lowerCamelCase,
NoStdMarshalers: *noStdMarshalers,
DisallowUnknownFields: *disallowUnknownFields,
SkipMemberNameUnescaping: *skipMemberNameUnescaping,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
NoFormat: *noformat,
SimpleBytes: *simpleBytes,
}
if err := g.Run(); err != nil {
它首先遍历go struct的抽象语法树,解析得到生成代码需要的struct的各种信息,然后结合输入选项,准备好各种参数后使用Run方法来生成代码。
代码语言:javascript复制var allStructs = flag.Bool("all", false, "generate marshaler/unmarshalers for all structs in a file")
golang struct 抽象语法树解析的代码位于:
github.com/mailru/easyjson@v0.7.7/parser/parser.go
代码语言:javascript复制type Parser struct {
PkgPath string
PkgName string
StructNames []string
AllStructs bool
}
重点看下它的Parse方法,和常用抽象语法树walker的思路一样,采用访问者模式提取有用信息:
代码语言:javascript复制func (p *Parser) Parse(fname string, isDir bool) error {
if p.PkgPath, err = getPkgPath(fname, isDir); err != nil {
fset := token.NewFileSet()
packages, err := parser.ParseDir(fset, fname, excludeTestFiles, parser.ParseComments)
for _, pckg := range packages {
ast.Walk(&visitor{Parser: p}, pckg)
访问者定义如下:
代码语言:javascript复制type visitor struct {
*Parser
name string
}
它的核心方法是Visit方法:可以看到,它除了解析需要的包的信息外,多数逻辑都是通过遍历语法树的各个节点,提取struct的field的各种信息。
代码语言:javascript复制func (v *visitor) Visit(n ast.Node) (w ast.Visitor) {
switch n := n.(type) {
case *ast.Package:
return v
case *ast.File:
v.PkgName = n.Name.String()
return v
case *ast.GenDecl:
skip, explicit := v.needType(n.Doc)
if skip || explicit {
for _, nc := range n.Specs {
switch nct := nc.(type) {
case *ast.TypeSpec:
nct.Doc = n.Doc
}
}
}
return v
case *ast.TypeSpec:
skip, explicit := v.needType(n.Doc)
if skip {
return nil
}
if !explicit && !v.AllStructs {
return nil
}
v.name = n.Name.String()
// Allow to specify non-structs explicitly independent of '-all' flag.
if explicit {
v.StructNames = append(v.StructNames, v.name)
return nil
}
return v
case *ast.StructType:
v.StructNames = append(v.StructNames, v.name)
return nil
}
return nil
}
解析完抽象语法树后的信息放在Generator里面,代码位于:
github.com/mailru/easyjson@v0.7.7/bootstrap/bootstrap.go
代码语言:javascript复制type Generator struct {
PkgPath, PkgName string
Types []string
NoStdMarshalers bool
SnakeCase bool
LowerCamelCase bool
OmitEmpty bool
DisallowUnknownFields bool
SkipMemberNameUnescaping bool
OutName string
BuildTags string
GenBuildFlags string
StubsOnly bool
LeaveTemps bool
NoFormat bool
SimpleBytes bool
}
Run方法完成代码的生成:对应每个类型先生成对应的简单的桩代码,用于后面生成详细代码做准备,只包含几个函数定义。然后生成中间代码,最后通过go命令,运行中间代码生成目标代码。
代码语言:javascript复制 func (g *Generator) Run() error {
if err := g.writeStub(); err != nil {
path, err := g.writeMain()
f, err := os.Create(g.OutName ".tmp")
execArgs := []string{"run"}
execArgs = append(execArgs, "-tags", g.BuildTags, filepath.Base(path))
cmd := exec.Command("go", execArgs...)
if err = cmd.Run(); err != nil {
out, err := format.Source(in)
return ioutil.WriteFile(g.OutName, out, 0644)
代码语言:javascript复制func (g *Generator) writeStub() error {
for _, t := range g.Types {
fmt.Fprintln(f, "func (", t, ") MarshalEasyJSON(w *jwriter.Writer) {}")
fmt.Fprintln(f, "func (*", t, ") UnmarshalEasyJSON(l *jlexer.Lexer) {}")
代码语言:javascript复制// writeMain creates a .go file that launches the generator if 'go run'.
func (g *Generator) writeMain() (path string, err error) {
f, err := ioutil.TempFile(filepath.Dir(g.OutName), "easyjson-bootstrap")
fmt.Fprintln(f, "func main() {")
fmt.Fprintf(f, " g := gen.NewGenerator(%q)n", filepath.Base(g.OutName))
fmt.Fprintf(f, " g.SetPkg(%q, %q)n", g.PkgName, g.PkgPath)
sort.Strings(g.Types)
for _, v := range g.Types {
fmt.Fprintln(f, " g.Add(pkg.EasyJSON_exporter_" v "(nil))")
}
fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {")
重点看下,中间writeMain代码,它生成生成用于生成最终代码的main函数。在main函数里初始化了gen.NewGenerator,并且设置相关包,然后调用里generator的Run方法。
代码语言:javascript复制const genPackage = "github.com/mailru/easyjson/gen"
const pkgWriter = "github.com/mailru/easyjson/jwriter"
const pkgLexer = "github.com/mailru/easyjson/jlexer"
对应的定义位于:github.com/mailru/easyjson@v0.7.7/gen/generator.go
代码语言:javascript复制func NewGenerator(filename string) *Generator {
ret := &Generator{
imports: map[string]string{
pkgWriter: "jwriter",
pkgLexer: "jlexer",
pkgEasyJSON: "easyjson",
"encoding/json": "json",
},
fieldNamer: DefaultFieldNamer{},
marshalers: make(map[reflect.Type]bool),
typesSeen: make(map[reflect.Type]bool),
functionNames: make(map[string]reflect.Type),
}
代码语言:javascript复制type Generator struct {
out *bytes.Buffer
pkgName string
pkgPath string
buildTags string
hashString string
varCounter int
noStdMarshalers bool
omitEmpty bool
disallowUnknownFields bool
fieldNamer FieldNamer
simpleBytes bool
skipMemberNameUnescaping bool
// package path to local alias map for tracking imports
imports map[string]string
// types that marshalers were requested for by user
marshalers map[reflect.Type]bool
// types that encoders were already generated for
typesSeen map[reflect.Type]bool
// types that encoders were requested for (e.g. by encoders of other types)
typesUnseen []reflect.Type
// function name to relevant type maps to track names of de-/encoders in
// case of a name clash or unnamed structs
functionNames map[string]reflect.Type
}
在Run函数里完成了需要的函数的生成,比如生成解码函数、编码函数等:
代码语言:javascript复制func (g *Generator) Run(out io.Writer) error {
if err := g.genDecoder(t); err != nil {
if err := g.genEncoder(t); err != nil {
if err := g.genStructMarshaler(t); err != nil {
if err := g.genStructUnmarshaler(t); err != nil {
g.printHeader()
解码函数定义在github.com/mailru/easyjson@v0.7.7/gen/decoder.go
它是根据反射得到具体的类型,然后根据类型来生成对应的代码。
代码语言:javascript复制func (g *Generator) genDecoder(t reflect.Type) error {
switch t.Kind() {
case reflect.Slice, reflect.Array, reflect.Map:
return g.genSliceArrayDecoder(t)
default:
return g.genStructDecoder(t)
}
}
剩下的就是代码字符串的拼接:
代码语言:javascript复制func (g *Generator) genSliceArrayDecoder(t reflect.Type) error {
fname := g.getDecoderName(t)
typ := g.getType(t)
fmt.Fprintln(g.out, "func " fname "(in *jlexer.Lexer, out *" typ ") {")
fmt.Fprintln(g.out, " isTopLevel := in.IsStart()")
err := g.genTypeDecoderNoCheck(t, "*out", fieldTags{}, 1)
生成解码函数的过程类似,也是基于反射:
代码语言:javascript复制func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
if dec := customDecoders[t.String()]; dec != "" {
fmt.Fprintln(g.out, ws out " = " dec)
switch t.Kind() {
case reflect.Slice:
tmpVar := g.uniqueVarName()
elem := t.Elem()
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws "if in.IsNull() {")
对于结构体,它需要生成每个field对应的编解码代码:
代码语言:javascript复制func (g *Generator) genStructDecoder(t reflect.Type) error {
fname := g.getDecoderName(t)
typ := g.getType(t)
fs, err := getStructFields(t)
for _, f := range fs {
g.genRequiredFieldSet(t, f)
}
代码语言:javascript复制func getStructFields(t reflect.Type) ([]reflect.StructField, error) {
for i := 0; i < t.NumField(); i {
f := t.Field(i)
tags := parseFieldTags(f)
在序列化的时候需要一个writer,它内部其实是一个Buffer
代码语言:javascript复制type Writer struct {
Flags Flags
Error error
Buffer buffer.Buffer
NoEscapeHTML bool
}
在反序列化的时候需要json得lexer,它的源码位于:
github.com/mailru/easyjson@v0.7.7/jlexer/lexer.go
代码语言:javascript复制type Lexer struct {
Data []byte // Input data given to the lexer.
start int // Start of the current token.
pos int // Current unscanned position in the input stream.
token token // Last scanned token, if token.kind != tokenUndef.
firstElement bool // Whether current element is the first in array or an object.
wantSep byte // A comma or a colon character, which need to occur before a token.
UseMultipleErrors bool // If we want to use multiple errors.
fatalError error // Fatal error occurred during lexing. It is usually a syntax error.
multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors.
}
它本质上是一个json反解析状态机。