blob: 4fddbff657b1e59d49fa589a80933690441d1706 [file] [log] [blame]
package gen
import (
"encoding"
"encoding/json"
"fmt"
"reflect"
"strings"
"unicode"
"github.com/mailru/easyjson"
)
// Target this byte size for initial slice allocation to reduce garbage collection.
const minSliceBytes = 64
func (g *Generator) getDecoderName(t reflect.Type) string {
return g.functionName("decode", t)
}
var primitiveDecoders = map[reflect.Kind]string{
reflect.String: "in.String()",
reflect.Bool: "in.Bool()",
reflect.Int: "in.Int()",
reflect.Int8: "in.Int8()",
reflect.Int16: "in.Int16()",
reflect.Int32: "in.Int32()",
reflect.Int64: "in.Int64()",
reflect.Uint: "in.Uint()",
reflect.Uint8: "in.Uint8()",
reflect.Uint16: "in.Uint16()",
reflect.Uint32: "in.Uint32()",
reflect.Uint64: "in.Uint64()",
reflect.Float32: "in.Float32()",
reflect.Float64: "in.Float64()",
}
var primitiveStringDecoders = map[reflect.Kind]string{
reflect.String: "in.String()",
reflect.Int: "in.IntStr()",
reflect.Int8: "in.Int8Str()",
reflect.Int16: "in.Int16Str()",
reflect.Int32: "in.Int32Str()",
reflect.Int64: "in.Int64Str()",
reflect.Uint: "in.UintStr()",
reflect.Uint8: "in.Uint8Str()",
reflect.Uint16: "in.Uint16Str()",
reflect.Uint32: "in.Uint32Str()",
reflect.Uint64: "in.Uint64Str()",
reflect.Uintptr: "in.UintptrStr()",
reflect.Float32: "in.Float32Str()",
reflect.Float64: "in.Float64Str()",
}
var customDecoders = map[string]string{
"json.Number": "in.JsonNumber()",
}
// genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t.
func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent)
unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(unmarshalerIface) {
fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)")
return nil
}
unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(unmarshalerIface) {
fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {")
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )")
fmt.Fprintln(g.out, ws+"}")
return nil
}
unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(unmarshalerIface) {
fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {")
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )")
fmt.Fprintln(g.out, ws+"}")
return nil
}
err := g.genTypeDecoderNoCheck(t, out, tags, indent)
return err
}
// returns true if the type t implements one of the custom unmarshaler interfaces
func hasCustomUnmarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem())
}
func hasUnknownsUnmarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsUnmarshaler)(nil)).Elem())
}
func hasUnknownsMarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsMarshaler)(nil)).Elem())
}
// genTypeDecoderNoCheck generates decoding code for the type t.
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent)
// Check whether type is primitive, needs to be done after interface check.
if dec := customDecoders[t.String()]; dec != "" {
fmt.Fprintln(g.out, ws+out+" = "+dec)
return nil
} else if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString {
if tags.intern && t.Kind() == reflect.String {
dec = "in.StringIntern()"
}
fmt.Fprintln(g.out, ws+out+" = "+g.getType(t)+"("+dec+")")
return nil
} else if dec := primitiveDecoders[t.Kind()]; dec != "" {
if tags.intern && t.Kind() == reflect.String {
dec = "in.StringIntern()"
}
if tags.noCopy && t.Kind() == reflect.String {
dec = "in.UnsafeString()"
}
fmt.Fprintln(g.out, ws+out+" = "+g.getType(t)+"("+dec+")")
return nil
}
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() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+"} else {")
if g.simpleBytes {
fmt.Fprintln(g.out, ws+" "+out+" = []byte(in.String())")
} else {
fmt.Fprintln(g.out, ws+" "+out+" = in.Bytes()")
}
fmt.Fprintln(g.out, ws+"}")
} else {
capacity := 1
if elem.Size() > 0 {
capacity = minSliceBytes / int(elem.Size())
}
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" in.Delim('[')")
fmt.Fprintln(g.out, ws+" if "+out+" == nil {")
fmt.Fprintln(g.out, ws+" if !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" "+out+" = make("+g.getType(t)+", 0, "+fmt.Sprint(capacity)+")")
fmt.Fprintln(g.out, ws+" } else {")
fmt.Fprintln(g.out, ws+" "+out+" = "+g.getType(t)+"{}")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" } else { ")
fmt.Fprintln(g.out, ws+" "+out+" = ("+out+")[:0]")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
if err := g.genTypeDecoder(elem, tmpVar, fieldTags{}, indent+2); err != nil {
return err
}
fmt.Fprintln(g.out, ws+" "+out+" = append("+out+", "+tmpVar+")")
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" in.Delim(']')")
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Array:
iterVar := g.uniqueVarName()
elem := t.Elem()
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" copy("+out+"[:], in.Bytes())")
fmt.Fprintln(g.out, ws+"}")
} else {
length := t.Len()
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" in.Delim('[')")
fmt.Fprintln(g.out, ws+" "+iterVar+" := 0")
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" if "+iterVar+" < "+fmt.Sprint(length)+" {")
if err := g.genTypeDecoder(elem, "("+out+")["+iterVar+"]", fieldTags{}, indent+3); err != nil {
return err
}
fmt.Fprintln(g.out, ws+" "+iterVar+"++")
fmt.Fprintln(g.out, ws+" } else {")
fmt.Fprintln(g.out, ws+" in.SkipRecursive()")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" in.Delim(']')")
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Struct:
dec := g.getDecoderName(t)
g.addType(t)
if len(out) > 0 && out[0] == '*' {
// NOTE: In order to remove an extra reference to a pointer
fmt.Fprintln(g.out, ws+dec+"(in, "+out[1:]+")")
} else {
fmt.Fprintln(g.out, ws+dec+"(in, &"+out+")")
}
case reflect.Ptr:
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" if "+out+" == nil {")
fmt.Fprintln(g.out, ws+" "+out+" = new("+g.getType(t.Elem())+")")
fmt.Fprintln(g.out, ws+" }")
if err := g.genTypeDecoder(t.Elem(), "*"+out, fieldTags{}, indent+1); err != nil {
return err
}
fmt.Fprintln(g.out, ws+"}")
case reflect.Map:
key := t.Key()
keyDec, ok := primitiveStringDecoders[key.Kind()]
if !ok && !hasCustomUnmarshaler(key) {
return fmt.Errorf("map type %v not supported: only string and integer keys and types implementing json.Unmarshaler are allowed", key)
} // else assume the caller knows what they are doing and that the custom unmarshaler performs the translation from string or integer keys to the key type
elem := t.Elem()
tmpVar := g.uniqueVarName()
keepEmpty := tags.required || tags.noOmitEmpty || (!g.omitEmpty && !tags.omitEmpty)
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" in.Delim('{')")
if !keepEmpty {
fmt.Fprintln(g.out, ws+" if !in.IsDelim('}') {")
}
fmt.Fprintln(g.out, ws+" "+out+" = make("+g.getType(t)+")")
if !keepEmpty {
fmt.Fprintln(g.out, ws+" } else {")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+" }")
}
fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {")
// NOTE: extra check for TextUnmarshaler. It overrides default methods.
if reflect.PtrTo(key).Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()) {
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {")
fmt.Fprintln(g.out, ws+" in.AddError(key.UnmarshalText(data) )")
fmt.Fprintln(g.out, ws+"}")
} else if keyDec != "" {
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
} else {
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
if err := g.genTypeDecoder(key, "key", fieldTags{}, indent+2); err != nil {
return err
}
}
fmt.Fprintln(g.out, ws+" in.WantColon()")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
if err := g.genTypeDecoder(elem, tmpVar, fieldTags{}, indent+2); err != nil {
return err
}
fmt.Fprintln(g.out, ws+" ("+out+")[key] = "+tmpVar)
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" in.Delim('}')")
fmt.Fprintln(g.out, ws+"}")
case reflect.Interface:
fmt.Printf("//%v: %v", out, g.interfaceIsEasyjsonUnmarshaller(t))
if t.NumMethod() != 0 {
if g.interfaceIsEasyjsonUnmarshaller(t) {
fmt.Fprintln(g.out, ws+out+".UnmarshalEasyJSON(in)")
} else if g.interfaceIsJsonUnmarshaller(t) {
fmt.Fprintln(g.out, ws+out+".UnmarshalJSON(in.Raw())")
} else {
return fmt.Errorf("interface type %v not supported: only interface{} and easyjson/json Unmarshaler are allowed", t)
}
} else {
fmt.Fprintln(g.out, ws+"if m, ok := "+out+".(easyjson.Unmarshaler); ok {")
fmt.Fprintln(g.out, ws+"m.UnmarshalEasyJSON(in)")
fmt.Fprintln(g.out, ws+"} else if m, ok := "+out+".(json.Unmarshaler); ok {")
fmt.Fprintln(g.out, ws+"_ = m.UnmarshalJSON(in.Raw())")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" "+out+" = in.Interface()")
fmt.Fprintln(g.out, ws+"}")
}
default:
return fmt.Errorf("don't know how to decode %v", t)
}
return nil
}
func (g *Generator) interfaceIsEasyjsonUnmarshaller(t reflect.Type) bool {
return t.Implements(reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem())
}
func (g *Generator) interfaceIsJsonUnmarshaller(t reflect.Type) bool {
return t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem())
}
func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) error {
jsonName := g.fieldNamer.GetJSONFieldName(t, f)
tags := parseFieldTags(f)
if tags.omit {
return nil
}
fmt.Fprintf(g.out, " case %q:\n", jsonName)
if err := g.genTypeDecoder(f.Type, "out."+f.Name, tags, 3); err != nil {
return err
}
if tags.required {
fmt.Fprintf(g.out, "%sSet = true\n", f.Name)
}
return nil
}
func (g *Generator) genRequiredFieldSet(t reflect.Type, f reflect.StructField) {
tags := parseFieldTags(f)
if !tags.required {
return
}
fmt.Fprintf(g.out, "var %sSet bool\n", f.Name)
}
func (g *Generator) genRequiredFieldCheck(t reflect.Type, f reflect.StructField) {
jsonName := g.fieldNamer.GetJSONFieldName(t, f)
tags := parseFieldTags(f)
if !tags.required {
return
}
g.imports["fmt"] = "fmt"
fmt.Fprintf(g.out, "if !%sSet {\n", f.Name)
fmt.Fprintf(g.out, " in.AddError(fmt.Errorf(\"key '%s' is required\"))\n", jsonName)
fmt.Fprintf(g.out, "}\n")
}
func mergeStructFields(fields1, fields2 []reflect.StructField) (fields []reflect.StructField) {
used := map[string]bool{}
for _, f := range fields2 {
used[f.Name] = true
fields = append(fields, f)
}
for _, f := range fields1 {
if !used[f.Name] {
fields = append(fields, f)
}
}
return
}
func getStructFields(t reflect.Type) ([]reflect.StructField, error) {
if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("got %v; expected a struct", t)
}
var efields []reflect.StructField
var fields []reflect.StructField
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tags := parseFieldTags(f)
if !f.Anonymous || tags.name != "" {
continue
}
t1 := f.Type
if t1.Kind() == reflect.Ptr {
t1 = t1.Elem()
}
if t1.Kind() == reflect.Struct {
fs, err := getStructFields(t1)
if err != nil {
return nil, fmt.Errorf("error processing embedded field: %v", err)
}
efields = mergeStructFields(efields, fs)
} else if (t1.Kind() >= reflect.Bool && t1.Kind() < reflect.Complex128) || t1.Kind() == reflect.String {
if strings.Contains(f.Name, ".") || unicode.IsUpper([]rune(f.Name)[0]) {
fields = append(fields, f)
}
}
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tags := parseFieldTags(f)
if f.Anonymous && tags.name == "" {
continue
}
c := []rune(f.Name)[0]
if unicode.IsUpper(c) {
fields = append(fields, f)
}
}
return mergeStructFields(efields, fields), nil
}
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)
}
}
func (g *Generator) genSliceArrayDecoder(t reflect.Type) error {
switch t.Kind() {
case reflect.Slice, reflect.Array, reflect.Map:
default:
return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice/array/map type", t)
}
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)
if err != nil {
return err
}
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, "}")
return nil
}
func (g *Generator) genStructDecoder(t reflect.Type) error {
if t.Kind() != reflect.Struct {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t)
}
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()")
fmt.Fprintln(g.out, " if in.IsNull() {")
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.Skip()")
fmt.Fprintln(g.out, " return")
fmt.Fprintln(g.out, " }")
// Init embedded pointer fields.
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if !f.Anonymous || f.Type.Kind() != reflect.Ptr {
continue
}
fmt.Fprintln(g.out, " out."+f.Name+" = new("+g.getType(f.Type.Elem())+")")
}
fs, err := getStructFields(t)
if err != nil {
return fmt.Errorf("cannot generate decoder for %v: %v", t, err)
}
for _, f := range fs {
g.genRequiredFieldSet(t, f)
}
fmt.Fprintln(g.out, " in.Delim('{')")
fmt.Fprintln(g.out, " for !in.IsDelim('}') {")
fmt.Fprintf(g.out, " key := in.UnsafeFieldName(%v)\n", g.skipMemberNameUnescaping)
fmt.Fprintln(g.out, " in.WantColon()")
fmt.Fprintln(g.out, " if in.IsNull() {")
fmt.Fprintln(g.out, " in.Skip()")
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " continue")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " switch key {")
for _, f := range fs {
if err := g.genStructFieldDecoder(t, f); err != nil {
return err
}
}
fmt.Fprintln(g.out, " default:")
if g.disallowUnknownFields {
fmt.Fprintln(g.out, ` in.AddError(&jlexer.LexerError{
Offset: in.GetPos(),
Reason: "unknown field",
Data: key,
})`)
} else if hasUnknownsUnmarshaler(t) {
fmt.Fprintln(g.out, " out.UnmarshalUnknown(in, key)")
} else {
fmt.Fprintln(g.out, " in.SkipRecursive()")
}
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.Delim('}')")
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
for _, f := range fs {
g.genRequiredFieldCheck(t, f)
}
fmt.Fprintln(g.out, "}")
return nil
}
func (g *Generator) genStructUnmarshaler(t reflect.Type) error {
switch t.Kind() {
case reflect.Slice, reflect.Array, reflect.Map, reflect.Struct:
default:
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice/array/map type", t)
}
fname := g.getDecoderName(t)
typ := g.getType(t)
if !g.noStdMarshalers {
fmt.Fprintln(g.out, "// UnmarshalJSON supports json.Unmarshaler interface")
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalJSON(data []byte) error {")
fmt.Fprintln(g.out, " r := jlexer.Lexer{Data: data}")
fmt.Fprintln(g.out, " "+fname+"(&r, v)")
fmt.Fprintln(g.out, " return r.Error()")
fmt.Fprintln(g.out, "}")
}
fmt.Fprintln(g.out, "// UnmarshalEasyJSON supports easyjson.Unmarshaler interface")
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalEasyJSON(l *jlexer.Lexer) {")
fmt.Fprintln(g.out, " "+fname+"(l, v)")
fmt.Fprintln(g.out, "}")
return nil
}