accounts/abi: implement struct parsing first attempt

Signed-off-by: VoR0220 <catalanor0220@gmail.com>
This commit is contained in:
VoR0220 2017-11-10 17:39:39 -06:00
parent 86f6568f66
commit a16610fc1e
2 changed files with 136 additions and 80 deletions

View file

@ -29,21 +29,32 @@ type Argument struct {
Indexed bool // indexed is only used by events Indexed bool // indexed is only used by events
} }
// Type only used for unmarshalling json argument types
type unmarshalArg struct {
Name string
Type string
Components []unmarshalArg // used for tuples/structs
Indexed bool
}
func (a *Argument) UnmarshalJSON(data []byte) error { func (a *Argument) UnmarshalJSON(data []byte) error {
var extarg struct { var extarg unmarshalArg
Name string
Type string
Indexed bool
}
err := json.Unmarshal(data, &extarg) err := json.Unmarshal(data, &extarg)
if err != nil { if err != nil {
return fmt.Errorf("argument json err: %v", err) return fmt.Errorf("argument json err: %v", err)
} }
a.Type, err = NewType(extarg.Type) if len(extarg.Components) > 0 {
a.Type, err = ParseStructType(extarg.Type, extarg.Components...)
} else {
a.Type, err = NewType(extarg.Type)
}
if err != nil { if err != nil {
return err return err
} }
a.Name = extarg.Name a.Name = extarg.Name
a.Indexed = extarg.Indexed a.Indexed = extarg.Indexed

View file

@ -24,6 +24,7 @@ import (
"strings" "strings"
) )
// Type enumerator
const ( const (
IntTy byte = iota IntTy byte = iota
UintTy UintTy
@ -37,6 +38,7 @@ const (
HashTy HashTy
FixedPointTy FixedPointTy
FunctionTy FunctionTy
StructTy
) )
// Type is the reflection of the supported argument type // Type is the reflection of the supported argument type
@ -58,21 +60,96 @@ var (
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) { func NewType(t string) (typ Type, err error) {
// check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi")
}
typ.stringKind = t
// if there are brackets, get ready to go into slice/array mode and // if there are brackets, get ready to go into slice/array mode and
// recursively create the type // recursively create the type
var found bool
if typ, found, err = checkForSlices(t); found == true || err != nil {
return typ, err
}
typ.stringKind = t
// parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
// varSize is the size of the variable
var varSize int
if len(parsedType[3]) > 0 {
var err error
varSize, err = strconv.Atoi(parsedType[2])
if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
}
} else {
if parsedType[0] == "uint" || parsedType[0] == "int" {
// this should fail because it means that there's something wrong with
// the abi type (the compiler should always format it to the size...always)
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
}
// varType is the parsed abi type
switch varType := parsedType[1]; varType {
case "int":
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
typ.Type = reflect.TypeOf(bool(false))
case "address":
typ.Kind = reflect.Array
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Type = reflect.TypeOf("")
typ.T = StringTy
case "bytes":
if varSize == 0 {
typ.T = BytesTy
typ.Kind = reflect.Slice
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
} else {
typ.T = FixedBytesTy
typ.Kind = reflect.Array
typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
}
case "function":
typ.Kind = reflect.Array
typ.T = FunctionTy
typ.Size = 24
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
return
}
// if brackets surround the type, create it recursively, otherwise note that brackets weren't found
func checkForSlices(t string, structComponents ...unmarshalArg) (typ Type, found bool, err error) {
// check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, false, fmt.Errorf("invalid arg type in abi")
}
if strings.Count(t, "[") != 0 { if strings.Count(t, "[") != 0 {
i := strings.LastIndex(t, "[") i := strings.LastIndex(t, "[")
// recursively embed the type // recursively embed the type
embeddedType, err := NewType(t[:i]) var embeddedType Type
if len(structComponents) > 0 {
embeddedType, err = ParseStructType(t, structComponents...)
} else {
embeddedType, err = NewType(t[:i])
}
if err != nil { if err != nil {
return Type{}, err return Type{}, false, err
} }
// grab the last cell and create a type from there // grab the last cell and create a type from there
sliced := t[i:] sliced := t[i:]
@ -93,78 +170,46 @@ func NewType(t string) (typ Type, err error) {
typ.Elem = &embeddedType typ.Elem = &embeddedType
typ.Size, err = strconv.Atoi(intz[0]) typ.Size, err = strconv.Atoi(intz[0])
if err != nil { if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) return Type{}, false, fmt.Errorf("abi: error parsing variable size: %v", err)
} }
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type) typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
} else { } else {
return Type{}, fmt.Errorf("invalid formatting of array type") return Type{}, false, fmt.Errorf("invalid formatting of array type")
}
return typ, err
} else {
// parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
// varSize is the size of the variable
var varSize int
if len(parsedType[3]) > 0 {
var err error
varSize, err = strconv.Atoi(parsedType[2])
if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
}
} else {
if parsedType[0] == "uint" || parsedType[0] == "int" {
// this should fail because it means that there's something wrong with
// the abi type (the compiler should always format it to the size...always)
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
}
// varType is the parsed abi type
varType := parsedType[1]
switch varType {
case "int":
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
typ.Type = reflect.TypeOf(bool(false))
case "address":
typ.Kind = reflect.Array
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Type = reflect.TypeOf("")
typ.T = StringTy
case "bytes":
if varSize == 0 {
typ.T = BytesTy
typ.Kind = reflect.Slice
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
} else {
typ.T = FixedBytesTy
typ.Kind = reflect.Array
typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
}
case "function":
typ.Kind = reflect.Array
typ.T = FunctionTy
typ.Size = 24
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
} }
return typ, true, err
} }
return Type{}, false, nil
}
return func ParseStructType(t string, components ...unmarshalArg) (typ Type, err error) {
// if there are brackets, get ready to go into slice/array mode and
// recursively create the type
var found bool
if typ, found, err = checkForSlices(t, components...); found == true || err != nil {
return typ, err
}
typ.stringKind = t
typ.T = StructTy
typ.Kind = reflect.Struct
// create the struct type
var fields []reflect.StructField
for i, component := range components {
// it's a embedded struct type
var fieldType Type
if len(component.Components) > 0 {
fieldType, err = ParseStructType(component.Type, component.Components...)
} else {
fieldType, err = NewType(component.Type)
}
if err != nil {
return Type{}, err
}
fields[i] = reflect.StructField{Name: component.Name, Type: fieldType.Type, Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, component.Name))}
}
typ.Type = reflect.StructOf(fields)
return typ, nil
} }
// String implements Stringer // String implements Stringer