Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 4 additions & 0 deletions java/fory-core/src/main/java/org/apache/fory/Fory.java
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,10 @@ public boolean isCompatible() {
return config.getCompatibleMode() == CompatibleMode.COMPATIBLE;
}

public boolean isShareMeta() {
return shareMeta;
}

public boolean trackingRef() {
return refTracking;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,6 @@ protected Expression getOrCreateSerializer(Class<?> cls) {
&& !MapLikeSerializer.class.isAssignableFrom(serializerClass)) {
serializerClass = MapLikeSerializer.class;
}
TypeRef<? extends Serializer> serializerTypeRef = TypeRef.of(serializerClass);
Expression fieldTypeExpr = getClassExpr(cls);
// Don't invoke `Serializer.newSerializer` here, since it(ex. ObjectSerializer) may set itself
// as global serializer, which overwrite serializer updates in jit callback.
Expand All @@ -656,6 +655,7 @@ protected Expression getOrCreateSerializer(Class<?> cls) {
false, ctx.type(Serializer.class), name, cast(newSerializerExpr, SERIALIZER_TYPE));
serializerRef = new Reference(name, SERIALIZER_TYPE, false);
} else {
TypeRef<? extends Serializer> serializerTypeRef = TypeRef.of(serializerClass);
ctx.addField(
true, ctx.type(serializerClass), name, cast(newSerializerExpr, serializerTypeRef));
serializerRef = fieldRef(name, serializerTypeRef);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
Expand Down Expand Up @@ -110,6 +111,7 @@
import org.apache.fory.serializer.CompatibleSerializer;
import org.apache.fory.serializer.EnumSerializer;
import org.apache.fory.serializer.ExternalizableSerializer;
import org.apache.fory.serializer.FinalFieldReplaceResolveSerializer;
import org.apache.fory.serializer.ForyCopyableSerializer;
import org.apache.fory.serializer.JavaSerializer;
import org.apache.fory.serializer.JdkProxySerializer;
Expand Down Expand Up @@ -789,8 +791,8 @@ public void clearSerializer(Class<?> cls) {
}
}

