Prechádzať zdrojové kódy

rewrite how eface and iface are handled

Tao Wen 7 rokov pred
rodič
commit
2fcbb23d96

+ 10 - 0
api_tests/config_test.go

@@ -35,4 +35,14 @@ func Test_customize_tag_key(t *testing.T) {
 	str, err := json.MarshalToString(TestObject{"hello"})
 	should.Nil(err)
 	should.Equal(`{"field":"hello"}`, str)
+}
+
+func Test_read_large_number_as_interface(t *testing.T) {
+	should := require.New(t)
+	var val interface{}
+	err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
+	should.Nil(err)
+	output, err := jsoniter.MarshalToString(val)
+	should.Nil(err)
+	should.Equal(`123456789123456789123456789`, output)
 }

+ 0 - 6
config.go

@@ -2,7 +2,6 @@ package jsoniter
 
 import (
 	"encoding/json"
-	"errors"
 	"io"
 	"reflect"
 	"unsafe"
@@ -267,11 +266,6 @@ func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
 	data = data[:lastNotSpacePos(data)]
 	iter := cfg.BorrowIterator(data)
 	defer cfg.ReturnIterator(iter)
-	typ := reflect.TypeOf(v)
-	if typ.Kind() != reflect.Ptr {
-		// return non-pointer error
-		return errors.New("the second param must be ptr type")
-	}
 	iter.ReadVal(v)
 	if iter.head == iter.tail {
 		iter.loadMore()

+ 4 - 4
misc_tests/jsoniter_float_test.go

@@ -49,13 +49,13 @@ func Test_read_float64_cursor(t *testing.T) {
 func Test_read_float_scientific(t *testing.T) {
 	should := require.New(t)
 	var obj interface{}
-	should.Nil(jsoniter.UnmarshalFromString(`1e1`, &obj))
+	should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj))
 	should.Equal(float64(10), obj)
-	should.Nil(json.Unmarshal([]byte(`1e1`), &obj))
+	should.NoError(json.Unmarshal([]byte(`1e1`), &obj))
 	should.Equal(float64(10), obj)
-	should.Nil(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
+	should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
 	should.Equal(float64(10), obj)
-	should.Nil(json.Unmarshal([]byte(`1.0e1`), &obj))
+	should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj))
 	should.Equal(float64(10), obj)
 }
 

+ 12 - 58
misc_tests/jsoniter_interface_test.go

@@ -2,36 +2,15 @@ package misc_tests
 
 import (
 	"encoding/json"
-	"fmt"
 	"testing"
 	"github.com/stretchr/testify/require"
 	"github.com/json-iterator/go"
+	"io"
 )
 
