Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 2 additions & 2 deletions java/fory-core/src/main/java/org/apache/fory/Fory.java
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback ca
if (writeContext.getDepth() > 0) {
throwDepthSerializationException();
}
writeContext.writeRef(obj);
writeContext.writeRootRef(obj);
return buffer;
} catch (Throwable t) {
throw processSerializationError(t);
Expand Down Expand Up @@ -512,7 +512,7 @@ public Object deserialize(MemoryBuffer buffer, Iterable<MemoryBuffer> outOfBandB
if (readContext.getDepth() > 0) {
throwDepthDeserializationException();
}
return readContext.readRef();
return readContext.readRootRef();
} finally {
jitContext.unlock();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
import org.apache.fory.serializer.ReplaceResolveSerializer;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.StringSerializer;
import org.apache.fory.serializer.UnknownClassSerializers;
import org.apache.fory.serializer.collection.CollectionFlags;
import org.apache.fory.serializer.collection.CollectionLikeSerializer;
import org.apache.fory.serializer.collection.CollectionSerializer;
Expand Down Expand Up @@ -701,6 +702,18 @@ private Expression serializeForNotNullObjectForField(
Expression inputObject, Expression buffer, Descriptor descriptor, Expression serializer) {
TypeRef<?> typeRef = descriptor.getTypeRef();
Class<?> clz = getRawType(typeRef);
if (isEnumType(clz)) {
Expression enumSerializer = serializer == null ? getSerializerForField(clz) : serializer;
if (hasEnumValueMethods(enumSerializer)) {
Class<?> serializerClass = getRawType(enumSerializer.type());
Expression value =
EnumSerializer.class.isAssignableFrom(serializerClass)
? cast(inputObject, TypeRef.of(Enum.class))
: inputObject;
return new Invoke(enumSerializer, "writeValue", writeContextRef(), buffer, value);
}
return new Invoke(enumSerializer, writeMethodName, writeContextRef(), inputObject);
}
if (serializer != null) {
return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject);
}
Expand All @@ -717,6 +730,17 @@ private Expression getSerializerForField(Class<?> cls) {
return getOrCreateSerializer(cls, true);
}

private static boolean isEnumType(Class<?> cls) {
return cls != Enum.class && Enum.class.isAssignableFrom(cls);
}

private static boolean hasEnumValueMethods(Expression serializer) {
Class<?> serializerClass = getRawType(serializer.type());
// UnknownEnumSerializer is enum-shaped but does not extend EnumSerializer.
return EnumSerializer.class.isAssignableFrom(serializerClass)
|| UnknownClassSerializers.UnknownEnumSerializer.class.isAssignableFrom(serializerClass);
}

protected Expression serializeForNullable(
Expression inputObject, Expression buffer, TypeRef<?> typeRef, boolean nullable) {
return serializeForNullable(inputObject, buffer, typeRef, null, false, nullable);
Expand Down Expand Up @@ -885,6 +909,7 @@ protected Expression writeForNotNullNonFinalObject(
Class<?> clz = getRawType(typeRef);
Expression clsExpr = new Invoke(inputObject, "getClass", "cls", CLASS_TYPE);
ListExpression writeClassAndObject = new ListExpression();
Expression exactClassWrite = exactClassWrite(inputObject, buffer, clz);
Tuple2<Reference, Boolean> classInfoRef = addTypeInfoField(clz);
Expression classInfo = classInfoRef.f0;
if (classInfoRef.f1) {
Expand All @@ -904,12 +929,25 @@ protected Expression writeForNotNullNonFinalObject(
PRIMITIVE_VOID_TYPE,
writeContextRef(),
inputObject));
Expression write =
exactClassWrite == null
? writeClassAndObject
: new If(eq(clsExpr, getClassExpr(clz)), exactClassWrite, writeClassAndObject, false);
return invokeGenerated(
ctx,
writeCutPoints(buffer, inputObject),
writeClassAndObject,
"writeClassAndObject",
false);
ctx, writeCutPoints(buffer, inputObject), write, "writeClassAndObject", false);
}

private Expression exactClassWrite(Expression inputObject, Expression buffer, Class<?> clz) {
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) {
return null;
}
Reference typeInfo = addExactTypeInfoField(clz);
Expression serializer = getOrCreateSerializer(clz);
return new ListExpression(
typeResolver(
r -> r.writeExactClassExpr(typeResolverRef, writeContextRef(), buffer, typeInfo, clz)),
new Invoke(
serializer, writeMethodName, PRIMITIVE_VOID_TYPE, writeContextRef(), inputObject));
}

protected Expression writeTypeInfo(
Expand Down Expand Up @@ -1128,6 +1166,21 @@ protected Tuple2<Reference, Boolean> addTypeInfoField(Class<?> cls) {
return classInfoRef;
}

protected Reference addExactTypeInfoField(Class<?> cls) {
String key = "exactClassInfo:" + cls;
Reference reference = (Reference) sharedFieldMap.get(key);
if (reference != null) {
return reference;
}
Expression classInfoExpr =
inlineInvoke(typeResolverRef, "getTypeInfo", classInfoTypeRef, getClassExpr(cls));
String name = ctx.newName(ctx.newName(cls) + "ExactTypeInfo");
ctx.addField(true, ctx.type(TypeInfo.class), name, classInfoExpr);
reference = fieldRef(name, classInfoTypeRef);
sharedFieldMap.put(key, reference);
return reference;
}

protected Reference addTypeInfoHolderField(Class<?> cls) {
// Final type need to write classinfo when meta share enabled.
String key;
Expand Down Expand Up @@ -1334,11 +1387,18 @@ protected Expression writeCollectionData(
builder.add(
writeContainerElements(elementType, true, null, null, buffer, collection, size));
} else {
Expression declSameNoNull =
eq(flags, ofInt(CollectionFlags.DECL_SAME_TYPE_NOT_HAS_NULL), "declSameNoNull");
Literal hasNullFlag = ofInt(CollectionFlags.HAS_NULL);
Expression hasNull = eq(new BitAnd(flags, hasNullFlag), hasNullFlag, "hasNull");
builder.add(
declSameNoNull,
hasNull,
writeContainerElements(elementType, false, null, hasNull, buffer, collection, size));
new If(
declSameNoNull,
writeContainerElements(elementType, false, null, null, buffer, collection, size),
writeContainerElements(elementType, false, null, hasNull, buffer, collection, size),
false));
}
} else {
Literal flag = ofInt(CollectionFlags.IS_SAME_TYPE);
Expand Down Expand Up @@ -1398,9 +1458,28 @@ protected Expression writeCollectionData(
differentTypeWrite);
} else {
// if declared elem type don't track ref, all elements must not write ref.
Expression declSameNoNull =
eq(flags, ofInt(CollectionFlags.DECL_SAME_TYPE_NOT_HAS_NULL), "declSameNoNull");
Literal hasNullFlag = ofInt(CollectionFlags.HAS_NULL);
Expression hasNull = eq(new BitAnd(flags, hasNullFlag), hasNullFlag, "hasNull");
builder.add(hasNull);
builder.add(declSameNoNull, hasNull);
Expression declaredNoNullWrite = null;
if (maybeDecl) {
declaredNoNullWrite =
invokeGenerated(
ctx,
writeCutPoints(buffer, collection, size),
writeContainerElements(
elementType,
false,
cast(getOrCreateSerializer(elemClass), serializerType),
null,
buffer,
collection,
size),
"declSameNoNullWrite",
false);
}
ListExpression writeBuilder = new ListExpression(elemSerializer);
writeBuilder.add(
writeContainerElements(
Expand All @@ -1415,6 +1494,9 @@ protected Expression writeCollectionData(
invokeGenerated(ctx, cutPoint, writeBuilder, "sameElementClassWrite", false),
writeContainerElements(
elementType, false, null, hasNull, buffer, collection, size));
if (declaredNoNullWrite != null) {
action = new If(declSameNoNull, declaredNoNullWrite, action, false);
}
}
builder.add(action);
}
Expand Down Expand Up @@ -2344,6 +2426,16 @@ private Expression deserializeForNotNullForField(
return StringSerializer.readStringExpr(
getOrCreateStringSerializer(), buffer, config.compressString());
}
if (isEnumType(cls)) {
Expression enumSerializer = serializer == null ? getSerializerForField(cls) : serializer;
if (!hasEnumValueMethods(enumSerializer)) {
return cast(
new Invoke(enumSerializer, readMethodName, OBJECT_TYPE, readContextRef()),
TypeRef.of(Enum.class));
}
return new Invoke(
enumSerializer, "readValue", TypeRef.of(Enum.class), readContextRef(), buffer);
}
Expression obj;
if (usesPrimitiveListArrayProtocol(descriptor)) {
serializer = getPrimitiveListArraySerializer(cls);
Expand Down Expand Up @@ -2595,7 +2687,7 @@ protected Expression readForNotNullNonFinal(
classInfo = readTypeInfo(getRawType(typeRef), buffer);
}
} else {
classInfo = readTypeInfo(getRawType(typeRef), buffer);
classInfo = readTypeInfo(rawType, buffer);
}
serializer = inlineInvoke(classInfo, "getSerializer", SERIALIZER_TYPE);
}
Expand Down Expand Up @@ -2638,16 +2730,18 @@ protected Expression deserializeForCollection(
Expression collection =
new Invoke(serializer, "newCollection", COLLECTION_TYPE, readContextRef);
Expression size = new Invoke(serializer, "getAndClearNumElements", "size", PRIMITIVE_INT_TYPE);
// if add branch by `ArrayList`, generated code will be > 325 bytes.
// and List#add is more likely be inlined if there is only one subclass.
Expression hookRead = readCollectionCodegen(buffer, collection, size, elementType);
hookRead = new Invoke(serializer, "onCollectionRead", OBJECT_TYPE, hookRead);
Expression action =
new If(
supportHook,
new ListExpression(collection, hookRead),
read(serializer, buffer, OBJECT_TYPE),
Expression fallbackAction = read(serializer, buffer, OBJECT_TYPE);
Expression fallbackRead =
invokeGenerated(
ctx,
readCutPoints(buffer, serializer),
new ListExpression(fallbackAction, new Return(fallbackAction)),
"readCollectionFallback",
false);
Expression action =
new If(supportHook, new ListExpression(collection, hookRead), fallbackRead, false);
if (invokeHint != null && invokeHint.genNewMethod) {
invokeHint.add(buffer);
invokeHint.add(readContextRef());
Expand All @@ -2664,8 +2758,12 @@ protected Expression deserializeForCollection(
protected Expression readCollectionCodegen(
Expression buffer, Expression collection, Expression size, TypeRef<?> elementType) {
ListExpression builder = new ListExpression();
Invoke flags = new Invoke(buffer, "readByte", "flags", PRIMITIVE_INT_TYPE, false);
builder.add(flags);
Expression readerIndex = new Invoke(buffer, "readerIndex", "readerIndex", PRIMITIVE_INT_TYPE);
Expression flags =
cast(
new Invoke(buffer, "_unsafeGetByte", PRIMITIVE_BYTE_TYPE, readerIndex),
PRIMITIVE_INT_TYPE);
builder.add(readerIndex, flags, new Invoke(buffer, "_increaseReaderIndexUnsafe", ofInt(1)));
Class<?> elemClass = TypeUtils.getRawType(elementType);
walkPath.add(elementType.toString());
boolean finalType = isMonomorphic(elemClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@ private Expression varHandleSetField(Expression bean, Descriptor descriptor, Exp
if (descriptor.getTypeRef().isPrimitive()) {
Preconditions.checkArgument(getRawType(value.type()) == getRawType(fieldType));
}
// Janino cannot compile VarHandle's signature-polymorphic access modes directly. Keep generated
// serializers on typed helper methods even though the VarHandle itself is a static final field.
return new StaticInvoke(
varHandleSupportClass(),
varHandleSetMethod(fieldType),
Expand Down Expand Up @@ -609,6 +611,7 @@ protected Reference getOrCreateField(
private Expression varHandleGetField(Expression inputObject, Descriptor descriptor) {
TypeRef<?> returnType =
descriptor.getTypeRef().isPrimitive() ? descriptor.getTypeRef() : OBJECT_TYPE;
// See varHandleSetField: direct VarHandle access-mode calls are not valid Janino output.
Expression getValue =
new StaticInvoke(
varHandleSupportClass(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -799,9 +799,10 @@ public Expression buildDecodeExpression() {
Expression bean;
if (!isRecord) {
bean = newBean();
Expression referenceObject = invokeReadContext("reference", bean);
expressions.add(bean);
expressions.add(referenceObject);
if (typeResolver.trackingRef()) {
expressions.add(invokeReadContext("reference", bean));
}
} else {
if (recordCtrAccessible) {
bean = new FieldsCollector();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ public final class ReadContext {
private final Generics generics;
private final TypeResolver typeResolver;
private final RefReader refReader;
private final TypeInfoHolder rootTypeInfoHolder;
private final MetaStringReader metaStringReader;
private final StringSerializer stringSerializer;
private final boolean crossLanguage;
private final boolean trackingRef;
private final boolean compressInt;
private final Int64Encoding longEncoding;
private final int maxDepth;
Expand Down Expand Up @@ -86,9 +88,11 @@ public ReadContext(
this.generics = generics;
this.typeResolver = typeResolver;
this.refReader = refReader;
rootTypeInfoHolder = typeResolver.nilTypeInfoHolder();
this.metaStringReader = metaStringReader;
stringSerializer = (StringSerializer) typeResolver.getSerializer(String.class);
crossLanguage = config.isXlang();
trackingRef = config.trackingRef();
compressInt = config.compressInt();
longEncoding = config.longEncoding();
maxDepth = config.maxDepth();
Expand Down Expand Up @@ -578,6 +582,20 @@ public <T> T readRef(Serializer<T> serializer) {
return (T) readNonRef(serializer);
}

/** Reads the root object for one deserialization operation. */
public Object readRootRef() {
if (trackingRef) {
return readRef(rootTypeInfoHolder);
}
MemoryBuffer buffer = this.buffer;
int headFlag = buffer.readByte();
if (headFlag >= Fory.NOT_NULL_VALUE_FLAG) {
TypeInfo typeInfo = typeResolver.readTypeInfo(this, rootTypeInfoHolder);
return readNonRef(typeInfo);
}
return null;
}

/** Reads a non-null, first-seen object together with its type metadata. */
public Object readNonRef() {
TypeInfo typeInfo = typeResolver.readTypeInfo(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ public final class WriteContext {
private final Generics generics;
private final TypeResolver typeResolver;
private final RefWriter refWriter;
private final TypeInfoHolder rootTypeInfoHolder;
private final MetaStringWriter metaStringWriter;
private final StringSerializer stringSerializer;
private final boolean crossLanguage;
private final boolean trackingRef;
private final boolean compressInt;
private final Int64Encoding longEncoding;
private final boolean forVirtualThread;
Expand All @@ -85,9 +87,11 @@ public WriteContext(
this.generics = generics;
this.typeResolver = typeResolver;
this.refWriter = refWriter;
rootTypeInfoHolder = typeResolver.nilTypeInfoHolder();
this.metaStringWriter = metaStringWriter;
stringSerializer = (StringSerializer) typeResolver.getSerializer(String.class);
crossLanguage = config.isXlang();
trackingRef = config.trackingRef();
compressInt = config.compressInt();
longEncoding = config.longEncoding();
forVirtualThread = config.forVirtualThread();
Expand Down Expand Up @@ -527,6 +531,30 @@ public <T> void writeRef(T obj, Serializer<T> serializer) {
}
}

/** Writes the root object for one serialization operation. */
public void writeRootRef(Object obj) {
if (trackingRef) {
writeRef(obj, rootTypeInfoHolder);
return;
}
MemoryBuffer buffer = this.buffer;
if (obj == null) {
buffer.writeByte(Fory.NULL_FLAG);
return;
}
buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG);
TypeResolver resolver = typeResolver;
TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), rootTypeInfoHolder);
if (crossLanguage && typeInfo.getType() == UnknownStruct.class) {
depth++;
typeInfo.getSerializer().write(this, obj);
depth--;
return;
}
resolver.writeTypeInfo(this, typeInfo);
writeData(typeInfo, obj);
}

/**
* Writes a non-null, first-seen object together with its type metadata.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,9 @@ public void writeByte(byte value) {
} else {
final int writerIdx = writerIndex;
final int newIdx = writerIdx + 1;
ensure(newIdx);
if (newIdx > size) {
globalAllocator.grow(this, newIdx);
}
final long pos = address + writerIdx;
UNSAFE.putByte(heapMemory, pos, value);
writerIndex = newIdx;
Expand Down
Loading
Loading