/** Ass serializer for specified class. */
private void addSerializer(Class<?> type, Serializer<?> serializer) {
/** Add serializer for specified class. */
public void addSerializer(Class<?> type, Serializer<?> serializer) {
Preconditions.checkNotNull(serializer);
// 1. Try to get ClassInfo from `registeredId2ClassInfo` and
// `classInfoMap` or create a new `ClassInfo`.
Expand All @@ -801,7 +803,8 @@ private void addSerializer(Class<?> type, Serializer<?> serializer) {
if (registered) {
classInfo = registeredId2ClassInfo[classId];
} else {
if (serializer instanceof ReplaceResolveSerializer) {
if (serializer instanceof ReplaceResolveSerializer
&& !(serializer instanceof FinalFieldReplaceResolveSerializer)) {
classId = REPLACE_STUB_ID;
} else {
classId = NO_CLASS_ID;
Expand Down Expand Up @@ -896,7 +899,11 @@ public Class<? extends Serializer> getSerializerClass(Class<?> cls, boolean code
return Serializers.CharsetSerializer.class;
} else if (ReflectionUtils.isJdkProxy(cls)) {
if (JavaSerializer.getWriteReplaceMethod(cls) != null) {
return ReplaceResolveSerializer.class;
if (!fory.isCompatible() && !fory.isShareMeta() && Modifier.isFinal(cls.getModifiers())) {
return FinalFieldReplaceResolveSerializer.class;
} else {
return ReplaceResolveSerializer.class;
}
} else {
return JdkProxySerializer.class;
}
Expand Down Expand Up @@ -968,7 +975,11 @@ public Class<? extends Serializer> getSerializerClass(Class<?> cls, boolean code
LOG.warn("Class {} isn't supported for cross-language serialization.", cls);
}
if (useReplaceResolveSerializer(cls)) {
return ReplaceResolveSerializer.class;
if (!fory.isCompatible() && !fory.isShareMeta() && Modifier.isFinal(cls.getModifiers())) {
return FinalFieldReplaceResolveSerializer.class;
} else {
return ReplaceResolveSerializer.class;
}
}
if (Externalizable.class.isAssignableFrom(cls)) {
return ExternalizableSerializer.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,15 @@ protected T newBean() {
// TODO(chaokunyang) Support Pojo<T> generics besides Map/Collection subclass
// when it's supported in BaseObjectCodecBuilder.
for (Descriptor d : finals) {
finalFields[cnt++] = new FinalTypeField(fory, d);
FinalTypeField typeField = new FinalTypeField(fory, d);
// overwrite replace resolve serializer for final field
if (!fory.isShareMeta()
&& !fory.isCompatible()
&& typeField.classInfo.getSerializer() instanceof ReplaceResolveSerializer) {
typeField.classInfo.setSerializer(
new FinalFieldReplaceResolveSerializer(fory, typeField.classInfo.getCls()));
}
finalFields[cnt++] = typeField;
}
boolean[] isFinal = new boolean[finalFields.length];
for (int i = 0; i < isFinal.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.fory.serializer;

import org.apache.fory.Fory;
import org.apache.fory.memory.MemoryBuffer;

/**
* Serializer for class which 1) has jdk `writeReplace`/`readResolve` method defined, 2) is a final
* field of a class. //TODO do we ned to write the flag REPLACED_NEW_TYPE/REPLACED_SAME_TYPE even
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waht does this TODO means?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also document that this serializer only works for consist mode, and add checks in FinalFieldReplaceResolveSerializer constructor.

Copy link
Contributor Author

@mchernyakov mchernyakov Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in 446cc65 and removed the TODO

* for the final field?
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class FinalFieldReplaceResolveSerializer extends ReplaceResolveSerializer {

public FinalFieldReplaceResolveSerializer(Fory fory, Class type) {
super(fory, type, true);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super class will invoke :

  public ReplaceResolveSerializer(Fory fory, Class type) {
    super(fory, type);
    refResolver = fory.getRefResolver();
    classResolver = fory.getClassResolver();
    // `setSerializer` before `newJDKMethodInfoCache` since it query classinfo from `classResolver`,
    // which create serializer in turn.
    // ReplaceResolveSerializer is used as data serializer for ImmutableList/Map,
    // which serializer is already set.
    classResolver.setSerializerIfAbsent(type, this);

classResolver.setSerializerIfAbsent(type, this); may set wrong serializer, could we add another protected contrcutore in parent class to pass setSerializer param and pass false here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

@Override
protected void writeObject(
MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) {
jdkMethodInfoCache.objectSerializer.write(buffer, value);
}

@Override
protected Object readObject(MemoryBuffer buffer) {
MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(type);
Object o = jdkMethodInfoCache.objectSerializer.read(buffer);
ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info;
if (replaceResolveInfo.readResolveMethod == null) {
return o;
}
return replaceResolveInfo.readResolve(o);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ public class ReplaceResolveSerializer extends Serializer {
*/
public static class ReplaceStub {}

private static final byte ORIGINAL = 0;
private static final byte REPLACED_NEW_TYPE = 1;
private static final byte REPLACED_SAME_TYPE = 2;
protected static final byte ORIGINAL = 0;
protected static final byte REPLACED_NEW_TYPE = 1;
protected static final byte REPLACED_SAME_TYPE = 2;

// Extract Method Info to cache for graalvm build time lambda generation and avoid
// generate function repeatedly too.
private static class ReplaceResolveInfo {
private final Method writeReplaceMethod;
private final Method readResolveMethod;
protected static class ReplaceResolveInfo {
protected final Method writeReplaceMethod;
protected final Method readResolveMethod;
private final Function writeReplaceFunc;
private final Function readResolveFunc;

Expand Down Expand Up @@ -162,10 +162,10 @@ protected ReplaceResolveInfo computeValue(Class<?> type) {
}
};

private static class MethodInfoCache {
private final ReplaceResolveInfo info;
protected static class MethodInfoCache {
protected final ReplaceResolveInfo info;

private Serializer objectSerializer;
protected Serializer objectSerializer;

public MethodInfoCache(ReplaceResolveInfo info) {
this.info = info;
Expand Down Expand Up @@ -210,13 +210,17 @@ private static Serializer createDataSerializer(
return serializer;
}

private final RefResolver refResolver;
private final ClassResolver classResolver;
private final MethodInfoCache jdkMethodInfoWriteCache;
private final ClassInfo writeClassInfo;
private final Map<Class<?>, MethodInfoCache> classClassInfoHolderMap = new HashMap<>();
protected final RefResolver refResolver;
protected final ClassResolver classResolver;
protected final MethodInfoCache jdkMethodInfoWriteCache;
protected final ClassInfo writeClassInfo;
protected final Map<Class<?>, MethodInfoCache> classClassInfoHolderMap = new HashMap<>();

public ReplaceResolveSerializer(Fory fory, Class type) {
this(fory, type, false);
}

public ReplaceResolveSerializer(Fory fory, Class type, boolean isFinalField) {
super(fory, type);
refResolver = fory.getRefResolver();
classResolver = fory.getClassResolver();
Expand All @@ -228,8 +232,12 @@ public ReplaceResolveSerializer(Fory fory, Class type) {
if (type != ReplaceStub.class) {
jdkMethodInfoWriteCache = newJDKMethodInfoCache(type, fory);
classClassInfoHolderMap.put(type, jdkMethodInfoWriteCache);
// FIXME new classinfo may miss serializer update in async compilation mode.
writeClassInfo = classResolver.newClassInfo(type, this, ClassResolver.NO_CLASS_ID);
if (isFinalField) {
writeClassInfo = null;
} else {
// FIXME new classinfo may miss serializer update in async compilation mode.
writeClassInfo = classResolver.newClassInfo(type, this, ClassResolver.NO_CLASS_ID);
}
} else {
jdkMethodInfoWriteCache = null;
writeClassInfo = null;
Expand Down Expand Up @@ -280,7 +288,8 @@ public void write(MemoryBuffer buffer, Object value) {
}
}

private void writeObject(MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) {
protected void writeObject(
MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) {
classResolver.writeClassInternal(buffer, writeClassInfo);
jdkMethodInfoCache.objectSerializer.write(buffer, value);
}
Expand Down Expand Up @@ -319,7 +328,7 @@ public Object read(MemoryBuffer buffer) {
}
}

private Object readObject(MemoryBuffer buffer) {
protected Object readObject(MemoryBuffer buffer) {
Class cls = classResolver.readClassInternal(buffer);
MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(cls);
Object o = jdkMethodInfoCache.objectSerializer.read(buffer);
Expand Down Expand Up @@ -350,7 +359,7 @@ public Object copy(Object originObj) {
return newObj;
}

private MethodInfoCache getMethodInfoCache(Class<?> cls) {
protected MethodInfoCache getMethodInfoCache(Class<?> cls) {
MethodInfoCache jdkMethodInfoCache = classClassInfoHolderMap.get(cls);
if (jdkMethodInfoCache == null) {
jdkMethodInfoCache = newJDKMethodInfoCache(cls, fory);
Expand Down
Loading
Loading