accounts/abi: Modified unpackAtomic to accept struct lvalues

This commit is contained in:
Javier Peletier 2018-03-05 16:01:40 +01:00
parent 13b566e06e
commit 60a999f238
3 changed files with 49 additions and 19 deletions

View File

@ -86,7 +86,7 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
} }
return method.Outputs.Unpack(v, output) return method.Outputs.Unpack(v, output)
} else if event, ok := abi.Events[name]; ok { } else if event, ok := abi.Events[name]; ok {
return event.Inputs.unpackTuple(v, output) return event.Inputs.Unpack(v, output)
} }
return fmt.Errorf("abi: could not locate named method or event") return fmt.Errorf("abi: could not locate named method or event")
} }

View File

@ -113,16 +113,8 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
} }
// If the output interface is a struct, make sure names don't collide // If the output interface is a struct, make sure names don't collide
if kind == reflect.Struct { if kind == reflect.Struct {
exists := make(map[string]bool) if err := requireUniqueStructFieldNames(arguments); err != nil {
for _, arg := range arguments { return err
field := capitalise(arg.Name)
if field == "" {
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
}
if exists[field] {
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
}
exists[field] = true
} }
} }
for i, arg := range arguments.NonIndexed() { for i, arg := range arguments.NonIndexed() {
@ -131,14 +123,9 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
switch kind { switch kind {
case reflect.Struct: case reflect.Struct:
name := capitalise(arg.Name) err := unpackStruct(value, reflectValue, arg)
for j := 0; j < typ.NumField(); j++ { if err != nil {
// TODO read tags: `abi:"fieldName"` return err
if typ.Field(j).Name == name {
if err := set(value.Field(j), reflectValue, arg); err != nil {
return err
}
}
} }
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if value.Len() < i { if value.Len() < i {
@ -165,8 +152,20 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interf
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues)) return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
} }
elem := reflect.ValueOf(v).Elem() elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0]) reflectValue := reflect.ValueOf(marshalledValues[0])
if kind == reflect.Struct {
//make sure names don't collide
if err := requireUniqueStructFieldNames(arguments); err != nil {
return err
}
return unpackStruct(elem, reflectValue, arguments[0])
}
return set(elem, reflectValue, arguments.NonIndexed()[0]) return set(elem, reflectValue, arguments.NonIndexed()[0])
} }
// Computes the full size of an array; // Computes the full size of an array;
@ -278,3 +277,18 @@ func capitalise(input string) string {
} }
return strings.ToUpper(input[:1]) + input[1:] return strings.ToUpper(input[:1]) + input[1:]
} }
//unpackStruct extracts each argument into its corresponding struct field
func unpackStruct(value, reflectValue reflect.Value, arg Argument) error {
name := capitalise(arg.Name)
typ := value.Type()
for j := 0; j < typ.NumField(); j++ {
// TODO read tags: `abi:"fieldName"`
if typ.Field(j).Name == name {
if err := set(value.Field(j), reflectValue, arg); err != nil {
return err
}
}
}
return nil
}

View File

@ -110,3 +110,19 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
} }
return nil return nil
} }
// requireUniqueStructFieldNames makes sure field names don't collide
func requireUniqueStructFieldNames(args Arguments) error {
exists := make(map[string]bool)
for _, arg := range args {
field := capitalise(arg.Name)
if field == "" {
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
}
if exists[field] {
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
}
exists[field] = true
}
return nil
}