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

     1  package amino
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"unicode"
     7  )
     8  
     9  // ----------------------------------------
    10  // Constants
    11  
    12  var errorType = reflect.TypeOf(new(error)).Elem()
    13  
    14  // ----------------------------------------
    15  // encode: see binary-encode.go and json-encode.go
    16  // decode: see binary-decode.go and json-decode.go
    17  
    18  // ----------------------------------------
    19  // Misc.
    20  
    21  // CONTRACT: by the time this is called, len(bz) >= _n
    22  // Returns true so you can write one-liners.
    23  func slide(bz *[]byte, n *int, _n int) bool {
    24  	if bz != nil {
    25  		if _n < 0 || _n > len(*bz) {
    26  			panic(fmt.Sprintf("impossible slide: len:%v _n:%v", len(*bz), _n))
    27  		}
    28  		*bz = (*bz)[_n:]
    29  	}
    30  	if n != nil {
    31  		*n += _n
    32  	}
    33  	return true
    34  }
    35  
    36  // maybe dereference if pointer.
    37  // drv: the final non-pointer value (which may be invalid).
    38  // isPtr: whether rv.Kind() == reflect.Ptr.
    39  // isNilPtr: whether a nil pointer at any level.
    40  func maybeDerefValue(rv reflect.Value) (drv reflect.Value, rvIsPtr bool, rvIsNilPtr bool) {
    41  	if rv.Kind() == reflect.Ptr {
    42  		rvIsPtr = true
    43  		if rv.IsNil() {
    44  			rvIsNilPtr = true
    45  			return
    46  		}
    47  		rv = rv.Elem()
    48  	}
    49  	drv = rv
    50  	return
    51  }
    52  
    53  // Dereference-and-construct pointers.
    54  func maybeDerefAndConstruct(rv reflect.Value) reflect.Value {
    55  	if rv.Kind() == reflect.Ptr {
    56  		if rv.IsNil() {
    57  			newPtr := reflect.New(rv.Type().Elem())
    58  			rv.Set(newPtr)
    59  		}
    60  		rv = rv.Elem()
    61  	}
    62  	if rv.Kind() == reflect.Ptr {
    63  		panic("unexpected pointer pointer")
    64  	}
    65  	return rv
    66  }
    67  
    68  // Returns isDefaultValue=true iff is zero and isn't a struct.
    69  // NOTE: Also works for Maps, Chans, and Funcs, though they are not
    70  // otherwise supported by Amino.  For future?
    71  func isNonstructDefaultValue(rv reflect.Value) (isDefault bool) {
    72  	// time.Duration is a special case,
    73  	// it is considered a struct for encoding purposes.
    74  	switch rv.Type() {
    75  	case durationType:
    76  		return false
    77  	}
    78  	// general cae
    79  	switch rv.Kind() {
    80  	case reflect.Ptr:
    81  		if rv.IsNil() {
    82  			return true
    83  		} else {
    84  			erv := rv.Elem()
    85  			return isNonstructDefaultValue(erv)
    86  		}
    87  	case reflect.Bool:
    88  		return rv.Bool() == false
    89  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    90  		return rv.Int() == 0
    91  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    92  		return rv.Uint() == 0
    93  	case reflect.String:
    94  		return rv.Len() == 0
    95  	case reflect.Chan, reflect.Map, reflect.Slice:
    96  		return rv.IsNil() || rv.Len() == 0
    97  	case reflect.Func, reflect.Interface:
    98  		return rv.IsNil()
    99  	case reflect.Struct:
   100  		return false
   101  	default:
   102  		return false
   103  	}
   104  }
   105  
   106  // Returns the default value of a type.  For a time type or a
   107  // pointer(s) to time, the default value is not zero (or nil), but the
   108  // time value of 1970.
   109  //
   110  // The default value of a struct pointer is nil, while the default value of
   111  // other pointers is not nil.  This is due to a proto3 wart, e.g. while there
   112  // is a way to distinguish between a nil struct/message vs an empty one (via
   113  // its presence or absence in an outer struct), there is no such way to
   114  // distinguish between nil bytes/lists and empty bytes/lists, are they are all
   115  // absent in binary encoding.
   116  func defaultValue(rt reflect.Type) (rv reflect.Value) {
   117  	switch rt.Kind() {
   118  	case reflect.Ptr:
   119  		// Dereference all the way and see if it's a time type.
   120  		ert := rt.Elem()
   121  		if ert.Kind() == reflect.Ptr {
   122  			panic("nested pointers not allowed")
   123  		}
   124  		if ert == timeType {
   125  			// Start from the top and construct pointers as needed.
   126  			rv = reflect.New(rt.Elem())
   127  			// Set to 1970, the whole point of this function.
   128  			rv.Elem().Set(reflect.ValueOf(emptyTime))
   129  			return rv
   130  		} else if ert.Kind() == reflect.Struct {
   131  			rv = reflect.Zero(rt)
   132  			return rv
   133  		} else {
   134  			rv = reflect.New(rt.Elem())
   135  			return rv
   136  		}
   137  	case reflect.Struct:
   138  		if rt == timeType {
   139  			// Set to 1970, the whole point of this function.
   140  			rv = reflect.New(rt).Elem()
   141  			rv.Set(reflect.ValueOf(emptyTime))
   142  			return rv
   143  		} else {
   144  			return reflect.Zero(rt)
   145  		}
   146  	}
   147  
   148  	// Just return the default Go zero object.
   149  	// Return an empty struct.
   150  	return reflect.Zero(rt)
   151  }
   152  
   153  // NOTE: Also works for Maps and Chans, though they are not
   154  // otherwise supported by Amino.  For future?
   155  func isNil(rv reflect.Value) bool {
   156  	switch rv.Kind() {
   157  	case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice:
   158  		return rv.IsNil()
   159  	default:
   160  		return false
   161  	}
   162  }
   163  
   164  // constructConcreteType creates the concrete value as
   165  // well as the corresponding settable value for it.
   166  // Return irvSet which should be set on caller's interface rv.
   167  func constructConcreteType(cinfo *TypeInfo) (crv, irvSet reflect.Value) {
   168  	// Construct new concrete type.
   169  	if cinfo.PointerPreferred {
   170  		cPtrRv := reflect.New(cinfo.Type)
   171  		crv = cPtrRv.Elem()
   172  		irvSet = cPtrRv
   173  	} else {
   174  		crv = reflect.New(cinfo.Type).Elem()
   175  		irvSet = crv
   176  	}
   177  	return
   178  }
   179  
   180  func toReprObject(rv reflect.Value) (rrv reflect.Value, err error) {
   181  	var mwrm reflect.Value
   182  	if rv.CanAddr() {
   183  		mwrm = rv.Addr().MethodByName("MarshalAmino")
   184  	} else {
   185  		mwrm = rv.MethodByName("MarshalAmino")
   186  	}
   187  	mwouts := mwrm.Call(nil)
   188  	if !mwouts[1].IsNil() {
   189  		erri := mwouts[1].Interface()
   190  		if erri != nil {
   191  			err = erri.(error)
   192  			return rrv, err
   193  		}
   194  	}
   195  	rrv = mwouts[0]
   196  	return
   197  }
   198  
   199  func isExported(field reflect.StructField) bool {
   200  	// Test 1:
   201  	if field.PkgPath != "" {
   202  		return false
   203  	}
   204  	// Test 2:
   205  	var first rune
   206  	for _, c := range field.Name {
   207  		first = c
   208  		break
   209  	}
   210  	// TODO: JAE: I'm not sure that the unicode spec
   211  	// is the correct spec to use, so this might be wrong.
   212  
   213  	return unicode.IsUpper(first)
   214  }
   215  
   216  func marshalAminoReprType(rm reflect.Method) (rrt reflect.Type) {
   217  	// Verify form of this method.
   218  	if rm.Type.NumIn() != 1 {
   219  		panic(fmt.Sprintf("MarshalAmino should have 1 input parameters (including receiver); got %v", rm.Type))
   220  	}
   221  	if rm.Type.NumOut() != 2 {
   222  		panic(fmt.Sprintf("MarshalAmino should have 2 output parameters; got %v", rm.Type))
   223  	}
   224  	if out := rm.Type.Out(1); out != errorType {
   225  		panic(fmt.Sprintf("MarshalAmino should have second output parameter of error type, got %v", out))
   226  	}
   227  	rrt = rm.Type.Out(0)
   228  	if rrt.Kind() == reflect.Ptr {
   229  		panic(fmt.Sprintf("Representative objects cannot be pointers; got %v", rrt))
   230  	}
   231  	return
   232  }
   233  
   234  func unmarshalAminoReprType(rm reflect.Method) (rrt reflect.Type) {
   235  	// Verify form of this method.
   236  	if rm.Type.NumIn() != 2 {
   237  		panic(fmt.Sprintf("UnmarshalAmino should have 2 input parameters (including receiver); got %v", rm.Type))
   238  	}
   239  	if in1 := rm.Type.In(0); in1.Kind() != reflect.Ptr {
   240  		panic(fmt.Sprintf("UnmarshalAmino first input parameter should be pointer type but got %v", in1))
   241  	}
   242  	if rm.Type.NumOut() != 1 {
   243  		panic(fmt.Sprintf("UnmarshalAmino should have 1 output parameters; got %v", rm.Type))
   244  	}
   245  	if out := rm.Type.Out(0); out != errorType {
   246  		panic(fmt.Sprintf("UnmarshalAmino should have first output parameter of error type, got %v", out))
   247  	}
   248  	rrt = rm.Type.In(1)
   249  	if rrt.Kind() == reflect.Ptr {
   250  		panic(fmt.Sprintf("Representative objects cannot be pointers; got %v", rrt))
   251  	}
   252  	return
   253  }
   254  
   255  // NOTE: do not change this definition.
   256  // It is also defined for genproto.
   257  func isListType(rt reflect.Type) bool {
   258  	return rt.Kind() == reflect.Slice ||
   259  		rt.Kind() == reflect.Array
   260  }