Skip to content

Commit

Permalink
fix(java): child container deep copy (#1911)
Browse files Browse the repository at this point in the history

## What does this PR do?
fix child container bug


<!-- Describe the purpose of this PR. -->

## Related issues
#1855
<!--
Is there any related issue? Please attach here.

- #xxxx0
- #xxxx1
- #xxxx2
-->

## Does this PR introduce any user-facing change?

<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->

- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?

## Benchmark

<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
-->
  • Loading branch information
zhaommmmomo authored Oct 26, 2024
1 parent 86d1201 commit b222660
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,19 @@ public static List<Field> getFields(Class<?> cls, boolean searchParent) {
return fields;
}

public static List<Field> getFieldsWithoutSuperClasses(Class<?> cls, Set<Class<?>> superClasses) {
Preconditions.checkNotNull(cls);
List<Field> fields = new ArrayList<>();
Class<?> clazz = cls;
do {
if (!superClasses.contains(clazz)) {
Collections.addAll(fields, clazz.getDeclaredFields());
}
clazz = clazz.getSuperclass();
} while (clazz != null);
return fields;
}

/** Get fields values from provided object. */
public static List<Object> getFieldValues(Collection<Field> fields, Object o) {
List<Object> results = new ArrayList<>(fields.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,56 @@ private void copyFields(T originObj, T newObj) {
}
}

public static void copyFields(
Fury fury, InternalFieldInfo[] fieldInfos, Object originObj, Object newObj) {
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, fieldOffset, Platform.getByte(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
Platform.putChar(newObj, fieldOffset, Platform.getChar(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
Platform.putShort(newObj, fieldOffset, Platform.getShort(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_INT_CLASS_ID:
Platform.putInt(newObj, fieldOffset, Platform.getInt(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
Platform.putLong(newObj, fieldOffset, Platform.getLong(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
Platform.putFloat(newObj, fieldOffset, Platform.getFloat(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
Platform.putDouble(newObj, fieldOffset, Platform.getDouble(originObj, fieldOffset));
break;
case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
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, fieldOffset, fury.copyObject(Platform.getObject(originObj, fieldOffset)));
}
}
}

private Object copyField(Object targetObject, long fieldOffset, short classId) {
switch (classId) {
case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
Expand Down Expand Up @@ -256,6 +306,29 @@ private InternalFieldInfo[] buildFieldsInfo() {
return fieldInfos;
}

public static InternalFieldInfo[] buildFieldsInfo(Fury fury, List<Field> fields) {
List<Descriptor> descriptors = new ArrayList<>();
for (Field field : fields) {
if (!Modifier.isTransient(field.getModifiers()) && !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<Tuple2<FinalTypeField[], boolean[]>, GenericTypeField[], GenericTypeField[]> infos =
buildFieldInfos(fury, descriptorGrouper);
InternalFieldInfo[] 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 {
Expand Down Expand Up @@ -336,7 +409,7 @@ private static GenericTypeField buildContainerField(Fury fury, Descriptor d) {
fury);
}

static class InternalFieldInfo {
public static class InternalFieldInfo {
protected final short classId;
protected final String qualifiedFieldName;
protected final FieldAccessor fieldAccessor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import static org.apache.fury.collection.Collections.ofHashSet;

import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -40,6 +41,8 @@
import org.apache.fury.reflect.ReflectionUtils;
import org.apache.fury.resolver.ClassResolver;
import org.apache.fury.resolver.FieldResolver;
import org.apache.fury.serializer.AbstractObjectSerializer;
import org.apache.fury.serializer.AbstractObjectSerializer.InternalFieldInfo;
import org.apache.fury.serializer.CompatibleSerializer;
import org.apache.fury.serializer.JavaSerializer;
import org.apache.fury.serializer.ObjectSerializer;
Expand Down Expand Up @@ -127,7 +130,7 @@ public static class ChildCollectionSerializer<T extends Collection>
ArrayList.class, LinkedList.class, ArrayDeque.class, Vector.class, HashSet.class
// PriorityQueue/TreeSet/ConcurrentSkipListSet need comparator as constructor argument
);
protected Serializer<T> objectSerializer;
protected InternalFieldInfo[] fieldInfos;
protected final Serializer[] slotsSerializers;

public ChildCollectionSerializer(Fury fury, Class<T> cls) {
Expand All @@ -151,11 +154,14 @@ public Collection newCollection(MemoryBuffer buffer) {
}

@Override
public T copy(T originCollection) {
if (objectSerializer == null) {
objectSerializer = new ObjectSerializer<>(fury, type, false);
public Collection newCollection(Collection originCollection) {
Collection newCollection = super.newCollection(originCollection);
if (fieldInfos == null) {
List<Field> fields = ReflectionUtils.getFieldsWithoutSuperClasses(type, superClasses);
fieldInfos = AbstractObjectSerializer.buildFieldsInfo(fury, fields);
}
return fury.copyObject(originCollection, objectSerializer);
AbstractObjectSerializer.copyFields(fury, fieldInfos, originCollection, newCollection);
return newCollection;
}
}

Expand Down Expand Up @@ -185,8 +191,8 @@ public static class ChildMapSerializer<T extends Map> extends MapSerializer<T> {
HashMap.class, LinkedHashMap.class, ConcurrentHashMap.class
// TreeMap/ConcurrentSkipListMap need comparator as constructor argument
);
private Serializer<T> objectSerializer;
private final Serializer[] slotsSerializers;
private InternalFieldInfo[] fieldInfos;

public ChildMapSerializer(Fury fury, Class<T> cls) {
super(fury, cls);
Expand All @@ -210,11 +216,14 @@ public Map newMap(MemoryBuffer buffer) {
}

@Override
public T copy(T originMap) {
if (objectSerializer == null) {
objectSerializer = new ObjectSerializer<>(fury, type, false);
public Map newMap(Map originMap) {
Map newMap = super.newMap(originMap);
if (fieldInfos == null || fieldInfos.length == 0) {
List<Field> fields = ReflectionUtils.getFieldsWithoutSuperClasses(type, superClasses);
fieldInfos = AbstractObjectSerializer.buildFieldsInfo(fury, fields);
}
return fury.copyObject(originMap, objectSerializer);
AbstractObjectSerializer.copyFields(fury, fieldInfos, originMap, newMap);
return newMap;
}
}

Expand Down

0 comments on commit b222660

Please sign in to comment.