diff --git a/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java b/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java new file mode 100644 index 0000000000..1940871fc6 --- /dev/null +++ b/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java @@ -0,0 +1,55 @@ +/* + * 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 org.apache.fury.benchmark; + +import java.io.IOException; +import org.apache.fury.benchmark.state.FuryState; +import org.apache.fury.benchmark.state.KryoState; +import org.openjdk.jmh.Main; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Mode; + +@BenchmarkMode(Mode.Throughput) +@CompilerControl(value = CompilerControl.Mode.INLINE) +public class CopyBenchmark { + + @Benchmark + public Object fury_copy(FuryState.FuryUserTypeState state) { + return state.fury.copy(state.object); + } + + @Benchmark + public Object kryo_copy(KryoState.KryoUserTypeState state) { + return state.kryo.copy(state.object); + } + + public static void main(String[] args) throws IOException { + if (args.length == 0) { + String commandLine = + "org.apache.fury.*CopyBenchmark.* -f 1 -wi 3 -i 3 -t 1 -w 2s -r 2s -rf csv " + + "-p bufferType=array -p references=false"; + System.out.println(commandLine); + args = commandLine.split(" "); + } + Main.main(args); + } +} diff --git a/java/fury-core/src/main/java/org/apache/fury/Fury.java b/java/fury-core/src/main/java/org/apache/fury/Fury.java index 4cb45b21d3..87c635d77f 100644 --- a/java/fury-core/src/main/java/org/apache/fury/Fury.java +++ b/java/fury-core/src/main/java/org/apache/fury/Fury.java @@ -1273,14 +1273,6 @@ public T copyObject(T obj) { Object copy; ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass()); switch (classInfo.getClassId()) { - case ClassResolver.BOOLEAN_CLASS_ID: - case ClassResolver.BYTE_CLASS_ID: - case ClassResolver.CHAR_CLASS_ID: - case ClassResolver.SHORT_CLASS_ID: - case ClassResolver.INTEGER_CLASS_ID: - case ClassResolver.FLOAT_CLASS_ID: - case ClassResolver.LONG_CLASS_ID: - case ClassResolver.DOUBLE_CLASS_ID: case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID: case ClassResolver.PRIMITIVE_BYTE_CLASS_ID: case ClassResolver.PRIMITIVE_CHAR_CLASS_ID: @@ -1289,6 +1281,14 @@ public T copyObject(T obj) { case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID: case ClassResolver.PRIMITIVE_LONG_CLASS_ID: case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID: + case ClassResolver.BOOLEAN_CLASS_ID: + case ClassResolver.BYTE_CLASS_ID: + case ClassResolver.CHAR_CLASS_ID: + case ClassResolver.SHORT_CLASS_ID: + case ClassResolver.INTEGER_CLASS_ID: + case ClassResolver.FLOAT_CLASS_ID: + case ClassResolver.LONG_CLASS_ID: + case ClassResolver.DOUBLE_CLASS_ID: case ClassResolver.STRING_CLASS_ID: return obj; case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_CLASS_ID: @@ -1319,22 +1319,49 @@ public T copyObject(T obj) { String[] stringArr = (String[]) obj; return (T) Arrays.copyOf(stringArr, stringArr.length); case ClassResolver.ARRAYLIST_CLASS_ID: - copyDepth++; copy = arrayListSerializer.copy((ArrayList) obj); break; case ClassResolver.HASHMAP_CLASS_ID: - copyDepth++; copy = hashMapSerializer.copy((HashMap) obj); break; // todo: add fastpath for other types. default: copyDepth++; copy = classInfo.getSerializer().copy(obj); + copyDepth--; } - copyDepth--; return (T) copy; } + public T copyObject(T obj, int classId) { + if (obj == null) { + return null; + } + // Fast path to avoid cost of query class map. + switch (classId) { + case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID: + case ClassResolver.PRIMITIVE_BYTE_CLASS_ID: + case ClassResolver.PRIMITIVE_CHAR_CLASS_ID: + case ClassResolver.PRIMITIVE_SHORT_CLASS_ID: + case ClassResolver.PRIMITIVE_INT_CLASS_ID: + case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID: + case ClassResolver.PRIMITIVE_LONG_CLASS_ID: + case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID: + case ClassResolver.BOOLEAN_CLASS_ID: + case ClassResolver.BYTE_CLASS_ID: + case ClassResolver.CHAR_CLASS_ID: + case ClassResolver.SHORT_CLASS_ID: + case ClassResolver.INTEGER_CLASS_ID: + case ClassResolver.FLOAT_CLASS_ID: + case ClassResolver.LONG_CLASS_ID: + case ClassResolver.DOUBLE_CLASS_ID: + case ClassResolver.STRING_CLASS_ID: + return obj; + default: + return (T) classResolver.getOrUpdateClassInfo(obj.getClass()).getSerializer().copy(obj); + } + } + /** * Track ref for copy. * diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java index c609ce303d..8591c6c8f0 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java @@ -19,34 +19,47 @@ package org.apache.fury.serializer; +import static org.apache.fury.type.DescriptorGrouper.createDescriptorGrouper; +import static org.apache.fury.type.TypeUtils.getRawType; + import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.apache.fury.Fury; +import org.apache.fury.collection.Tuple2; +import org.apache.fury.collection.Tuple3; import org.apache.fury.memory.Platform; import org.apache.fury.reflect.FieldAccessor; import org.apache.fury.reflect.ReflectionUtils; +import org.apache.fury.reflect.TypeRef; +import org.apache.fury.resolver.ClassInfo; +import org.apache.fury.resolver.ClassInfoHolder; import org.apache.fury.resolver.ClassResolver; -import org.apache.fury.resolver.FieldResolver.FieldInfo; import org.apache.fury.resolver.RefResolver; +import org.apache.fury.type.Descriptor; +import org.apache.fury.type.DescriptorGrouper; +import org.apache.fury.type.FinalObjectTypeStub; +import org.apache.fury.type.GenericType; +import org.apache.fury.util.record.RecordComponent; import org.apache.fury.util.record.RecordUtils; public abstract class AbstractObjectSerializer extends Serializer { - protected final RefResolver refResolver; protected final ClassResolver classResolver; protected final boolean isRecord; protected final MethodHandle constructor; + private InternalFieldInfo[] fieldInfos; public AbstractObjectSerializer(Fury fury, Class type) { - super(fury, type); - this.refResolver = fury.getRefResolver(); - this.classResolver = fury.getClassResolver(); - this.isRecord = RecordUtils.isRecord(type); - if (isRecord) { - this.constructor = RecordUtils.getRecordConstructor(type).f1; - } else { - this.constructor = ReflectionUtils.getCtrHandle(type, false); - } + this( + fury, + type, + RecordUtils.isRecord(type) + ? RecordUtils.getRecordConstructor(type).f1 + : ReflectionUtils.getCtrHandle(type, false)); } public AbstractObjectSerializer(Fury fury, Class type, MethodHandle constructor) { @@ -71,68 +84,167 @@ public T copy(T originObj) { } return originObj; } - T newObj = newBean(); + T newObj; if (needToCopyRef) { T copyObject = (T) fury.getCopyObject(originObj); if (copyObject != null) { return copyObject; } + newObj = newBean(); fury.reference(originObj, newObj); + } else { + newObj = newBean(); } copyFields(originObj, newObj); return newObj; } private Object[] copyFields(T originObj) { - return classResolver.getFieldResolver(type).getAllFieldsList().stream() - .map( - fieldInfo -> { - FieldAccessor fieldAccessor = fieldInfo.getFieldAccessor(); - if (classResolver.isPrimitive(fieldInfo.getEmbeddedClassId())) { - return fieldAccessor.get(originObj); - } - return fury.copyObject(fieldAccessor.get(originObj)); - }) - .toArray(); + InternalFieldInfo[] fieldInfos = this.fieldInfos; + if (fieldInfos == null) { + fieldInfos = buildFieldsInfo(); + } + Object[] fieldValues = new Object[fieldInfos.length]; + for (int i = 0; i < fieldInfos.length; i++) { + InternalFieldInfo fieldInfo = fieldInfos[i]; + FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; + long fieldOffset = fieldAccessor.getFieldOffset(); + if (fieldOffset != -1) { + fieldValues[i] = copyField(originObj, fieldOffset, fieldInfo.classId); + } else { + // field in record class has offset -1 + Object fieldValue = fieldAccessor.get(originObj); + fieldValues[i] = fury.copyObject(fieldValue, fieldInfo.classId); + } + } + return fieldValues; } private void copyFields(T originObj, T newObj) { - List fieldsList = classResolver.getFieldResolver(type).getAllFieldsList(); - for (FieldInfo info : fieldsList) { - FieldAccessor fieldAccessor = info.getFieldAccessor(); - long offset = fieldAccessor.getFieldOffset(); - switch (info.getEmbeddedClassId()) { + InternalFieldInfo[] fieldInfos = this.fieldInfos; + if (fieldInfos == null) { + fieldInfos = buildFieldsInfo(); + } + for (InternalFieldInfo fieldInfo : fieldInfos) { + FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; + long fieldOffset = fieldAccessor.getFieldOffset(); + // record class won't go to this path; + assert fieldOffset != -1; + switch (fieldInfo.classId) { case ClassResolver.PRIMITIVE_BYTE_CLASS_ID: - Platform.putByte(newObj, offset, Platform.getByte(originObj, offset)); + Platform.putByte(newObj, fieldOffset, Platform.getByte(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_CHAR_CLASS_ID: - Platform.putChar(newObj, offset, Platform.getChar(originObj, offset)); + Platform.putChar(newObj, fieldOffset, Platform.getChar(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_SHORT_CLASS_ID: - Platform.putShort(newObj, offset, Platform.getShort(originObj, offset)); + Platform.putShort(newObj, fieldOffset, Platform.getShort(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_INT_CLASS_ID: - Platform.putInt(newObj, offset, Platform.getInt(originObj, offset)); + Platform.putInt(newObj, fieldOffset, Platform.getInt(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_LONG_CLASS_ID: - Platform.putLong(newObj, offset, Platform.getLong(originObj, offset)); + Platform.putLong(newObj, fieldOffset, Platform.getLong(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID: - Platform.putFloat(newObj, offset, Platform.getFloat(originObj, offset)); + Platform.putFloat(newObj, fieldOffset, Platform.getFloat(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID: - Platform.putDouble(newObj, offset, Platform.getDouble(originObj, offset)); + Platform.putDouble(newObj, fieldOffset, Platform.getDouble(originObj, fieldOffset)); break; case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID: - Platform.putBoolean(newObj, offset, Platform.getBoolean(originObj, offset)); + Platform.putBoolean(newObj, fieldOffset, Platform.getBoolean(originObj, fieldOffset)); + break; + case ClassResolver.BOOLEAN_CLASS_ID: + case ClassResolver.BYTE_CLASS_ID: + case ClassResolver.CHAR_CLASS_ID: + case ClassResolver.SHORT_CLASS_ID: + case ClassResolver.INTEGER_CLASS_ID: + case ClassResolver.FLOAT_CLASS_ID: + case ClassResolver.LONG_CLASS_ID: + case ClassResolver.DOUBLE_CLASS_ID: + case ClassResolver.STRING_CLASS_ID: + Platform.putObject(newObj, fieldOffset, Platform.getObject(originObj, fieldOffset)); break; default: Platform.putObject( - newObj, offset, fury.copyObject(Platform.getObject(originObj, offset))); + newObj, fieldOffset, fury.copyObject(Platform.getObject(originObj, fieldOffset))); } } } + private Object copyField(Object targetObject, long fieldOffset, short classId) { + switch (classId) { + case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID: + return Platform.getBoolean(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_BYTE_CLASS_ID: + return Platform.getByte(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_CHAR_CLASS_ID: + return Platform.getChar(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_SHORT_CLASS_ID: + return Platform.getShort(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_INT_CLASS_ID: + return Platform.getInt(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID: + return Platform.getFloat(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_LONG_CLASS_ID: + return Platform.getLong(targetObject, fieldOffset); + case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID: + return Platform.getDouble(targetObject, fieldOffset); + case ClassResolver.BOOLEAN_CLASS_ID: + case ClassResolver.BYTE_CLASS_ID: + case ClassResolver.CHAR_CLASS_ID: + case ClassResolver.SHORT_CLASS_ID: + case ClassResolver.INTEGER_CLASS_ID: + case ClassResolver.FLOAT_CLASS_ID: + case ClassResolver.LONG_CLASS_ID: + case ClassResolver.DOUBLE_CLASS_ID: + case ClassResolver.STRING_CLASS_ID: + return Platform.getObject(targetObject, fieldOffset); + default: + return fury.copyObject(Platform.getObject(targetObject, fieldOffset)); + } + } + + private InternalFieldInfo[] buildFieldsInfo() { + List descriptors = new ArrayList<>(); + if (RecordUtils.isRecord(type)) { + RecordComponent[] components = RecordUtils.getRecordComponents(type); + assert components != null; + try { + for (RecordComponent component : components) { + Field field = type.getDeclaredField(component.getName()); + descriptors.add( + new Descriptor( + field, TypeRef.of(field.getGenericType()), component.getAccessor(), null)); + } + } catch (NoSuchFieldException e) { + // impossible + Platform.throwException(e); + } + } else { + for (Field field : ReflectionUtils.getFields(type, true)) { + if (!Modifier.isStatic(field.getModifiers())) { + descriptors.add(new Descriptor(field, TypeRef.of(field.getGenericType()), null, null)); + } + } + } + DescriptorGrouper descriptorGrouper = + createDescriptorGrouper( + fury.getClassResolver()::isMonomorphic, + descriptors, + false, + fury.compressInt(), + fury.compressLong()); + Tuple3, GenericTypeField[], GenericTypeField[]> infos = + buildFieldInfos(fury, descriptorGrouper); + fieldInfos = new InternalFieldInfo[descriptors.size()]; + System.arraycopy(infos.f0.f0, 0, fieldInfos, 0, infos.f0.f0.length); + System.arraycopy(infos.f1, 0, fieldInfos, infos.f0.f0.length, infos.f1.length); + System.arraycopy(infos.f2, 0, fieldInfos, fieldInfos.length - infos.f2.length, infos.f2.length); + return fieldInfos; + } + protected T newBean() { if (constructor != null) { try { @@ -143,4 +255,157 @@ protected T newBean() { } return Platform.newInstance(type); } + + static Tuple3, GenericTypeField[], GenericTypeField[]> + buildFieldInfos(Fury fury, DescriptorGrouper grouper) { + // When a type is both Collection/Map and final, add it to collection/map fields to keep + // consistent with jit. + Collection primitives = grouper.getPrimitiveDescriptors(); + Collection boxed = grouper.getBoxedDescriptors(); + Collection finals = grouper.getFinalDescriptors(); + FinalTypeField[] finalFields = + new FinalTypeField[primitives.size() + boxed.size() + finals.size()]; + int cnt = 0; + for (Descriptor d : primitives) { + finalFields[cnt++] = buildFinalTypeField(fury, d); + } + for (Descriptor d : boxed) { + finalFields[cnt++] = buildFinalTypeField(fury, d); + } + // TODO(chaokunyang) Support Pojo generics besides Map/Collection subclass + // when it's supported in BaseObjectCodecBuilder. + for (Descriptor d : finals) { + finalFields[cnt++] = buildFinalTypeField(fury, d); + } + boolean[] isFinal = new boolean[finalFields.length]; + for (int i = 0; i < isFinal.length; i++) { + ClassInfo classInfo = finalFields[i].classInfo; + isFinal[i] = classInfo != null && fury.getClassResolver().isMonomorphic(classInfo.getCls()); + } + cnt = 0; + GenericTypeField[] otherFields = new GenericTypeField[grouper.getOtherDescriptors().size()]; + for (Descriptor descriptor : grouper.getOtherDescriptors()) { + GenericTypeField genericTypeField = + new GenericTypeField( + descriptor.getRawType(), + descriptor.getDeclaringClass() + "." + descriptor.getName(), + descriptor.getField() != null + ? FieldAccessor.createAccessor(descriptor.getField()) + : null, + fury); + otherFields[cnt++] = genericTypeField; + } + cnt = 0; + Collection collections = grouper.getCollectionDescriptors(); + Collection maps = grouper.getMapDescriptors(); + GenericTypeField[] containerFields = new GenericTypeField[collections.size() + maps.size()]; + for (Descriptor d : collections) { + containerFields[cnt++] = buildContainerField(fury, d); + } + for (Descriptor d : maps) { + containerFields[cnt++] = buildContainerField(fury, d); + } + return Tuple3.of(Tuple2.of(finalFields, isFinal), otherFields, containerFields); + } + + private static FinalTypeField buildFinalTypeField(Fury fury, Descriptor d) { + return new FinalTypeField( + d.getRawType(), + d.getDeclaringClass() + "." + d.getName(), + // `d.getField()` will be null when peer class doesn't have this field. + d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : null, + fury); + } + + private static GenericTypeField buildContainerField(Fury fury, Descriptor d) { + return new GenericTypeField( + d.getTypeRef(), + d.getDeclaringClass() + "." + d.getName(), + d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : null, + fury); + } + + static class InternalFieldInfo { + protected final short classId; + protected final String qualifiedFieldName; + protected final FieldAccessor fieldAccessor; + + private InternalFieldInfo( + short classId, String qualifiedFieldName, FieldAccessor fieldAccessor) { + this.classId = classId; + this.qualifiedFieldName = qualifiedFieldName; + this.fieldAccessor = fieldAccessor; + } + + @Override + public String toString() { + return "InternalFieldInfo{" + + "classId=" + + classId + + ", fieldName=" + + qualifiedFieldName + + ", field=" + + (fieldAccessor != null ? fieldAccessor.getField() : null) + + '}'; + } + } + + static final class FinalTypeField extends InternalFieldInfo { + final ClassInfo classInfo; + + private FinalTypeField(Class type, String fieldName, FieldAccessor accessor, Fury fury) { + super(getRegisteredClassId(fury, type), fieldName, accessor); + // invoke `copy` to avoid ObjectSerializer construct clear serializer by `clearSerializer`. + if (type == FinalObjectTypeStub.class) { + // `FinalObjectTypeStub` has no fields, using its `classInfo` + // will make deserialization failed. + classInfo = null; + } else { + classInfo = fury.getClassResolver().getClassInfo(type); + } + } + } + + static final class GenericTypeField extends InternalFieldInfo { + final GenericType genericType; + final ClassInfoHolder classInfoHolder; + final boolean trackingRef; + + private GenericTypeField( + Class cls, String qualifiedFieldName, FieldAccessor accessor, Fury fury) { + super(getRegisteredClassId(fury, cls), qualifiedFieldName, accessor); + // TODO support generics in Pojo, see ComplexObjectSerializer.getGenericTypes + genericType = fury.getClassResolver().buildGenericType(cls); + classInfoHolder = fury.getClassResolver().nilClassInfoHolder(); + trackingRef = fury.getClassResolver().needToWriteRef(cls); + } + + private GenericTypeField( + TypeRef typeRef, String qualifiedFieldName, FieldAccessor accessor, Fury fury) { + super(getRegisteredClassId(fury, getRawType(typeRef)), qualifiedFieldName, accessor); + // TODO support generics in Pojo, see ComplexObjectSerializer.getGenericTypes + genericType = fury.getClassResolver().buildGenericType(typeRef); + classInfoHolder = fury.getClassResolver().nilClassInfoHolder(); + trackingRef = fury.getClassResolver().needToWriteRef(getRawType(typeRef)); + } + + @Override + public String toString() { + return "GenericTypeField{" + + "genericType=" + + genericType + + ", classId=" + + classId + + ", qualifiedFieldName=" + + qualifiedFieldName + + ", field=" + + (fieldAccessor != null ? fieldAccessor.getField() : null) + + '}'; + } + } + + private static short getRegisteredClassId(Fury fury, Class cls) { + Short classId = fury.getClassResolver().getRegisteredClassId(cls); + return classId == null ? ClassResolver.NO_CLASS_ID : classId; + } } diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java index 77647d8e40..1292fcbdb6 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java @@ -96,7 +96,7 @@ public MetaSharedSerializer(Fury fury, Class type, ClassDef classDef) { Tuple2, ObjectSerializer.GenericTypeField[], ObjectSerializer.GenericTypeField[]> - infos = ObjectSerializer.buildFieldInfos(fury, descriptorGrouper); + infos = AbstractObjectSerializer.buildFieldInfos(fury, descriptorGrouper); finalFields = infos.f0.f0; isFinal = infos.f0.f1; otherFields = infos.f1; diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java b/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java index 974bef4656..fa8768b928 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java @@ -167,7 +167,7 @@ private ClassFieldsInfo getClassFieldsInfo(ClassDef classDef) { Tuple2, ObjectSerializer.GenericTypeField[], ObjectSerializer.GenericTypeField[]> - tuple = ObjectSerializer.buildFieldInfos(fury, descriptorGrouper); + tuple = AbstractObjectSerializer.buildFieldInfos(fury, descriptorGrouper); int classVersionHash = 0; if (fury.checkClassVersion()) { classVersionHash = ObjectSerializer.computeVersionHash(descriptors); diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java index 9edd5fb8d4..2c72fb2e9b 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java @@ -20,7 +20,6 @@ package org.apache.fury.serializer; import static org.apache.fury.type.DescriptorGrouper.createDescriptorGrouper; -import static org.apache.fury.type.TypeUtils.getRawType; import java.util.ArrayList; import java.util.Arrays; @@ -36,15 +35,11 @@ import org.apache.fury.memory.Platform; import org.apache.fury.meta.ClassDef; import org.apache.fury.reflect.FieldAccessor; -import org.apache.fury.reflect.TypeRef; import org.apache.fury.resolver.ClassInfo; -import org.apache.fury.resolver.ClassInfoHolder; import org.apache.fury.resolver.ClassResolver; import org.apache.fury.resolver.RefResolver; import org.apache.fury.type.Descriptor; import org.apache.fury.type.DescriptorGrouper; -import org.apache.fury.type.FinalObjectTypeStub; -import org.apache.fury.type.GenericType; import org.apache.fury.type.Generics; import org.apache.fury.util.record.RecordInfo; import org.apache.fury.util.record.RecordUtils; @@ -127,75 +122,6 @@ public ObjectSerializer(Fury fury, Class cls, boolean resolveParent) { containerFields = infos.f2; } - static Tuple3, GenericTypeField[], GenericTypeField[]> - buildFieldInfos(Fury fury, DescriptorGrouper grouper) { - // When a type is both Collection/Map and final, add it to collection/map fields to keep - // consistent with jit. - Collection primitives = grouper.getPrimitiveDescriptors(); - Collection boxed = grouper.getBoxedDescriptors(); - Collection finals = grouper.getFinalDescriptors(); - FinalTypeField[] finalFields = - new FinalTypeField[primitives.size() + boxed.size() + finals.size()]; - int cnt = 0; - for (Descriptor d : primitives) { - finalFields[cnt++] = buildFinalTypeField(fury, d); - } - for (Descriptor d : boxed) { - finalFields[cnt++] = buildFinalTypeField(fury, d); - } - // TODO(chaokunyang) Support Pojo generics besides Map/Collection subclass - // when it's supported in BaseObjectCodecBuilder. - for (Descriptor d : finals) { - finalFields[cnt++] = buildFinalTypeField(fury, d); - } - boolean[] isFinal = new boolean[finalFields.length]; - for (int i = 0; i < isFinal.length; i++) { - ClassInfo classInfo = finalFields[i].classInfo; - isFinal[i] = classInfo != null && fury.getClassResolver().isMonomorphic(classInfo.getCls()); - } - cnt = 0; - GenericTypeField[] otherFields = new GenericTypeField[grouper.getOtherDescriptors().size()]; - for (Descriptor descriptor : grouper.getOtherDescriptors()) { - GenericTypeField genericTypeField = - new GenericTypeField( - descriptor.getRawType(), - descriptor.getDeclaringClass() + "." + descriptor.getName(), - descriptor.getField() != null - ? FieldAccessor.createAccessor(descriptor.getField()) - : null, - fury); - otherFields[cnt++] = genericTypeField; - } - cnt = 0; - Collection collections = grouper.getCollectionDescriptors(); - Collection maps = grouper.getMapDescriptors(); - GenericTypeField[] containerFields = new GenericTypeField[collections.size() + maps.size()]; - for (Descriptor d : collections) { - containerFields[cnt++] = buildContainerField(fury, d); - } - for (Descriptor d : maps) { - containerFields[cnt++] = buildContainerField(fury, d); - } - return Tuple3.of(Tuple2.of(finalFields, isFinal), otherFields, containerFields); - } - - private static FinalTypeField buildFinalTypeField(Fury fury, Descriptor d) { - return new FinalTypeField( - d.getRawType(), - d.getDeclaringClass() + "." + d.getName(), - // `d.getField()` will be null when peer class doesn't have this field. - d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : null, - fury); - } - - private static GenericTypeField buildContainerField(Fury fury, Descriptor d) { - return new GenericTypeField( - d.getTypeRef(), - d.getDeclaringClass() + "." + d.getName(), - d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : null, - fury); - } - @Override public void write(MemoryBuffer buffer, T value) { Fury fury = this.fury; @@ -856,90 +782,6 @@ static boolean readBasicObjectFieldValueFailed( } } - static class InternalFieldInfo { - protected final short classId; - protected final String qualifiedFieldName; - protected final FieldAccessor fieldAccessor; - - private InternalFieldInfo( - short classId, String qualifiedFieldName, FieldAccessor fieldAccessor) { - this.classId = classId; - this.qualifiedFieldName = qualifiedFieldName; - this.fieldAccessor = fieldAccessor; - } - - @Override - public String toString() { - return "InternalFieldInfo{" - + "classId=" - + classId - + ", fieldName=" - + qualifiedFieldName - + ", field=" - + (fieldAccessor != null ? fieldAccessor.getField() : null) - + '}'; - } - } - - static final class FinalTypeField extends InternalFieldInfo { - final ClassInfo classInfo; - - private FinalTypeField(Class type, String fieldName, FieldAccessor accessor, Fury fury) { - super(getRegisteredClassId(fury, type), fieldName, accessor); - // invoke `copy` to avoid ObjectSerializer construct clear serializer by `clearSerializer`. - if (type == FinalObjectTypeStub.class) { - // `FinalObjectTypeStub` has no fields, using its `classInfo` - // will make deserialization failed. - classInfo = null; - } else { - classInfo = fury.getClassResolver().getClassInfo(type); - } - } - } - - static final class GenericTypeField extends InternalFieldInfo { - private final GenericType genericType; - final ClassInfoHolder classInfoHolder; - final boolean trackingRef; - - private GenericTypeField( - Class cls, String qualifiedFieldName, FieldAccessor accessor, Fury fury) { - super(getRegisteredClassId(fury, cls), qualifiedFieldName, accessor); - // TODO support generics in Pojo, see ComplexObjectSerializer.getGenericTypes - genericType = fury.getClassResolver().buildGenericType(cls); - classInfoHolder = fury.getClassResolver().nilClassInfoHolder(); - trackingRef = fury.getClassResolver().needToWriteRef(cls); - } - - private GenericTypeField( - TypeRef typeRef, String qualifiedFieldName, FieldAccessor accessor, Fury fury) { - super(getRegisteredClassId(fury, getRawType(typeRef)), qualifiedFieldName, accessor); - // TODO support generics in Pojo, see ComplexObjectSerializer.getGenericTypes - genericType = fury.getClassResolver().buildGenericType(typeRef); - classInfoHolder = fury.getClassResolver().nilClassInfoHolder(); - trackingRef = fury.getClassResolver().needToWriteRef(getRawType(typeRef)); - } - - @Override - public String toString() { - return "GenericTypeField{" - + "genericType=" - + genericType - + ", classId=" - + classId - + ", qualifiedFieldName=" - + qualifiedFieldName - + ", field=" - + (fieldAccessor != null ? fieldAccessor.getField() : null) - + '}'; - } - } - - private static short getRegisteredClassId(Fury fury, Class cls) { - Short classId = fury.getClassResolver().getRegisteredClassId(cls); - return classId == null ? ClassResolver.NO_CLASS_ID : classId; - } - public static int computeVersionHash(Collection descriptors) { // TODO(chaokunyang) use murmurhash List list = new ArrayList<>();