Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix pointer value set #361

Merged
merged 5 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 72 additions & 74 deletions codec.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading