2017-11-20 09:30:52 -05:00
|
|
|
package jsoniter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-07-25 04:17:02 -04:00
|
|
|
"github.com/modern-go/reflect2"
|
2017-11-20 09:30:52 -05:00
|
|
|
"io"
|
|
|
|
"reflect"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
2018-07-25 04:17:02 -04:00
|
|
|
func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder {
|
2017-11-20 09:30:52 -05:00
|
|
|
type bindingTo struct {
|
|
|
|
binding *Binding
|
|
|
|
toName string
|
|
|
|
ignored bool
|
|
|
|
}
|
|
|
|
orderedBindings := []*bindingTo{}
|
2018-07-25 04:17:02 -04:00
|
|
|
structDescriptor := describeStruct(ctx, typ)
|
2017-11-20 09:30:52 -05:00
|
|
|
for _, binding := range structDescriptor.Fields {
|
|
|
|
for _, toName := range binding.ToNames {
|
|
|
|
new := &bindingTo{
|
|
|
|
binding: binding,
|
|
|
|
toName: toName,
|
|
|
|
}
|
|
|
|
for _, old := range orderedBindings {
|
|
|
|
if old.toName != toName {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-25 04:17:02 -04:00
|
|
|
old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
orderedBindings = append(orderedBindings, new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(orderedBindings) == 0 {
|
2018-07-25 04:17:02 -04:00
|
|
|
return &emptyStructEncoder{}
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
finalOrderedFields := []structFieldTo{}
|
|
|
|
for _, bindingTo := range orderedBindings {
|
|
|
|
if !bindingTo.ignored {
|
|
|
|
finalOrderedFields = append(finalOrderedFields, structFieldTo{
|
|
|
|
encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
|
|
|
|
toName: bindingTo.toName,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-07-25 04:17:02 -04:00
|
|
|
return &structEncoder{typ, finalOrderedFields}
|
|
|
|
}
|
|
|
|
|
|
|
|
func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty {
|
|
|
|
encoder := createEncoderOfNative(ctx, typ)
|
|
|
|
if encoder != nil {
|
|
|
|
return encoder
|
|
|
|
}
|
|
|
|
kind := typ.Kind()
|
|
|
|
switch kind {
|
|
|
|
case reflect.Interface:
|
|
|
|
return &dynamicEncoder{typ}
|
|
|
|
case reflect.Struct:
|
|
|
|
return &structEncoder{typ: typ}
|
|
|
|
case reflect.Array:
|
|
|
|
return &arrayEncoder{}
|
|
|
|
case reflect.Slice:
|
|
|
|
return &sliceEncoder{}
|
|
|
|
case reflect.Map:
|
|
|
|
return encoderOfMap(ctx, typ)
|
|
|
|
case reflect.Ptr:
|
|
|
|
return &OptionalEncoder{}
|
|
|
|
default:
|
|
|
|
return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)}
|
|
|
|
}
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
|
2018-07-25 04:17:02 -04:00
|
|
|
newTagged := new.Field.Tag().Get(cfg.getTagKey()) != ""
|
|
|
|
oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != ""
|
2017-11-20 09:30:52 -05:00
|
|
|
if newTagged {
|
|
|
|
if oldTagged {
|
|
|
|
if len(old.levels) > len(new.levels) {
|
|
|
|
return true, false
|
|
|
|
} else if len(new.levels) > len(old.levels) {
|
|
|
|
return false, true
|
|
|
|
} else {
|
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return true, false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if oldTagged {
|
|
|
|
return true, false
|
|
|
|
}
|
|
|
|
if len(old.levels) > len(new.levels) {
|
|
|
|
return true, false
|
|
|
|
} else if len(new.levels) > len(old.levels) {
|
|
|
|
return false, true
|
|
|
|
} else {
|
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type structFieldEncoder struct {
|
2018-07-25 04:17:02 -04:00
|
|
|
field reflect2.StructField
|
2017-11-20 09:30:52 -05:00
|
|
|
fieldEncoder ValEncoder
|
|
|
|
omitempty bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
2018-07-25 04:17:02 -04:00
|
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
2017-11-20 09:30:52 -05:00
|
|
|
encoder.fieldEncoder.Encode(fieldPtr, stream)
|
|
|
|
if stream.Error != nil && stream.Error != io.EOF {
|
2018-07-25 04:17:02 -04:00
|
|
|
stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error())
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
2018-07-25 04:17:02 -04:00
|
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
2017-11-20 09:30:52 -05:00
|
|
|
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
|
|
|
}
|
|
|
|
|
2018-07-25 04:17:02 -04:00
|
|
|
func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
|
|
|
isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
|
|
|
|
if !converted {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
|
|
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
|
|
|
}
|
|
|
|
|
|
|
|
type IsEmbeddedPtrNil interface {
|
|
|
|
IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
|
|
|
|
}
|
|
|
|
|
2017-11-20 09:30:52 -05:00
|
|
|
type structEncoder struct {
|
2018-07-25 04:17:02 -04:00
|
|
|
typ reflect2.Type
|
|
|
|
fields []structFieldTo
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type structFieldTo struct {
|
|
|
|
encoder *structFieldEncoder
|
|
|
|
toName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
|
|
stream.WriteObjectStart()
|
|
|
|
isNotFirst := false
|
|
|
|
for _, field := range encoder.fields {
|
|
|
|
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
|
|
|
continue
|
|
|
|
}
|
2018-07-25 04:17:02 -04:00
|
|
|
if field.encoder.IsEmbeddedPtrNil(ptr) {
|
|
|
|
continue
|
|
|
|
}
|
2017-11-20 09:30:52 -05:00
|
|
|
if isNotFirst {
|
|
|
|
stream.WriteMore()
|
|
|
|
}
|
|
|
|
stream.WriteObjectField(field.toName)
|
|
|
|
field.encoder.Encode(ptr, stream)
|
|
|
|
isNotFirst = true
|
|
|
|
}
|
|
|
|
stream.WriteObjectEnd()
|
2018-07-25 04:17:02 -04:00
|
|
|
if stream.Error != nil && stream.Error != io.EOF {
|
|
|
|
stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error())
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
type emptyStructEncoder struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
|
|
stream.WriteEmptyObject()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
|
|
return false
|
|
|
|
}
|
2018-07-25 04:17:02 -04:00
|
|
|
|
|
|
|
type stringModeNumberEncoder struct {
|
|
|
|
elemEncoder ValEncoder
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
|
|
stream.writeByte('"')
|
|
|
|
encoder.elemEncoder.Encode(ptr, stream)
|
|
|
|
stream.writeByte('"')
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
|
|
return encoder.elemEncoder.IsEmpty(ptr)
|
|
|
|
}
|
|
|
|
|
|
|
|
type stringModeStringEncoder struct {
|
|
|
|
elemEncoder ValEncoder
|
|
|
|
cfg *frozenConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
|
|
tempStream := encoder.cfg.BorrowStream(nil)
|
[20.10] vendor: github.com/json-iterator/go v1.1.12 for Go 1.18 compatibility
full diff: https://github.com/json-iterator/go/compare/0ff49de124c6f76f8494e194af75bde0f1a49a29...024077e996b048517130b21ea6bf12aa23055d3d
Fixes a nil-pointer exception on go 1.18;
```
=== FAIL: cli/context/kubernetes TestSaveLoadContexts (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x40fcbc]
goroutine 19 [running]:
testing.tRunner.func1.2({0xa7e080, 0x1073930})
/usr/local/go/src/testing/testing.go:1389 +0x24e
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1392 +0x39f
panic({0xa7e080, 0x1073930})
/usr/local/go/src/runtime/panic.go:838 +0x207
reflect.mapiternext(0x40?)
/usr/local/go/src/runtime/map.go:1378 +0x19
github.com/docker/cli/vendor/github.com/modern-go/reflect2.(*UnsafeMapIterator).UnsafeNext(0x8?)
/go/src/github.com/docker/cli/vendor/github.com/modern-go/reflect2/unsafe_map.go:136 +0x32
github.com/docker/cli/vendor/github.com/json-iterator/go.(*sortKeysMapEncoder).Encode(0xc000478930, 0xc0000ca3a8, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_map.go:293 +0x335
github.com/docker/cli/vendor/github.com/json-iterator/go.(*placeholderEncoder).Encode(0xc00046c898?, 0x95d787?, 0xc0000bae58?)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect.go:327 +0x22
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structFieldEncoder).Encode(0xc000482630, 0xa2790c?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:110 +0x56
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structEncoder).Encode(0xc000482780, 0xb3a599?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:158 +0x652
github.com/docker/cli/vendor/github.com/json-iterator/go.(*placeholderEncoder).Encode(0xc00046ca10?, 0x95d787?, 0xc0000bae58?)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect.go:327 +0x22
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structFieldEncoder).Encode(0xc0004829f0, 0xa0fd11?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:110 +0x56
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structEncoder).Encode(0xc000482a50, 0x40aa15?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:158 +0x652
github.com/docker/cli/vendor/github.com/json-iterator/go.(*sliceEncoder).Encode(0xc00047e198, 0xc0003a83c8, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_slice.go:38 +0x2bb
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structFieldEncoder).Encode(0xc0004837a0, 0xa12e12?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:110 +0x56
github.com/docker/cli/vendor/github.com/json-iterator/go.(*structEncoder).Encode(0xc000483890, 0x0?, 0xc0000bae40)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_struct_encoder.go:158 +0x652
github.com/docker/cli/vendor/github.com/json-iterator/go.(*OptionalEncoder).Encode(0xc0003b6be0?, 0x0?, 0x0?)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect_optional.go:74 +0xa4
github.com/docker/cli/vendor/github.com/json-iterator/go.(*onePtrEncoder).Encode(0xc000471e30, 0xc0003a8370, 0xc000460720?)
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect.go:214 +0x82
github.com/docker/cli/vendor/github.com/json-iterator/go.(*Stream).WriteVal(0xc0000bae40, {0xabe4a0, 0xc0003a8370})
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/reflect.go:93 +0x158
github.com/docker/cli/vendor/github.com/json-iterator/go.(*frozenConfig).Marshal(0xc0003b6be0, {0xabe4a0, 0xc0003a8370})
/go/src/github.com/docker/cli/vendor/github.com/json-iterator/go/config.go:299 +0xc9
github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime/serializer/json.(*Serializer).Encode(0xc00043aee0?, {0xc375c0?, 0xc0003a8370?}, {0xc339e0, 0xc000460210})
/go/src/github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go:310 +0x6d
github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning.(*codec).Encode(0xc0000f8480, {0xc37570?, 0xc0000bacc0}, {0xc339e0, 0xc000460210})
/go/src/github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go:231 +0x926
github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime.Encode({0x7f48b36ce5c0, 0xc0000f8480}, {0xc37570, 0xc0000bacc0})
/go/src/github.com/docker/cli/vendor/k8s.io/apimachinery/pkg/runtime/codec.go:46 +0x64
github.com/docker/cli/vendor/k8s.io/client-go/tools/clientcmd.Write(...)
/go/src/github.com/docker/cli/vendor/k8s.io/client-go/tools/clientcmd/loader.go:469
github.com/docker/cli/cli/context/kubernetes.TestSaveLoadContexts(0xc0004561a0?)
/go/src/github.com/docker/cli/cli/context/kubernetes/endpoint_test.go:75 +0xf0a
testing.tRunner(0xc0004561a0, 0xb89ea0)
/usr/local/go/src/testing/testing.go:1439 +0x102
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:1486 +0x35f
```
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-08-19 10:12:25 -04:00
|
|
|
tempStream.Attachment = stream.Attachment
|
2018-07-25 04:17:02 -04:00
|
|
|
defer encoder.cfg.ReturnStream(tempStream)
|
|
|
|
encoder.elemEncoder.Encode(ptr, tempStream)
|
|
|
|
stream.WriteString(string(tempStream.Buffer()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
|
|
return encoder.elemEncoder.IsEmpty(ptr)
|
|
|
|
}
|