This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 7a02b04d3 feat(java): support codegen for xlang mode in java (#2613)
7a02b04d3 is described below
commit 7a02b04d36a48afc9880511e201d3f1634ce268b
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Sep 16 10:34:41 2025 +0800
feat(java): support codegen for xlang mode in java (#2613)
## Why?
support codegen for xlang mode in java to speed uo xlang mode
performance
## What does this PR do?
support codegen for xlang mode in java
## Related issues
Closes https://github.com/apache/fory/issues/2286
Closes https://github.com/apache/fory/pull/2312
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] 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.
Delete section if not applicable.
-->
---
.../src/main/java/org/apache/fory/Fory.java | 14 +-
.../fory/builder/BaseObjectCodecBuilder.java | 150 ++++++++++---------
.../java/org/apache/fory/builder/CodecBuilder.java | 3 +
.../fory/builder/CompatibleCodecBuilder.java | 6 +-
.../fory/builder/MetaSharedCodecBuilder.java | 7 +-
.../apache/fory/builder/ObjectCodecBuilder.java | 13 +-
.../java/org/apache/fory/config/ForyBuilder.java | 3 -
.../org/apache/fory/resolver/ClassResolver.java | 151 ++-----------------
.../org/apache/fory/resolver/FieldResolver.java | 1 +
.../org/apache/fory/resolver/TypeResolver.java | 145 ++++++++++++++++---
.../org/apache/fory/resolver/XtypeResolver.java | 159 +++++++++++++++++++--
.../fory/serializer/DeferedLazySerializer.java | 82 +++++++++++
.../apache/fory/serializer/ObjectSerializer.java | 9 +-
.../collection/CollectionLikeSerializer.java | 9 +-
.../collection/CollectionSerializer.java | 10 --
.../collection/CollectionSerializers.java | 49 +++++++
.../fory/serializer/collection/MapSerializers.java | 32 +++++
.../java/org/apache/fory/CrossLanguageTest.java | 39 ++---
.../collection/XlangCollectionSerializerTest.java | 6 +-
python/pyfory/serializer.py | 2 +-
python/pyfory/tests/test_cross_language.py | 10 +-
21 files changed, 606 insertions(+), 294 deletions(-)
diff --git a/java/fory-core/src/main/java/org/apache/fory/Fory.java
b/java/fory-core/src/main/java/org/apache/fory/Fory.java
index 61073d13b..cfde3b446 100644
--- a/java/fory-core/src/main/java/org/apache/fory/Fory.java
+++ b/java/fory-core/src/main/java/org/apache/fory/Fory.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.concurrent.NotThreadSafe;
+import org.apache.fory.annotation.Internal;
import org.apache.fory.builder.JITContext;
import org.apache.fory.collection.IdentityMap;
import org.apache.fory.config.CompatibleMode;
@@ -58,6 +59,7 @@ import org.apache.fory.resolver.MetaStringResolver;
import org.apache.fory.resolver.NoRefResolver;
import org.apache.fory.resolver.RefResolver;
import org.apache.fory.resolver.SerializationContext;
+import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.resolver.XtypeResolver;
import org.apache.fory.serializer.ArraySerializers;
import org.apache.fory.serializer.BufferCallback;
@@ -1636,8 +1638,16 @@ public final class Fory implements BaseFory {
return xtypeResolver;
}
- // don't provide getTypeResolver directly to avoid users use this
- // API too much since it has polymorphic invoke cost.
+ /**
+ * Don't use this API for type resolving and dispatch, methods on returned
resolver has
+ * polymorphic invoke cost.
+ */
+ @Internal
+ // CHECKSTYLE.OFF:MethodName
+ public TypeResolver _getTypeResolver() {
+ // CHECKSTYLE.ON:MethodName
+ return crossLanguage ? xtypeResolver : classResolver;
+ }
public MetaStringResolver getMetaStringResolver() {
return metaStringResolver;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java
index 0b129581b..c1c8494a0 100644
---
a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java
+++
b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java
@@ -109,6 +109,7 @@ import org.apache.fory.resolver.ClassInfo;
import org.apache.fory.resolver.ClassInfoHolder;
import org.apache.fory.resolver.ClassResolver;
import org.apache.fory.resolver.RefResolver;
+import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.serializer.CompatibleSerializer;
import org.apache.fory.serializer.EnumSerializer;
import org.apache.fory.serializer.ObjectSerializer;
@@ -133,10 +134,9 @@ import org.apache.fory.util.StringUtils;
public abstract class BaseObjectCodecBuilder extends CodecBuilder {
public static final String BUFFER_NAME = "_f_buffer";
public static final String REF_RESOLVER_NAME = "_f_refResolver";
- public static final String CLASS_RESOLVER_NAME = "_f_classResolver";
+ public static final String TYPE_RESOLVER_NAME = "_f_typeResolver";
public static final String POJO_CLASS_TYPE_NAME = "_f_classType";
public static final String STRING_SERIALIZER_NAME = "_f_strSerializer";
- private static final TypeRef<?> CLASS_RESOLVER_TYPE_TOKEN =
TypeRef.of(ClassResolver.class);
private static final TypeRef<?> STRING_SERIALIZER_TYPE_TOKEN =
TypeRef.of(StringSerializer.class);
private static final TypeRef<?> SERIALIZER_TYPE =
TypeRef.of(Serializer.class);
private static final TypeRef<?> COLLECTION_SERIALIZER_TYPE =
@@ -144,24 +144,36 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
private static final TypeRef<?> MAP_SERIALIZER_TYPE =
TypeRef.of(MapLikeSerializer.class);
private static final TypeRef<?> GENERIC_TYPE = TypeRef.of(GenericType.class);
- protected final Reference refResolverRef;
- protected final Reference classResolverRef =
- fieldRef(CLASS_RESOLVER_NAME, CLASS_RESOLVER_TYPE_TOKEN);
protected final Fory fory;
+ protected final Reference refResolverRef;
+ protected final Reference typeResolverRef;
+ protected final TypeResolver typeResolver;
protected final Reference stringSerializerRef;
private final Map<Class<?>, Reference> serializerMap = new HashMap<>();
private final Map<String, Object> sharedFieldMap = new HashMap<>();
protected final Class<?> parentSerializerClass;
private final Map<String, Expression> jitCallbackUpdateFields;
protected LinkedList<String> walkPath = new LinkedList<>();
+ protected final String writeMethodName;
+ protected final String readMethodName;
public BaseObjectCodecBuilder(TypeRef<?> beanType, Fory fory, Class<?>
parentSerializerClass) {
super(new CodegenContext(), beanType);
this.fory = fory;
+ typeResolver = fory._getTypeResolver();
+ TypeRef<?> typeResolverType = TypeRef.of(typeResolver.getClass());
this.parentSerializerClass = parentSerializerClass;
+ if (fory.isCrossLanguage()) {
+ writeMethodName = "xwrite";
+ readMethodName = "xread";
+ } else {
+ writeMethodName = "write";
+ readMethodName = "read";
+ }
addCommonImports();
ctx.reserveName(REF_RESOLVER_NAME);
- ctx.reserveName(CLASS_RESOLVER_NAME);
+ ctx.reserveName(TYPE_RESOLVER_NAME);
+ typeResolverRef = fieldRef(TYPE_RESOLVER_NAME, typeResolverType);
TypeRef<?> refResolverTypeRef =
TypeRef.of(fory.getRefResolver().getClass());
refResolverRef = fieldRef(REF_RESOLVER_NAME, refResolverTypeRef);
Expression refResolverExpr =
@@ -170,15 +182,17 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
ctx.type(refResolverTypeRef),
REF_RESOLVER_NAME,
new Cast(refResolverExpr, refResolverTypeRef));
- Expression classResolverExpr =
- inlineInvoke(foryRef, "getClassResolver", CLASS_RESOLVER_TYPE_TOKEN);
- ctx.addField(ctx.type(CLASS_RESOLVER_TYPE_TOKEN), CLASS_RESOLVER_NAME,
classResolverExpr);
+ Expression typeResolverExpr =
+ cast(
+ inlineInvoke(foryRef, "_getTypeResolver",
TypeRef.of(TypeResolver.class)),
+ typeResolverType);
+ ctx.addField(ctx.type(typeResolverType), TYPE_RESOLVER_NAME,
typeResolverExpr);
ctx.reserveName(STRING_SERIALIZER_NAME);
stringSerializerRef = fieldRef(STRING_SERIALIZER_NAME,
STRING_SERIALIZER_TYPE_TOKEN);
ctx.addField(
ctx.type(TypeRef.of(StringSerializer.class)),
STRING_SERIALIZER_NAME,
- inlineInvoke(foryRef, "getStringSerializer",
CLASS_RESOLVER_TYPE_TOKEN));
+ inlineInvoke(foryRef, "getStringSerializer", typeResolverType));
jitCallbackUpdateFields = new HashMap<>();
}
@@ -224,8 +238,16 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
return fory.getJITContext().asyncVisitFory(function);
}
+ protected <T> T typeResolver(Function<TypeResolver, T> function) {
+ return typeResolver(fory, function);
+ }
+
+ protected static <T> T typeResolver(Fory fory, Function<TypeResolver, T>
function) {
+ return fory.getJITContext().asyncVisitFory(f ->
function.apply(f._getTypeResolver()));
+ }
+
private boolean needWriteRef(TypeRef<?> type) {
- return fory(fory -> fory.getClassResolver().needToWriteRef(type));
+ return typeResolver(r -> r.needToWriteRef(type));
}
@Override
@@ -244,7 +266,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
""
+ "super(${fory}, ${cls});\n"
+ "this.${fory} = ${fory};\n"
- + "${fory}.getClassResolver().setSerializerIfAbsent(${cls},
this);\n",
+ + "${fory}._getTypeResolver().setSerializerIfAbsent(${cls},
this);\n",
"fory",
FORY_NAME,
"cls",
@@ -257,14 +279,14 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
String decodeCode = decodeExpr.genCode(ctx).code();
decodeCode = ctx.optimizeMethodCode(decodeCode);
ctx.overrideMethod(
- "write",
+ writeMethodName,
encodeCode,
void.class,
MemoryBuffer.class,
BUFFER_NAME,
Object.class,
ROOT_OBJECT_NAME);
- ctx.overrideMethod("read", decodeCode, Object.class, MemoryBuffer.class,
BUFFER_NAME);
+ ctx.overrideMethod(readMethodName, decodeCode, Object.class,
MemoryBuffer.class, BUFFER_NAME);
registerJITNotifyCallback();
ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class,
POJO_CLASS_TYPE_NAME);
return ctx.genCode();
@@ -487,7 +509,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
protected boolean useCollectionSerialization(Class<?> type) {
- return fory(f -> f.getClassResolver().isCollection(type));
+ return typeResolver(r -> r.isCollection(type));
}
protected boolean useMapSerialization(TypeRef<?> typeRef) {
@@ -495,7 +517,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
protected boolean useMapSerialization(Class<?> type) {
- return fory(f -> f.getClassResolver().isMap(type));
+ return typeResolver(r -> r.isMap(type));
}
/**
@@ -513,11 +535,11 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
Expression inputObject, Expression buffer, TypeRef<?> typeRef,
Expression serializer) {
Class<?> clz = getRawType(typeRef);
if (serializer != null) {
- return new Invoke(serializer, "write", buffer, inputObject);
+ return new Invoke(serializer, writeMethodName, buffer, inputObject);
}
if (isMonomorphic(clz)) {
serializer = getOrCreateSerializer(clz);
- return new Invoke(serializer, "write", buffer, inputObject);
+ return new Invoke(serializer, writeMethodName, buffer, inputObject);
} else {
return writeForNotNullNonFinalObject(inputObject, buffer, typeRef);
}
@@ -537,14 +559,14 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
neq(new Invoke(classInfo, "getCls", CLASS_TYPE), clsExpr),
new Assign(
classInfo,
- inlineInvoke(classResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
+ inlineInvoke(typeResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
}
writeClassAndObject.add(
- fory(f -> f.getClassResolver().writeClassExpr(classResolverRef,
buffer, classInfo)));
+ typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer,
classInfo)));
writeClassAndObject.add(
new Invoke(
invokeInline(classInfo, "getSerializer", getSerializerType(clz)),
- "write",
+ writeMethodName,
PRIMITIVE_VOID_TYPE,
buffer,
inputObject));
@@ -562,9 +584,8 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
neq(inlineInvoke(classInfo, "getCls", CLASS_TYPE), clsExpr),
new Assign(
classInfo,
- inlineInvoke(classResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
- writeClassAction.add(
- fory(f -> f.getClassResolver().writeClassExpr(classResolverRef,
buffer, classInfo)));
+ inlineInvoke(typeResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
+ writeClassAction.add(typeResolver(r -> r.writeClassExpr(typeResolverRef,
buffer, classInfo)));
if (returnSerializer) {
writeClassAction.add(
invoke(classInfo, "getSerializer", "serializer",
getSerializerType(declaredClass)));
@@ -581,14 +602,8 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
// Preconditions.checkArgument(isMonomorphic(cls), cls);
Reference serializerRef = serializerMap.get(cls);
if (serializerRef == null) {
- Class<? extends Serializer> serializerClass;
- if (fory.isCrossLanguage()) {
- // xlang will take all map/collection interface as monomorphic
- serializerClass = fory(f ->
f.getXtypeResolver().getSerializer(cls)).getClass();
- } else {
- // potential recursive call for seq codec generation is handled in
`getSerializerClass`.
- serializerClass = fory(f ->
f.getClassResolver().getSerializerClass(cls));
- }
+ // potential recursive call for seq codec generation is handled in
`getSerializerClass`.
+ Class<? extends Serializer> serializerClass = typeResolver(r ->
r.getSerializerClass(cls));
Preconditions.checkNotNull(serializerClass, "Unsupported for class " +
cls);
if (!ReflectionUtils.isPublic(serializerClass)) {
// TODO(chaokunyang) add jdk17+ unexported class check.
@@ -629,7 +644,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
// Don't invoke `Serializer.newSerializer` here, since it(ex.
ObjectSerializer) may set itself
// as global serializer, which overwrite serializer updates in jit
callback.
Expression newSerializerExpr =
- inlineInvoke(classResolverRef, "getRawSerializer", SERIALIZER_TYPE,
fieldTypeExpr);
+ inlineInvoke(typeResolverRef, "getRawSerializer", SERIALIZER_TYPE,
fieldTypeExpr);
String name =
ctx.newName(StringUtils.uncapitalize(serializerClass.getSimpleName()));
// It's ok it jit already finished and this method return false, in such
cases
// `serializerClass` is already jit generated class.
@@ -653,7 +668,13 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
if (sourcePublicAccessible(cls)) {
return Literal.ofClass(cls);
} else {
- return staticClassFieldExpr(cls, "__class__" +
cls.getName().replace(".", "_"));
+ String name = cls.getName();
+ if (cls.isArray()) {
+ Tuple2<Class<?>, Integer> info = TypeUtils.getArrayComponentInfo(cls);
+ name = StringUtils.repeat("a", info.f1) + "_" + info.f0.getName();
+ }
+ name = name.replace(".", "_");
+ return staticClassFieldExpr(cls, "__class__" + name);
}
}
@@ -683,7 +704,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
name,
() ->
new Invoke(
- classResolverRef,
+ typeResolverRef,
"getGenericTypeInStruct",
GENERIC_TYPE,
beanClassExpr(),
@@ -710,13 +731,13 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
if (!needUpdate) {
Expression clsExpr = getClassExpr(cls);
- classInfoExpr = inlineInvoke(classResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr);
+ classInfoExpr = inlineInvoke(typeResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr);
// Use `ctx.freshName(cls)` to avoid wrong name for arr type.
String name = ctx.newName(ctx.newName(cls) + "ClassInfo");
ctx.addField(true, ctx.type(ClassInfo.class), name, classInfoExpr);
classInfoRef = Tuple2.of(fieldRef(name, classInfoTypeRef), false);
} else {
- classInfoExpr = inlineInvoke(classResolverRef, "nilClassInfo",
classInfoTypeRef);
+ classInfoExpr = inlineInvoke(typeResolverRef, "nilClassInfo",
classInfoTypeRef);
String name = ctx.newName(cls, "ClassInfo");
ctx.addField(false, ctx.type(ClassInfo.class), name, classInfoExpr);
// Can't use fieldRef, since the field is not final.
@@ -739,7 +760,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
return reference;
}
Expression classInfoHolderExpr =
- inlineInvoke(classResolverRef, "nilClassInfoHolder",
classInfoHolderTypeRef);
+ inlineInvoke(typeResolverRef, "nilClassInfoHolder",
classInfoHolderTypeRef);
String name = ctx.newName(cls, "ClassInfoHolder");
ctx.addField(true, ctx.type(ClassInfoHolder.class), name,
classInfoHolderExpr);
// The class info field read only once, no need to shallow.
@@ -757,19 +778,18 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
Reference classInfoRef = addClassInfoField(cls).f0;
if (inlineReadClassInfo) {
return inlineInvoke(
- classResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoRef);
+ typeResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoRef);
} else {
- return new Invoke(
- classResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoRef);
+ return new Invoke(typeResolverRef, "readClassInfo", classInfoTypeRef,
buffer, classInfoRef);
}
}
Reference classInfoHolderRef = addClassInfoHolderField(cls);
if (inlineReadClassInfo) {
return inlineInvoke(
- classResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoHolderRef);
+ typeResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoHolderRef);
} else {
return new Invoke(
- classResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoHolderRef);
+ typeResolverRef, "readClassInfo", classInfoTypeRef, buffer,
classInfoHolderRef);
}
}
@@ -778,9 +798,9 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
protected TypeRef<?> getSerializerType(Class<?> objType) {
- if (fory(f -> f.getClassResolver().isCollection(objType))) {
+ if (typeResolver(r -> r.isCollection(objType))) {
return COLLECTION_SERIALIZER_TYPE;
- } else if (fory(f -> f.getClassResolver().isMap(objType))) {
+ } else if (typeResolver(r -> r.isMap(objType))) {
return MAP_SERIALIZER_TYPE;
}
return SERIALIZER_TYPE;
@@ -815,9 +835,9 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
neq(new Invoke(classInfo, "getCls", CLASS_TYPE), clsExpr),
new Assign(
classInfo,
- inlineInvoke(classResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
+ inlineInvoke(typeResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
writeClassAction.add(
- fory(f -> f.getClassResolver().writeClassExpr(classResolverRef,
buffer, classInfo)));
+ typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer,
classInfo)));
writeClassAction.add(
new Return(invokeInline(classInfo, "getSerializer",
getSerializerType(typeRef))));
// Spit this into a separate method to avoid method too big to inline.
@@ -839,7 +859,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
new If(
inlineInvoke(serializer, "supportCodegenHook",
PRIMITIVE_BOOLEAN_TYPE),
writeCollectionData(buffer, collection, serializer, elementType),
- new Invoke(serializer, "write", buffer, collection));
+ new Invoke(serializer, writeMethodName, buffer, collection));
actions.add(write);
if (generateNewMethod) {
return invokeGenerated(
@@ -897,7 +917,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
Literal notDeclTypeFlag = ofInt(CollectionFlags.NOT_DECL_ELEMENT_TYPE);
Expression isDeclType = neq(new BitAnd(flags, notDeclTypeFlag),
notDeclTypeFlag);
Expression elemSerializer; // make it in scope of `if(sameElementClass)`
- boolean maybeDecl = fory(f ->
f.getClassResolver().isSerializable(elemClass));
+ boolean maybeDecl = typeResolver(r -> r.isSerializable(elemClass));
TypeRef<?> serializerType = getSerializerType(elementType);
if (maybeDecl) {
elemSerializer =
@@ -1128,10 +1148,10 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
neq(new Invoke(classInfo, "getCls", CLASS_TYPE), clsExpr),
new Assign(
classInfo,
- inlineInvoke(classResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
+ inlineInvoke(typeResolverRef, "getClassInfo",
classInfoTypeRef, clsExpr))));
// Note: writeClassExpr is thread safe.
writeClassAction.add(
- fory(f -> f.getClassResolver().writeClassExpr(classResolverRef,
buffer, classInfo)));
+ typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer,
classInfo)));
writeClassAction.add(
new Return(invokeInline(classInfo, "getSerializer",
MAP_SERIALIZER_TYPE)));
// Spit this into a separate method to avoid method too big to inline.
@@ -1146,7 +1166,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
new If(
inlineInvoke(serializer, "supportCodegenHook",
PRIMITIVE_BOOLEAN_TYPE),
jitWriteMap(buffer, map, serializer, typeRef),
- new Invoke(serializer, "write", buffer, map));
+ new Invoke(serializer, writeMethodName, buffer, map));
if (generateNewMethod) {
return invokeGenerated(ctx, ofHashSet(buffer, map, serializer), write,
"writeMap", false);
}
@@ -1180,8 +1200,8 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
boolean inline = keyMonomorphic && valueMonomorphic;
Class<?> keyTypeRawType = keyType.getRawType();
Class<?> valueTypeRawType = valueType.getRawType();
- boolean trackingKeyRef = fory(fory ->
fory.getClassResolver().needToWriteRef(keyType));
- boolean trackingValueRef = fory(fory ->
fory.getClassResolver().needToWriteRef(valueType));
+ boolean trackingKeyRef = typeResolver(resolver ->
resolver.needToWriteRef(keyType));
+ boolean trackingValueRef = typeResolver(resolver ->
resolver.needToWriteRef(valueType));
Tuple2<Expression, Expression> mapKVSerializer =
getMapKVSerializer(keyTypeRawType, valueTypeRawType);
Expression keySerializer = mapKVSerializer.f0;
@@ -1193,10 +1213,8 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
boolean hasGenerics =
keyTypeRawType != Object.class || valueTypeRawType !=
Object.class;
String method = hasGenerics ? "writeJavaNullChunkGeneric" :
"writeJavaNullChunk";
- GenericType keyGenericType =
- fory(f -> f.getClassResolver().buildGenericType(keyType));
- GenericType valueGenericType =
- fory(f -> f.getClassResolver().buildGenericType(valueType));
+ GenericType keyGenericType = typeResolver(r ->
r.buildGenericType(keyType));
+ GenericType valueGenericType = typeResolver(r ->
r.buildGenericType(valueType));
if (keyGenericType.hasGenericParameters()
|| valueGenericType.hasGenericParameters()) {
method = "writeJavaNullChunkGeneric";
@@ -1298,8 +1316,8 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
Expression chunkHeader;
Expression keySerializer, valueSerializer;
- boolean trackingKeyRef = fory(fory ->
fory.getClassResolver().needToWriteRef(keyType));
- boolean trackingValueRef = fory(fory ->
fory.getClassResolver().needToWriteRef(valueType));
+ boolean trackingKeyRef = typeResolver(resolver ->
resolver.needToWriteRef(keyType));
+ boolean trackingValueRef = typeResolver(resolver ->
resolver.needToWriteRef(valueType));
Expression keyWriteRef = Literal.ofBoolean(trackingKeyRef);
Expression valueWriteRef = Literal.ofBoolean(trackingValueRef);
boolean inline = keyMonomorphic && valueMonomorphic;
@@ -1504,7 +1522,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
TypeRef<?> typeRef,
Function<Expression, Expression> callback,
InvokeHint invokeHint) {
- if (fory(f -> f.getClassResolver().needToWriteRef(typeRef))) {
+ if (typeResolver(r -> r.needToWriteRef(typeRef))) {
return readRef(buffer, callback, () -> deserializeForNotNull(buffer,
typeRef, invokeHint));
} else {
if (typeRef.isPrimitive()) {
@@ -1522,7 +1540,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
TypeRef<?> typeRef,
Function<Expression, Expression> callback,
boolean nullable) {
- if (fory(f -> f.getClassResolver().needToWriteRef(typeRef))) {
+ if (typeResolver(r -> r.needToWriteRef(typeRef))) {
return readRef(buffer, callback, () -> deserializeForNotNull(buffer,
typeRef, null));
} else {
if (typeRef.isPrimitive()) {
@@ -1641,7 +1659,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
if (isMonomorphic(cls)) {
serializer = getOrCreateSerializer(cls);
Class<?> returnType =
- ReflectionUtils.getReturnType(getRawType(serializer.type()),
"read");
+ ReflectionUtils.getReturnType(getRawType(serializer.type()),
readMethodName);
obj = read(serializer, buffer, TypeRef.of(returnType));
} else {
obj = readForNotNullNonFinal(buffer, typeRef, serializer);
@@ -1653,7 +1671,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
protected Expression read(Expression serializer, Expression buffer,
TypeRef<?> returnType) {
Class<?> type = returnType.getRawType();
- Expression read = new Invoke(serializer, "read", returnType, buffer);
+ Expression read = new Invoke(serializer, readMethodName, returnType,
buffer);
if (ReflectionUtils.isMonomorphic(type) &&
!TypeUtils.hasExpandableLeafs(type)) {
return read;
}
@@ -1726,7 +1744,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
Class<?> elemClass = TypeUtils.getRawType(elementType);
walkPath.add(elementType.toString());
boolean finalType = isMonomorphic(elemClass);
- boolean trackingRef = fory(fory ->
fory.getClassResolver().needToWriteRef(elementType));
+ boolean trackingRef = typeResolver(resolver ->
resolver.needToWriteRef(elementType));
if (finalType) {
if (trackingRef) {
builder.add(readContainerElements(elementType, true, null, null,
buffer, collection, size));
@@ -1748,7 +1766,7 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
inlineInvoke(readClassInfo(elemClass, buffer), "getSerializer",
SERIALIZER_TYPE);
TypeRef<?> serializerType = getSerializerType(elementType);
Expression elemSerializer; // make it in scope of `if(sameElementClass)`
- boolean maybeDecl = fory(f ->
f.getClassResolver().isSerializable(elemClass));
+ boolean maybeDecl = typeResolver(r -> r.isSerializable(elemClass));
if (maybeDecl) {
elemSerializer =
new If(
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
index ca155eb8c..17a25ab03 100644
--- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java
@@ -168,6 +168,9 @@ public abstract class CodecBuilder {
&& !expression.type().wrap().isSubtypeOf(targetType.wrap())) {
return new Cast(expression, targetType, valuePrefix);
}
+ if (rawType.isArray()) {
+ return new Cast(expression, OBJECT_ARRAY_TYPE, valuePrefix);
+ }
return expression;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/CompatibleCodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/CompatibleCodecBuilder.java
index 6a1342a52..d6b2a1254 100644
---
a/java/fory-core/src/main/java/org/apache/fory/builder/CompatibleCodecBuilder.java
+++
b/java/fory-core/src/main/java/org/apache/fory/builder/CompatibleCodecBuilder.java
@@ -81,6 +81,7 @@ import org.apache.fory.util.function.SerializableSupplier;
import org.apache.fory.util.record.RecordUtils;
/** A jit-version of {@link CompatibleSerializer}. */
+@Deprecated
public class CompatibleCodecBuilder extends BaseObjectCodecBuilder {
public static final String FIELD_RESOLVER_NAME = "fieldResolver";
private final FieldResolver fieldResolver;
@@ -104,7 +105,7 @@ public class CompatibleCodecBuilder extends
BaseObjectCodecBuilder {
fieldResolverRef = fieldRef(FIELD_RESOLVER_NAME, fieldResolverTypeRef);
Expression fieldResolverExpr =
inlineInvoke(
- classResolverRef,
+ typeResolverRef,
"getFieldResolver",
fieldResolverTypeRef,
getClassExpr(getRawType(beanType)));
@@ -878,12 +879,13 @@ public class CompatibleCodecBuilder extends
BaseObjectCodecBuilder {
protected Expression writeFinalClassInfo(Expression buffer, Class<?> cls) {
Preconditions.checkArgument(ReflectionUtils.isMonomorphic(cls));
+ Preconditions.checkArgument(!fory.isCrossLanguage());
ClassInfo classInfo = fory(f -> f.getClassResolver().getClassInfo(cls,
false));
if (classInfo != null && classInfo.getClassId() !=
ClassResolver.NO_CLASS_ID) {
return fory(f -> f.getClassResolver().writeClassExpr(buffer,
classInfo.getClassId()));
}
Expression classInfoExpr = getFinalClassInfo(cls);
- return new Invoke(classResolverRef, "writeClassInfo", buffer,
classInfoExpr);
+ return new Invoke(typeResolverRef, "writeClassInfo", buffer,
classInfoExpr);
}
protected Expression skipFinalClassInfo(Class<?> cls, Expression buffer) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java
index 1c25a77a1..4c075d282 100644
---
a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java
+++
b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java
@@ -88,7 +88,7 @@ public class MetaSharedCodecBuilder extends
ObjectCodecBuilder {
f.isCrossLanguage() ? f.getXtypeResolver() :
f.getClassResolver(),
beanClass,
classDef));
- DescriptorGrouper grouper =
fory.getClassResolver().createDescriptorGrouper(descriptors, false);
+ DescriptorGrouper grouper = typeResolver(r ->
r.createDescriptorGrouper(descriptors, false));
objectCodecOptimizer =
new ObjectCodecOptimizer(beanClass, grouper,
!fory.isBasicTypesRefIgnored(), ctx);
@@ -164,7 +164,7 @@ public class MetaSharedCodecBuilder extends
ObjectCodecBuilder {
Expression decodeExpr = buildDecodeExpression();
String decodeCode = decodeExpr.genCode(ctx).code();
decodeCode = ctx.optimizeMethodCode(decodeCode);
- ctx.overrideMethod("read", decodeCode, Object.class, MemoryBuffer.class,
BUFFER_NAME);
+ ctx.overrideMethod(readMethodName, decodeCode, Object.class,
MemoryBuffer.class, BUFFER_NAME);
registerJITNotifyCallback();
ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class,
POJO_CLASS_TYPE_NAME);
return ctx.genCode();
@@ -181,8 +181,7 @@ public class MetaSharedCodecBuilder extends
ObjectCodecBuilder {
public static Serializer setCodegenSerializer(
Fory fory, Class<?> cls, GeneratedMetaSharedSerializer s) {
if (GraalvmSupport.isGraalRuntime()) {
- return fory.getJITContext()
- .asyncVisitFory(f ->
f.getClassResolver().getSerializer(s.getType()));
+ return typeResolver(fory, r -> r.getSerializer(s.getType()));
}
// This method hold jit lock, so create jit serializer async to avoid
block serialization.
Class serializerClass =
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
index 9bb402ee8..d8fca68c0 100644
---
a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
+++
b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
@@ -58,6 +58,7 @@ import org.apache.fory.memory.Platform;
import org.apache.fory.reflect.TypeRef;
import org.apache.fory.serializer.ObjectSerializer;
import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer;
+import org.apache.fory.serializer.SerializationUtils;
import org.apache.fory.type.Descriptor;
import org.apache.fory.type.DescriptorGrouper;
import org.apache.fory.util.Preconditions;
@@ -87,20 +88,18 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
super(TypeRef.of(beanClass), fory,
Generated.GeneratedObjectSerializer.class);
Collection<Descriptor> descriptors;
boolean shareMeta = fory.getConfig().isMetaShareEnabled();
- boolean xlang = fory.isCrossLanguage();
if (shareMeta) {
descriptors =
fory(
f ->
f.getClassResolver()
- .getClassDef(beanClass, true)
- .getDescriptors(
- xlang ? f.getXtypeResolver() : f.getClassResolver(),
beanClass));
+ .getTypeDef(beanClass, true)
+
.getDescriptors(SerializationUtils.getTypeResolver(fory), beanClass));
} else {
- descriptors = fory.getClassResolver().getFieldDescriptors(beanClass,
true);
+ descriptors = typeResolver(r -> r.getFieldDescriptors(beanClass, true));
}
Collection<Descriptor> p = descriptors;
- DescriptorGrouper grouper = fory(f ->
f.getClassResolver().createDescriptorGrouper(p, false));
+ DescriptorGrouper grouper = typeResolver(r -> r.createDescriptorGrouper(p,
false));
descriptors = grouper.getSortedDescriptors();
classVersionHash =
fory.checkClassVersion()
@@ -141,7 +140,7 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
/** Mark non-inner registered final types as non-final to write class def
for those types. */
@Override
protected boolean isMonomorphic(Class<?> clz) {
- return fory(f -> f.getClassResolver().isMonomorphic(clz));
+ return typeResolver(r -> r.isMonomorphic(clz));
}
/**
diff --git
a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
index 69ee680b1..0feff07c9 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
@@ -97,9 +97,6 @@ public final class ForyBuilder {
*/
public ForyBuilder withLanguage(Language language) {
this.language = language;
- if (language != Language.JAVA) {
- codeGenEnabled = false;
- }
return this;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index 818b56c22..ff41a8b99 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -161,9 +161,7 @@ import org.apache.fory.serializer.shim.ShimDispatcher;
import org.apache.fory.type.Descriptor;
import org.apache.fory.type.DescriptorGrouper;
import org.apache.fory.type.GenericType;
-import org.apache.fory.type.ScalaTypes;
import org.apache.fory.type.TypeUtils;
-import org.apache.fory.type.Types;
import org.apache.fory.util.GraalvmSupport;
import org.apache.fory.util.GraalvmSupport.GraalvmSerializerHolder;
import org.apache.fory.util.Preconditions;
@@ -175,7 +173,7 @@ import org.apache.fory.util.function.Functions;
* up relations between serializer and types.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
-public class ClassResolver implements TypeResolver {
+public class ClassResolver extends TypeResolver {
private static final Logger LOG =
LoggerFactory.getLogger(ClassResolver.class);
// bit 0 unset indicates class is written as an id.
@@ -281,6 +279,7 @@ public class ClassResolver implements TypeResolver {
}
public ClassResolver(Fory fory) {
+ super(fory);
this.fory = fory;
metaStringResolver = fory.getMetaStringResolver();
classInfoCache = NIL_CLASS_INFO;
@@ -638,24 +637,6 @@ public class ClassResolver implements TypeResolver {
*/
@Override
public boolean isMonomorphic(Class<?> clz) {
- if (fory.isCrossLanguage()) {
- if (TypeUtils.unwrap(clz).isPrimitive() || clz.isEnum() || clz ==
String.class) {
- return true;
- }
- if (clz.isArray() && TypeUtils.getArrayComponent(clz).isPrimitive()) {
- return true;
- }
- ClassInfo classInfo = xtypeResolver.getClassInfo(clz, false);
- if (classInfo != null) {
- if (classInfo.serializer instanceof TimeSerializers.TimeSerializer) {
- return true;
- }
- if (classInfo.serializer instanceof
TimeSerializers.ImmutableTimeSerializer) {
- return true;
- }
- }
- return false;
- }
if (fory.getConfig().isMetaShareEnabled()) {
// can't create final map/collection type using
TypeUtils.mapOf(TypeToken<K>,
// TypeToken<V>)
@@ -663,7 +644,7 @@ public class ClassResolver implements TypeResolver {
return false;
}
if (Map.class.isAssignableFrom(clz) ||
Collection.class.isAssignableFrom(clz)) {
- return false;
+ return true;
}
if (clz.isArray()) {
Class<?> component = TypeUtils.getArrayComponent(clz);
@@ -783,6 +764,7 @@ public class ClassResolver implements TypeResolver {
* callback to update serializer won't take effect in some cases since it
can't change that
* classinfo.
*/
+ @Override
public <T> void setSerializer(Class<T> cls, Serializer<T> serializer) {
addSerializer(cls, serializer);
}
@@ -842,6 +824,7 @@ public class ClassResolver implements TypeResolver {
* serializer. This method is used to avoid overwriting existing serializer
for class when
* creating a data serializer for serialization of parts fields of a class.
*/
+ @Override
public <T> void setSerializerIfAbsent(Class<T> cls, Serializer<T>
serializer) {
Serializer<T> s = getSerializer(cls, false);
if (s == null) {
@@ -913,23 +896,13 @@ public class ClassResolver implements TypeResolver {
*/
@Internal
@CodegenInvoke
+ @Override
public Serializer<?> getRawSerializer(Class<?> cls) {
Preconditions.checkNotNull(cls);
return getOrUpdateClassInfo(cls).serializer;
}
- public boolean isSerializable(Class<?> cls) {
- if (ReflectionUtils.isAbstract(cls) || cls.isInterface()) {
- return false;
- }
- try {
- getSerializerClass(cls, false);
- return true;
- } catch (Throwable t) {
- return false;
- }
- }
-
+ @Override
public Class<? extends Serializer> getSerializerClass(Class<?> cls) {
boolean codegen =
supportCodegenForJavaSerialization(cls) &&
fory.getConfig().isCodeGenEnabled();
@@ -1077,45 +1050,6 @@ public class ClassResolver implements TypeResolver {
}
}
- public boolean isCollection(Class<?> cls) {
- if (Collection.class.isAssignableFrom(cls)) {
- return true;
- }
- if (fory.getConfig().isScalaOptimizationEnabled()) {
- // Scala map is scala iterable too.
- if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) {
- return false;
- }
- return ScalaTypes.getScalaIterableType().isAssignableFrom(cls);
- } else {
- return false;
- }
- }
-
- public boolean isSet(Class<?> cls) {
- if (Set.class.isAssignableFrom(cls)) {
- return true;
- }
- if (fory.getConfig().isScalaOptimizationEnabled()) {
- // Scala map is scala iterable too.
- if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) {
- return false;
- }
- return ScalaTypes.getScalaSetType().isAssignableFrom(cls);
- } else {
- return false;
- }
- }
-
- public boolean isMap(Class<?> cls) {
- if (cls == NonexistentMetaShared.class) {
- return false;
- }
- return Map.class.isAssignableFrom(cls)
- || (fory.getConfig().isScalaOptimizationEnabled()
- && ScalaTypes.getScalaMapType().isAssignableFrom(cls));
- }
-
public Class<? extends Serializer> getObjectSerializerClass(
Class<?> cls, JITContext.SerializerJITCallback<Class<? extends
Serializer>> callback) {
boolean codegen =
@@ -1123,7 +1057,7 @@ public class ClassResolver implements TypeResolver {
return getObjectSerializerClass(cls, false, codegen, callback);
}
- private Class<? extends Serializer> getObjectSerializerClass(
+ public Class<? extends Serializer> getObjectSerializerClass(
Class<?> cls,
boolean shareMeta,
boolean codegen,
@@ -1215,6 +1149,7 @@ public class ClassResolver implements TypeResolver {
return fieldResolver;
}
+ @Override
public List<Descriptor> getFieldDescriptors(Class<?> clz, boolean
searchParent) {
SortedMap<Member, Descriptor> allDescriptors = getAllDescriptorsMap(clz,
searchParent);
List<Descriptor> result = new ArrayList<>(allDescriptors.size());
@@ -1769,7 +1704,8 @@ public class ClassResolver implements TypeResolver {
return tuple2;
}
- public ClassDef getClassDef(Class<?> cls, boolean resolveParent) {
+ @Override
+ public ClassDef getTypeDef(Class<?> cls, boolean resolveParent) {
if (resolveParent) {
return classDefMap.computeIfAbsent(cls, k ->
ClassDef.buildClassDef(fory, cls));
}
@@ -1781,18 +1717,6 @@ public class ClassResolver implements TypeResolver {
return classDef;
}
- /**
- * Native code for ClassResolver.writeClassInfo is too big to inline, so
inline it manually.
- *
- * <p>See `already compiled into a big method` in <a
- *
href="https://wiki.openjdk.org/display/HotSpot/Server+Compiler+Inlining+Messages">Server+Compiler+Inlining+Messages</a>
- */
- // Note: Thread safe for jit thread to call.
- public Expression writeClassExpr(
- Expression classResolverRef, Expression buffer, Expression classInfo) {
- return new Invoke(classResolverRef, "writeClassInfo", buffer, classInfo);
- }
-
// Note: Thread safe for jit thread to call.
public Expression writeClassExpr(Expression buffer, short classId) {
Preconditions.checkArgument(classId != NO_CLASS_ID);
@@ -1800,7 +1724,7 @@ public class ClassResolver implements TypeResolver {
}
// Note: Thread safe for jit thread to call.
- public Expression writeClassExpr(Expression buffer, Expression classId) {
+ private Expression writeClassExpr(Expression buffer, Expression classId) {
return new Invoke(buffer, "writeVarUint32", new Expression.BitShift("<<",
classId, 1));
}
@@ -2153,33 +2077,11 @@ public class ClassResolver implements TypeResolver {
extRegistry.codeGeneratorMap.put(Arrays.asList(loaders), codeGenerator);
}
- public DescriptorGrouper createDescriptorGrouper(
- Collection<Descriptor> descriptors, boolean descriptorsGroupedOrdered) {
- return createDescriptorGrouper(descriptors, descriptorsGroupedOrdered,
null);
- }
-
+ @Override
public DescriptorGrouper createDescriptorGrouper(
Collection<Descriptor> descriptors,
boolean descriptorsGroupedOrdered,
Function<Descriptor, Descriptor> descriptorUpdator) {
- if (fory.isCrossLanguage()) {
- return DescriptorGrouper.createDescriptorGrouper(
- this::isMonomorphic,
- descriptors,
- descriptorsGroupedOrdered,
- descriptorUpdator,
- fory.compressInt(),
- fory.compressLong(),
- (o1, o2) -> {
- int xtypeId = getXtypeId(o1.getRawType());
- int xtypeId2 = getXtypeId(o2.getRawType());
- if (xtypeId == xtypeId2) {
- return o1.getSnakeCaseName().compareTo(o2.getSnakeCaseName());
- } else {
- return xtypeId - xtypeId2;
- }
- });
- }
return DescriptorGrouper.createDescriptorGrouper(
fory.getClassResolver()::isMonomorphic,
descriptors,
@@ -2190,33 +2092,6 @@ public class ClassResolver implements TypeResolver {
DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME);
}
- private static final int UNKNOWN_TYPE_ID = -1;
-
- private int getXtypeId(Class<?> cls) {
- if (isCollection(cls)) {
- return Types.LIST;
- }
- if (cls.isArray() && !cls.getComponentType().isPrimitive()) {
- return Types.LIST;
- }
- if (isMap(cls)) {
- return Types.MAP;
- }
- if (fory.getXtypeResolver().isRegistered(cls)) {
- return fory.getXtypeResolver().getClassInfo(cls).getXtypeId();
- } else {
- if (ReflectionUtils.isMonomorphic(cls)) {
- throw new UnsupportedOperationException(cls + " is not supported for
xlang serialization");
- }
- return UNKNOWN_TYPE_ID;
- }
- }
-
- @Override
- public Fory getFory() {
- return fory;
- }
-
/**
* Ensure all compilation for serializers and accessors even for lazy
initialized serializers.
* This method will block until all compilation is done.
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/FieldResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/FieldResolver.java
index b7d75dfce..2fa2c3507 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/FieldResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/FieldResolver.java
@@ -107,6 +107,7 @@ import org.apache.fory.util.Preconditions;
* @see org.apache.fory.serializer.CompatibleSerializerBase
*/
@SuppressWarnings({"rawtypes", "UnstableApiUsage"})
+@Deprecated
public class FieldResolver {
/** Max registered class id for embed in field info. */
public static final short MAX_EMBED_CLASS_ID = 127;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
index 92dbdb821..4367b3a0e 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
@@ -20,50 +20,157 @@
package org.apache.fory.resolver;
import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
import org.apache.fory.Fory;
import org.apache.fory.annotation.Internal;
+import org.apache.fory.codegen.Expression;
+import org.apache.fory.codegen.Expression.Invoke;
import org.apache.fory.memory.MemoryBuffer;
+import org.apache.fory.meta.ClassDef;
+import org.apache.fory.reflect.ReflectionUtils;
import org.apache.fory.reflect.TypeRef;
+import org.apache.fory.serializer.NonexistentClass.NonexistentMetaShared;
import org.apache.fory.serializer.Serializer;
+import org.apache.fory.type.Descriptor;
+import org.apache.fory.type.DescriptorGrouper;
import org.apache.fory.type.GenericType;
+import org.apache.fory.type.ScalaTypes;
// Internal type dispatcher.
// Do not use this interface outside of fory package
@Internal
-public interface TypeResolver {
- boolean needToWriteRef(TypeRef<?> typeRef);
+public abstract class TypeResolver {
+ private final Fory fory;
- boolean isRegistered(Class<?> cls);
+ protected TypeResolver(Fory fory) {
+ this.fory = fory;
+ }
- boolean isRegisteredById(Class<?> cls);
+ public abstract boolean needToWriteRef(TypeRef<?> typeRef);
- boolean isRegisteredByName(Class<?> cls);
+ public abstract boolean isRegistered(Class<?> cls);
- boolean isMonomorphic(Class<?> clz);
+ public abstract boolean isRegisteredById(Class<?> cls);
- ClassInfo getClassInfo(Class<?> cls);
+ public abstract boolean isRegisteredByName(Class<?> cls);
- ClassInfo getClassInfo(Class<?> cls, boolean createIfAbsent);
+ public abstract boolean isMonomorphic(Class<?> clz);
- ClassInfo getClassInfo(Class<?> cls, ClassInfoHolder classInfoHolder);
+ public abstract ClassInfo getClassInfo(Class<?> cls);
- void writeClassInfo(MemoryBuffer buffer, ClassInfo classInfo);
+ public abstract ClassInfo getClassInfo(Class<?> cls, boolean createIfAbsent);
- ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfoHolder
classInfoHolder);
+ public abstract ClassInfo getClassInfo(Class<?> cls, ClassInfoHolder
classInfoHolder);
- ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfo classInfoCache);
+ public abstract void writeClassInfo(MemoryBuffer buffer, ClassInfo
classInfo);
- <T> Serializer<T> getSerializer(Class<T> cls);
+ /**
+ * Native code for ClassResolver.writeClassInfo is too big to inline, so
inline it manually.
+ *
+ * <p>See `already compiled into a big method` in <a
+ *
href="https://wiki.openjdk.org/display/HotSpot/Server+Compiler+Inlining+Messages">Server+Compiler+Inlining+Messages</a>
+ */
+ // Note: Thread safe for jit thread to call.
+ public Expression writeClassExpr(
+ Expression classResolverRef, Expression buffer, Expression classInfo) {
+ return new Invoke(classResolverRef, "writeClassInfo", buffer, classInfo);
+ }
- ClassInfo nilClassInfo();
+ public abstract ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfoHolder
classInfoHolder);
- ClassInfoHolder nilClassInfoHolder();
+ public abstract ClassInfo readClassInfo(MemoryBuffer buffer, ClassInfo
classInfoCache);
- GenericType buildGenericType(TypeRef<?> typeRef);
+ public abstract <T> Serializer<T> getSerializer(Class<T> cls);
- GenericType buildGenericType(Type type);
+ public abstract Serializer<?> getRawSerializer(Class<?> cls);
- void initialize();
+ public abstract <T> void setSerializer(Class<T> cls, Serializer<T>
serializer);
- Fory getFory();
+ public abstract <T> void setSerializerIfAbsent(Class<T> cls, Serializer<T>
serializer);
+
+ public abstract ClassInfo nilClassInfo();
+
+ public abstract ClassInfoHolder nilClassInfoHolder();
+
+ public abstract GenericType buildGenericType(TypeRef<?> typeRef);
+
+ public abstract GenericType buildGenericType(Type type);
+
+ public abstract void initialize();
+
+ public abstract ClassDef getTypeDef(Class<?> cls, boolean resolveParent);
+
+ public final boolean isSerializable(Class<?> cls) {
+ if (ReflectionUtils.isAbstract(cls) || cls.isInterface()) {
+ return false;
+ }
+ try {
+ getSerializerClass(cls, false);
+ return true;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public abstract Class<? extends Serializer> getSerializerClass(Class<?> cls);
+
+ public abstract Class<? extends Serializer> getSerializerClass(Class<?> cls,
boolean codegen);
+
+ public boolean isCollection(Class<?> cls) {
+ if (Collection.class.isAssignableFrom(cls)) {
+ return true;
+ }
+ if (fory.getConfig().isScalaOptimizationEnabled()) {
+ // Scala map is scala iterable too.
+ if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) {
+ return false;
+ }
+ return ScalaTypes.getScalaIterableType().isAssignableFrom(cls);
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isSet(Class<?> cls) {
+ if (Set.class.isAssignableFrom(cls)) {
+ return true;
+ }
+ if (fory.getConfig().isScalaOptimizationEnabled()) {
+ // Scala map is scala iterable too.
+ if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) {
+ return false;
+ }
+ return ScalaTypes.getScalaSetType().isAssignableFrom(cls);
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isMap(Class<?> cls) {
+ if (cls == NonexistentMetaShared.class) {
+ return false;
+ }
+ return Map.class.isAssignableFrom(cls)
+ || (fory.getConfig().isScalaOptimizationEnabled()
+ && ScalaTypes.getScalaMapType().isAssignableFrom(cls));
+ }
+
+ public DescriptorGrouper createDescriptorGrouper(
+ Collection<Descriptor> descriptors, boolean descriptorsGroupedOrdered) {
+ return createDescriptorGrouper(descriptors, descriptorsGroupedOrdered,
null);
+ }
+
+ public abstract DescriptorGrouper createDescriptorGrouper(
+ Collection<Descriptor> descriptors,
+ boolean descriptorsGroupedOrdered,
+ Function<Descriptor, Descriptor> descriptorUpdator);
+
+ public abstract Collection<Descriptor> getFieldDescriptors(Class<?>
beanClass, boolean b);
+
+ public final Fory getFory() {
+ return fory;
+ }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index 3c32204c9..6c25e9060 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -50,12 +50,15 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
import org.apache.fory.Fory;
import org.apache.fory.annotation.Internal;
import org.apache.fory.collection.IdentityMap;
import org.apache.fory.collection.IdentityObjectIntMap;
import org.apache.fory.collection.LongMap;
import org.apache.fory.collection.ObjectMap;
+import org.apache.fory.collection.Tuple2;
import org.apache.fory.config.Config;
import org.apache.fory.exception.ClassUnregisteredException;
import org.apache.fory.exception.SerializerUnregisteredException;
@@ -69,6 +72,7 @@ import org.apache.fory.meta.MetaString;
import org.apache.fory.reflect.ReflectionUtils;
import org.apache.fory.reflect.TypeRef;
import org.apache.fory.serializer.ArraySerializers;
+import
org.apache.fory.serializer.DeferedLazySerializer.DeferedLazyObjectSerializer;
import org.apache.fory.serializer.EnumSerializer;
import org.apache.fory.serializer.LazySerializer;
import org.apache.fory.serializer.NonexistentClass;
@@ -77,12 +81,18 @@ import org.apache.fory.serializer.ObjectSerializer;
import org.apache.fory.serializer.SerializationUtils;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.Serializers;
+import org.apache.fory.serializer.TimeSerializers;
import org.apache.fory.serializer.collection.CollectionLikeSerializer;
import org.apache.fory.serializer.collection.CollectionSerializer;
import
org.apache.fory.serializer.collection.CollectionSerializers.ArrayListSerializer;
import
org.apache.fory.serializer.collection.CollectionSerializers.HashSetSerializer;
+import
org.apache.fory.serializer.collection.CollectionSerializers.XlangListDefaultSerializer;
+import
org.apache.fory.serializer.collection.CollectionSerializers.XlangSetDefaultSerializer;
import org.apache.fory.serializer.collection.MapLikeSerializer;
import org.apache.fory.serializer.collection.MapSerializer;
+import org.apache.fory.serializer.collection.MapSerializers.XlangMapSerializer;
+import org.apache.fory.type.Descriptor;
+import org.apache.fory.type.DescriptorGrouper;
import org.apache.fory.type.GenericType;
import org.apache.fory.type.Generics;
import org.apache.fory.type.TypeUtils;
@@ -91,7 +101,7 @@ import org.apache.fory.util.Preconditions;
@SuppressWarnings({"unchecked", "rawtypes"})
// TODO(chaokunyang) Abstract type resolver for java/xlang type resolution.
-public class XtypeResolver implements TypeResolver {
+public class XtypeResolver extends TypeResolver {
private static final Logger LOG =
LoggerFactory.getLogger(XtypeResolver.class);
private static final float loadFactor = 0.5f;
@@ -121,6 +131,7 @@ public class XtypeResolver implements TypeResolver {
private final Generics generics;
public XtypeResolver(Fory fory) {
+ super(fory);
this.config = fory.getConfig();
this.fory = fory;
this.classResolver = fory.getClassResolver();
@@ -238,9 +249,27 @@ public class XtypeResolver implements TypeResolver {
if (type.isEnum()) {
classInfo.serializer = new EnumSerializer(fory, (Class<Enum>) type);
} else {
+ AtomicBoolean updated = new AtomicBoolean(false);
+ AtomicReference<Serializer> ref = new AtomicReference(null);
classInfo.serializer =
- new LazySerializer.LazyObjectSerializer(
- fory, type, () -> new ObjectSerializer<>(fory, type));
+ new DeferedLazyObjectSerializer(
+ fory,
+ type,
+ () -> {
+ if (ref.get() == null) {
+ Class<? extends Serializer> c =
+ classResolver.getObjectSerializerClass(
+ type,
+ shareMeta,
+ fory.getConfig().isCodeGenEnabled(),
+ sc -> ref.set(Serializers.newSerializer(fory,
type, sc)));
+ ref.set(Serializers.newSerializer(fory, type, c));
+ if (!fory.getConfig().isAsyncCompilationEnabled()) {
+ updated.set(true);
+ }
+ }
+ return Tuple2.of(updated.get(), ref.get());
+ });
}
}
classInfoMap.put(type, classInfo);
@@ -374,7 +403,27 @@ public class XtypeResolver implements TypeResolver {
@Override
public boolean isMonomorphic(Class<?> clz) {
- return classResolver.isMonomorphic(clz);
+ if (TypeUtils.unwrap(clz).isPrimitive() || clz.isEnum() || clz ==
String.class) {
+ return true;
+ }
+ if (clz.isArray()) {
+ return true;
+ }
+ ClassInfo classInfo = getClassInfo(clz, false);
+ if (classInfo != null) {
+ Serializer<?> s = classInfo.serializer;
+ if (s instanceof TimeSerializers.TimeSerializer
+ || s instanceof MapLikeSerializer
+ || s instanceof CollectionLikeSerializer) {
+ return true;
+ }
+
+ return s instanceof TimeSerializers.ImmutableTimeSerializer;
+ }
+ if (isMap(clz) || isCollection(clz)) {
+ return true;
+ }
+ return false;
}
@Override
@@ -546,10 +595,20 @@ public class XtypeResolver implements TypeResolver {
classInfoMap.put(defaultType, classInfo);
xtypeIdToClassMap.put(xtypeId, classInfo);
for (Class<?> otherType : otherTypes) {
- Serializer<?> serializer =
- ReflectionUtils.isAbstract(otherType)
- ? classInfo.serializer
- : classResolver.getSerializer(otherType);
+ Serializer<?> serializer;
+ if (ReflectionUtils.isAbstract(otherType)) {
+ if (isMap(otherType)) {
+ serializer = new XlangMapSerializer(fory, otherType);
+ } else if (isSet(otherType)) {
+ serializer = new XlangSetDefaultSerializer(fory, otherType);
+ } else if (isCollection(otherType)) {
+ serializer = new XlangListDefaultSerializer(fory, otherType);
+ } else {
+ serializer = classInfo.serializer;
+ }
+ } else {
+ serializer = classResolver.getSerializer(otherType);
+ }
ClassInfo info = newClassInfo(otherType, serializer, (short) xtypeId);
classInfoMap.put(otherType, info);
}
@@ -619,6 +678,22 @@ public class XtypeResolver implements TypeResolver {
return (Serializer) getClassInfo(cls).serializer;
}
+ public Serializer<?> getRawSerializer(Class<?> cls) {
+ return getClassInfo(cls).serializer;
+ }
+
+ @Override
+ public <T> void setSerializer(Class<T> cls, Serializer<T> serializer) {
+ getClassInfo(cls).serializer = serializer;
+ }
+
+ @Override
+ public <T> void setSerializerIfAbsent(Class<T> cls, Serializer<T>
serializer) {
+ ClassInfo classInfo = classInfoMap.get(cls);
+ Preconditions.checkNotNull(classInfo);
+ Preconditions.checkNotNull(classInfo.serializer);
+ }
+
@Override
public ClassInfo nilClassInfo() {
return classResolver.nilClassInfo();
@@ -778,12 +853,72 @@ public class XtypeResolver implements TypeResolver {
return classInfo;
}
- private boolean isEnum(int internalTypeId) {
- return internalTypeId == Types.ENUM || internalTypeId == Types.NAMED_ENUM;
+ @Override
+ public DescriptorGrouper createDescriptorGrouper(
+ Collection<Descriptor> descriptors,
+ boolean descriptorsGroupedOrdered,
+ Function<Descriptor, Descriptor> descriptorUpdator) {
+ return DescriptorGrouper.createDescriptorGrouper(
+ this::isMonomorphic,
+ descriptors,
+ descriptorsGroupedOrdered,
+ descriptorUpdator,
+ fory.compressInt(),
+ fory.compressLong(),
+ (o1, o2) -> {
+ int xtypeId = getXtypeId(o1.getRawType());
+ int xtypeId2 = getXtypeId(o2.getRawType());
+ if (xtypeId == xtypeId2) {
+ return o1.getSnakeCaseName().compareTo(o2.getSnakeCaseName());
+ } else {
+ return xtypeId - xtypeId2;
+ }
+ });
+ }
+
+ private static final int UNKNOWN_TYPE_ID = -1;
+
+ private int getXtypeId(Class<?> cls) {
+ if (isCollection(cls)) {
+ return Types.LIST;
+ }
+ if (cls.isArray() && !cls.getComponentType().isPrimitive()) {
+ return Types.LIST;
+ }
+ if (isMap(cls)) {
+ return Types.MAP;
+ }
+ if (fory.getXtypeResolver().isRegistered(cls)) {
+ return fory.getXtypeResolver().getClassInfo(cls).getXtypeId();
+ } else {
+ if (ReflectionUtils.isMonomorphic(cls)) {
+ throw new UnsupportedOperationException(cls + " is not supported for
xlang serialization");
+ }
+ return UNKNOWN_TYPE_ID;
+ }
}
@Override
- public Fory getFory() {
- return fory;
+ public List<Descriptor> getFieldDescriptors(Class<?> clz, boolean
searchParent) {
+ return classResolver.getFieldDescriptors(clz, searchParent);
+ }
+
+ @Override
+ public ClassDef getTypeDef(Class<?> cls, boolean resolveParent) {
+ return classResolver.getTypeDef(cls, resolveParent);
+ }
+
+ @Override
+ public Class<? extends Serializer> getSerializerClass(Class<?> cls) {
+ return getSerializer(cls).getClass();
+ }
+
+ @Override
+ public Class<? extends Serializer> getSerializerClass(Class<?> cls, boolean
codegen) {
+ return getSerializer(cls).getClass();
+ }
+
+ private boolean isEnum(int internalTypeId) {
+ return internalTypeId == Types.ENUM || internalTypeId == Types.NAMED_ENUM;
}
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java
new file mode 100644
index 000000000..0e580b929
--- /dev/null
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java
@@ -0,0 +1,82 @@
+/*
+ * 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 java.util.function.Supplier;
+import org.apache.fory.Fory;
+import org.apache.fory.collection.Tuple2;
+import org.apache.fory.memory.MemoryBuffer;
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class DeferedLazySerializer extends Serializer {
+ private final Supplier<Tuple2<Boolean, Serializer>> serializerSupplier;
+ private Serializer serializer;
+
+ public DeferedLazySerializer(
+ Fory fory, Class type, Supplier<Tuple2<Boolean, Serializer>>
serializerSupplier) {
+ super(fory, type);
+ this.serializerSupplier = serializerSupplier;
+ }
+
+ @Override
+ public void write(MemoryBuffer buffer, Object value) {
+ getSerializer().write(buffer, value);
+ }
+
+ @Override
+ public Object read(MemoryBuffer buffer) {
+ return getSerializer().read(buffer);
+ }
+
+ @Override
+ public void xwrite(MemoryBuffer buffer, Object value) {
+ getSerializer().xwrite(buffer, value);
+ }
+
+ @Override
+ public Object xread(MemoryBuffer buffer) {
+ return getSerializer().xread(buffer);
+ }
+
+ private Serializer getSerializer() {
+ if (serializer == null) {
+ Tuple2<Boolean, Serializer> tuple2 = serializerSupplier.get();
+ if (tuple2.f0) {
+ serializer = tuple2.f1;
+ fory._getTypeResolver().setSerializer(type, serializer);
+ } else {
+ return tuple2.f1;
+ }
+ }
+ return serializer;
+ }
+
+ @Override
+ public Object copy(Object value) {
+ return getSerializer().copy(value);
+ }
+
+ public static class DeferedLazyObjectSerializer extends
DeferedLazySerializer {
+ public DeferedLazyObjectSerializer(
+ Fory fory, Class type, Supplier<Tuple2<Boolean, Serializer>>
serializerSupplier) {
+ super(fory, type, serializerSupplier);
+ }
+ }
+}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
index 0640edd8f..69edcb4fe 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
@@ -97,12 +97,12 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
Collection<Descriptor> descriptors;
boolean shareMeta = fory.getConfig().isMetaShareEnabled();
if (shareMeta) {
- ClassDef classDef = classResolver.getClassDef(cls, resolveParent);
+ ClassDef classDef = typeResolver.getTypeDef(cls, resolveParent);
descriptors = classDef.getDescriptors(typeResolver, cls);
} else {
- descriptors = fory.getClassResolver().getFieldDescriptors(cls,
resolveParent);
+ descriptors = typeResolver.getFieldDescriptors(cls, resolveParent);
}
- DescriptorGrouper descriptorGrouper =
classResolver.createDescriptorGrouper(descriptors, false);
+ DescriptorGrouper descriptorGrouper =
typeResolver.createDescriptorGrouper(descriptors, false);
descriptors = descriptorGrouper.getSortedDescriptors();
if (isRecord) {
List<String> fieldNames =
@@ -363,8 +363,7 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
id = Types.MAP;
} else {
try {
- TypeResolver resolver =
- fory.isCrossLanguage() ? fory.getXtypeResolver() :
fory.getClassResolver();
+ TypeResolver resolver = fory._getTypeResolver();
Class<?> cls = typeRef.getRawType();
if (!ReflectionUtils.isAbstract(cls) && !cls.isInterface()) {
ClassInfo classInfo = resolver.getClassInfo(cls);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java
index 43b38be79..4f5397155 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionLikeSerializer.java
@@ -462,7 +462,14 @@ public abstract class CollectionLikeSerializer<T> extends
Serializer<T> {
}
@Override
- public abstract T read(MemoryBuffer buffer);
+ public T read(MemoryBuffer buffer) {
+ Collection collection = newCollection(buffer);
+ int numElements = getAndClearNumElements();
+ if (numElements != 0) {
+ readElements(fory, buffer, collection, numElements);
+ }
+ return onCollectionRead(collection);
+ }
/**
* Read data except size and elements, return empty collection to be filled.
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializer.java
index 517577506..b2094f317 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializer.java
@@ -64,14 +64,4 @@ public class CollectionSerializer<T extends Collection>
extends CollectionLikeSe
copyElements(originCollection, newCollection);
return (T) newCollection;
}
-
- @Override
- public T read(MemoryBuffer buffer) {
- Collection collection = newCollection(buffer);
- int numElements = getAndClearNumElements();
- if (numElements != 0) {
- readElements(fory, buffer, collection, numElements);
- }
- return onCollectionRead(collection);
- }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
index fa0b2ce7c..046109055 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
@@ -773,6 +773,55 @@ public class CollectionSerializers {
}
}
+ public abstract static class XlangCollectionDefaultSerializer extends
CollectionLikeSerializer {
+
+ public XlangCollectionDefaultSerializer(Fory fory, Class cls) {
+ super(fory, cls);
+ }
+
+ @Override
+ public Collection onCollectionWrite(MemoryBuffer buffer, Object value) {
+ Collection v = (Collection) value;
+ buffer.writeVarUint32Small7(v.size());
+ return v;
+ }
+
+ @Override
+ public Object onCollectionRead(Collection collection) {
+ return collection;
+ }
+ }
+
+ public static class XlangListDefaultSerializer extends
XlangCollectionDefaultSerializer {
+ public XlangListDefaultSerializer(Fory fory, Class cls) {
+ super(fory, cls);
+ }
+
+ @Override
+ public List newCollection(MemoryBuffer buffer) {
+ int numElements = buffer.readVarUint32Small7();
+ setNumElements(numElements);
+ ArrayList list = new ArrayList(numElements);
+ fory.getRefResolver().reference(list);
+ return list;
+ }
+ }
+
+ public static class XlangSetDefaultSerializer extends
XlangCollectionDefaultSerializer {
+ public XlangSetDefaultSerializer(Fory fory, Class cls) {
+ super(fory, cls);
+ }
+
+ @Override
+ public Set newCollection(MemoryBuffer buffer) {
+ int numElements = buffer.readVarUint32Small7();
+ setNumElements(numElements);
+ HashSet set = new HashSet(numElements);
+ fory.getRefResolver().reference(set);
+ return set;
+ }
+ }
+
// TODO add
JDK11:JdkImmutableListSerializer,JdkImmutableMapSerializer,JdkImmutableSetSerializer
// by jit codegen those constructor for compiling in jdk8.
// TODO Support ArraySubListSerializer, SubListSerializer
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java
index 1b7112813..33c103e71 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java
@@ -473,6 +473,38 @@ public class MapSerializers {
}
}
+ public static class XlangMapSerializer extends MapLikeSerializer {
+
+ public XlangMapSerializer(Fory fory, Class cls) {
+ super(fory, cls, true);
+ }
+
+ @Override
+ public Map onMapWrite(MemoryBuffer buffer, Object value) {
+ Map v = (Map) value;
+ buffer.writeVarUint32Small7(v.size());
+ return v;
+ }
+
+ @Override
+ public Object onMapCopy(Map map) {
+ throw new IllegalStateException("should not be called");
+ }
+
+ public Map newMap(MemoryBuffer buffer) {
+ int numElements = buffer.readVarUint32Small7();
+ setNumElements(numElements);
+ HashMap<Object, Object> map = new HashMap<>(numElements);
+ fory.getRefResolver().reference(map);
+ return map;
+ }
+
+ @Override
+ public Object onMapRead(Map map) {
+ return map;
+ }
+ }
+
// TODO(chaokunyang) support ConcurrentSkipListMap.SubMap mo efficiently.
public static void registerDefaultSerializers(Fory fory) {
ClassResolver resolver = fory.getClassResolver();
diff --git
a/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
b/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
index 22ac18d67..17b0cba60 100644
--- a/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/CrossLanguageTest.java
@@ -62,6 +62,7 @@ import org.apache.fory.logging.Logger;
import org.apache.fory.logging.LoggerFactory;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.MemoryUtils;
+import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.serializer.ArraySerializersTest;
import org.apache.fory.serializer.BufferObject;
import org.apache.fory.serializer.EnumSerializerTest;
@@ -465,6 +466,7 @@ public class CrossLanguageTest extends ForyTestBase {
Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
+ .withCodegen(false)
.requireClassRegistration(false)
.build();
fory.register(ComplexObject1.class, "test.ComplexObject1");
@@ -473,10 +475,9 @@ public class CrossLanguageTest extends ForyTestBase {
Method method =
ObjectSerializer.class.getDeclaredMethod("computeStructHash",
Fory.class, Collection.class);
method.setAccessible(true);
- Collection<Descriptor> descriptors =
- fory.getClassResolver().getFieldDescriptors(ComplexObject1.class,
true);
- descriptors =
- fory.getClassResolver().createDescriptorGrouper(descriptors,
false).getSortedDescriptors();
+ TypeResolver resolver = fory._getTypeResolver();
+ Collection<Descriptor> descriptors =
resolver.getFieldDescriptors(ComplexObject1.class, false);
+ descriptors = resolver.createDescriptorGrouper(descriptors,
false).getSortedDescriptors();
Integer hash = (Integer) method.invoke(serializer, fory, descriptors);
MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(4);
buffer.writeInt32(hash);
@@ -515,11 +516,12 @@ public class CrossLanguageTest extends ForyTestBase {
structRoundBack(fory, obj2, "test_register_by_id");
}
- @Test
- public void testRegisterByIdMetaShare() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testRegisterByIdMetaShare(boolean enableCodegen) throws
Exception {
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
+ .withCodegen(enableCodegen)
.withRefTracking(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
@@ -881,11 +883,12 @@ public class CrossLanguageTest extends ForyTestBase {
structRoundBack(fory, obj, "test_cross_language_meta_share");
}
- @Test
- public void testCrossLanguageMetaShareComplex() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testCrossLanguageMetaShareComplex(boolean enableCodegen) throws
Exception {
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
+ .withCodegen(enableCodegen)
.withRefTracking(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
@@ -942,12 +945,13 @@ public class CrossLanguageTest extends ForyTestBase {
Boolean active; // Another new field
}
- @Test
- public void testSchemaEvolution() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testSchemaEvolution(boolean enableCodegen) throws Exception {
// Test simple schema evolution compatibility
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
+ .withCodegen(enableCodegen)
.withRefTracking(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
@@ -965,12 +969,13 @@ public class CrossLanguageTest extends ForyTestBase {
structRoundBack(fory, objV1, "test_schema_evolution");
}
- @Test
- public void testBackwardCompatibility() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testBackwardCompatibility(boolean enableCodegen) throws
Exception {
// Test that old version can read new data (ignoring unknown fields)
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
+ .withCodegen(enableCodegen)
.withRefTracking(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
@@ -991,12 +996,13 @@ public class CrossLanguageTest extends ForyTestBase {
structBackwardCompatibility(fory, objV2, "test_backward_compatibility");
}
- @Test
- public void testFieldReorderingCompatibility() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testFieldReorderingCompatibility(boolean enableCodegen) throws
Exception {
// Test that field reordering doesn't break compatibility
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
+ .withCodegen(enableCodegen)
.withRefTracking(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
@@ -1022,13 +1028,14 @@ public class CrossLanguageTest extends ForyTestBase {
CompatTestV2 newObject;
}
- @Test
- public void testCrossVersionCompatibility() throws Exception {
+ @Test(dataProvider = "enableCodegen")
+ public void testCrossVersionCompatibility(boolean enableCodegen) throws
Exception {
// Test mixed version compatibility in one test
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
+ .withCodegen(enableCodegen)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.requireClassRegistration(false)
.build();
diff --git
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/XlangCollectionSerializerTest.java
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/XlangCollectionSerializerTest.java
index 7c599a04a..e8f72fd25 100644
---
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/XlangCollectionSerializerTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/XlangCollectionSerializerTest.java
@@ -45,9 +45,9 @@ public class XlangCollectionSerializerTest extends
ForyTestBase {
LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
}
- @Test
- public void testContainerType() {
- Fory fory = Fory.builder().withLanguage(Language.XLANG).build();
+ @Test(dataProvider = "enableCodegen")
+ public void testContainerType(boolean enableCodegen) {
+ Fory fory =
Fory.builder().withLanguage(Language.XLANG).withCodegen(enableCodegen).build();
fory.register(SomeClass.class, "SomeClass");
SomeClass someClass = new SomeClass();
diff --git a/python/pyfory/serializer.py b/python/pyfory/serializer.py
index 27045840e..2c20cc1ad 100644
--- a/python/pyfory/serializer.py
+++ b/python/pyfory/serializer.py
@@ -314,7 +314,7 @@ class DataClassSerializer(Serializer):
for index, key in enumerate(self._field_names):
serializer = infer_field(key, self._type_hints[key],
visitor, types_path=[])
self._serializers[index] = serializer
- self._field_names, self._serializers =
_sort_fields(fory.type_resolver, self._field_names, self._serializers)
+ self._field_names, self._serializers =
_sort_fields(fory.type_resolver, self._field_names, self._serializers)
self._hash = 0 # Will be computed on first xwrite/xread
self._generated_xwrite_method = self._gen_xwrite_method()
self._generated_xread_method = self._gen_xread_method()
diff --git a/python/pyfory/tests/test_cross_language.py
b/python/pyfory/tests/test_cross_language.py
index 3c757a576..9dd8dd61a 100644
--- a/python/pyfory/tests/test_cross_language.py
+++ b/python/pyfory/tests/test_cross_language.py
@@ -922,11 +922,11 @@ def test_cross_version_compatibility(data_file_path):
debug_print(f"Deserialized mixed version container: {obj}")
# Verify the nested objects
- assert obj.oldObject.name == "Old Format"
- assert obj.oldObject.age == 20
- assert obj.newObject.name == "New Format"
- assert obj.newObject.age == 25
- assert obj.newObject.email == "[email protected]"
+ assert obj.oldObject.name == "Old Format", obj.oldObject.name
+ assert obj.oldObject.age == 20, obj.oldObject.age
+ assert obj.newObject.name == "New Format", obj.newObject.name
+ assert obj.newObject.age == 25, obj.newObject.age
+ assert obj.newObject.email == "[email protected]", obj.newObject.email
# Serialize back
new_serialized = fory.serialize(obj)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]