github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/genproto/bindings.go (about)

     1  package genproto
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/printer"
     8  	"go/token"
     9  	"os"
    10  	"path"
    11  	"reflect"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/gnolang/gno/tm2/pkg/amino"
    17  	"github.com/gnolang/gno/tm2/pkg/amino/genproto/stringutil"
    18  	"github.com/gnolang/gno/tm2/pkg/amino/pkg"
    19  )
    20  
    21  const (
    22  	uint8Str = "uint8"
    23  )
    24  
    25  // Given genproto generated schema files for Go objects, generate
    26  // mappers to and from pb messages.  The purpose of this is to let Amino
    27  // use already-optimized probuf logic for serialization.
    28  func GenerateProtoBindingsForTypes(pkg *amino.Package, rtz ...reflect.Type) (file *ast.File, err error) {
    29  	// for TypeInfos.
    30  	cdc := amino.NewCodec()
    31  	cdc.RegisterPackage(pkg)
    32  
    33  	file = &ast.File{
    34  		Name:    _i(pkg.GoPkgName),
    35  		Decls:   nil,
    36  		Imports: nil,
    37  	}
    38  
    39  	// Generate Imports
    40  	scope := ast.NewScope(nil)
    41  	imports := _imports(
    42  		"proto", "google.golang.org/protobuf/proto",
    43  		"amino", "github.com/gnolang/gno/tm2/pkg/amino")
    44  	addImportAuto(imports, scope, pkg.GoPkgName+"pb", pkg.P3GoPkgPath)
    45  	file.Decls = append(file.Decls, imports)
    46  
    47  	// Generate Decls
    48  	for _, type_ := range rtz {
    49  		info, err := cdc.GetTypeInfo(type_)
    50  		if err != nil {
    51  			return file, err
    52  		}
    53  		if info.Type.Kind() == reflect.Interface {
    54  			continue
    55  		}
    56  
    57  		// Generate methods for each type.
    58  		methods, err := generateMethodsForType(imports, scope, pkg, info)
    59  		if err != nil {
    60  			return file, err
    61  		}
    62  		file.Decls = append(file.Decls, methods...)
    63  	}
    64  	return file, nil
    65  }
    66  
    67  // Writes in the same directory as the origin package.
    68  // Assumes pb imports in origGoPkgPath+"/pb".
    69  func WriteProtoBindings(pkg *amino.Package) {
    70  	filename := path.Join(pkg.DirName, "pbbindings.go")
    71  	fmt.Printf("writing proto3 bindings to %v for package %v\n", filename, pkg)
    72  	err := WriteProtoBindingsForTypes(filename, pkg, pkg.ReflectTypes()...)
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  }
    77  
    78  func WriteProtoBindingsForTypes(filename string, pkg *amino.Package, rtz ...reflect.Type) (err error) {
    79  	var buf bytes.Buffer
    80  	fset := token.NewFileSet()
    81  	var file *ast.File
    82  	file, err = GenerateProtoBindingsForTypes(pkg, rtz...)
    83  	if err != nil {
    84  		return
    85  	}
    86  	err = printer.Fprint(&buf, fset, file)
    87  	if err != nil {
    88  		return
    89  	}
    90  	err = os.WriteFile(filename, buf.Bytes(), 0o644)
    91  	if err != nil {
    92  		return
    93  	}
    94  	return
    95  }
    96  
    97  // modified imports if necessary.
    98  func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.Package, info *amino.TypeInfo) (methods []ast.Decl, err error) {
    99  	if info.Type.Kind() == reflect.Interface {
   100  		panic("should not happen")
   101  	}
   102  
   103  	pbote_ := p3goTypeExprString(pkg, imports, scope, info, amino.FieldOptions{})
   104  	if pbote_[0] != '*' {
   105  		panic("expected pointer kind for p3goTypeExprString (of registered type)")
   106  	}
   107  	dpbote_ := pbote_[1:]
   108  
   109  	// -----------
   110  	// ToPBMessage()
   111  	{
   112  		scope2 := ast.NewScope(scope)
   113  		addVars(scope2, "cdc", "goo", "pbo", "msg", "err")
   114  		// Set toProto function.
   115  		methods = append(methods, _func("ToPBMessage",
   116  			"goo", info.Type.Name(),
   117  			_fields("cdc", "*amino.Codec"),
   118  			_fields("msg", "proto.Message", "err", "error"),
   119  			_block(
   120  				// Body: declaration for pb message.
   121  				_var("pbo", _x(pbote_), nil),
   122  				// Body: copying over fields.
   123  				_block(go2pbStmts(pkg, true, imports, scope2, _i("pbo"), _i("goo"), false, info, amino.FieldOptions{}, 0)...),
   124  				// Body: return value.
   125  				_a("msg", "=", "pbo"),
   126  				_return(),
   127  			),
   128  		))
   129  	}
   130  
   131  	// -----------
   132  	// EmptyPBMessage()
   133  	// Use to create the pbm to proto.Unmarshal to before FromPBMessage.
   134  	{
   135  		scope2 := ast.NewScope(scope)
   136  		addVars(scope2, "cdc", "goo", "pbo", "msg", "err")
   137  		// Set toProto function.
   138  		methods = append(methods, _func("EmptyPBMessage",
   139  			"goo", info.Type.Name(),
   140  			_fields("cdc", "*amino.Codec"),
   141  			_fields("msg", "proto.Message"),
   142  			_block(
   143  				// Body: declaration for pb message.
   144  				_a("pbo", ":=", _x("new~(~%v~)", dpbote_)),
   145  				// Body: return value.
   146  				_a("msg", "=", "pbo"),
   147  				_return(),
   148  			),
   149  		))
   150  	}
   151  
   152  	// -----------
   153  	// FromPBMessage()
   154  	{
   155  		scope2 := ast.NewScope(scope)
   156  		addVars(scope2, "cdc", "goo", "pbo", "msg", "err")
   157  		methods = append(methods, _func("FromPBMessage",
   158  			"goo", "*"+info.Type.Name(),
   159  			_fields("cdc", "*amino.Codec", "msg", "proto.Message"),
   160  			_fields("err", "error"),
   161  			_block(
   162  				// Body: declaration for pb message.
   163  				_var("pbo", _x(pbote_),
   164  					_x("%v.~(~%v~)", "msg", pbote_)),
   165  				// Body: copying over fields.
   166  				_block(pb2goStmts(pkg, true, imports, scope2, _i("goo"), true, info, _i("pbo"), amino.FieldOptions{}, 0)...),
   167  				// Body: return.
   168  				_return(),
   169  			),
   170  		))
   171  	}
   172  
   173  	// -----------
   174  	// TypeUrl()
   175  	{
   176  		methods = append(methods, _func("GetTypeURL",
   177  			"", info.Type.Name(),
   178  			_fields(),
   179  			_fields("typeURL", "string"),
   180  			_block(
   181  				_return(_s(info.TypeURL)),
   182  			),
   183  		))
   184  	}
   185  
   186  	// -----------
   187  	// Is*ReprEmpty()
   188  	{
   189  		rinfo := info.ReprType
   190  		scope2 := ast.NewScope(scope)
   191  		addVars(scope2, "goo", "empty")
   192  		goorte := goTypeExpr(pkg, rinfo.Type, imports, scope2)
   193  		methods = append(methods, _func(fmt.Sprintf("Is%vReprEmpty", info.Name),
   194  			"", "",
   195  			_fields("goor", goorte),
   196  			_fields("empty", "bool"),
   197  			_block(
   198  				// Body: check fields.
   199  				_block(append(
   200  					[]ast.Stmt{_a("empty", "=", "true")},
   201  					isReprEmptyStmts(pkg, true, imports, scope2, _i("goor"), false, info)...,
   202  				)...),
   203  				// Body: return.
   204  				_return(),
   205  			),
   206  		))
   207  	}
   208  	return
   209  }
   210  
   211  // These don't have ToPBMessage functions.
   212  // TODO make this a property of the package?
   213  var noBindings = struct{}{}
   214  
   215  var noBindingsPkgs = map[string]struct{}{
   216  	"":     noBindings,
   217  	"time": noBindings,
   218  }
   219  
   220  func hasPBBindings(info *amino.TypeInfo) bool {
   221  	if info.Type.Kind() == reflect.Ptr {
   222  		return false
   223  	}
   224  	pkg := info.Package.GoPkgPath
   225  	_, ok := noBindingsPkgs[pkg]
   226  	return !ok
   227  }
   228  
   229  // END
   230  
   231  // isRoot: false for fields and list elements.
   232  // imports: global imports -- may be modified.
   233  // pbo: protobuf variable or field.
   234  // goo: native go variable or field.
   235  // gooIsPtr: whether goo is ptr.
   236  // gooType: type info for goo's type (elem type if pointer).
   237  // CONTRACT: pbo is assignable.
   238  //   - The general case is `_a(pbo, "=", goo)`
   239  //   - The struct case is like `_a(_sel(pbo, field.Name), "=", goo)`
   240  //
   241  // CONTRACT: for arrays and lists, memory must be allocated beforehand, but new
   242  // instances are created within this function.
   243  func go2pbStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, scope *ast.Scope, pbo ast.Expr, goo ast.Expr, gooIsPtr bool, gooType *amino.TypeInfo, fopts amino.FieldOptions, options uint64) (b []ast.Stmt) {
   244  	const (
   245  		option_bytes         = 0x01 // if goo's repr is uint8 as an element of bytes.
   246  		option_implicit_list = 0x02 // if goo is a repeated list & also an element.
   247  	)
   248  
   249  	// Special case if nil-pointer.
   250  	if gooIsPtr || gooType.Type.Kind() == reflect.Interface {
   251  		defer func() {
   252  			// Wrap penultimate b with if statement.
   253  			b = []ast.Stmt{_if(_b(goo, "!=", _i("nil")),
   254  				b...,
   255  			)}
   256  		}()
   257  	}
   258  	// Below, we can assume that goo isn't nil.
   259  
   260  	// External case.
   261  	// If gooType is registered and repr is struct, just call ToPBMessage.
   262  	// TODO If not registered?
   263  	if !isRoot &&
   264  		gooType.Registered && hasPBBindings(gooType) &&
   265  		gooType.ReprType.Type.Kind() == reflect.Struct &&
   266  		(options&option_bytes == 0) {
   267  		// Call ToPBMessage().
   268  		pbote_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   269  		pbom_ := addVarUniq(scope, "pbom")
   270  		b = append(b,
   271  			_a(pbom_, ":=", _x("proto.Message~(~nil~)")),
   272  			_a(pbom_, _i("err"), "=", _call(_sel(goo, "ToPBMessage"), _i("cdc"))),
   273  			_if(_x("err__!=__nil"),
   274  				_return(),
   275  			),
   276  			_a(pbo, "=", _x("%v.~(~%v~)", pbom_, pbote_)),
   277  		)
   278  		if gooIsPtr {
   279  			if pbote_[0] != '*' {
   280  				panic("expected pointer kind for p3goTypeExprString (of registered type)")
   281  			}
   282  			dpbote_ := pbote_[1:]
   283  			b = append(b,
   284  				_if(_b(pbo, "==", "nil"),
   285  					_a(pbo, "=", _x("new~(~%v~)", dpbote_))))
   286  		}
   287  		return
   288  	}
   289  
   290  	// Use *goor* for goo's repr.
   291  	var goor ast.Expr
   292  	var goorType *amino.TypeInfo
   293  
   294  	// Maybe wrap pbo.
   295  	// NOTE: Instead of writing code to determine the .Value type,
   296  	// just lazily construct before assigning to pbo.
   297  	var wrapImplicitStruct bool
   298  	maybeWrap := func(goor ast.Expr) ast.Expr {
   299  		if wrapImplicitStruct {
   300  			pbote_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   301  			if pbote_[0] != '*' {
   302  				panic("expected pointer kind for p3goTypeExprString (of type to be wrapped)")
   303  			}
   304  			dpbote_ := pbote_[1:]
   305  			return _ref(&ast.CompositeLit{
   306  				Type:       _x(dpbote_),
   307  				Elts:       []ast.Expr{_kv("Value", goor)},
   308  				Incomplete: false,
   309  			})
   310  		} else {
   311  			return goor
   312  		}
   313  	}
   314  
   315  	// Special case if IsAminoMarshaler.
   316  	if gooType.IsAminoMarshaler {
   317  		// First, derive repr instance.
   318  		goor_ := addVarUniq(scope, "goor")
   319  		err_ := addVarUniq(scope, "err") // do not shadow original err
   320  		b = append(b,
   321  			_a(goor_, err_, ":=", _call(_sel(goo, "MarshalAmino"))),
   322  			_if(_x("%v__!=__nil", err_),
   323  				_return(_x("nil"), _i(err_)),
   324  			),
   325  		)
   326  		// If isRoot and repr type isn't struct, an implicit struct is needed.
   327  		// If option_bytes, special case as we will encode as uint8.
   328  		if isRoot &&
   329  			gooType.ReprType.Type.Kind() != reflect.Struct &&
   330  			options&option_bytes == 0 {
   331  			if gooType.ReprType.Type.Kind() == reflect.Interface {
   332  				panic("not yet tested")
   333  			}
   334  			wrapImplicitStruct = true
   335  		}
   336  		// Assign *goor*.
   337  		goor = _i(goor_)
   338  		goorType = gooType.ReprType
   339  	} else {
   340  		// If isRoot and gooType isn't struct nor interface, an implicit
   341  		// struct wrapper is needed.
   342  		if isRoot &&
   343  			gooType.Type.Kind() != reflect.Struct &&
   344  			gooType.Type.Kind() != reflect.Interface {
   345  			wrapImplicitStruct = true
   346  		}
   347  		// Assign *goor*.
   348  		goor = goo
   349  		goorType = gooType
   350  		if gooIsPtr {
   351  			dgoor_ := addVarUniq(scope, "dgoor")
   352  			b = append(b,
   353  				_a(dgoor_, ":=", _deref(goor)),
   354  				_a(dgoor_, "=", dgoor_)) // XXX
   355  			goor = _i(dgoor_)
   356  		}
   357  	}
   358  	// Below, goor is dereferenced if goo is pointer..
   359  
   360  	// Special case, time & duration.
   361  	switch goorType.Type {
   362  	case timeType:
   363  		pkgName := addImportAuto(
   364  			imports, scope, "timestamppb", "google.golang.org/protobuf/types/known/timestamppb")
   365  		if gooIsPtr { // (non-nil)
   366  			b = append(b,
   367  				_a(pbo, "=", _call(_sel(_x(pkgName), "New"), goor)))
   368  		} else {
   369  			b = append(b,
   370  				_if(_not(_call(_x("amino.IsEmptyTime"), goor)),
   371  					_a(pbo, "=", _call(_sel(_x(pkgName), "New"), goor))))
   372  		}
   373  		return
   374  	case durationType:
   375  		pkgName := addImportAuto(
   376  			imports, scope, "durationpb", "google.golang.org/protobuf/types/known/durationpb")
   377  		if gooIsPtr { // (non-nil)
   378  			b = append(b,
   379  				_a(pbo, "=", _call(_sel(_x(pkgName), "New"), goor)))
   380  		} else {
   381  			b = append(b,
   382  				_if(_b(_call(_sel(goor, "Nanoseconds")), "!=", "0"),
   383  					_a(pbo, "=", _call(_sel(_x(pkgName), "New"), goor))))
   384  		}
   385  		return
   386  	}
   387  
   388  	// Special case, external empty types.
   389  	if gooType.Registered && hasPBBindings(gooType) {
   390  		if isRoot {
   391  			pbote_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   392  			pbov_ := addVarUniq(scope, "pbov")
   393  			b = append(b,
   394  				_if(_call(_x("Is%vReprEmpty", gooType.Name), goor),
   395  					_var(pbov_, _x(pbote_), nil),
   396  					_a("msg", "=", pbov_),
   397  					_return()))
   398  		} else if !gooIsPtr {
   399  			pkgPrefix := goPkgPrefix(rootPkg, gooType.Type, gooType, imports, scope)
   400  			// b switcharoo pattern
   401  			// statements after this pattern appended to b
   402  			// will come after the injected if-condition.
   403  			oldb := b
   404  			b = []ast.Stmt(nil)
   405  			defer func() {
   406  				newb := b // named for clarity
   407  				b = append(oldb,
   408  					_if(_not(_call(_x("%vIs%vReprEmpty", pkgPrefix, gooType.Name), goor)),
   409  						newb...))
   410  			}()
   411  			// end b switcharoo pattern
   412  		}
   413  	}
   414  
   415  	// General case
   416  	switch goork := goorType.Type.Kind(); goork {
   417  	case reflect.Interface:
   418  		typeUrl_ := addVarUniq(scope, "typeUrl")
   419  		bz_ := addVarUniq(scope, "bz")
   420  		anyte_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   421  		if anyte_[0] != '*' {
   422  			panic("expected pointer kind for p3goTypeExprString (of interface type)")
   423  		}
   424  		danyte_ := anyte_[1:]
   425  		b = append(b,
   426  			_a(typeUrl_, ":=", _call(_x("cdc.GetTypeURL"), goo)),
   427  			_a(bz_, ":=", "[]byte~(~nil~)"),
   428  			_a(bz_, "err", "=", _call(_x("cdc.Marshal"), goor)),
   429  			_if(_x("err__!=__nil"),
   430  				_return(),
   431  			),
   432  			_a(pbo, "=", _x("&%v~{~TypeUrl:typeUrl,Value:bz~}", danyte_)),
   433  		)
   434  
   435  	case reflect.Int:
   436  		b = append(b,
   437  			_a(pbo, "=", maybeWrap(_call(_i("int64"), goor))))
   438  	case reflect.Int16, reflect.Int8:
   439  		b = append(b,
   440  			_a(pbo, "=", maybeWrap(_call(_i("int32"), goor))))
   441  	case reflect.Uint:
   442  		b = append(b,
   443  			_a(pbo, "=", maybeWrap(_call(_i("uint64"), goor))))
   444  	case reflect.Uint16:
   445  		b = append(b,
   446  			_a(pbo, "=", maybeWrap(_call(_i("uint32"), goor))))
   447  	case reflect.Uint8:
   448  		if options&option_bytes == 0 {
   449  			b = append(b,
   450  				_a(pbo, "=", maybeWrap(_call(_i("uint32"), goor))))
   451  		} else {
   452  			b = append(b,
   453  				_a(pbo, "=", _call(_i("byte"), goor)))
   454  		}
   455  	case reflect.Array, reflect.Slice:
   456  		var newoptions uint64
   457  		gooreIsPtr := goorType.ElemIsPtr
   458  		gooreType := goorType.Elem
   459  		var dpbote_ string
   460  		pboIsImplicit := isImplicitList(goorType, fopts)
   461  		pboeIsImplicit := isImplicitList(gooreType, fopts)
   462  		pbote_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   463  		pboete_ := p3goTypeExprString(rootPkg, imports, scope, gooreType, fopts)
   464  
   465  		if gooreType.ReprType.Type.Kind() == reflect.Uint8 {
   466  			// Special bytes optimization for recursive case.
   467  			pboete_ = uint8Str
   468  			newoptions |= option_bytes
   469  		} else if pboeIsImplicit {
   470  			// Special implicit list struct for recursive call.
   471  			newoptions |= option_implicit_list
   472  		}
   473  
   474  		// Iff also option & option_implicit_list, wrap with implicit list struct.
   475  		if pboIsImplicit {
   476  			if pbote_[0] != '*' {
   477  				panic("expected pointer kind for p3goTypeExprString (of implicit list-struct type)")
   478  			}
   479  			dpbote_ = pbote_[1:]
   480  		} else {
   481  			dpbote_ = "XXX" // needed for _x() parsing regardless of _ctif condition.
   482  		}
   483  
   484  		// Construct, translate, assign.
   485  		goorl_ := addVarUniq(scope, "goorl")
   486  		pbos_ := addVarUniq(scope, "pbos")
   487  		scope2 := ast.NewScope(scope)
   488  		addVars(scope2, "i", "goore", "pbose")
   489  		b = append(b,
   490  			_a(goorl_, ":=", _len(goor)),
   491  			_ife(_x("%v__==__0", goorl_),
   492  				_block( // then
   493  					// Prefer nil for empty slices for less gc overhead.
   494  					_a(pbo, "=", _i("nil")),
   495  				),
   496  				_block( // else
   497  					_var(pbos_, nil, _x("make~(~[]%v,%v~)", pboete_, goorl_)),
   498  					_for(
   499  						_a("i", ":=", "0"),
   500  						_x("i__<__%v", goorl_),
   501  						_a("i", "+=", "1"),
   502  						_block(
   503  							// Translate in place.
   504  							_a("goore", ":=", _idx(goor, _i("i"))),
   505  							_block(go2pbStmts(rootPkg, false, imports, scope2, _x("%v~[~i~]", pbos_), _i("goore"), gooreIsPtr, gooreType, fopts, newoptions)...),
   506  						),
   507  					),
   508  					_ctif((pboIsImplicit && options&option_implicit_list != 0), // compile time if
   509  						_a(pbo, "=", _x("&%v~{~Value:%v~}", dpbote_, pbos_)), // then
   510  						_a(pbo, "=", maybeWrap(_i(pbos_))),                   // else
   511  					),
   512  				)))
   513  
   514  	case reflect.Struct:
   515  		pbote_ := p3goTypeExprString(rootPkg, imports, scope, gooType, fopts)
   516  		if pbote_[0] != '*' {
   517  			panic("expected pointer kind for p3goTypeExprString of struct type")
   518  		}
   519  		dpbote_ := pbote_[1:]
   520  
   521  		b = append(b,
   522  			_a(pbo, "=", _x("new~(~%v~)", dpbote_)))
   523  
   524  		for _, field := range goorType.Fields {
   525  			goorfIsPtr := field.IsPtr()
   526  			goorfType := field.TypeInfo
   527  			goorf := _sel(goor, field.Name) // next goo
   528  			// The protobuf field is lower_snake_case and protoc converts it to CamelCase.
   529  			pbof := _sel(pbo, CamelCase(stringutil.ToLowerSnakeCase(field.Name))) // next pbo
   530  
   531  			// Translate in place.
   532  			scope2 := ast.NewScope(scope)
   533  			b = append(b,
   534  				_block(go2pbStmts(rootPkg, false, imports, scope2, pbof, goorf, goorfIsPtr, goorfType, field.FieldOptions, 0)...),
   535  			)
   536  		}
   537  
   538  	default:
   539  		// General translation.
   540  		b = append(b,
   541  			_a(pbo, "=", maybeWrap(_call(_i(goork.String()), goor))))
   542  	}
   543  	return b
   544  }
   545  
   546  // package: the package for the concrete type for which we are generating go2pbStmts.
   547  // isRoot: false for fields and list elements.
   548  // imports: global imports -- used to look up package names.
   549  // goo: native go variable or field.
   550  // gooIsPtr: is goo a pointer?
   551  // gooType: type info for goo's ultimate type (elem if pointer)..
   552  // pbo: protobuf variable or field.
   553  // CONTRACT: goo is addressable.
   554  // CONTRACT: for arrays and lists, memory must be allocated beforehand, but new
   555  // instances are created within this function.
   556  func pb2goStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, scope *ast.Scope, goo ast.Expr, gooIsPtr bool, gooType *amino.TypeInfo, pbo ast.Expr, fopts amino.FieldOptions, options uint64) (b []ast.Stmt) {
   557  	const (
   558  		option_bytes = 0x01 // if goo's repr is uint8 as an element of bytes.
   559  		// option_implicit_list = 0x02 // if goo is a repeated list & also an element.
   560  	)
   561  
   562  	// Special case if pbo is a nil struct pointer (that isn't timestamp)
   563  	//
   564  	// We especially want this behavior (and optimization) for for
   565  	// amino.Marshalers, because of the construction cost.
   566  	switch gooType.ReprType.Type.Kind() {
   567  	case reflect.Struct:
   568  		if gooType.ReprType.Type != timeType {
   569  			defer func(pbo ast.Expr) {
   570  				// Wrap penultimate b with if statement.
   571  				b = []ast.Stmt{_if(_b(pbo, "!=", "nil"),
   572  					b...,
   573  				)}
   574  			}(pbo)
   575  		}
   576  	}
   577  	// Below, we can assume that pbo isn't a nil struct (that isn't timestamp).
   578  
   579  	if !isRoot {
   580  		// First we need to construct the goo.
   581  		// NOTE Unlike go2pb, due to the asymmetry of FromPBMessage/ToPBMessage,
   582  		// and MarshalAmino/UnmarshalAmino, both pairs which require that goo not
   583  		// be nil (so we must instantiate new() here).  On the other hand, go2pb's
   584  		// instantiation of corresponding pb objects depends on the kind, so it
   585  		// cannot be done before the switch cases like here.
   586  		if gooIsPtr {
   587  			dgoote_ := goTypeExprString(rootPkg, imports, scope, false, gooType)
   588  			b = append(b,
   589  				_a(goo, "=", _x("new~(~%v~)", dgoote_)))
   590  		}
   591  
   592  		// External case.
   593  		// If gooType is registered and repr is struct, just call FromPBMessage.
   594  		// TODO If not registered?
   595  		if gooType.Registered && hasPBBindings(gooType) &&
   596  			gooType.ReprType.Type.Kind() == reflect.Struct &&
   597  			(options&option_bytes == 0) {
   598  			b = append(b,
   599  				_a(_i("err"), "=", _call(_sel(goo, "FromPBMessage"), _i("cdc"), pbo)),
   600  				_if(_x("err__!=__nil"),
   601  					_return(),
   602  				),
   603  			)
   604  			return
   605  		}
   606  	}
   607  
   608  	// Use *goor* for goo's repr.
   609  	var goor ast.Expr
   610  	var goorType *amino.TypeInfo
   611  	var goorteFn func() string
   612  
   613  	// Maybe unwrap pbo.
   614  	var unwrapImplicitStruct bool
   615  	maybeUnwrap := func(pbo ast.Expr) ast.Expr {
   616  		if unwrapImplicitStruct {
   617  			return _sel(pbo, "Value")
   618  		} else {
   619  			return pbo
   620  		}
   621  	}
   622  
   623  	// Special case if IsAminoMarshaler.
   624  	// NOTE: doesn't matter whether goo is ptr or not.
   625  	if gooType.IsAminoMarshaler {
   626  		// First, construct new repr instance.
   627  		goorteFn = goTypeExprStringFn(rootPkg, imports, scope, false, gooType.ReprType)
   628  		goorte_ := goorteFn() // goorteFn maybe still needed later.
   629  		goor_ := addVarUniq(scope, "goor")
   630  		b = append(b,
   631  			_var(goor_, _x(goorte_), nil))
   632  		// After doing what we'd normally do w/ repr,
   633  		// unmarshal amino onto goo.
   634  		defer func() {
   635  			// Finally, unmarshal to goo.
   636  			b = append(b,
   637  				_a("err", "=", _call(_sel(goo, "UnmarshalAmino"), _i(goor_))),
   638  				_if(_x("err__!=__nil"),
   639  					_return(),
   640  				),
   641  			)
   642  		}()
   643  		// If isRoot and repr type isn't struct, an implicit struct is needed.
   644  		if isRoot &&
   645  			gooType.ReprType.Type.Kind() != reflect.Struct &&
   646  			options&option_bytes == 0 {
   647  			if gooType.ReprType.Type.Kind() == reflect.Interface {
   648  				panic("not yet tested")
   649  			}
   650  			unwrapImplicitStruct = true
   651  		}
   652  		// Assign *goor*
   653  		goor = _i(goor_)
   654  		goorType = gooType.ReprType
   655  		// goorte_ already set.
   656  	} else {
   657  		// If isRoot and gooType isn't struct nor interface, an implicit
   658  		// struct wrapper is needed.
   659  		if isRoot &&
   660  			gooType.Type.Kind() != reflect.Struct &&
   661  			gooType.Type.Kind() != reflect.Interface {
   662  			unwrapImplicitStruct = true
   663  		}
   664  		// Assign *goor*
   665  		goor = goo
   666  		if gooIsPtr {
   667  			goor = _deref(goo)
   668  		}
   669  		goorType = gooType
   670  		goorteFn = goTypeExprStringFn(rootPkg, imports, scope, false, gooType)
   671  	}
   672  
   673  	// Special case for time/duration.
   674  	switch goorType.Type {
   675  	case timeType:
   676  		b = append(b,
   677  			_a(goor, "=", _call(_sel(pbo, "AsTime"))))
   678  		return
   679  	case durationType:
   680  		b = append(b,
   681  			_a(goor, "=", _call(_sel(pbo, "AsDuration"))))
   682  		return
   683  	}
   684  
   685  	// General case
   686  	switch goorType.Type.Kind() {
   687  	case reflect.Interface:
   688  		typeUrl_ := addVarUniq(scope, "typeUrl")
   689  		bz_ := addVarUniq(scope, "bz")
   690  		goorp_ := addVarUniq(scope, "goorp")
   691  		b = append(b,
   692  			_a(typeUrl_, ":=", _sel(pbo, "TypeUrl")),
   693  			_a(bz_, ":=", _sel(pbo, "Value")),
   694  			_a(goorp_, ":=", _ref(goor)), // goor is addressable. NOTE &*a == a if a != nil.
   695  			_a("err", "=", _x("cdc.UnmarshalAny2~(~%v,%v,%v~)",
   696  				typeUrl_, bz_, goorp_)),
   697  			_if(_x("err__!=__nil"),
   698  				_return(),
   699  			),
   700  		)
   701  		// wrap b with if stmt.
   702  		b = []ast.Stmt{_if(_b(pbo, "!=", "nil"),
   703  			b...,
   704  		)}
   705  
   706  	case reflect.Int:
   707  		b = append(b,
   708  			_a(goor, "=", _call(_i(goorteFn()), _call(_i("int"), maybeUnwrap(pbo)))))
   709  	case reflect.Int16:
   710  		b = append(b,
   711  			_a(goor, "=", _call(_i(goorteFn()), _call(_i("int16"), maybeUnwrap(pbo)))))
   712  	case reflect.Int8:
   713  		b = append(b,
   714  			_a(goor, "=", _call(_i(goorteFn()), _call(_i("int8"), maybeUnwrap(pbo)))))
   715  	case reflect.Uint:
   716  		b = append(b,
   717  			_a(goor, "=", _call(_i(goorteFn()), _call(_i("uint"), maybeUnwrap(pbo)))))
   718  	case reflect.Uint16:
   719  		b = append(b,
   720  			_a(goor, "=", _call(_i(goorteFn()), _call(_i("uint16"), maybeUnwrap(pbo)))))
   721  	case reflect.Uint8:
   722  		b = append(b,
   723  			_a(goor, "=", _call(_i(goorteFn()), _call(_i(uint8Str), maybeUnwrap(pbo)))))
   724  
   725  	case reflect.Array:
   726  		var newoptions uint64
   727  		goorLen := goorType.Type.Len()
   728  		gooreType := goorType.Elem
   729  		gooreIsPtr := goorType.ElemIsPtr
   730  		goorete_ := goTypeExprString(rootPkg, imports, scope, gooreIsPtr, gooreType)
   731  		pboeIsImplicit := isImplicitList(gooreType, fopts)
   732  
   733  		// Special bytes optimization for recursive case.
   734  		if gooreType.ReprType.Type.Kind() == reflect.Uint8 {
   735  			newoptions |= option_bytes
   736  		}
   737  
   738  		// Construct, translate, assign.
   739  		goors_ := addVarUniq(scope, "goors")
   740  		scope2 := ast.NewScope(scope)
   741  		addVars(scope2, "i", "pboe", "pboev")
   742  		subStmts := pb2goStmts(rootPkg, false, imports, scope2, _x("%v~[~i~]", goors_), gooreIsPtr, gooreType, _i("pboev"), fopts, newoptions)
   743  		b = append(b,
   744  			_var(goors_, nil, _x("[%v]%v~{~~}", goorLen, goorete_)),
   745  			_for(
   746  				_a("i", ":=", "0"),
   747  				_x("i__<__%v", goorLen),
   748  				_a("i", "+=", "1"),
   749  				_block(
   750  					// Translate in place.
   751  					_a("pboe", ":=", _idx(maybeUnwrap(pbo), _i("i"))),
   752  					_ctif(pboeIsImplicit,
   753  						_if(_x("pboe__!=__nil"),
   754  							_block(append([]ast.Stmt{
   755  								_a("pboev", ":=", "pboe.Value"),
   756  							},
   757  								subStmts...)...),
   758  						),
   759  						_block(append([]ast.Stmt{
   760  							_a("pboev", ":=", "pboe"),
   761  						},
   762  							subStmts...)...),
   763  					),
   764  				),
   765  			),
   766  			_a(goor, "=", goors_),
   767  		)
   768  
   769  	case reflect.Slice:
   770  		var newoptions uint64
   771  		gooreType := goorType.Elem
   772  		gooreIsPtr := goorType.ElemIsPtr
   773  		goorete_ := goTypeExprString(rootPkg, imports, scope, gooreIsPtr, gooreType)
   774  		pboeIsImplicit := isImplicitList(gooreType, fopts)
   775  
   776  		// Special bytes optimization for recursive case.
   777  		if gooreType.ReprType.Type.Kind() == reflect.Uint8 {
   778  			newoptions |= option_bytes
   779  		}
   780  
   781  		// Construct, translate, assign.
   782  		pbol_ := addVarUniq(scope, "pbol")
   783  		goors_ := addVarUniq(scope, "goors")
   784  		scope2 := ast.NewScope(scope)
   785  		addVars(scope2, "i", "pboe", "pboev")
   786  		subStmts := pb2goStmts(rootPkg, false, imports, scope2, _x("%v~[~i~]", goors_), gooreIsPtr, gooreType, _i("pboev"), fopts, newoptions)
   787  		b = append(b,
   788  			_var(pbol_, _i("int"), _x("0")),
   789  			_if(_b(pbo, "!=", "nil"),
   790  				_a(pbol_, "=", _len(maybeUnwrap(pbo))),
   791  			),
   792  			_ife(_x("%v__==__0", pbol_),
   793  				_block( // then
   794  					// Prefer nil for empty slices for less gc overhead.
   795  					_a(goor, "=", _i("nil")),
   796  				),
   797  				_block( // else
   798  					_var(goors_, nil, _x("make~(~[]%v,%v~)", goorete_, pbol_)),
   799  					_for(
   800  						_a("i", ":=", "0"),
   801  						_x("i__<__%v", pbol_),
   802  						_a("i", "+=", "1"),
   803  						_block(
   804  							// Translate in place.
   805  							_a("pboe", ":=", _idx(maybeUnwrap(pbo), _i("i"))),
   806  							_ctif(pboeIsImplicit,
   807  								_if(_x("pboe__!=__nil"),
   808  									_block(append([]ast.Stmt{
   809  										_a("pboev", ":=", "pboe.Value"),
   810  									},
   811  										subStmts...)...)),
   812  								_block(append([]ast.Stmt{
   813  									_a("pboev", ":=", "pboe"),
   814  								},
   815  									subStmts...)...),
   816  							),
   817  						),
   818  					),
   819  					_a(goor, "=", goors_),
   820  				),
   821  			),
   822  		)
   823  
   824  	case reflect.Struct:
   825  		for _, field := range goorType.Fields {
   826  			// The protobuf field is lower_snake_case and protoc converts it to CamelCase.
   827  			pbof := _sel(pbo, CamelCase(stringutil.ToLowerSnakeCase(field.Name))) // next pbo.
   828  			goorfIsPtr := field.IsPtr()
   829  			goorfType := field.TypeInfo
   830  			goorf := _sel(goor, field.Name) // next goor.
   831  
   832  			// Translate in place.
   833  			scope2 := ast.NewScope(scope)
   834  			b = append(b,
   835  				_block(pb2goStmts(rootPkg, false, imports, scope2, goorf, goorfIsPtr, goorfType, pbof, field.FieldOptions, 0)...),
   836  			)
   837  		}
   838  
   839  	default:
   840  		// General translation.
   841  		b = append(b, _a(goor, "=", _call(_i(goorteFn()), maybeUnwrap(pbo))))
   842  	}
   843  	return b
   844  }
   845  
   846  func isReprEmptyStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, scope *ast.Scope, goo ast.Expr, gooIsPtr bool, gooType *amino.TypeInfo) (b []ast.Stmt) {
   847  	// Special case if non-nil struct-pointer.
   848  	// TODO: this could be precompiled and optimized (when !isRoot).
   849  	if gooIsPtr && gooType.ReprType.Type.Kind() == reflect.Struct {
   850  		b = []ast.Stmt{_if(_b(goo, "!=", _i("nil")),
   851  			_return(_i("false")),
   852  		)}
   853  		return
   854  	}
   855  
   856  	// Special case if nil-pointer.
   857  	if gooIsPtr || gooType.Type.Kind() == reflect.Interface {
   858  		defer func(goo ast.Expr) {
   859  			// Wrap penultimate b with if statement.
   860  			b = []ast.Stmt{_if(_b(goo, "!=", _i("nil")),
   861  				b...,
   862  			)}
   863  		}(goo)
   864  	}
   865  	// Below, we can assume that goo isn't nil.
   866  	// NOTE: just because it's not nil doesn't mean it's empty, specifically
   867  	// for time. Amino marshallers are empty iff nil.
   868  
   869  	// Use *goor* for goo's repr.
   870  	var goor ast.Expr
   871  	var goorType *amino.TypeInfo
   872  
   873  	// Special case if IsAminoMarshaler.
   874  	// NOTE: That this calls MarshalAminop due to the possible nested repr
   875  	// elements, means checking for empty will potentially be inefficient for
   876  	// complex structures, possibly doubling the encoding cost for some of
   877  	// these structures.  This is a proto3 issue where empty structs are encoded
   878  	// or not only depending on the nil-ness of a pointer to a struct.  Amino2
   879  	// doesn't need this restriction, if we are detaching from proto3.
   880  	if gooType.IsAminoMarshaler {
   881  		if isRoot {
   882  			// If isRoot, goo is already the repr type.
   883  			goor = goo
   884  			goorType = gooType.ReprType
   885  		} else {
   886  			// First, derive repr instance.
   887  			goor_ := addVarUniq(scope, "goor")
   888  			err_ := addVarUniq(scope, "err") // do not shadow original err
   889  			b = append(b,
   890  				_a(goor_, err_, ":=", _call(_sel(goo, "MarshalAmino"))),
   891  				_if(_x("%v__!=__nil", err_),
   892  					_return(_x("false")),
   893  				),
   894  			)
   895  			// Assign *goor*.
   896  			goor = _i(goor_)
   897  			goorType = gooType.ReprType
   898  		}
   899  	} else {
   900  		// Assign *goor*.
   901  		goor = goo
   902  		goorType = gooType
   903  		if gooIsPtr {
   904  			dgoor_ := addVarUniq(scope, "dgoor")
   905  			b = append(b,
   906  				_a(dgoor_, ":=", _deref(goor)),
   907  				_a(dgoor_, "=", dgoor_)) // XXX
   908  			goor = _i(dgoor_)
   909  		}
   910  	}
   911  	// Below, goor is dereferenced if goo is pointer.
   912  
   913  	// External case.
   914  	// If gooType is registered, just call is*ReprEmpty
   915  	// TODO If not registered?
   916  	if !isRoot && gooType.Registered && hasPBBindings(gooType) {
   917  		pkgPrefix := goPkgPrefix(rootPkg, gooType.Type, gooType, imports, scope)
   918  		e_ := addVarUniq(scope, "e")
   919  		b = append(b,
   920  			_a(e_, ":=", _call(_x("%vIs%vReprEmpty", pkgPrefix, gooType.Name), goor)),
   921  			_if(_x("%v__==__false", e_),
   922  				_return(_i("false")),
   923  			),
   924  		)
   925  		return
   926  	}
   927  
   928  	// General case
   929  	switch goorType.Type.Kind() {
   930  	case reflect.Interface:
   931  		b = append(b,
   932  			_return(_i("false")))
   933  
   934  	case reflect.Array, reflect.Slice:
   935  		b = append(b,
   936  			_if(_b(_len(goor), "!=", "0"),
   937  				_return(_i("false"))))
   938  
   939  	case reflect.Struct:
   940  		// Special case for time.  The default behavior is fine for time.Duration.
   941  		switch goorType.Type {
   942  		case timeType:
   943  			b = append(b,
   944  				_if(_not(_call(_x("amino.IsEmptyTime"), goor)),
   945  					_return(_x("false"))))
   946  			return
   947  		default:
   948  			for _, field := range goorType.Fields {
   949  				goorf := _sel(goor, field.Name) // next goo
   950  				goorfIsPtr := field.IsPtr()
   951  				goorfType := field.TypeInfo
   952  
   953  				// Translate in place.
   954  				scope2 := ast.NewScope(scope)
   955  				b = append(b,
   956  					_block(isReprEmptyStmts(rootPkg, false, imports, scope2, goorf, goorfIsPtr, goorfType)...),
   957  				)
   958  			}
   959  		}
   960  
   961  	default:
   962  		// General translation.
   963  		goorte := goTypeExprString(rootPkg, imports, scope, false, goorType)
   964  		b = append(b,
   965  			_if(_b(goor, "!=", _call(_x(goorte), defaultExpr(goorType.Type.Kind()))),
   966  				_return(_i("false"))))
   967  	}
   968  	return b
   969  }
   970  
   971  // ----------------------------------------
   972  // other....
   973  
   974  // Splits a Go expression into left and right parts.
   975  // Returns ok=false if not a binary operator.
   976  // Never panics.
   977  // If ok=true, left+op+right == expr.
   978  //
   979  // Examples:
   980  //   - "5 * 2":       left="5 ", op="*", right=" 2", ok=true
   981  //   - " 5*2 ":       left=" 5", op="*", right="2 ", ok=true
   982  //   - "1*2+ 3":      left="1*2", op="+", right=" 3", ok=true
   983  //   - "1*2+(3+ 4)":  left="1*2", op="+", right="(3+ 4)", ok=true
   984  //   - "'foo'+'bar'": left="'foo'", op="+", right="'bar'", ok=true
   985  //   - "'x'":         ok=false
   986  func chopBinary(expr string) (left, op, right string, ok bool) {
   987  	// XXX implementation redacted for CHALLENGE1.
   988  	// TODO restore implementation and replace '__'
   989  	parts := strings.Split(expr, "__")
   990  	if len(parts) != 3 {
   991  		return
   992  	}
   993  	left = parts[0]
   994  	op = parts[1]
   995  	right = parts[2]
   996  	ok = true
   997  	return
   998  }
   999  
  1000  // Given that 'in' ends with ')', '}', or ']',
  1001  // find the matching opener, while processing escape
  1002  // sequences of strings and rune literals.
  1003  // `tok` is the corresponding opening token.
  1004  // `right` excludes the last character (closer).
  1005  func chopRight(expr string) (left string, tok rune, right string) {
  1006  	// XXX implementation redacted for CHALLENGE1.
  1007  	// TODO restore implementation and replace '~'
  1008  	parts := strings.Split(expr, "~")
  1009  	if len(parts) != 4 {
  1010  		return
  1011  	}
  1012  	left = parts[0]
  1013  	tok = rune(parts[1][0])
  1014  	right = parts[2]
  1015  	// close = parts[3]
  1016  	return
  1017  }
  1018  
  1019  // ----------------------------------------
  1020  // AST Construction (Expr)
  1021  
  1022  func _i(name string) *ast.Ident {
  1023  	if name == "" {
  1024  		panic("unexpected empty identifier")
  1025  	}
  1026  	return &ast.Ident{Name: name}
  1027  }
  1028  
  1029  // recvTypeName is empty if there are no receivers.
  1030  // recvTypeName cannot contain any dots.
  1031  func _func(name string, recvRef string, recvTypeName string, params *ast.FieldList, results *ast.FieldList, b *ast.BlockStmt) *ast.FuncDecl {
  1032  	fn := &ast.FuncDecl{
  1033  		Name: _i(name),
  1034  		Type: &ast.FuncType{
  1035  			Params:  params,
  1036  			Results: results,
  1037  		},
  1038  		Body: b,
  1039  	}
  1040  	if recvRef == "" {
  1041  		recvRef = "_"
  1042  	}
  1043  	if recvTypeName != "" {
  1044  		fn.Recv = &ast.FieldList{
  1045  			List: []*ast.Field{
  1046  				{
  1047  					Names: []*ast.Ident{_i(recvRef)},
  1048  					Type:  _i(recvTypeName),
  1049  				},
  1050  			},
  1051  		}
  1052  	}
  1053  	return fn
  1054  }
  1055  
  1056  // Usage: _fields("a", "int", "b", "int32", ...) and so on.
  1057  // The name and types get parsed by _i()/_x() unless they are already ast.Expr.
  1058  // Identical type (instances or strings) are compressed into Names automatically.
  1059  // args must always be even in length.
  1060  func _fields(args ...interface{}) *ast.FieldList {
  1061  	list := []*ast.Field{}
  1062  	names := []*ast.Ident{}
  1063  	lasti := interface{}(nil)
  1064  	maybePop := func() {
  1065  		if len(names) > 0 {
  1066  			var last ast.Expr
  1067  			if lastte_, ok := lasti.(string); ok {
  1068  				last = _x(lastte_)
  1069  			} else {
  1070  				last = lasti.(ast.Expr)
  1071  			}
  1072  			list = append(list, &ast.Field{
  1073  				Names: names,
  1074  				Type:  last,
  1075  			})
  1076  			names = []*ast.Ident{}
  1077  		}
  1078  	}
  1079  	for i := 0; i < len(args); i++ {
  1080  		name, ok := args[i].(*ast.Ident)
  1081  		if !ok {
  1082  			name = _i(args[i].(string))
  1083  		}
  1084  		te_ := args[i+1]
  1085  		i += 1
  1086  		// NOTE: This comparison could be improved, to say, deep equality,
  1087  		// but is that the behavior we want?
  1088  		if lasti == te_ {
  1089  			names = append(names, name)
  1090  			continue
  1091  		} else {
  1092  			maybePop()
  1093  			names = append(names, name)
  1094  			lasti = te_
  1095  		}
  1096  	}
  1097  	maybePop()
  1098  	return &ast.FieldList{
  1099  		List: list,
  1100  	}
  1101  }
  1102  
  1103  // Parses simple expressions (but not all).
  1104  // Useful for parsing strings to ast nodes, like foo.bar["qwe"](),
  1105  // new(bytes.Buffer), *bytes.Buffer, package.MyStruct{FieldA:1}, numeric
  1106  //
  1107  //   - num/char (e.g. e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f')
  1108  //   - strings (e.g. "foo" or `\m\n\o`), nil, function calls
  1109  //   - square bracket indexing
  1110  //   - dot notation
  1111  //   - star expression for pointers
  1112  //   - struct construction
  1113  //   - nil
  1114  //   - type assertions, for EXPR.(EXPR) and also EXPR.(type)
  1115  //   - []type slice types
  1116  //   - [n]type array types
  1117  //   - &something referencing
  1118  //   - unary operations, namely
  1119  //     "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
  1120  //   - binary operations, namely
  1121  //     "||", "&&",
  1122  //     "==" | "!=" | "<" | "<=" | ">" | ">="
  1123  //     "+" | "-" | "|" | "^"
  1124  //     "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
  1125  //
  1126  // NOTE: This isn't trying to implement everything -- just what is
  1127  // intuitively elegant to implement.  Why don't we use a parser generator?
  1128  // Cuz I'm testing the hypothesis that for the scope of what we're trying
  1129  // to do here, given this language, that this is easier to understand and
  1130  // maintain than using advanced tooling.
  1131  func _x(expr string, args ...interface{}) ast.Expr {
  1132  	if expr == "" {
  1133  		panic("_x requires argument")
  1134  	}
  1135  	expr = fmt.Sprintf(expr, args...)
  1136  	expr = strings.TrimSpace(expr)
  1137  	first := expr[0]
  1138  
  1139  	// 1: Binary operators have a lower precedence than unary operators (or
  1140  	// monoids).
  1141  	left, op, right, ok := chopBinary(expr)
  1142  	if ok {
  1143  		return _b(_x(left), op, _x(right))
  1144  	}
  1145  
  1146  	// 2: Unary operators that depend on the first letter.
  1147  	switch first {
  1148  	case '*':
  1149  		return &ast.StarExpr{
  1150  			X: _x(expr[1:]),
  1151  		}
  1152  	case '+', '-', '!', '^', '&':
  1153  		return &ast.UnaryExpr{
  1154  			Op: _op(expr[:1]),
  1155  			X:  _x(expr[1:]),
  1156  		}
  1157  	case '<':
  1158  		second := expr[1] // is required.
  1159  		if second != '-' {
  1160  			panic("unparseable expression " + expr)
  1161  		}
  1162  		return &ast.UnaryExpr{
  1163  			Op: _op("<-"),
  1164  			X:  _x(expr[2:]),
  1165  		}
  1166  	}
  1167  
  1168  	// 3: Unary operators or literals that don't depend on the first letter,
  1169  	// and have some distinct suffix.
  1170  	if len(expr) > 1 {
  1171  		last := expr[len(expr)-1]
  1172  		switch last {
  1173  		case 'l':
  1174  			if expr == "nil" {
  1175  				return _i("nil")
  1176  			}
  1177  		case 'i':
  1178  			if '0' <= expr[0] && expr[0] <= '9' {
  1179  				num := _x(expr[:len(expr)-1]).(*ast.BasicLit)
  1180  				if num.Kind != token.INT && num.Kind != token.FLOAT {
  1181  					panic("expected int or float before 'i'")
  1182  				}
  1183  				num.Kind = token.IMAG
  1184  				return num
  1185  			}
  1186  		case '\'':
  1187  			if first != last {
  1188  				panic("unmatched quote")
  1189  			}
  1190  			return &ast.BasicLit{
  1191  				Kind:  token.CHAR,
  1192  				Value: expr[1 : len(expr)-1],
  1193  			}
  1194  		case '"', '`':
  1195  			if first != last {
  1196  				panic("unmatched quote")
  1197  			}
  1198  			return &ast.BasicLit{
  1199  				Kind:  token.STRING,
  1200  				Value: expr,
  1201  			}
  1202  		case ')':
  1203  			left, _, right := chopRight(expr)
  1204  			if left == "" {
  1205  				// Special case, not a function call.
  1206  				return _x(right)
  1207  			} else if left[len(left)-1] == '.' {
  1208  				// Special case, a type assert.
  1209  				var x, t ast.Expr = _x(left[:len(left)-1]), nil
  1210  				if right == "type" {
  1211  					t = nil
  1212  				} else {
  1213  					t = _x(right)
  1214  				}
  1215  				return &ast.TypeAssertExpr{
  1216  					X:    x,
  1217  					Type: t,
  1218  				}
  1219  			}
  1220  
  1221  			fn := _x(left)
  1222  			args := []ast.Expr{}
  1223  			parts := strings.Split(right, ",")
  1224  			for _, part := range parts {
  1225  				// NOTE: repeated commas have no effect,
  1226  				// nor do trailing commas.
  1227  				if len(part) > 0 {
  1228  					args = append(args, _x(part))
  1229  				}
  1230  			}
  1231  			return &ast.CallExpr{
  1232  				Fun:  fn,
  1233  				Args: args,
  1234  			}
  1235  		case '}':
  1236  			left, _, right := chopRight(expr)
  1237  			ty := _x(left)
  1238  			elts := []ast.Expr{}
  1239  			parts := strings.Split(right, ",")
  1240  			for _, part := range parts {
  1241  				if strings.TrimSpace(part) != "" {
  1242  					parts := strings.Split(part, ":")
  1243  					if len(parts) != 2 {
  1244  						panic("key:value requires 1 colon")
  1245  					}
  1246  					elts = append(elts, _kv(parts[0], parts[1]))
  1247  				}
  1248  			}
  1249  			return &ast.CompositeLit{
  1250  				Type:       ty,
  1251  				Elts:       elts,
  1252  				Incomplete: false,
  1253  			}
  1254  		case ']':
  1255  			left, _, right := chopRight(expr)
  1256  			return &ast.IndexExpr{
  1257  				X:     _x(left),
  1258  				Index: _x(right),
  1259  			}
  1260  		}
  1261  	}
  1262  	// 4.  Monoids of array or slice type.
  1263  	// NOTE: []foo.bar requires this to have lower precedence than dots.
  1264  	switch first {
  1265  	case '[':
  1266  		if expr[1] == ']' {
  1267  			return &ast.ArrayType{
  1268  				Len: nil,
  1269  				Elt: _x(expr[2:]),
  1270  			}
  1271  		} else {
  1272  			idx := strings.Index(expr, "]")
  1273  			if idx == -1 {
  1274  				panic(fmt.Sprintf(
  1275  					"mismatched '[' in slice expr %v",
  1276  					expr))
  1277  			}
  1278  			return &ast.ArrayType{
  1279  				Len: _x(expr[1:idx]),
  1280  				Elt: _x(expr[idx+1:]),
  1281  			}
  1282  		}
  1283  	}
  1284  	// Numeric int?  We do these before dots, because dots are legal in numbers.
  1285  	const (
  1286  		DGTS = `(?:[0-9]+)`
  1287  		HExX = `(?:0[xX][0-9a-fA-F]+)`
  1288  		PSCI = `(?:[eE]+?[0-9]+)`
  1289  		NSCI = `(?:[eE]-[1-9][0-9]+)`
  1290  		ASCI = `(?:[eE][-+]?[0-9]+)`
  1291  	)
  1292  	isInt, err := regexp.Match(
  1293  		`^-?(?:`+
  1294  			DGTS+`|`+
  1295  			HExX+`)`+PSCI+`?$`,
  1296  		[]byte(expr),
  1297  	)
  1298  	if err != nil {
  1299  		panic("should not happen")
  1300  	}
  1301  	if isInt {
  1302  		return &ast.BasicLit{
  1303  			Kind:  token.INT,
  1304  			Value: expr,
  1305  		}
  1306  	}
  1307  	// Numeric float?  We do these before dots, because dots are legal in floats.
  1308  	isFloat, err := regexp.Match(
  1309  		`^-?(?:`+
  1310  			DGTS+`\.`+DGTS+ASCI+`?|`+
  1311  			DGTS+NSCI+`)$`,
  1312  		[]byte(expr),
  1313  	)
  1314  	if err != nil {
  1315  		panic("should not happen")
  1316  	}
  1317  	if isFloat {
  1318  		return &ast.BasicLit{
  1319  			Kind:  token.FLOAT,
  1320  			Value: expr,
  1321  		}
  1322  	}
  1323  	// Last case, handle dots.
  1324  	// It's last, meaning it's got the highest precedence.
  1325  	if idx := strings.LastIndex(expr, "."); idx != -1 {
  1326  		return &ast.SelectorExpr{
  1327  			X:   _x(expr[:idx]),
  1328  			Sel: _i(expr[idx+1:]),
  1329  		}
  1330  	}
  1331  	return _i(expr)
  1332  }
  1333  
  1334  // Returns idx=-1 if not a binary operator.
  1335  // Precedence    Operator
  1336  //
  1337  //	5             *  /  %  <<  >>  &  &^
  1338  //	4             +  -  |  ^
  1339  //	3             ==  !=  <  <=  >  >=
  1340  //	2             &&
  1341  //	1             ||
  1342  func _kv(k, v interface{}) *ast.KeyValueExpr {
  1343  	var kx, vx ast.Expr
  1344  	if ks, ok := k.(string); ok {
  1345  		kx = _x(ks)
  1346  	} else {
  1347  		kx = k.(ast.Expr)
  1348  	}
  1349  	if vs, ok := v.(string); ok {
  1350  		vx = _x(vs)
  1351  	} else {
  1352  		vx = v.(ast.Expr)
  1353  	}
  1354  	return &ast.KeyValueExpr{
  1355  		Key:   kx,
  1356  		Value: vx,
  1357  	}
  1358  }
  1359  
  1360  func _block(b ...ast.Stmt) *ast.BlockStmt {
  1361  	return &ast.BlockStmt{
  1362  		List: b,
  1363  	}
  1364  }
  1365  
  1366  // Usage: _a(lhs1, lhs2, ..., ":=", rhs1, rhs2, ...)
  1367  // Token can be ":=", "=", "+=", etc.
  1368  // Other strings are automatically parsed as _x(arg).
  1369  func _a(args ...interface{}) *ast.AssignStmt {
  1370  	lhs := []ast.Expr(nil)
  1371  	tok := token.ILLEGAL
  1372  	rhs := []ast.Expr(nil)
  1373  
  1374  	setTok := func(t token.Token) {
  1375  		if tok != token.ILLEGAL {
  1376  			panic("too many assignment operators")
  1377  		}
  1378  		tok = t
  1379  	}
  1380  
  1381  	for _, arg := range args {
  1382  		if s, ok := arg.(string); ok {
  1383  			switch s {
  1384  			case "=", ":=", "+=", "-=", "*=", "/=", "%=",
  1385  				"&=", "|=", "^=", "<<=", ">>=", "&^=":
  1386  				setTok(_aop(s))
  1387  				continue
  1388  			default:
  1389  				arg = _x(s)
  1390  			}
  1391  		}
  1392  		// append to lhs or rhs depending on tok.
  1393  		if tok == token.ILLEGAL {
  1394  			lhs = append(lhs, arg.(ast.Expr))
  1395  		} else {
  1396  			rhs = append(rhs, arg.(ast.Expr))
  1397  		}
  1398  	}
  1399  
  1400  	return &ast.AssignStmt{
  1401  		Lhs: lhs,
  1402  		Tok: tok,
  1403  		Rhs: rhs,
  1404  	}
  1405  }
  1406  
  1407  func _not(x ast.Expr) *ast.UnaryExpr {
  1408  	return &ast.UnaryExpr{
  1409  		Op: _op("!"),
  1410  		X:  x,
  1411  	}
  1412  }
  1413  
  1414  // Binary expression.  x, y can be ast.Expr or string.
  1415  func _b(x interface{}, op string, y interface{}) ast.Expr {
  1416  	var xx, yx ast.Expr
  1417  	if xstr, ok := x.(string); ok {
  1418  		xx = _x(xstr)
  1419  	} else {
  1420  		xx = x.(ast.Expr)
  1421  	}
  1422  	if ystr, ok := y.(string); ok {
  1423  		yx = _x(ystr)
  1424  	} else {
  1425  		yx = y.(ast.Expr)
  1426  	}
  1427  	return &ast.BinaryExpr{
  1428  		X:  xx,
  1429  		Op: _op(op),
  1430  		Y:  yx,
  1431  	}
  1432  }
  1433  
  1434  func _call(fn ast.Expr, args ...ast.Expr) *ast.CallExpr {
  1435  	return &ast.CallExpr{
  1436  		Fun:  fn,
  1437  		Args: args,
  1438  	}
  1439  }
  1440  
  1441  func _sel(x ast.Expr, sel string) *ast.SelectorExpr {
  1442  	return &ast.SelectorExpr{
  1443  		X:   x,
  1444  		Sel: _i(sel),
  1445  	}
  1446  }
  1447  
  1448  func _idx(x ast.Expr, idx ast.Expr) *ast.IndexExpr {
  1449  	return &ast.IndexExpr{
  1450  		X:     x,
  1451  		Index: idx,
  1452  	}
  1453  }
  1454  
  1455  func _s(s string) *ast.BasicLit {
  1456  	return &ast.BasicLit{
  1457  		Kind:  token.STRING,
  1458  		Value: strconv.Quote(s),
  1459  	}
  1460  }
  1461  
  1462  func _ref(x ast.Expr) *ast.UnaryExpr {
  1463  	return &ast.UnaryExpr{
  1464  		Op: token.AND,
  1465  		X:  x,
  1466  	}
  1467  }
  1468  
  1469  func _deref(x ast.Expr) *ast.StarExpr {
  1470  	return &ast.StarExpr{
  1471  		X: x,
  1472  	}
  1473  }
  1474  
  1475  // NOTE: Same as _deref, but different contexts.
  1476  func _ptr(x ast.Expr) *ast.StarExpr {
  1477  	return &ast.StarExpr{
  1478  		X: x,
  1479  	}
  1480  }
  1481  
  1482  func _arr(l int, x ast.Expr) *ast.ArrayType {
  1483  	return &ast.ArrayType{
  1484  		Len: _x(fmt.Sprintf("%v", l)),
  1485  		Elt: x,
  1486  	}
  1487  }
  1488  
  1489  func _sl(x ast.Expr) *ast.ArrayType {
  1490  	return &ast.ArrayType{
  1491  		Len: nil,
  1492  		Elt: x,
  1493  	}
  1494  }
  1495  
  1496  // ----------------------------------------
  1497  // AST Construction (Stmt)
  1498  
  1499  func _if(cond ast.Expr, b ...ast.Stmt) *ast.IfStmt {
  1500  	return &ast.IfStmt{
  1501  		Cond: cond,
  1502  		Body: _block(b...),
  1503  	}
  1504  }
  1505  
  1506  func _ife(cond ast.Expr, bdy, els ast.Stmt) *ast.IfStmt {
  1507  	if _, ok := bdy.(*ast.BlockStmt); !ok {
  1508  		bdy = _block(bdy)
  1509  	}
  1510  	if _, ok := els.(*ast.BlockStmt); !ok {
  1511  		if _, ok := els.(*ast.IfStmt); !ok {
  1512  			els = _block(els)
  1513  		}
  1514  	}
  1515  	return &ast.IfStmt{
  1516  		Cond: cond,
  1517  		Body: bdy.(*ast.BlockStmt),
  1518  		Else: els,
  1519  	}
  1520  }
  1521  
  1522  func _return(results ...ast.Expr) *ast.ReturnStmt {
  1523  	return &ast.ReturnStmt{
  1524  		Results: results,
  1525  	}
  1526  }
  1527  
  1528  // even/odd args are paired,
  1529  // name1, path1, name2, path2, etc.
  1530  func _imports(nameAndPaths ...string) *ast.GenDecl {
  1531  	decl := &ast.GenDecl{
  1532  		Tok:   token.IMPORT,
  1533  		Specs: []ast.Spec{},
  1534  	}
  1535  	for i := 0; i < len(nameAndPaths); i += 2 {
  1536  		spec := &ast.ImportSpec{
  1537  			Name: _i(nameAndPaths[i]),
  1538  			Path: _s(nameAndPaths[i+1]),
  1539  		}
  1540  		decl.Specs = append(decl.Specs, spec)
  1541  	}
  1542  	return decl
  1543  }
  1544  
  1545  func _for(init ast.Stmt, cond ast.Expr, post ast.Stmt, b ...ast.Stmt) *ast.ForStmt {
  1546  	return &ast.ForStmt{
  1547  		Init: init,
  1548  		Cond: cond,
  1549  		Post: post,
  1550  		Body: _block(b...),
  1551  	}
  1552  }
  1553  
  1554  func _len(x ast.Expr) *ast.CallExpr {
  1555  	return _call(_i("len"), x)
  1556  }
  1557  
  1558  func _var(name string, type_ ast.Expr, value ast.Expr) *ast.DeclStmt {
  1559  	if value == nil {
  1560  		return &ast.DeclStmt{
  1561  			Decl: &ast.GenDecl{
  1562  				Tok: token.VAR,
  1563  				Specs: []ast.Spec{
  1564  					&ast.ValueSpec{
  1565  						Names:  []*ast.Ident{_i(name)},
  1566  						Type:   type_,
  1567  						Values: nil,
  1568  					},
  1569  				},
  1570  			},
  1571  		}
  1572  	} else {
  1573  		return &ast.DeclStmt{
  1574  			Decl: &ast.GenDecl{
  1575  				Tok: token.VAR,
  1576  				Specs: []ast.Spec{
  1577  					&ast.ValueSpec{
  1578  						Names:  []*ast.Ident{_i(name)},
  1579  						Type:   type_,
  1580  						Values: []ast.Expr{value},
  1581  					},
  1582  				},
  1583  			},
  1584  		}
  1585  	}
  1586  }
  1587  
  1588  func defaultExpr(k reflect.Kind) ast.Expr {
  1589  	switch k {
  1590  	case reflect.Interface, reflect.Ptr, reflect.Slice:
  1591  		return _x("nil")
  1592  	case reflect.String:
  1593  		return _x("\"\"")
  1594  	case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16,
  1595  		reflect.Int8, reflect.Uint, reflect.Uint64, reflect.Uint32,
  1596  		reflect.Uint16, reflect.Uint8:
  1597  		return _x("0")
  1598  	case reflect.Bool:
  1599  		return _x("false")
  1600  	default:
  1601  		panic(fmt.Sprintf("no fixed default expression for kind %v", k))
  1602  	}
  1603  }
  1604  
  1605  // binary and unary operators, excluding assignment operators.
  1606  func _op(op string) token.Token {
  1607  	switch op {
  1608  	case "+":
  1609  		return token.ADD
  1610  	case "-":
  1611  		return token.SUB
  1612  	case "*":
  1613  		return token.MUL
  1614  	case "/":
  1615  		return token.QUO
  1616  	case "%":
  1617  		return token.REM
  1618  	case "&":
  1619  		return token.AND
  1620  	case "|":
  1621  		return token.OR
  1622  	case "^":
  1623  		return token.XOR
  1624  	case "<<":
  1625  		return token.SHL
  1626  	case ">>":
  1627  		return token.SHR
  1628  	case "&^":
  1629  		return token.AND_NOT
  1630  	case "&&":
  1631  		return token.LAND
  1632  	case "||":
  1633  		return token.LOR
  1634  	case "<-":
  1635  		return token.ARROW
  1636  	case "++":
  1637  		return token.INC
  1638  	case "--":
  1639  		return token.DEC
  1640  	case "==":
  1641  		return token.EQL
  1642  	case "<":
  1643  		return token.LSS
  1644  	case ">":
  1645  		return token.GTR
  1646  	case "!":
  1647  		return token.NOT
  1648  	case "!=":
  1649  		return token.NEQ
  1650  	case "<=":
  1651  		return token.LEQ
  1652  	case ">=":
  1653  		return token.GEQ
  1654  	default:
  1655  		panic("unrecognized binary/unary operator " + op)
  1656  	}
  1657  }
  1658  
  1659  // assignment operators.
  1660  func _aop(op string) token.Token {
  1661  	switch op {
  1662  	case "=":
  1663  		return token.ASSIGN
  1664  	case ":=":
  1665  		return token.DEFINE
  1666  	case "+=":
  1667  		return token.ADD_ASSIGN
  1668  	case "-=":
  1669  		return token.SUB_ASSIGN
  1670  	case "*=":
  1671  		return token.MUL_ASSIGN
  1672  	case "/=":
  1673  		return token.QUO_ASSIGN
  1674  	case "%=":
  1675  		return token.REM_ASSIGN
  1676  	case "&=":
  1677  		return token.AND_ASSIGN
  1678  	case "|=":
  1679  		return token.OR_ASSIGN
  1680  	case "^=":
  1681  		return token.XOR_ASSIGN
  1682  	case "<<=":
  1683  		return token.SHL_ASSIGN
  1684  	case ">>=":
  1685  		return token.SHR_ASSIGN
  1686  	case "&^=":
  1687  		return token.AND_NOT_ASSIGN
  1688  	default:
  1689  		panic("unrecognized assignment operator " + op)
  1690  	}
  1691  }
  1692  
  1693  // ----------------------------------------
  1694  // AST Compile-Time
  1695  
  1696  func _ctif(cond bool, then_, else_ ast.Stmt) ast.Stmt {
  1697  	if cond {
  1698  		return then_
  1699  	} else if else_ != nil {
  1700  		return else_
  1701  	} else {
  1702  		return &ast.EmptyStmt{Implicit: true} // TODO
  1703  	}
  1704  }
  1705  
  1706  // ----------------------------------------
  1707  // AST query and manipulation.
  1708  
  1709  func importPathForName(name string, imports *ast.GenDecl) (path string, exists bool) {
  1710  	if imports.Tok != token.IMPORT {
  1711  		panic("unexpected ast.GenDecl token " + imports.Tok.String())
  1712  	}
  1713  	for _, spec := range imports.Specs {
  1714  		if ispec, ok := spec.(*ast.ImportSpec); ok {
  1715  			if ispec.Name.Name == name {
  1716  				path, err := strconv.Unquote(ispec.Path.Value)
  1717  				if err != nil {
  1718  					panic("malformed path " + ispec.Path.Value)
  1719  				}
  1720  				return path, true
  1721  			}
  1722  		}
  1723  	}
  1724  	return "", false
  1725  }
  1726  
  1727  func rootScope(scope *ast.Scope) *ast.Scope {
  1728  	for scope.Outer != nil {
  1729  		scope = scope.Outer
  1730  	}
  1731  	return scope
  1732  }
  1733  
  1734  func addImport(imports *ast.GenDecl, scope *ast.Scope, name, path string) {
  1735  	epath, exists := importPathForName(name, imports)
  1736  	if path == epath {
  1737  		return
  1738  	} else if exists {
  1739  		panic(fmt.Sprintf("import already exists for name %v", name))
  1740  	} else {
  1741  		imports.Specs = append(imports.Specs, &ast.ImportSpec{
  1742  			Name: _i(name),
  1743  			Path: _s(path),
  1744  		})
  1745  		addPkgNameToRootScope(name, rootScope(scope))
  1746  	}
  1747  }
  1748  
  1749  func addImportAuto(imports *ast.GenDecl, scope *ast.Scope, name, path string) string {
  1750  	if path0, exists := importPathForName(name, imports); exists {
  1751  		if path0 == path {
  1752  			return name
  1753  		}
  1754  		for i := 1; ; i++ {
  1755  			n := fmt.Sprintf("%v%v", name, i)
  1756  			if _, exists := importPathForName(n, imports); !exists {
  1757  				addImport(imports, scope, n, path)
  1758  				return n
  1759  			}
  1760  		}
  1761  	} else {
  1762  		addImport(imports, scope, name, path)
  1763  		return name
  1764  	}
  1765  }
  1766  
  1767  func addPkgNameToRootScope(name string, scope *ast.Scope) {
  1768  	if scope.Outer != nil {
  1769  		panic("should not happen")
  1770  	}
  1771  	scope.Insert(ast.NewObj(ast.Pkg, name))
  1772  }
  1773  
  1774  func addVars(scope *ast.Scope, names ...string) {
  1775  	for _, name := range names {
  1776  		if scope.Lookup(name) != nil {
  1777  			panic("already declared in scope")
  1778  		} else {
  1779  			scope.Insert(ast.NewObj(ast.Var, name))
  1780  		}
  1781  	}
  1782  }
  1783  
  1784  func addVarUniq(scope *ast.Scope, name string) string {
  1785  OUTER:
  1786  	for i := 0; ; i++ {
  1787  		tryName := name
  1788  		if i > 0 {
  1789  			tryName = fmt.Sprintf("%v%v", name, i)
  1790  		}
  1791  		s := scope
  1792  		for {
  1793  			if s.Lookup(tryName) != nil {
  1794  				continue OUTER
  1795  			}
  1796  			if s.Outer == nil {
  1797  				break
  1798  			} else {
  1799  				s = s.Outer
  1800  			}
  1801  		}
  1802  		scope.Insert(ast.NewObj(ast.Var, tryName))
  1803  		return tryName
  1804  	}
  1805  }
  1806  
  1807  // If rt.PkgPath() is "",
  1808  func goPkgPrefix(rootPkg *amino.Package, rt reflect.Type, info *amino.TypeInfo, imports *ast.GenDecl, scope *ast.Scope) (pkgPrefix string) {
  1809  	if rt.Name() == "" {
  1810  		panic("expected rt to have name")
  1811  	}
  1812  	if rt.PkgPath() == "" {
  1813  		return "" // native type.
  1814  	}
  1815  	var pkgName string
  1816  	pkgPath := rt.PkgPath()
  1817  	if pkgPath == "" || rootPkg.GoPkgPath == pkgPath {
  1818  		return ""
  1819  	}
  1820  	if info != nil && info.Package != nil {
  1821  		if info.Package.GoPkgPath != pkgPath {
  1822  			panic("conflicting package paths")
  1823  		}
  1824  		pkgName = info.Package.GoPkgName
  1825  	} else {
  1826  		pkgName = pkg.DefaultPkgName(pkgPath)
  1827  	}
  1828  	pkgName = addImportAuto(imports, scope, pkgName, pkgPath)
  1829  	return pkgName + "."
  1830  }
  1831  
  1832  func goTypeExprStringFn(rootPkg *amino.Package, imports *ast.GenDecl, scope *ast.Scope, isPtr bool, info *amino.TypeInfo) func() string {
  1833  	memo := ""
  1834  	return func() string { // lazy.
  1835  		if memo == "" {
  1836  			memo = goTypeExprString(rootPkg, imports, scope, isPtr, info)
  1837  		}
  1838  		return memo // cached.
  1839  	}
  1840  }
  1841  
  1842  func goTypeExprString(rootPkg *amino.Package, imports *ast.GenDecl, scope *ast.Scope, isPtr bool, info *amino.TypeInfo) string {
  1843  	if isPtr {
  1844  		return "*" + goTypeExprString(rootPkg, imports, scope, false, info)
  1845  	}
  1846  	// Below, assume isPtr is false.
  1847  	rt := info.Type
  1848  	// Below, the logic mirrors that of goTypeExpr.
  1849  	name := rt.Name()
  1850  	if name != "" {
  1851  		// If name exists, use the name rather than the kind.
  1852  		pkgPath := rt.PkgPath()
  1853  		// Sanity check
  1854  		if info.Package != nil && pkgPath != info.Package.GoPkgPath {
  1855  			panic(fmt.Sprintf("mismatching packages. info says %v, reflect says %v", info.Package.GoPkgPath, pkgPath))
  1856  		}
  1857  		// END Sanity check
  1858  		pkgPrefix := goPkgPrefix(rootPkg, rt, info, imports, scope)
  1859  		return fmt.Sprintf("%v%v", pkgPrefix, name)
  1860  	}
  1861  	switch info.Type.Kind() {
  1862  	case reflect.Array:
  1863  		return fmt.Sprintf("[%v]%v", info.Type.Len(), goTypeExprString(rootPkg, imports, scope, info.ElemIsPtr, info.Elem))
  1864  	case reflect.Slice:
  1865  		return fmt.Sprintf("[]%v", goTypeExprString(rootPkg, imports, scope, info.ElemIsPtr, info.Elem))
  1866  	default:
  1867  		expr := rt.String()
  1868  		if strings.Contains(expr, ".") {
  1869  			panic("does not work (yet) with anonymous interface/struct types with imports")
  1870  		}
  1871  		return expr
  1872  	}
  1873  }
  1874  
  1875  func goTypeExpr(rootPkg *amino.Package, rt reflect.Type, imports *ast.GenDecl, scope *ast.Scope) ast.Expr {
  1876  	name := rt.Name()
  1877  	if name != "" {
  1878  		pkgPrefix := goPkgPrefix(rootPkg, rt, nil, imports, scope)
  1879  		return _x(fmt.Sprintf("%v%v", pkgPrefix, name))
  1880  	}
  1881  	switch rt.Kind() {
  1882  	case reflect.Array:
  1883  		return _arr(rt.Len(), goTypeExpr(rootPkg, rt.Elem(), imports, scope))
  1884  	case reflect.Slice:
  1885  		return _sl(goTypeExpr(rootPkg, rt.Elem(), imports, scope))
  1886  	case reflect.Ptr:
  1887  		return _ptr(goTypeExpr(rootPkg, rt.Elem(), imports, scope))
  1888  	default:
  1889  		expr := rt.String()
  1890  		if strings.Contains(expr, ".") {
  1891  			panic("does not work (yet) with anonymous interface/struct types with imports")
  1892  		}
  1893  		return _x(expr)
  1894  	}
  1895  }
  1896  
  1897  // The relevant p3-protoc generated go-type's type-expr given pre-repr info.
  1898  // This is used for construction.
  1899  func p3goTypeExprString(rootPkg *amino.Package, imports *ast.GenDecl, scope *ast.Scope, info *amino.TypeInfo, fopts amino.FieldOptions) (typeExpr string) {
  1900  	// If registered non-native type:
  1901  	pkg := info.Package
  1902  	if pkg != nil && pkg.GoPkgPath != "" && info.Type.Kind() != reflect.Interface {
  1903  		// Special cases.
  1904  		// TODO: somehow refactor into wellknown.go
  1905  		switch info.Type {
  1906  		case timeType:
  1907  			pkgName := addImportAuto(
  1908  				imports, scope, "timestamppb", "google.golang.org/protobuf/types/known/timestamppb")
  1909  			return fmt.Sprintf("*%v.%v", pkgName, "Timestamp")
  1910  		case durationType:
  1911  			pkgName := addImportAuto(
  1912  				imports, scope, "durationpb", "google.golang.org/protobuf/types/known/durationpb")
  1913  			return fmt.Sprintf("*%v.%v", pkgName, "Duration")
  1914  		}
  1915  		pkgName := addImportAuto(imports, scope, pkg.GoPkgName+"pb", pkg.P3GoPkgPath)
  1916  		typeExpr = fmt.Sprintf("*%v.%v", pkgName, CamelCase(info.Name))
  1917  		return
  1918  	}
  1919  	// Else, if unregistered or non-concrete type:
  1920  	k := info.Type.Kind()
  1921  	switch k {
  1922  	case reflect.Array, reflect.Slice:
  1923  		// As a special case, if the repr-type's type is bytes,
  1924  		// we prefer bytes/[]byte instead of []struct{Value byte}.
  1925  		if info.Elem.ReprType.Type.Kind() == reflect.Uint8 {
  1926  			return "[]byte"
  1927  		} else {
  1928  			nl := newNList(rootPkg, info, fopts)
  1929  			return nl.P3GoExprString(imports, scope)
  1930  		}
  1931  	case reflect.String:
  1932  		return "string"
  1933  	case reflect.Interface:
  1934  		anypb := addImportAuto(imports, scope, "anypb", "google.golang.org/protobuf/types/known/anypb")
  1935  		return fmt.Sprintf("*%v.Any", anypb)
  1936  	case reflect.Struct:
  1937  		panic(fmt.Sprintf("package not registered for type %v", info))
  1938  	case reflect.Int:
  1939  		return "int64"
  1940  	case reflect.Uint:
  1941  		return "uint64"
  1942  	case reflect.Int8:
  1943  		return "int32"
  1944  	case reflect.Uint8:
  1945  		return uint8Str // bytes
  1946  	case reflect.Int16:
  1947  		return "int32"
  1948  	case reflect.Uint16:
  1949  		return "uint32"
  1950  	default:
  1951  		if info.Type.PkgPath() != "" {
  1952  			panic("unexpected unregistered type " + info.Type.String())
  1953  		}
  1954  		typeExpr = info.Type.String()
  1955  		if typeExpr == "" {
  1956  			panic("unexpected empty type expr string")
  1957  		}
  1958  		return
  1959  	}
  1960  }
  1961  
  1962  // Returns true if when info's repr is an unpacked list, thus requiring an
  1963  // implicit struct for an array of info when info is further an element of an
  1964  // array or slice.
  1965  //
  1966  // In other words, returns true when an implicit struct{Value []SomethingList}
  1967  // is required in a parent list, required for the following transform:
  1968  //
  1969  //	go:   struct{NestedList: [][]SomethingList} -->
  1970  //	p3go: struct{NestedList: []struct{Value []SomethingListRepr}}
  1971  func isImplicitList(info *amino.TypeInfo, fopts amino.FieldOptions) (implicit bool) {
  1972  	k := info.ReprType.Type.Kind()
  1973  	switch k {
  1974  	case reflect.Array, reflect.Slice:
  1975  		if info.ReprType.Elem.ReprType.Type.Kind() == reflect.Uint8 {
  1976  			return false
  1977  		} else {
  1978  			nl := newNList(nil, info, fopts)
  1979  			return nl.Dimensions >= 1
  1980  		}
  1981  	default:
  1982  		return false
  1983  	}
  1984  }