Don't decode CBOR tag data to interface (tinygo only)
tinygo v0.33 doesn't implement Type.AssignableTo(), which is needed under the hood when decoding registered CBOR tag data to Go interface. More details in https://github.com/tinygo-org/tinygo/issues/4277. This commit returns error of UnmarshalTypeError type when decoding registered CBOR tag data to Go interface. This change can be reverted after tinygo implements Type.AssignalbeTo().
This commit is contained in:
parent
950ea6de1b
commit
e1bc59bbec
@ -1386,8 +1386,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
|
||||
registeredType := d.dm.tags.getTypeFromTagNum(tagNums)
|
||||
if registeredType != nil {
|
||||
if registeredType.Implements(tInfo.nonPtrType) ||
|
||||
reflect.PtrTo(registeredType).Implements(tInfo.nonPtrType) {
|
||||
if implements(registeredType, tInfo.nonPtrType) {
|
||||
v.Set(reflect.New(registeredType))
|
||||
v = v.Elem()
|
||||
tInfo = getTypeInfo(registeredType)
|
||||
|
||||
13
decode_not_tinygo.go
Normal file
13
decode_not_tinygo.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import "reflect"
|
||||
|
||||
func implements(concreteType reflect.Type, interfaceType reflect.Type) bool {
|
||||
return concreteType.Implements(interfaceType) ||
|
||||
reflect.PointerTo(concreteType).Implements(interfaceType)
|
||||
}
|
||||
@ -47,3 +47,63 @@ func TestUnmarshalDeepNesting(t *testing.T) {
|
||||
&readback, root)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRegisteredTagToInterface(t *testing.T) {
|
||||
var err error
|
||||
tags := NewTagSet()
|
||||
err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(C{}), 279)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(D{}), 280)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
encMode, _ := PreferredUnsortedEncOptions().EncModeWithTags(tags)
|
||||
decMode, _ := DecOptions{}.DecModeWithTags(tags)
|
||||
|
||||
v1 := A1{Field: &C{Field: 5}}
|
||||
data1, err := encMode.Marshal(v1)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v1, err)
|
||||
}
|
||||
|
||||
v2 := A2{Fields: []B{&C{Field: 5}, &D{Field: "a"}}}
|
||||
data2, err := encMode.Marshal(v2)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v2, err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
unmarshalToObj interface{}
|
||||
wantValue interface{}
|
||||
}{
|
||||
{
|
||||
name: "interface type",
|
||||
data: data1,
|
||||
unmarshalToObj: &A1{},
|
||||
wantValue: &v1,
|
||||
},
|
||||
{
|
||||
name: "slice of interface type",
|
||||
data: data2,
|
||||
unmarshalToObj: &A2{},
|
||||
wantValue: &v2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err = decMode.Unmarshal(tc.data, tc.unmarshalToObj)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned error %v", tc.data, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.unmarshalToObj, tc.wantValue) {
|
||||
t.Errorf("Unmarshal(0x%x) = %v, want %v", tc.data, tc.unmarshalToObj, tc.wantValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -7698,7 +7698,7 @@ type A2 struct {
|
||||
Fields []B
|
||||
}
|
||||
|
||||
func TestUnmarshalRegisteredTagToInterface(t *testing.T) {
|
||||
func TestUnmarshalRegisteredTagToConcreteType(t *testing.T) {
|
||||
var err error
|
||||
tags := NewTagSet()
|
||||
err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(C{}), 279)
|
||||
@ -7719,36 +7719,18 @@ func TestUnmarshalRegisteredTagToInterface(t *testing.T) {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v1, err)
|
||||
}
|
||||
|
||||
v2 := A2{Fields: []B{&C{Field: 5}, &D{Field: "a"}}}
|
||||
data2, err := encMode.Marshal(v2)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v2, err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
unmarshalToObj interface{}
|
||||
wantValue interface{}
|
||||
}{
|
||||
{
|
||||
name: "interface type",
|
||||
data: data1,
|
||||
unmarshalToObj: &A1{},
|
||||
wantValue: &v1,
|
||||
},
|
||||
{
|
||||
name: "concrete type",
|
||||
data: data1,
|
||||
unmarshalToObj: &A1{Field: &C{}},
|
||||
wantValue: &v1,
|
||||
},
|
||||
{
|
||||
name: "slice of interface type",
|
||||
data: data2,
|
||||
unmarshalToObj: &A2{},
|
||||
wantValue: &v2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
18
decode_tinygo.go
Normal file
18
decode_tinygo.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build tinygo
|
||||
|
||||
package cbor
|
||||
|
||||
import "reflect"
|
||||
|
||||
// tinygo v0.33 doesn't implement Type.AssignableTo() and it panics.
|
||||
// Type.AssignableTo() is used under the hood for Type.Implements().
|
||||
//
|
||||
// More details in https://github.com/tinygo-org/tinygo/issues/4277.
|
||||
//
|
||||
// implements() always returns false until tinygo implements Type.AssignableTo().
|
||||
func implements(concreteType reflect.Type, interfaceType reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
@ -50,3 +50,62 @@ func TestUnmarshalDeepNesting(t *testing.T) {
|
||||
&readback, root)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRegisteredTagToInterface(t *testing.T) {
|
||||
var err error
|
||||
tags := NewTagSet()
|
||||
err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(C{}), 279)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(D{}), 280)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
encMode, _ := PreferredUnsortedEncOptions().EncModeWithTags(tags)
|
||||
decMode, _ := DecOptions{}.DecModeWithTags(tags)
|
||||
|
||||
v1 := A1{Field: &C{Field: 5}}
|
||||
data1, err := encMode.Marshal(v1)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v1, err)
|
||||
}
|
||||
|
||||
v2 := A2{Fields: []B{&C{Field: 5}, &D{Field: "a"}}}
|
||||
data2, err := encMode.Marshal(v2)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returned error %v", v2, err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
unmarshalToObj interface{}
|
||||
wantValue interface{}
|
||||
}{
|
||||
{
|
||||
name: "interface type",
|
||||
data: data1,
|
||||
unmarshalToObj: &A1{},
|
||||
wantValue: &v1,
|
||||
},
|
||||
{
|
||||
name: "slice of interface type",
|
||||
data: data2,
|
||||
unmarshalToObj: &A2{},
|
||||
wantValue: &v2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err = decMode.Unmarshal(tc.data, tc.unmarshalToObj)
|
||||
if err == nil {
|
||||
t.Errorf("Unmarshal(0x%x) returned no error, expect error", tc.data)
|
||||
} else if _, ok := err.(*UnmarshalTypeError); !ok {
|
||||
t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.data, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user