This is an automated email from the ASF dual-hosted git repository.

alexstocks pushed a commit to branch 1.5
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/1.5 by this push:
     new 2dca192  Fix: struct2MapAll logics (#1356)
2dca192 is described below

commit 2dca19267ae3286cb6ae7fcff329fee4a26903fe
Author: XavierNiu <[email protected]>
AuthorDate: Tue Aug 3 08:35:24 2021 +0800

    Fix: struct2MapAll logics (#1356)
    
    * fix struct2MapAll logics
    
    * fix size computation for allocation may overflow
---
 cluster/router/tag/router_rule.go            |  2 +-
 filter/filter_impl/generic_filter.go         | 97 ++++++++++++++++++++--------
 filter/filter_impl/generic_filter_test.go    | 22 ++++++-
 filter/filter_impl/generic_service_filter.go |  2 +-
 4 files changed, 91 insertions(+), 32 deletions(-)

diff --git a/cluster/router/tag/router_rule.go 
b/cluster/router/tag/router_rule.go
index 512d8f1..8993a6c 100644
--- a/cluster/router/tag/router_rule.go
+++ b/cluster/router/tag/router_rule.go
@@ -58,7 +58,7 @@ func getRule(rawRule string) (*RouterRule, error) {
 
 // parseTags use for flattening tags data to @addressToTagNames and 
@tagNameToAddresses
 func (t *RouterRule) parseTags() {
-       t.AddressToTagNames = make(map[string][]string, 2*len(t.Tags))
+       t.AddressToTagNames = make(map[string][]string, len(t.Tags))
        t.TagNameToAddresses = make(map[string][]string, len(t.Tags))
        for _, tag := range t.Tags {
                for _, address := range tag.Addresses {
diff --git a/filter/filter_impl/generic_filter.go 
b/filter/filter_impl/generic_filter.go
index cf307d0..86f8cfd 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -31,6 +31,7 @@ import (
 import (
        "github.com/apache/dubbo-go/common/constant"
        "github.com/apache/dubbo-go/common/extension"
+       "github.com/apache/dubbo-go/common/logger"
        "github.com/apache/dubbo-go/filter"
        "github.com/apache/dubbo-go/protocol"
        invocation2 "github.com/apache/dubbo-go/protocol/invocation"
@@ -59,7 +60,7 @@ func (ef *GenericFilter) Invoke(ctx context.Context, invoker 
protocol.Invoker, i
                if oldParams, ok := oldArguments[2].([]interface{}); ok {
                        newParams := make([]hessian.Object, 0, len(oldParams))
                        for i := range oldParams {
-                               newParams = append(newParams, 
hessian.Object(struct2MapAll(oldParams[i])))
+                               newParams = append(newParams, 
hessian.Object(objToMap(oldParams[i])))
                        }
                        newArguments := []interface{}{
                                oldArguments[0],
@@ -85,44 +86,70 @@ func GetGenericFilter() filter.Filter {
        return &GenericFilter{}
 }
 
-func struct2MapAll(obj interface{}) interface{} {
+func objToMap(obj interface{}) interface{} {
        if obj == nil {
                return obj
        }
+
        t := reflect.TypeOf(obj)
        v := reflect.ValueOf(obj)
-       if t.Kind() == reflect.Struct {
+
+       // if obj is a POJO, get the struct from the pointer (if it is a 
pointer)
+       pojo, isPojo := obj.(hessian.POJO)
+       if isPojo {
+               for t.Kind() == reflect.Ptr {
+                       t = t.Elem()
+                       v = v.Elem()
+               }
+       }
+
+       switch t.Kind() {
+       case reflect.Struct:
                result := make(map[string]interface{}, t.NumField())
+               if isPojo {
+                       result["class"] = pojo.JavaClassName()
+               }
                for i := 0; i < t.NumField(); i++ {
                        field := t.Field(i)
                        value := v.Field(i)
                        kind := value.Kind()
-                       if kind == reflect.Struct || kind == reflect.Slice || 
kind == reflect.Map {
-                               if value.CanInterface() {
-                                       tmp := value.Interface()
-                                       if _, ok := tmp.(time.Time); ok {
-                                               setInMap(result, field, tmp)
-                                               continue
-                                       }
-                                       setInMap(result, field, 
struct2MapAll(tmp))
+                       if !value.CanInterface() {
+                               logger.Debugf("objToMap for %v is skipped 
because it couldn't be converted to interface", field)
+                               continue
+                       }
+                       valueIface := value.Interface()
+                       switch kind {
+                       case reflect.Ptr:
+                               if value.IsNil() {
+                                       setInMap(result, field, nil)
+                                       continue
                                }
-                       } else {
-                               if value.CanInterface() {
-                                       setInMap(result, field, 
value.Interface())
+                               setInMap(result, field, objToMap(valueIface))
+                       case reflect.Struct, reflect.Slice, reflect.Map:
+                               if isPrimitive(valueIface) {
+                                       logger.Warnf("\"%s\" is primitive. The 
application may crash if it's transferred between "+
+                                               "systems implemented by 
different languages, e.g. dubbo-go <-> dubbo-java. We recommend "+
+                                               "you represent the object by 
basic types, like string.", value.Type())
+                                       setInMap(result, field, valueIface)
+                                       continue
                                }
+
+                               setInMap(result, field, objToMap(valueIface))
+                       default:
+                               setInMap(result, field, valueIface)
                        }
                }
                return result
-       } else if t.Kind() == reflect.Slice {
+       case reflect.Array, reflect.Slice:
                value := reflect.ValueOf(obj)
-               var newTemps = make([]interface{}, 0, value.Len())
+               newTemps := make([]interface{}, 0, value.Len())
                for i := 0; i < value.Len(); i++ {
-                       newTemp := struct2MapAll(value.Index(i).Interface())
+                       newTemp := objToMap(value.Index(i).Interface())
                        newTemps = append(newTemps, newTemp)
                }
                return newTemps
-       } else if t.Kind() == reflect.Map {
-               var newTempMap = make(map[interface{}]interface{}, v.Len())
+       case reflect.Map:
+               newTempMap := make(map[interface{}]interface{}, v.Len())
                iter := v.MapRange()
                for iter.Next() {
                        if !iter.Value().CanInterface() {
@@ -130,15 +157,18 @@ func struct2MapAll(obj interface{}) interface{} {
                        }
                        key := iter.Key()
                        mapV := iter.Value().Interface()
-                       newTempMap[convertMapKey(key)] = struct2MapAll(mapV)
+                       newTempMap[mapKey(key)] = objToMap(mapV)
                }
                return newTempMap
-       } else {
+       case reflect.Ptr:
+               return objToMap(v.Elem().Interface())
+       default:
                return obj
        }
 }
 
-func convertMapKey(key reflect.Value) interface{} {
+// mapKey converts the map key to interface type
+func mapKey(key reflect.Value) interface{} {
        switch key.Kind() {
        case reflect.Bool, reflect.Int, reflect.Int8,
                reflect.Int16, reflect.Int32, reflect.Int64,
@@ -147,21 +177,34 @@ func convertMapKey(key reflect.Value) interface{} {
                reflect.Float64, reflect.String:
                return key.Interface()
        default:
-               return key.String()
+               name := key.String()
+               if name == "class" {
+                       panic(`"class" is a reserved keyword`)
+               }
+               return name
        }
 }
 
+// setInMap sets the struct into the map using the tag or the name of the 
struct as the key
 func setInMap(m map[string]interface{}, structField reflect.StructField, value 
interface{}) (result map[string]interface{}) {
        result = m
        if tagName := structField.Tag.Get("m"); tagName == "" {
-               result[headerAtoa(structField.Name)] = value
+               result[toUnexport(structField.Name)] = value
        } else {
                result[tagName] = value
        }
        return
 }
 
-func headerAtoa(a string) (b string) {
-       b = strings.ToLower(a[:1]) + a[1:]
-       return
+// toUnexport is to lower the first letter
+func toUnexport(a string) string {
+       return strings.ToLower(a[:1]) + a[1:]
+}
+
+// isPrimitive determines if the object is primitive
+func isPrimitive(obj interface{}) bool {
+       if _, ok := obj.(time.Time); ok {
+               return true
+       }
+       return false
 }
diff --git a/filter/filter_impl/generic_filter_test.go 
b/filter/filter_impl/generic_filter_test.go
index 4203dd6..4a1f12e 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -50,7 +50,7 @@ func TestStruct2MapAll(t *testing.T) {
        testData.CaCa.XxYy.Xx = "3"
        testData.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
        testData.EeEe = 100
-       m := struct2MapAll(testData).(map[string]interface{})
+       m := objToMap(testData).(map[string]interface{})
        assert.Equal(t, "1", m["aaAa"].(string))
        assert.Equal(t, "1", m["baBa"].(string))
        assert.Equal(t, "2", 
m["caCa"].(map[string]interface{})["aaAa"].(string))
@@ -85,7 +85,7 @@ func TestStruct2MapAllSlice(t *testing.T) {
        tmp.XxYy.xxXx = "3"
        tmp.XxYy.Xx = "3"
        testData.CaCa = append(testData.CaCa, tmp)
-       m := struct2MapAll(testData).(map[string]interface{})
+       m := objToMap(testData).(map[string]interface{})
 
        assert.Equal(t, "1", m["aaAa"].(string))
        assert.Equal(t, "1", m["baBa"].(string))
@@ -120,7 +120,7 @@ func TestStruct2MapAllMap(t *testing.T) {
        testData.CaCa["k1"] = "v1"
        testData.CaCa["kv2"] = "v2"
        testData.IntMap[1] = 1
-       m := struct2MapAll(testData)
+       m := objToMap(testData)
 
        assert.Equal(t, reflect.Map, reflect.TypeOf(m).Kind())
        mappedStruct := m.(map[string]interface{})
@@ -135,3 +135,19 @@ func TestStruct2MapAllMap(t *testing.T) {
        assert.Equal(t, reflect.Map, reflect.TypeOf(intMap).Kind())
        assert.Equal(t, 1, intMap.(map[interface{}]interface{})[1])
 }
+
+type mockParent struct {
+       Children []*mockChild
+}
+
+func (p *mockParent) JavaClassName() string {
+       return "org.apache.dubbo.mockParent"
+}
+
+type mockChild struct {
+       Name string
+}
+
+func (p *mockChild) JavaClassName() string {
+       return "org.apache.dubbo.mockChild"
+}
diff --git a/filter/filter_impl/generic_service_filter.go 
b/filter/filter_impl/generic_service_filter.go
index e843f2e..6056224 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -121,7 +121,7 @@ func (ef *GenericServiceFilter) OnResponse(ctx 
context.Context, result protocol.
                if v.Kind() == reflect.Ptr {
                        v = v.Elem()
                }
-               result.SetResult(struct2MapAll(v.Interface()))
+               result.SetResult(objToMap(v.Interface()))
        }
        return result
 }

Reply via email to