-type MyInterface interface {
-	Hello() string
-}
-
-type MyString string
-
-func (ms MyString) Hello() string {
-	return string(ms)
-}
-
-func Test_decode_object_contain_non_empty_interface(t *testing.T) {
-	type TestObject struct {
-		Field MyInterface
-	}
-	should := require.New(t)
-	obj := TestObject{}
-	obj.Field = MyString("abc")
-	should.Nil(jsoniter.UnmarshalFromString(`{"Field": "hello"}`, &obj))
-	should.Equal(MyString("hello"), obj.Field)
-}
-
 func Test_nil_non_empty_interface(t *testing.T) {
 	type TestObject struct {
-		Field []MyInterface
+		Field []io.Closer
 	}
 	should := require.New(t)
 	obj := TestObject{}
@@ -40,31 +19,6 @@ func Test_nil_non_empty_interface(t *testing.T) {
 	should.NotNil(jsoniter.Unmarshal(b, &obj))
 }
 
-func Test_read_large_number_as_interface(t *testing.T) {
-	should := require.New(t)
-	var val interface{}
-	err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
-	should.Nil(err)
-	output, err := jsoniter.MarshalToString(val)
-	should.Nil(err)
-	should.Equal(`123456789123456789123456789`, output)
-}
-
-func Test_unmarshal_ptr_to_interface(t *testing.T) {
-	type TestData struct {
-		Name string `json:"name"`
-	}
-	should := require.New(t)
-	var obj interface{} = &TestData{}
-	err := json.Unmarshal([]byte(`{"name":"value"}`), &obj)
-	should.Nil(err)
-	should.Equal("&{value}", fmt.Sprintf("%v", obj))
-	obj = interface{}(&TestData{})
-	err = jsoniter.Unmarshal([]byte(`{"name":"value"}`), &obj)
-	should.Nil(err)
-	should.Equal("&{value}", fmt.Sprintf("%v", obj))
-}
-
 func Test_nil_out_null_interface(t *testing.T) {
 	type TestData struct {
 		Field interface{} `json:"field"`
@@ -86,7 +40,7 @@ func Test_nil_out_null_interface(t *testing.T) {
 
 	err = jsoniter.Unmarshal(data2, &obj)
 	should.NoError(err)
-	should.Equal(nil, obj.Field)
+	should.Nil(obj.Field)
 
 	// Checking stdlib behavior matches.
 	obj2 := TestData{
@@ -118,12 +72,12 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
 	}
 
 	err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
-	should.Equal(nil, err)
+	should.NoError(err)
 	should.Equal(&payload, wrapper.Payload)
 	should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
 
 	err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
-	should.Equal(nil, err)
+	should.NoError(err)
 	should.Equal(&payload, wrapper.Payload)
 	should.Equal((*Payload)(nil), payload)
 
@@ -138,7 +92,7 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
 	should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
 
 	err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
-	should.Equal(nil, err)
+	should.NoError(err)
 	should.Equal(&payload, wrapper.Payload)
 	should.Equal((*Payload)(nil), payload)
 }
@@ -159,11 +113,11 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) {
 	}
 
 	err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
-	should.Equal(nil, err)
+	should.NoError(err)
 	should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
 
 	err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
-	should.Equal(nil, err)
+	should.NoError(err)
 	should.Equal(nil, wrapper.Payload)
 	should.Equal(42, payload.Value)
 
@@ -198,12 +152,12 @@ func Test_unmarshal_into_nil(t *testing.T) {
 	}
 
 	err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
-	should.Nil(err)
+	should.NoError(err)
 	should.NotNil(wrapper.Payload)
 	should.Nil(payload)
 
 	err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
-	should.Nil(err)
+	should.NoError(err)
 	should.Nil(wrapper.Payload)
 	should.Nil(payload)
 
@@ -213,12 +167,12 @@ func Test_unmarshal_into_nil(t *testing.T) {
 	}
 
 	err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
-	should.Nil(err)
+	should.NoError(err)
 	should.NotNil(wrapper.Payload)
 	should.Nil(payload)
 
 	err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
-	should.Nil(err)
+	should.NoError(err)
 	should.Nil(wrapper.Payload)
 	should.Nil(payload)
 }

+ 13 - 33
reflect.go

@@ -36,14 +36,17 @@ type checkIsEmpty interface {
 // ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
 func (iter *Iterator) ReadVal(obj interface{}) {
 	typ := reflect.TypeOf(obj)
-	cacheKey := typ.Elem()
-	decoder := decoderOfType(iter.cfg, "", cacheKey)
-	e := (*emptyInterface)(unsafe.Pointer(&obj))
-	if e.word == nil {
+	if typ.Kind() != reflect.Ptr {
+		iter.ReportError("ReadVal", "can only unmarshal into pointer")
+		return
+	}
+	decoder := iter.cfg.DecoderOf(typ)
+	ptr := reflect2.PtrOf(obj)
+	if ptr == nil {
 		iter.ReportError("ReadVal", "can not read into nil pointer")
 		return
 	}
-	decoder.Decode(e.word, iter)
+	decoder.Decode(ptr, iter)
 }
 
 // WriteVal copy the go interface into underlying JSON, same as json.Marshal
@@ -63,7 +66,7 @@ func (cfg *frozenConfig) DecoderOf(typ reflect.Type) ValDecoder {
 	if decoder != nil {
 		return decoder
 	}
-	decoder = decoderOfType(cfg, "", typ)
+	decoder = decoderOfType(cfg, "", typ.Elem())
 	cfg.addDecoderToCache(cacheKey, decoder)
 	return decoder
 }
@@ -106,10 +109,11 @@ func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
 	}
 	switch typ.Kind() {
 	case reflect.Interface:
-		if typ.NumMethod() == 0 {
-			return &emptyInterfaceCodec{}
+		if typ.NumMethod() > 0 {
+			ifaceType := reflect2.Type2(typ).(*reflect2.UnsafeIFaceType)
+			return &ifaceDecoder{valType: ifaceType}
 		}
-		return &nonEmptyInterfaceCodec{}
+		return &efaceDecoder{}
 	case reflect.Struct:
 		return decoderOfStruct(cfg, prefix, typ)
 	case reflect.Array:
@@ -239,27 +243,3 @@ func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
 func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
 	return false
 }
-
-func extractInterface(val interface{}) emptyInterface {
-	return *((*emptyInterface)(unsafe.Pointer(&val)))
-}
-
-// emptyInterface is the header for an interface{} value.
-type emptyInterface struct {
-	typ  unsafe.Pointer
-	word unsafe.Pointer
-}
-
-// emptyInterface is the header for an interface with method (not interface{})
-type nonEmptyInterface struct {
-	// see ../runtime/iface.go:/Itab
-	itab *struct {
-		ityp   unsafe.Pointer // static interface type
-		typ    unsafe.Pointer // dynamic concrete type
-		link   unsafe.Pointer
-		bad    int32
-		unused int32
-		fun    [100000]unsafe.Pointer // method table
-	}
-	word unsafe.Pointer
-}

+ 70 - 0
reflect_dynamic.go

@@ -0,0 +1,70 @@
+package jsoniter
+
+import (
+	"github.com/v2pro/plz/reflect2"
+	"unsafe"
+	"reflect"
+)
+
+type dynamicEncoder struct {
+	valType reflect2.Type
+}
+
+func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
+	obj := encoder.valType.UnsafeIndirect(ptr)
+	stream.WriteVal(obj)
+}
+
+func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
+	return encoder.valType.UnsafeIndirect(ptr) == nil
+}
+
+type efaceDecoder struct {
+}
+
+func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
+	pObj := (*interface{})(ptr)
+	obj := *pObj
+	if obj == nil {
+		*pObj = iter.Read()
+		return
+	}
+	typ := reflect2.TypeOf(obj)
+	if typ.Kind() != reflect.Ptr {
+		*pObj = iter.Read()
+		return
+	}
+	ptrType := typ.(*reflect2.UnsafePtrType)
+	ptrElemType := ptrType.Elem()
+	if iter.WhatIsNext() == NilValue {
+		if ptrElemType.Kind() != reflect.Ptr {
+			iter.skipFourBytes('n', 'u', 'l', 'l')
+			*pObj = nil
+			return
+		}
+	}
+	if reflect2.IsNil(obj) {
+		obj := ptrElemType.New()
+		iter.ReadVal(obj)
+		*pObj = obj
+		return
+	}
+	iter.ReadVal(obj)
+}
+
+type ifaceDecoder struct {
+	valType *reflect2.UnsafeIFaceType
+}
+
+func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
+	if iter.ReadNil() {
+		decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew())
+		return
+	}
+	obj := decoder.valType.UnsafeIndirect(ptr)
+	if reflect2.IsNil(obj) {
+		iter.ReportError("decode non empty interface", "can not unmarshal into nil")
+		return
+	}
+	iter.ReadVal(obj)
+}

+ 3 - 2
reflect_extension.go

@@ -7,6 +7,7 @@ import (
 	"strings"
 	"unicode"
 	"unsafe"
+	"github.com/v2pro/plz/reflect2"
 )
 
 var typeDecoders = map[string]ValDecoder{}
@@ -240,7 +241,7 @@ func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecode
 	if typ.Kind() == reflect.Ptr {
 		decoder := typeDecoders[typ.Elem().String()]
 		if decoder != nil {
-			return &OptionalDecoder{typ.Elem(), decoder}
+			return &OptionalDecoder{reflect2.Type2(typ.Elem()), decoder}
 		}
 	}
 	return nil
@@ -317,7 +318,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
 					binding.Encoder = &dereferenceEncoder{binding.Encoder}
 					binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
-					binding.Decoder = &dereferenceDecoder{field.Type.Elem(), binding.Decoder}
+					binding.Decoder = &dereferenceDecoder{reflect2.Type2(field.Type.Elem()), binding.Decoder}
 					binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
 					embeddedBindings = append(embeddedBindings, binding)
 				}

+ 0 - 106
reflect_native.go

@@ -435,112 +435,6 @@ func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
 	return !(*((*bool)(ptr)))
 }
 
-type emptyInterfaceCodec struct {
-}
-
-func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
-	existing := *((*interface{})(ptr))
-
-	// Checking for both typed and untyped nil pointers.
-	if existing != nil &&
-		reflect.TypeOf(existing).Kind() == reflect.Ptr &&
-		!reflect.ValueOf(existing).IsNil() {
-
-		var ptrToExisting interface{}
-		for {
-			elem := reflect.ValueOf(existing).Elem()
-			if elem.Kind() != reflect.Ptr || elem.IsNil() {
-				break
-			}
-			ptrToExisting = existing
-			existing = elem.Interface()
-		}
-
-		if iter.ReadNil() {
-			if ptrToExisting != nil {
-				nilPtr := reflect.Zero(reflect.TypeOf(ptrToExisting).Elem())
-				reflect.ValueOf(ptrToExisting).Elem().Set(nilPtr)
-			} else {
-				*((*interface{})(ptr)) = nil
-			}
-		} else {
-			iter.ReadVal(existing)
-		}
-
-		return
-	}
-
-	if iter.ReadNil() {
-		*((*interface{})(ptr)) = nil
-	} else {
-		*((*interface{})(ptr)) = iter.Read()
-	}
-}
-
-func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
-	obj := *((*interface{})(ptr))
-	stream.WriteVal(obj)
-}
-
-func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
-	emptyInterface := (*emptyInterface)(ptr)
-	return emptyInterface.typ == nil
-}
-
-type nonEmptyInterfaceCodec struct {
-}
-
-func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
-	if iter.WhatIsNext() == NilValue {
-		iter.skipFourBytes('n', 'u', 'l', 'l')
-		*((*interface{})(ptr)) = nil
-		return
-	}
-	nonEmptyInterface := (*nonEmptyInterface)(ptr)
-	if nonEmptyInterface.itab == nil {
-		iter.ReportError("read non-empty interface", "do not know which concrete type to decode to")
-		return
-	}
-	var i interface{}
-	e := (*emptyInterface)(unsafe.Pointer(&i))
-	e.typ = nonEmptyInterface.itab.typ
-	e.word = nonEmptyInterface.word
-	iter.ReadVal(&i)
-	if e.word == nil {
-		nonEmptyInterface.itab = nil
-	}
-	nonEmptyInterface.word = e.word
-}
-
-func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
-	nonEmptyInterface := (*nonEmptyInterface)(ptr)
-	var i interface{}
-	if nonEmptyInterface.itab != nil {
-		e := (*emptyInterface)(unsafe.Pointer(&i))
-		e.typ = nonEmptyInterface.itab.typ
-		e.word = nonEmptyInterface.word
-	}
-	stream.WriteVal(i)
-}
-
-func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
-	nonEmptyInterface := (*nonEmptyInterface)(ptr)
-	return nonEmptyInterface.word == nil
-}
-
-type dynamicEncoder struct {
-	valType reflect2.Type
-}
-
-func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
-	obj := encoder.valType.UnsafeIndirect(ptr)
-	stream.WriteVal(obj)
-}
-
-func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
-	return encoder.valType.UnsafeIndirect(ptr) == nil
-}
-
 type base64Codec struct {
 	sliceType *reflect2.UnsafeSliceType
 	sliceDecoder ValDecoder

+ 12 - 11
reflect_optional.go

@@ -3,12 +3,16 @@ package jsoniter
 import (
 	"reflect"
 	"unsafe"
+	"github.com/v2pro/plz/reflect2"
 )
 
 func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
 	elemType := typ.Elem()
 	decoder := decoderOfType(cfg, prefix, elemType)
-	return &OptionalDecoder{elemType, decoder}
+	if prefix == "" && elemType.Kind() == reflect.Ptr {
+		return &dereferenceDecoder{reflect2.Type2(elemType), decoder}
+	}
+	return &OptionalDecoder{reflect2.Type2(elemType), decoder}
 }
 
 func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
