| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //go:generate embed |
| |
| // Package generate has support for generating encode and decode methods |
| // for the binary package automatically. |
| package generate |
| |
| import ( |
| "bytes" |
| "fmt" |
| "path" |
| "sort" |
| "strings" |
| "text/template" |
| |
| "android.googlesource.com/platform/tools/gpu/binary" |
| "golang.org/x/tools/go/types" |
| ) |
| |
| type File struct { |
| Generated string |
| Package string |
| Structs []*Struct |
| } |
| |
| // Struct is a description of an encodable struct. |
| // Signature includes the package, name and name and type of all the fields. |
| // Any change to the Signature will cause the ID to change. |
| type Struct struct { |
| Name string // The simple name of the type. |
| Package string // The package name the struct belongs to. |
| Fields []Field // Descriptions of the fields of the struct. |
| Signature string // The full string type signature of the Struct. |
| ID binary.ID // The unique type identifier for the Struct. |
| Delegating bool // True if the struct is a pure delegating type. |
| } |
| |
| // Kind describes the basic nature of a type. |
| type Kind int |
| |
| const ( |
| // Native is the kind for primitive types with corresponding direct methods on |
| // Encoder and Decoder |
| Native Kind = iota |
| // Remap is the kind for a type declared as alias to a primitive type. |
| // For example: type U32 uint32. |
| Remap |
| // Codeable is the kind for a direct in place struct. |
| Codeable |
| // Pointer is the kind for a pointer to a struct type. If the struct instance |
| // has equality (==) with a previously encoded object, then this struct will |
| // be encoded as a reference to the first encoded object. |
| Pointer |
| // Array is the kind for an in place slice, with a dynamic length. |
| Array |
| // Interface is the kind for an object boxed in an binary.Object interface |
| // (or superset of). If the object has equality (==) with a previously |
| // encoded object, then this object will be encoded as a reference to the |
| // first encoded object. |
| Interface |
| ) |
| |
| // Field holds a description of a single Struct member. |
| type Field struct { |
| // Name is the true field name. |
| Name string // The name the field was given. |
| Type *Type // A description of the type of the field. |
| Anonymous bool // Whether the field was anonymous. |
| } |
| |
| // Type is used to describe fields of a struct. |
| type Type struct { |
| Name string // The name of the type. |
| Native string // The go native name of the type. |
| Kind Kind // The types basic Kind. |
| SubType *Type // If the type is an Array, holds the element type. |
| Method string // The encode/decode method to use. |
| } |
| |
| // FromTypename creates and initializes a Struct from a types.Typename. |
| // It assumes that the typename will map to a types.Struct, and adds all the |
| // fields of that struct to the Struct information. |
| func FromTypename(pkg *types.Package, n *types.TypeName) *Struct { |
| t := n.Type().Underlying().(*types.Struct) |
| s := &Struct{Name: n.Name()} |
| s.Fields = make([]Field, t.NumFields()) |
| s.Package = pkg.Name() |
| for i := range s.Fields { |
| decl := t.Field(i) |
| f := &s.Fields[i] |
| f.Name = decl.Name() |
| f.Type = FromType(pkg, decl.Type()) |
| f.Anonymous = decl.Anonymous() |
| } |
| s.UpdateID() |
| s.Delegating = len(s.Fields) == 1 && s.Fields[0].Anonymous |
| return s |
| } |
| |
| |
| // UpdateID recalculates the struct ID from the current signature. |
| func (s *Struct) UpdateID() { |
| b := &bytes.Buffer{} |
| fmt.Fprintf(b, "struct %s.%s {", s.Package, s.Name) |
| for i, f := range s.Fields { |
| if i != 0 { |
| fmt.Fprint(b, ",") |
| } |
| fmt.Fprintf(b, " %s:%s", f.Name, f.Type.Name) |
| } |
| fmt.Fprint(b, " }") |
| s.Signature = b.String() |
| s.ID = binary.NewID([]byte(s.Signature)) |
| } |
| |
| // FromType creates a appropriate Type object from a types.Type. |
| func FromType(pkg *types.Package, from types.Type) *Type { |
| t := &Type{Name: path.Base(types.TypeString(pkg, from))} |
| if _, isNamed := from.(*types.Named); isNamed { |
| from = from.Underlying() |
| } |
| t.Native = from.String() |
| switch from := from.(type) { |
| case *types.Basic: |
| t.Kind = Native |
| switch from.Kind() { |
| case types.Int: |
| t.Native = "int32" |
| case types.Byte: |
| t.Native = "uint8" |
| } |
| t.Method = strings.Title(t.Native) |
| if t.Native != t.Name { |
| t.Kind = Remap |
| } |
| case *types.Pointer: |
| t.Kind = Pointer |
| t.SubType = FromType(pkg, from.Elem()) |
| case *types.Interface: |
| t.Kind = Interface |
| case *types.Slice: |
| t.Kind = Array |
| t.SubType = FromType(pkg, from.Elem()) |
| default: |
| t.Kind = Codeable |
| } |
| return t |
| } |
| |
| // Sort is used to ensure stable ordering of Struct slices. |
| // This is to ensure automatically generated code has minimum diffs. |
| // The sort order is by Struct name. |
| func Sort(structs []*Struct) { |
| sort.Sort(structsByName(structs)) |
| } |
| |
| type structsByName []*Struct |
| |
| func (a structsByName) Len() int { return len(a) } |
| func (a structsByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
| func (a structsByName) Less(i, j int) bool { return a[i].Name < a[j].Name } |
| |
| func getTemplate(t *template.Template, name string) *template.Template { |
| result := t.Lookup(name) |
| if result == nil { |
| panic(fmt.Errorf("Could not find template %s", name)) |
| } |
| return result |
| } |
| |
| type kindToTemplate map[Kind]*template.Template |
| |
| func kindDispatch(table kindToTemplate, name string, t *Type) string { |
| b := &bytes.Buffer{} |
| if err := table[t.Kind].Execute(b, Field{name, t, false}); err != nil { |
| panic(err) |
| } |
| return b.String() |
| } |