| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package hessian |
| |
| import ( |
| "io" |
| "reflect" |
| "strings" |
| "sync" |
| ) |
| |
| import ( |
| perrors "github.com/pkg/errors" |
| ) |
| |
| // get @v go struct name |
| func typeof(v interface{}) string { |
| return reflect.TypeOf(v).String() |
| } |
| |
| ///////////////////////////////////////// |
| // map/object |
| ///////////////////////////////////////// |
| |
| //class-def ::= 'C' string int string* // mandatory type string, the number of fields, and the field names. |
| //object ::= 'O' int value* // class-def id, value list |
| // ::= [x60-x6f] value* // class-def id, value list |
| // |
| //Object serialization |
| // |
| //class Car { |
| // String color; |
| // String model; |
| //} |
| // |
| //out.writeObject(new Car("red", "corvette")); |
| //out.writeObject(new Car("green", "civic")); |
| // |
| //--- |
| // |
| //C # object definition (#0) |
| // x0b example.Car # type is example.Car |
| // x92 # two fields |
| // x05 color # color field name |
| // x05 model # model field name |
| // |
| //O # object def (long form) |
| // x90 # object definition #0 |
| // x03 red # color field value |
| // x08 corvette # model field value |
| // |
| //x60 # object def #0 (short form) |
| // x05 green # color field value |
| // x05 civic # model field value |
| // |
| //enum Color { |
| // RED, |
| // GREEN, |
| // BLUE, |
| //} |
| // |
| //out.writeObject(Color.RED); |
| //out.writeObject(Color.GREEN); |
| //out.writeObject(Color.BLUE); |
| //out.writeObject(Color.GREEN); |
| // |
| //--- |
| // |
| //C # class definition #0 |
| // x0b example.Color # type is example.Color |
| // x91 # one field |
| // x04 name # enumeration field is "name" |
| // |
| //x60 # object #0 (class def #0) |
| // x03 RED # RED value |
| // |
| //x60 # object #1 (class def #0) |
| // x90 # object definition ref #0 |
| // x05 GREEN # GREEN value |
| // |
| //x60 # object #2 (class def #0) |
| // x04 BLUE # BLUE value |
| // |
| //x51 x91 # object ref #1, i.e. Color.GREEN |
| func (e *Encoder) encObject(v POJO) error { |
| var ( |
| ok bool |
| i int |
| idx int |
| num int |
| err error |
| clsDef classInfo |
| ) |
| |
| vv := reflect.ValueOf(v) |
| // check ref |
| if n, ok := e.checkRefMap(vv); ok { |
| e.buffer = encRef(e.buffer, n) |
| return nil |
| } |
| |
| vv = UnpackPtr(vv) |
| // check nil pointer |
| if !vv.IsValid() { |
| e.buffer = EncNull(e.buffer) |
| return nil |
| } |
| |
| // write object definition |
| idx = -1 |
| for i = range e.classInfoList { |
| if v.JavaClassName() == e.classInfoList[i].javaName { |
| idx = i |
| break |
| } |
| } |
| |
| if idx == -1 { |
| idx, ok = checkPOJORegistry(typeof(v)) |
| if !ok { |
| if reflect.TypeOf(v).Implements(javaEnumType) { |
| idx = RegisterJavaEnum(v.(POJOEnum)) |
| } else { |
| idx = RegisterPOJO(v) |
| } |
| } |
| _, clsDef, err = getStructDefByIndex(idx) |
| if err != nil { |
| return perrors.WithStack(err) |
| } |
| |
| idx = len(e.classInfoList) |
| e.classInfoList = append(e.classInfoList, clsDef) |
| e.buffer = append(e.buffer, clsDef.buffer...) |
| } |
| |
| // write object instance |
| if byte(idx) <= OBJECT_DIRECT_MAX { |
| e.buffer = encByte(e.buffer, byte(idx)+BC_OBJECT_DIRECT) |
| } else { |
| e.buffer = encByte(e.buffer, BC_OBJECT) |
| e.buffer = encInt32(e.buffer, int32(idx)) |
| } |
| |
| if reflect.TypeOf(v).Implements(javaEnumType) { |
| e.buffer = encString(e.buffer, v.(POJOEnum).String()) |
| return nil |
| } |
| |
| structs := []reflect.Value{vv} |
| for len(structs) > 0 { |
| vv := structs[0] |
| vvt := vv.Type() |
| num = vv.NumField() |
| for i = 0; i < num; i++ { |
| tf := vvt.Field(i) |
| // skip unexported anonymous field |
| if tf.PkgPath != "" { |
| continue |
| } |
| |
| // skip ignored field |
| if tag, _ := tf.Tag.Lookup(tagIdentifier); tag == `-` { |
| continue |
| } |
| |
| field := vv.Field(i) |
| if tf.Anonymous && field.Kind() == reflect.Struct { |
| structs = append(structs, field) |
| continue |
| } |
| |
| if err = e.Encode(field.Interface()); err != nil { |
| fieldName := field.Type().String() |
| return perrors.Wrapf(err, "failed to encode field: %s, %+v", fieldName, field.Interface()) |
| } |
| } |
| |
| structs = structs[1:] |
| } |
| |
| return nil |
| } |
| |
| ///////////////////////////////////////// |
| // Object |
| ///////////////////////////////////////// |
| |
| //class-def ::= 'C' string int string* // mandatory type string, the number of fields, and the field names. |
| //object ::= 'O' int value* // class-def id, value list |
| // ::= [x60-x6f] value* // class-def id, value list |
| // |
| //Object serialization |
| // |
| //class Car { |
| // String color; |
| // String model; |
| //} |
| // |
| //out.writeObject(new Car("red", "corvette")); |
| //out.writeObject(new Car("green", "civic")); |
| // |
| //--- |
| // |
| //C # object definition (#0) |
| // x0b example.Car # type is example.Car |
| // x92 # two fields |
| // x05 color # color field name |
| // x05 model # model field name |
| // |
| //O # object def (long form) |
| // x90 # object definition #0 |
| // x03 red # color field value |
| // x08 corvette # model field value |
| // |
| //x60 # object def #0 (short form) |
| // x05 green # color field value |
| // x05 civic # model field value |
| // |
| // |
| // |
| // |
| // |
| //enum Color { |
| // RED, |
| // GREEN, |
| // BLUE, |
| //} |
| // |
| //out.writeObject(Color.RED); |
| //out.writeObject(Color.GREEN); |
| //out.writeObject(Color.BLUE); |
| //out.writeObject(Color.GREEN); |
| // |
| //--- |
| // |
| //C # class definition #0 |
| // x0b example.Color # type is example.Color |
| // x91 # one field |
| // x04 name # enumeration field is "name" |
| // |
| //x60 # object #0 (class def #0) |
| // x03 RED # RED value |
| // |
| //x60 # object #1 (class def #0) |
| // x90 # object definition ref #0 |
| // x05 GREEN # GREEN value |
| // |
| //x60 # object #2 (class def #0) |
| // x04 BLUE # BLUE value |
| // |
| //x51 x91 # object ref #1, i.e. Color.GREEN |
| |
| func (d *Decoder) decClassDef() (interface{}, error) { |
| var ( |
| err error |
| clsName string |
| fieldNum int32 |
| fieldName string |
| fieldList []string |
| ) |
| |
| clsName, err = d.decString(TAG_READ) |
| if err != nil { |
| return nil, perrors.WithStack(err) |
| } |
| fieldNum, err = d.decInt32(TAG_READ) |
| if err != nil { |
| return nil, perrors.WithStack(err) |
| } |
| fieldList = make([]string, fieldNum) |
| for i := 0; i < int(fieldNum); i++ { |
| fieldName, err = d.decString(TAG_READ) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decClassDef->decString, field num:%d, index:%d", fieldNum, i) |
| } |
| fieldList[i] = fieldName |
| } |
| |
| return classInfo{javaName: clsName, fieldNameList: fieldList}, nil |
| } |
| |
| type fieldInfo struct { |
| indexes []int |
| field *reflect.StructField |
| } |
| |
| // map[rType][fieldName]indexes |
| var fieldIndexCache sync.Map |
| |
| func findFieldWithCache(name string, typ reflect.Type) ([]int, *reflect.StructField, error) { |
| typCache, _ := fieldIndexCache.Load(typ) |
| if typCache == nil { |
| typCache = &sync.Map{} |
| fieldIndexCache.Store(typ, typCache) |
| } |
| |
| iindexes, existCache := typCache.(*sync.Map).Load(name) |
| if existCache && iindexes != nil { |
| finfo := iindexes.(*fieldInfo) |
| var err error |
| if len(finfo.indexes) == 0 { |
| err = perrors.Errorf("failed to find field %s", name) |
| } |
| return finfo.indexes, finfo.field, err |
| } |
| |
| indexes, field, err := findField(name, typ) |
| typCache.(*sync.Map).Store(name, &fieldInfo{indexes: indexes, field: field}) |
| return indexes, field, err |
| } |
| |
| // findField find structField in rType |
| // |
| // return |
| // indexes []int |
| // field reflect.StructField |
| // err error |
| func findField(name string, typ reflect.Type) ([]int, *reflect.StructField, error) { |
| for i := 0; i < typ.NumField(); i++ { |
| // matching tag first, then lowerCamelCase, SameCase, lowerCase |
| |
| typField := typ.Field(i) |
| |
| tagVal, hasTag := typField.Tag.Lookup(tagIdentifier) |
| |
| fieldName := typField.Name |
| if hasTag && tagVal == name || |
| fieldName == name || |
| lowerCamelCase(fieldName) == name || |
| strings.ToLower(fieldName) == name { |
| |
| return []int{i}, &typField, nil |
| } |
| |
| if typField.Anonymous && typField.Type.Kind() == reflect.Struct { |
| next, field, _ := findField(name, typField.Type) |
| if len(next) > 0 { |
| indexes := []int{i} |
| indexes = append(indexes, next...) |
| |
| return indexes, field, nil |
| } |
| } |
| } |
| |
| return []int{}, nil, perrors.Errorf("failed to find field %s", name) |
| } |
| |
| func (d *Decoder) decInstance(typ reflect.Type, cls classInfo) (interface{}, error) { |
| if typ.Kind() != reflect.Struct { |
| return nil, perrors.Errorf("wrong type expect Struct but get:%s", typ.String()) |
| } |
| |
| vRef := reflect.New(typ) |
| // add pointer ref so that ref the same object |
| d.appendRefs(vRef.Interface()) |
| |
| vv := vRef.Elem() |
| for i := 0; i < len(cls.fieldNameList); i++ { |
| fieldName := cls.fieldNameList[i] |
| |
| index, fieldStruct, err := findFieldWithCache(fieldName, typ) |
| if err != nil { |
| d.DecodeValue() |
| continue |
| } |
| |
| // skip unexported anonymous field |
| if fieldStruct.PkgPath != "" { |
| continue |
| } |
| |
| field := vv.FieldByIndex(index) |
| if !field.CanSet() { |
| return nil, perrors.Errorf("decInstance CanSet false for field %s", fieldName) |
| } |
| |
| // get field type from type object, not do that from value |
| fldTyp := UnpackPtrType(field.Type()) |
| |
| // unpack pointer to enable value setting |
| fldRawValue := UnpackPtrValue(field) |
| kind := fldTyp.Kind() |
| |
| switch kind { |
| case reflect.String: |
| str, err := d.decString(TAG_READ) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->ReadString: %s", fieldName) |
| } |
| fldRawValue.SetString(str) |
| |
| case reflect.Int32, reflect.Int16, reflect.Int8: |
| num, err := d.decInt32(TAG_READ) |
| if err != nil { |
| // java enum |
| if fldRawValue.Type().Implements(javaEnumType) { |
| d.unreadByte() // Enum parsing, decInt64 above has read a byte, so you need to return a byte here |
| s, err := d.DecodeValue() |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decObject field name:%s", fieldName) |
| } |
| enumValue, _ := s.(JavaEnum) |
| num = int32(enumValue) |
| } else { |
| return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName) |
| } |
| } |
| fldRawValue.SetInt(int64(num)) |
| case reflect.Uint16, reflect.Uint8: |
| num, err := d.decInt32(TAG_READ) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName) |
| } |
| fldRawValue.SetUint(uint64(num)) |
| case reflect.Uint, reflect.Int, reflect.Int64: |
| num, err := d.decInt64(TAG_READ) |
| if err != nil { |
| if fldTyp.Implements(javaEnumType) { |
| d.unreadByte() // Enum parsing, decInt64 above has read a byte, so you need to return a byte here |
| s, err := d.Decode() |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decObject field name:%s", fieldName) |
| } |
| enumValue, _ := s.(JavaEnum) |
| num = int64(enumValue) |
| } else { |
| return nil, perrors.Wrapf(err, "decInstance->decInt64 field name:%s", fieldName) |
| } |
| } |
| |
| fldRawValue.SetInt(num) |
| case reflect.Uint32, reflect.Uint64: |
| num, err := d.decInt64(TAG_READ) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decInt64, field name:%s", fieldName) |
| } |
| fldRawValue.SetUint(uint64(num)) |
| case reflect.Bool: |
| b, err := d.Decode() |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->Decode field name:%s", fieldName) |
| } |
| v, ok := b.(bool) |
| if !ok { |
| return nil, perrors.Wrapf(err, "value convert to bool failed, field name:%s", fieldName) |
| } |
| |
| if fldRawValue.Kind() == reflect.Ptr && fldRawValue.CanSet() { |
| if b != nil { |
| field.Set(reflect.ValueOf(&v)) |
| } |
| } else if fldRawValue.Kind() != reflect.Ptr { |
| fldRawValue.SetBool(v) |
| } |
| |
| case reflect.Float32, reflect.Float64: |
| num, err := d.decDouble(TAG_READ) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decDouble field name:%s", fieldName) |
| } |
| fldRawValue.SetFloat(num.(float64)) |
| |
| case reflect.Map: |
| // decode map should use the original field value for correct value setting |
| err := d.decMapByValue(field) |
| if err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->decMapByValue field name: %s", fieldName) |
| } |
| |
| case reflect.Slice, reflect.Array: |
| m, err := d.decList(TAG_READ) |
| if err != nil { |
| if err == io.EOF { |
| break |
| } |
| return nil, perrors.WithStack(err) |
| } |
| |
| // set slice separately |
| err = SetSlice(fldRawValue, m) |
| if err != nil { |
| return nil, err |
| } |
| case reflect.Struct, reflect.Interface: |
| var ( |
| err error |
| s interface{} |
| ) |
| typ := UnpackPtrType(fldRawValue.Type()) |
| if typ.String() == "time.Time" { |
| s, err = d.decDate(TAG_READ) |
| if err != nil { |
| return nil, perrors.WithStack(err) |
| } |
| SetValue(fldRawValue, EnsurePackValue(s)) |
| } else { |
| s, err = d.decObject(TAG_READ) |
| if err != nil { |
| return nil, perrors.WithStack(err) |
| } |
| if s != nil { |
| // set value which accepting pointers |
| SetValue(fldRawValue, EnsurePackValue(s)) |
| } |
| } |
| |
| default: |
| return nil, perrors.Errorf("unknown struct member type: %v %v", kind, typ.Name()+"."+fieldStruct.Name) |
| } |
| } // end for |
| |
| return vRef.Interface(), nil |
| } |
| |
| func (d *Decoder) appendClsDef(cd classInfo) { |
| d.classInfoList = append(d.classInfoList, cd) |
| } |
| |
| func (d *Decoder) getStructDefByIndex(idx int) (reflect.Type, classInfo, error) { |
| var ( |
| ok bool |
| cls classInfo |
| s structInfo |
| err error |
| ) |
| |
| if len(d.classInfoList) <= idx || idx < 0 { |
| return nil, cls, perrors.Errorf("illegal class index @idx %d", idx) |
| } |
| cls = d.classInfoList[idx] |
| s, ok = getStructInfo(cls.javaName) |
| if !ok { |
| // exception |
| if s, ok = checkAndGetException(cls); ok { |
| return s.typ, cls, nil |
| } |
| if !d.isSkip { |
| err = perrors.Errorf("can not find go type name %s in registry", cls.javaName) |
| } |
| return nil, cls, err |
| } |
| |
| return s.typ, cls, nil |
| } |
| |
| func (d *Decoder) decEnum(javaName string, flag int32) (JavaEnum, error) { |
| var ( |
| err error |
| enumName string |
| ok bool |
| info structInfo |
| enumValue JavaEnum |
| ) |
| enumName, err = d.decString(TAG_READ) // java enum class member is "name" |
| if err != nil { |
| return InvalidJavaEnum, perrors.Wrap(err, "decString for decJavaEnum") |
| } |
| info, ok = getStructInfo(javaName) |
| if !ok { |
| return InvalidJavaEnum, perrors.Errorf("getStructInfo(javaName:%s) = false", javaName) |
| } |
| |
| enumValue = info.inst.(POJOEnum).EnumValue(enumName) |
| d.appendRefs(enumValue) |
| return enumValue, nil |
| } |
| |
| // skip this object |
| func (d *Decoder) skip(cls classInfo) error { |
| len := len(cls.fieldNameList) |
| if len < 1 { |
| return nil |
| } |
| |
| for i := 0; i < len; i++ { |
| // skip class fields. |
| if _, err := d.DecodeValue(); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (d *Decoder) decObject(flag int32) (interface{}, error) { |
| var ( |
| tag byte |
| idx int32 |
| err error |
| typ reflect.Type |
| cls classInfo |
| ) |
| |
| if flag != TAG_READ { |
| tag = byte(flag) |
| } else { |
| tag, _ = d.readByte() |
| } |
| |
| switch { |
| case tag == BC_NULL: |
| return nil, nil |
| case tag == BC_REF: |
| return d.decRef(int32(tag)) |
| case tag == BC_OBJECT_DEF: |
| clsDef, err := d.decClassDef() |
| if err != nil { |
| return nil, perrors.Wrap(err, "decObject->decClassDef byte double") |
| } |
| cls, _ = clsDef.(classInfo) |
| //add to slice |
| d.appendClsDef(cls) |
| |
| return d.DecodeValue() |
| |
| case tag == BC_OBJECT: |
| idx, err = d.decInt32(TAG_READ) |
| if err != nil { |
| return nil, err |
| } |
| |
| typ, cls, err = d.getStructDefByIndex(int(idx)) |
| if err != nil { |
| return nil, err |
| } |
| if typ == nil { |
| return nil, d.skip(cls) |
| } |
| if typ.Implements(javaEnumType) { |
| return d.decEnum(cls.javaName, TAG_READ) |
| } |
| |
| if c, ok := GetSerializer(cls.javaName); ok { |
| return c.DecObject(d, typ, cls) |
| } |
| |
| return d.decInstance(typ, cls) |
| |
| case BC_OBJECT_DIRECT <= tag && tag <= (BC_OBJECT_DIRECT+OBJECT_DIRECT_MAX): |
| typ, cls, err = d.getStructDefByIndex(int(tag - BC_OBJECT_DIRECT)) |
| if err != nil { |
| return nil, err |
| } |
| if typ == nil { |
| return nil, d.skip(cls) |
| } |
| if typ.Implements(javaEnumType) { |
| return d.decEnum(cls.javaName, TAG_READ) |
| } |
| |
| if c, ok := GetSerializer(cls.javaName); ok { |
| return c.DecObject(d, typ, cls) |
| } |
| |
| return d.decInstance(typ, cls) |
| |
| default: |
| return nil, perrors.Errorf("decObject illegal object type tag:%+v", tag) |
| } |
| } |