rlp: display even more type context in decode errors

It now says:

   rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I
This commit is contained in:
Felix Lange 2015-01-04 23:41:24 +01:00
parent 51d7ab7924
commit c40eebcfd2
2 changed files with 72 additions and 23 deletions

View file

@ -76,22 +76,37 @@ func Decode(r io.Reader, val interface{}) error {
type decodeError struct { type decodeError struct {
msg string msg string
typ reflect.Type typ reflect.Type
ctx []string
} }
func (err decodeError) Error() string { func (err *decodeError) Error() string {
return fmt.Sprintf("rlp: %s for %v", err.msg, err.typ) ctx := ""
if len(err.ctx) > 0 {
ctx = ", decoding into "
for i := len(err.ctx) - 1; i >= 0; i-- {
ctx += err.ctx[i]
}
}
return fmt.Sprintf("rlp: %s for %v%s", err.msg, err.typ, ctx)
} }
func wrapStreamError(err error, typ reflect.Type) error { func wrapStreamError(err error, typ reflect.Type) error {
switch err { switch err {
case ErrExpectedList: case ErrExpectedList:
return decodeError{"expected input list", typ} return &decodeError{msg: "expected input list", typ: typ}
case ErrExpectedString: case ErrExpectedString:
return decodeError{"expected input string or byte", typ} return &decodeError{msg: "expected input string or byte", typ: typ}
case errUintOverflow: case errUintOverflow:
return decodeError{"input string too long", typ} return &decodeError{msg: "input string too long", typ: typ}
case errNotAtEOL: case errNotAtEOL:
return decodeError{"input list has too many elements", typ} return &decodeError{msg: "input list has too many elements", typ: typ}
}
return err
}
func addErrorContext(err error, ctx string) error {
if decErr, ok := err.(*decodeError); ok {
decErr.ctx = append(decErr.ctx, ctx)
} }
return err return err
} }
@ -180,13 +195,13 @@ func makeListDecoder(typ reflect.Type) (decoder, error) {
return nil, err return nil, err
} }
if typ.Kind() == reflect.Array { isArray := typ.Kind() == reflect.Array
return func(s *Stream, val reflect.Value) error {
return decodeListArray(s, val, etypeinfo.decoder)
}, nil
}
return func(s *Stream, val reflect.Value) error { return func(s *Stream, val reflect.Value) error {
return decodeListSlice(s, val, etypeinfo.decoder) if isArray {
return decodeListArray(s, val, etypeinfo.decoder)
} else {
return decodeListSlice(s, val, etypeinfo.decoder)
}
}, nil }, nil
} }
@ -219,7 +234,7 @@ func decodeListSlice(s *Stream, val reflect.Value, elemdec decoder) error {
if err := elemdec(s, val.Index(i)); err == EOL { if err := elemdec(s, val.Index(i)); err == EOL {
break break
} else if err != nil { } else if err != nil {
return err return addErrorContext(err, fmt.Sprint("[", i, "]"))
} }
} }
if i < val.Len() { if i < val.Len() {
@ -248,7 +263,7 @@ func decodeListArray(s *Stream, val reflect.Value, elemdec decoder) error {
if err := elemdec(s, val.Index(i)); err == EOL { if err := elemdec(s, val.Index(i)); err == EOL {
break break
} else if err != nil { } else if err != nil {
return err return addErrorContext(err, fmt.Sprint("[", i, "]"))
} }
} }
if i < vlen { if i < vlen {
@ -280,14 +295,14 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
switch kind { switch kind {
case Byte: case Byte:
if val.Len() == 0 { if val.Len() == 0 {
return decodeError{"input string too long", val.Type()} return &decodeError{msg: "input string too long", typ: val.Type()}
} }
bv, _ := s.Uint() bv, _ := s.Uint()
val.Index(0).SetUint(bv) val.Index(0).SetUint(bv)
zero(val, 1) zero(val, 1)
case String: case String:
if uint64(val.Len()) < size { if uint64(val.Len()) < size {
return decodeError{"input string too long", val.Type()} return &decodeError{msg: "input string too long", typ: val.Type()}
} }
slice := val.Slice(0, int(size)).Interface().([]byte) slice := val.Slice(0, int(size)).Interface().([]byte)
if err := s.readFull(slice); err != nil { if err := s.readFull(slice); err != nil {
@ -334,7 +349,7 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
// too few elements. leave the rest at their zero value. // too few elements. leave the rest at their zero value.
break break
} else if err != nil { } else if err != nil {
return err return addErrorContext(err, "."+typ.Field(f.index).Name)
} }
} }
return wrapStreamError(s.ListEnd(), typ) return wrapStreamError(s.ListEnd(), typ)
@ -599,7 +614,13 @@ func (s *Stream) Decode(val interface{}) error {
if err != nil { if err != nil {
return err return err
} }
return info.decoder(s, rval.Elem())
err = info.decoder(s, rval.Elem())
if decErr, ok := err.(*decodeError); ok && len(decErr.ctx) > 0 {
// add decode target type to error so context has more meaning
decErr.ctx = append(decErr.ctx, fmt.Sprint("(", rtyp.Elem(), ")"))
}
return err
} }
// Reset discards any information about the current decoding context // Reset discards any information about the current decoding context

View file

@ -231,7 +231,12 @@ var decodeTests = []decodeTest{
{input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")}, {input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")},
{input: "C0", ptr: new([]byte), value: []byte{}}, {input: "C0", ptr: new([]byte), value: []byte{}},
{input: "C3010203", ptr: new([]byte), value: []byte{1, 2, 3}}, {input: "C3010203", ptr: new([]byte), value: []byte{1, 2, 3}},
{input: "C3820102", ptr: new([]byte), error: "rlp: input string too long for uint8"},
{
input: "C3820102",
ptr: new([]byte),
error: "rlp: input string too long for uint8, decoding into ([]uint8)[0]",
},
// byte arrays // byte arrays
{input: "01", ptr: new([5]byte), value: [5]byte{1}}, {input: "01", ptr: new([5]byte), value: [5]byte{1}},
@ -239,9 +244,22 @@ var decodeTests = []decodeTest{
{input: "850102030405", ptr: new([5]byte), value: [5]byte{1, 2, 3, 4, 5}}, {input: "850102030405", ptr: new([5]byte), value: [5]byte{1, 2, 3, 4, 5}},
{input: "C0", ptr: new([5]byte), value: [5]byte{}}, {input: "C0", ptr: new([5]byte), value: [5]byte{}},
{input: "C3010203", ptr: new([5]byte), value: [5]byte{1, 2, 3, 0, 0}}, {input: "C3010203", ptr: new([5]byte), value: [5]byte{1, 2, 3, 0, 0}},
{input: "C3820102", ptr: new([5]byte), error: "rlp: input string too long for uint8"},
{input: "86010203040506", ptr: new([5]byte), error: "rlp: input string too long for [5]uint8"}, {
{input: "850101", ptr: new([5]byte), error: io.ErrUnexpectedEOF.Error()}, input: "C3820102",
ptr: new([5]byte),
error: "rlp: input string too long for uint8, decoding into ([5]uint8)[0]",
},
{
input: "86010203040506",
ptr: new([5]byte),
error: "rlp: input string too long for [5]uint8",
},
{
input: "850101",
ptr: new([5]byte),
error: io.ErrUnexpectedEOF.Error(),
},
// byte array reuse (should be zeroed) // byte array reuse (should be zeroed)
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}}, {input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
@ -272,13 +290,23 @@ var decodeTests = []decodeTest{
{input: "C0", ptr: new(simplestruct), value: simplestruct{0, ""}}, {input: "C0", ptr: new(simplestruct), value: simplestruct{0, ""}},
{input: "C105", ptr: new(simplestruct), value: simplestruct{5, ""}}, {input: "C105", ptr: new(simplestruct), value: simplestruct{5, ""}},
{input: "C50583343434", ptr: new(simplestruct), value: simplestruct{5, "444"}}, {input: "C50583343434", ptr: new(simplestruct), value: simplestruct{5, "444"}},
{input: "C3010101", ptr: new(simplestruct), error: "rlp: input list has too many elements for rlp.simplestruct"},
{ {
input: "C501C302C103", input: "C501C302C103",
ptr: new(recstruct), ptr: new(recstruct),
value: recstruct{1, &recstruct{2, &recstruct{3, nil}}}, value: recstruct{1, &recstruct{2, &recstruct{3, nil}}},
}, },
{
input: "C3010101",
ptr: new(simplestruct),
error: "rlp: input list has too many elements for rlp.simplestruct",
},
{
input: "C501C3C00000",
ptr: new(recstruct),
error: "rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I",
},
// pointers // pointers
{input: "00", ptr: new(*uint), value: (*uint)(nil)}, {input: "00", ptr: new(*uint), value: (*uint)(nil)},
{input: "80", ptr: new(*uint), value: (*uint)(nil)}, {input: "80", ptr: new(*uint), value: (*uint)(nil)},