Skip to content

Commit

Permalink
Merge pull request #361 from wongoo/fix-ptr-value-set
Browse files Browse the repository at this point in the history
fix pointer value set
  • Loading branch information
AlexStocks committed May 6, 2023
2 parents 7231dc5 + f9c7f78 commit 8840018
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 234 deletions.
146 changes: 72 additions & 74 deletions codec.go
Expand Up @@ -172,6 +172,16 @@ func UnpackPtrType(typ reflect.Type) reflect.Type {
return typ
}

// UnpackType unpack pointer type to original type and return the pointer depth.
func UnpackType(typ reflect.Type) (reflect.Type, int) {
depth := 0
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
depth++
}
return typ, depth
}

// UnpackPtrValue unpack pointer value to original value
// return the pointer if its elem is zero value, because lots of operations on zero value is invalid
func UnpackPtrValue(v reflect.Value) reflect.Value {
Expand All @@ -181,6 +191,14 @@ func UnpackPtrValue(v reflect.Value) reflect.Value {
return v
}

// UnpackToRootAddressableValue unpack pointer value to the root addressable value.
func UnpackToRootAddressableValue(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Ptr && v.Elem().CanAddr() {
v = v.Elem()
}
return v
}

// SprintHex converts the []byte to a Hex string.
func SprintHex(b []byte) (rs string) {
rs = fmt.Sprintf("[]byte{")
Expand Down Expand Up @@ -253,109 +271,79 @@ func EnsureRawAny(in interface{}) interface{} {

// SetValue set the value to dest.
// It will auto check the Ptr pack level and unpack/pack to the right level.
// It make sure success to set value
// It makes sure success to set value
func SetValue(dest, v reflect.Value) {
// check whether the v is a ref holder
if v.IsValid() {
if h, ok := v.Interface().(*_refHolder); ok {
h.add(dest)
return
}
// zero value not need to set
if !v.IsValid() {
return
}
// temporary process, only handle the same type of situation
if v.IsValid() && UnpackPtrType(dest.Type()) == UnpackPtrType(v.Type()) && dest.Kind() == reflect.Ptr && dest.CanSet() {
for dest.Type() != v.Type() {
v = PackPtr(v)
}

vType := v.Type()
destType := dest.Type()

// for most cases, the types are the same and can set the value directly.
if dest.CanSet() && destType == vType {
dest.Set(v)
return
}

// if the kind of dest is Ptr, the original value will be zero value
// set value on zero value is not allowed
// unpack to one-level pointer
for dest.Kind() == reflect.Ptr && dest.Elem().Kind() == reflect.Ptr {
dest = dest.Elem()
// check whether the v is a ref holder
if vType == _refHolderPtrType {
h := v.Interface().(*_refHolder)
h.add(dest)
return
}

// if the kind of dest is Ptr, change the v to a Ptr value too.
if dest.Kind() == reflect.Ptr {
vRawType, vPtrDepth := UnpackType(vType)

// unpack to one-level pointer
for v.IsValid() && v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Ptr {
v = v.Elem()
}
// zero value not need to set
if !v.IsValid() {
return
}
// unpack to the root addressable value, so that to set the value.
dest = UnpackToRootAddressableValue(dest)
destType = dest.Type()
destRawType, destPtrDepth := UnpackType(destType)

if v.Kind() != reflect.Ptr {
// change the v to a Ptr value
v = PackPtr(v)
// it can set the value directly if the raw types are of the same type.
if destRawType == vRawType {
if destPtrDepth > vPtrDepth {
// pack to the same level of dest
for i := 0; i < destPtrDepth-vPtrDepth; i++ {
v = PackPtr(v)
}
} else if destPtrDepth < vPtrDepth {
// unpack to the same level of dest
for i := 0; i < vPtrDepth-destPtrDepth; i++ {
v = v.Elem()
}
}
} else {
v = UnpackPtrValue(v)
}
// zero value not need to set
if !v.IsValid() {
return
}

// set value as required type
if dest.Type() == v.Type() && dest.CanSet() {
dest.Set(v)
return
}

// unpack ptr so that to special check for float,int,uint kind
if dest.Kind() == reflect.Ptr {
dest = UnpackPtrValue(dest)
v = UnpackPtrValue(v)
return
}

kind := dest.Kind()
switch kind {
switch destType.Kind() {
case reflect.Float32, reflect.Float64:
dest.SetFloat(v.Float())
return
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
dest.SetInt(v.Int())
return
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
dest.SetUint(v.Uint())
// hessian only support 64-bit signed long integer.
dest.SetUint(uint64(v.Int()))
return
case reflect.Ptr:
setRawValueToPointer(dest, v)
SetValueToPtrDest(dest, v)
return
}

dest.Set(v)
}

// setRawValueToDest set the raw value to dest.
func setRawValueToDest(dest reflect.Value, v reflect.Value) {
if dest.Type() == v.Type() {
default:
// It's ok when the dest is an interface{}, while the v is a pointer.
dest.Set(v)
return
}

if dest.Type().Kind() == reflect.Ptr {
setRawValueToPointer(dest, v)
return
}

dest.Set(v)
}

// setRawValueToPointer set the raw value to dest.
func setRawValueToPointer(dest reflect.Value, v reflect.Value) {
pv := PackPtr(v)
if dest.Type() == pv.Type() {
dest.Set(pv)
return
}

// SetValueToPtrDest set the raw value to a pointer dest.
func SetValueToPtrDest(dest reflect.Value, v reflect.Value) {
// for number, the type of value may be different with the dest,
// must convert it to the correct type of value then set.
switch dest.Type() {
case _typeOfIntPtr:
vv := v.Int()
Expand Down Expand Up @@ -410,8 +398,18 @@ func setRawValueToPointer(dest reflect.Value, v reflect.Value) {
vv := v.Float()
dest.Set(reflect.ValueOf(&vv))
return
case _typeOfRunePtr:
if v.Kind() == reflect.String {
vv := Rune(v.String()[0])
dest.Set(reflect.ValueOf(&vv))
return
}

vv := Rune(v.Int())
dest.Set(reflect.ValueOf(&vv))
return
default:
dest.Set(pv)
dest.Set(v)
}
}

Expand Down
6 changes: 3 additions & 3 deletions date_test.go
Expand Up @@ -152,8 +152,8 @@ func TestEncDateNull(t *testing.T) {
assert.Equal(t, ZeroDate, res.(*DateDemo).Date)
assert.Equal(t, 2, len(res.(*DateDemo).Dates))
assert.Equal(t, tz.Local().String(), (*res.(*DateDemo).Dates[0]).String())
assert.Equal(t, &ZeroDate, res.(*DateDemo).NilDate)
assert.Equal(t, ZeroDate, *res.(*DateDemo).Date1)
assert.Nil(t, res.(*DateDemo).NilDate)
assert.Nil(t, res.(*DateDemo).Date1)
assert.Equal(t, tz.Local().String(), (*res.(*DateDemo).Date2).String())
assert.Equal(t, tz.Local().String(), (*(*res.(*DateDemo).Date3)).String())
}
Expand All @@ -174,6 +174,6 @@ func doTestDateNull(t *testing.T, method string) {
testDecodeFrameworkFunc(t, method, func(r interface{}) {
t.Logf("%#v", r)
assert.Equal(t, ZeroDate, r.(*DateDemo).Date)
assert.Equal(t, &ZeroDate, r.(*DateDemo).Date1)
assert.Nil(t, r.(*DateDemo).Date1)
})
}
47 changes: 47 additions & 0 deletions decode.go
Expand Up @@ -324,6 +324,53 @@ func (d *Decoder) DecodeValue() (interface{}, error) {
}
}

// decToDest decode data to dest value.
// Before and includes the version v1.12.1, it checks all possible types of the destination,
// and then decode the data according to the type.
// But there are too many cases, and it's impossible to handle all of them.
// After v1.12.1, it decodes the data first, and then set the value to the destination.
// If the destination is map, slice, array, it decodes separately.
func (d *Decoder) decToDest(dest reflect.Value) error {
destType := dest.Type()
destRawType := UnpackPtrType(destType)

// decode for special type, include map, slice, array.
switch destRawType.Kind() {
case reflect.Map:
return d.decMapByValue(dest)
case reflect.Slice, reflect.Array:
m, err := d.decList(TAG_READ)
if err != nil {
if perrors.Is(err, io.EOF) {
return nil
}
return perrors.WithStack(err)
}

return SetSlice(UnpackPtrValue(dest), m)
}

dec, err := d.DecodeValue()
if err != nil {
return perrors.Wrapf(err, "decToDest: %s", dest.Type().Name())
}

// if dec is nil, then return directly.
if dec == nil {
return nil
}

if ref, ok := dec.(*_refHolder); ok {
return unpackRefHolder(UnpackPtrValue(dest), destRawType, ref)
}

decValue := EnsurePackValue(dec)

SetValue(dest, decValue)

return nil
}

// ///////////////////////////////////////
// typeRefs
// ///////////////////////////////////////
Expand Down
4 changes: 3 additions & 1 deletion java_lang.go
Expand Up @@ -50,5 +50,7 @@ func init() {
type Rune rune

var (
_typeOfRune = reflect.TypeOf(Rune(0))
_varRune = Rune(0)
_typeOfRune = reflect.TypeOf(_varRune)
_typeOfRunePtr = reflect.TypeOf(&_varRune)
)
49 changes: 49 additions & 0 deletions java_lang_test.go
Expand Up @@ -176,3 +176,52 @@ func TestDecodeJavaCharacterArray(t *testing.T) {
t.Logf("%T %+v", got, got)
assert.Equal(t, arr, got)
}

type JavaLangObjectHolder struct {
FieldInteger *int32 `json:"fieldInteger"`
FieldLong *int64 `json:"fieldLong"`
FieldBoolean *bool `json:"fieldBoolean"`
FieldShort *int16 `json:"fieldShort"`
FieldByte *int8 `json:"fieldByte"`
FieldFloat *float32 `json:"fieldFloat"`
FieldDouble *float64 `json:"fieldDouble"`
FieldCharacter *Rune `json:"fieldCharacter"`
}

func (h JavaLangObjectHolder) JavaClassName() string {
return "test.model.JavaLangObjectHolder"
}

func TestDecodeJavaLangObjectHolder(t *testing.T) {
var a int32 = 123
var b int64 = 456
var c = true
var d int16 = 789
var e int8 = 12
var f float32 = 3.45
var g = 6.78
var h Rune = 'A'

obj := &JavaLangObjectHolder{
FieldInteger: &a,
FieldLong: &b,
FieldBoolean: &c,
FieldShort: &d,
FieldByte: &e,
FieldFloat: &f,
FieldDouble: &g,
FieldCharacter: &h,
}

RegisterPOJO(obj)

got, err := decodeJavaResponse(`customReplyJavaLangObjectHolder`, ``, false)
assert.NoError(t, err)
t.Logf("customReplyJavaLangObjectHolder: %T %+v", got, got)
assert.Equal(t, obj, got)

got, err = decodeJavaResponse(`customReplyJavaLangObjectHolderForNull`, ``, false)
assert.NoError(t, err)
t.Logf("customReplyJavaLangObjectHolderForNull: %T %+v", got, got)
assert.Equal(t, &JavaLangObjectHolder{}, got)
}
2 changes: 1 addition & 1 deletion list.go
Expand Up @@ -393,7 +393,7 @@ func (d *Decoder) readTypedListValue(length int, listTyp string, isVariableArr b
} else {
if it != nil {
//aryValue.Index(j).Set(EnsureRawValue(it))
setRawValueToDest(aryValue.Index(j), EnsureRawValue(it))
SetValue(aryValue.Index(j), EnsureRawValue(it))
}
}
}
Expand Down

0 comments on commit 8840018

Please sign in to comment.