@@ -19,7 +23,7 @@ func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEn
 }
 
 type OptionalDecoder struct {
-	ValueType    reflect.Type
+	ValueType    reflect2.Type
 	ValueDecoder ValDecoder
 }
 
@@ -28,12 +32,10 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
 		*((*unsafe.Pointer)(ptr)) = nil
 	} else {
 		if *((*unsafe.Pointer)(ptr)) == nil {
-			// TODO: use reflect2 instead
 			//pointer to null, we have to allocate memory to hold the value
-			value := reflect.New(decoder.ValueType)
-			newPtr := extractInterface(value.Interface()).word
+			newPtr := decoder.ValueType.UnsafeNew()
 			decoder.ValueDecoder.Decode(newPtr, iter)
-			*((*uintptr)(ptr)) = uintptr(newPtr)
+			*((*unsafe.Pointer)(ptr)) = newPtr
 		} else {
 			//reuse existing instance
 			decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
@@ -43,17 +45,16 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
 
 type dereferenceDecoder struct {
 	// only to deference a pointer
-	valueType    reflect.Type
+	valueType    reflect2.Type
 	valueDecoder ValDecoder
 }
 
 func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
 	if *((*unsafe.Pointer)(ptr)) == nil {
 		//pointer to null, we have to allocate memory to hold the value
-		value := reflect.New(decoder.valueType)
-		newPtr := extractInterface(value.Interface()).word
+		newPtr := decoder.valueType.UnsafeNew()
 		decoder.valueDecoder.Decode(newPtr, iter)
-		*((*uintptr)(ptr)) = uintptr(newPtr)
+		*((*unsafe.Pointer)(ptr)) = newPtr
 	} else {
 		//reuse existing instance
 		decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
@@ -127,4 +128,4 @@ type referenceDecoder struct {
 
 func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
 	decoder.decoder.Decode(unsafe.Pointer(&ptr), iter)
-}
+}

+ 78 - 0
value_tests/eface_test.go

@@ -0,0 +1,78 @@
+package test
+
+func init() {
+	var pEFace = func(val interface{}) *interface{} {
+		return &val
+	}
+	var pInt = func(val int) *int {
+		return &val
+	}
+	unmarshalCases = append(unmarshalCases, unmarshalCase{
+		ptr: (**interface{})(nil),
+		input: `"hello"`,
+	}, unmarshalCase{
+		ptr: (**interface{})(nil),
+		input: `1e1`,
+	}, unmarshalCase{
+		ptr: (**interface{})(nil),
+		input: `1.0e1`,
+	}, unmarshalCase{
+		ptr: (*[]interface{})(nil),
+		input: `[1.0e1]`,
+	}, unmarshalCase{
+		ptr: (*struct {
+			Field interface{}
+		})(nil),
+		input: `{"field":"hello"}`,
+	}, unmarshalCase{
+		obj: func() interface{} {
+			type TestData struct {
+				Name string `json:"name"`
+			}
+			o := &TestData{}
+			return &o
+		},
+		input: `{"name":"value"}`,
+	}, unmarshalCase{
+		obj: func() interface{} {
+			b := true
+			return &struct {
+				Field interface{} `json:"field"`
+			}{&b}
+		},
+		input: `{"field": null}`,
+	}, unmarshalCase{
+		obj: func() interface{} {
+			var pb *bool
+			return &struct {
+				Field interface{} `json:"field"`
+			}{&pb}
+		},
+		input: `{"field": null}`,
+	}, unmarshalCase{
+		obj: func() interface{} {
+			b := true
+			pb := &b
+			return &struct {
+				Field interface{} `json:"field"`
+			}{&pb}
+		},
+		input: `{"field": null}`,
+	})
+	marshalCases = append(marshalCases,
+		pEFace("hello"),
+		struct {
+			Field interface{}
+		}{"hello"},
+		struct {
+			Field interface{}
+		}{struct{
+			field chan int
+		}{}},
+		struct {
+			Field interface{}
+		}{struct{
+			Field *int
+		}{pInt(100)}},
+	)
+}

+ 45 - 0
value_tests/iface_test.go

@@ -0,0 +1,45 @@
+package test
+
+import "io"
+
+func init() {
+	var pCloser1 = func(str string) *io.Closer {
+		closer := io.Closer(strCloser1(str))
+		return &closer
+	}
+	var pCloser2 = func(str string) *io.Closer {
+		strCloser := strCloser2(str)
+		closer := io.Closer(&strCloser)
+		return &closer
+	}
+	marshalCases = append(marshalCases,
+		pCloser1("hello"),
+		pCloser2("hello"),
+	)
+	unmarshalCases = append(unmarshalCases, unmarshalCase{
+		ptr: (*[]io.Closer)(nil),
+		input: "[null]",
+	}, unmarshalCase{
+		obj: func() interface{} {
+			strCloser := strCloser2("")
+			return &struct {
+				Field io.Closer
+			}{
+				&strCloser,
+			}
+		},
+		input: `{"Field": "hello"}`,
+	})
+}
+
+type strCloser1 string
+
+func (closer strCloser1) Close() error {
+	return nil
+}
+
+type strCloser2 string
+
+func (closer *strCloser2) Close() error {
+	return nil
+}

+ 28 - 14
value_tests/ptr_test.go

@@ -1,25 +1,39 @@
 package test
 
 func init() {
-	var pEFace = func(val interface{}) *interface{} {
-		return &val
-	}
 	var pInt = func(val int) *int {
 		return &val
 	}
-	unmarshalCases = append(unmarshalCases, unmarshalCase{
-		ptr: (**interface{})(nil),
-		input: `"hello"`,
-	}, unmarshalCase{
-		ptr: (**interface{})(nil),
-		input: `1e1`,
-	}, unmarshalCase{
-		ptr: (**interface{})(nil),
-		input: `1.0e1`,
-	})
 	marshalCases = append(marshalCases,
-		pEFace("hello"),
 		(*int)(nil),
 		pInt(100),
 	)
+	unmarshalCases = append(unmarshalCases, unmarshalCase{
+		obj: func() interface{} {
+			var i int
+			return &i
+		},
+		input: "null",
+	}, unmarshalCase{
+		obj: func() interface{} {
+			var i *int
+			return &i
+		},
+		input: "10",
+	}, unmarshalCase{
+		obj: func() interface{} {
+			var i int
+			pi := &i
+			return &pi
+		},
+		input: "null",
+	}, unmarshalCase{
+		obj: func() interface{} {
+			var i int
+			pi := &i
+			ppi := &pi
+			return &ppi
+		},
+		input: "null",
+	})
 }

+ 26 - 9
value_tests/value_test.go

@@ -10,8 +10,10 @@ import (
 )
 
 type unmarshalCase struct {
+	obj func() interface{}
 	ptr interface{}
 	input string
+	selected bool
 }
 
 var unmarshalCases []unmarshalCase
@@ -25,16 +27,31 @@ type selectedMarshalCase struct  {
 }
 
 func Test_unmarshal(t *testing.T) {
-	should := require.New(t)
 	for _, testCase := range unmarshalCases {
-		valType := reflect.TypeOf(testCase.ptr).Elem()
-		ptr1Val := reflect.New(valType)
-		err1 := json.Unmarshal([]byte(testCase.input), ptr1Val.Interface())
-		should.NoError(err1)
-		ptr2Val := reflect.New(valType)
-		err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), ptr2Val.Interface())
-		should.NoError(err2)
-		should.Equal(ptr1Val.Interface(), ptr2Val.Interface())
+		if testCase.selected {
+			unmarshalCases = []unmarshalCase{testCase}
+			break
+		}
+	}
+	for i, testCase := range unmarshalCases {
+		t.Run(fmt.Sprintf("[%v]%s", i, testCase.input), func(t *testing.T) {
+			should := require.New(t)
+			var obj1 interface{}
+			var obj2 interface{}
+			if testCase.obj != nil {
+				obj1 = testCase.obj()
+				obj2 = testCase.obj()
+			} else {
+				valType := reflect.TypeOf(testCase.ptr).Elem()
+				obj1 = reflect.New(valType).Interface()
+				obj2 = reflect.New(valType).Interface()
+			}
+			err1 := json.Unmarshal([]byte(testCase.input), obj1)
+			should.NoError(err1, "json")
+			err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), obj2)
+			should.NoError(err2, "jsoniter")
+			should.Equal(obj1, obj2)
+		})
 	}
 }