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/fury.git
The following commit(s) were added to refs/heads/main by this push:
new d4380ea1 feat(java): Support furyField nullable in codeGen pattern
(#2191)
d4380ea1 is described below
commit d4380ea1db5e1beb0beb868c4d03f65cb8711e05
Author: hn <[email protected]>
AuthorDate: Thu May 15 22:44:22 2025 +0800
feat(java): Support furyField nullable in codeGen pattern (#2191)
<!--
**Thanks for contributing to Fury.**
**If this is your first time opening a PR on fury, you can refer to
[CONTRIBUTING.md](https://github.com/apache/fury/blob/main/CONTRIBUTING.md).**
Contribution Checklist
- The **Apache Fury (incubating)** community has restrictions on the
naming of pr titles. You can also find instructions in
[CONTRIBUTING.md](https://github.com/apache/fury/blob/main/CONTRIBUTING.md).
- Fury has a strong focus on performance. If the PR you submit will have
an impact on performance, please benchmark it first and provide the
benchmark result here.
-->
## What does this PR do?
<!-- Describe the purpose of this PR. -->
## Related issues
#2169
<!--
Is there any related issue? Please attach here.
- #xxxx0
- #xxxx1
- #xxxx2
-->
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fury/issues/new/choose) describing the
need to do so and update the document if necessary.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
-->
---------
Co-authored-by: hening <[email protected]>
---
.../fury/builder/BaseObjectCodecBuilder.java | 83 +++++++++++++++++++---
.../fury/builder/CompatibleCodecBuilder.java | 15 ++--
.../apache/fury/builder/ObjectCodecBuilder.java | 13 ++--
.../fury/serializer/AbstractObjectSerializer.java | 4 +-
.../main/java/org/apache/fury/type/Descriptor.java | 10 +++
5 files changed, 104 insertions(+), 21 deletions(-)
diff --git
a/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java
b/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java
index da8ac503..7a011cf8 100644
---
a/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java
+++
b/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java
@@ -333,10 +333,6 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
ctx.addImports(AbstractCollectionSerializer.class,
AbstractMapSerializer.class);
}
- protected Expression serializeFor(Expression inputObject, Expression buffer,
TypeRef<?> typeRef) {
- return serializeFor(inputObject, buffer, typeRef, false);
- }
-
/**
* Returns an expression that serialize an nullable <code>inputObject</code>
to <code>buffer
* </code>.
@@ -375,6 +371,42 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
}
+ protected Expression serializeForNullable(
+ Expression inputObject, Expression buffer, TypeRef<?> typeRef, boolean
nullable) {
+ return serializeForNullable(inputObject, buffer, typeRef, null, false,
nullable);
+ }
+
+ protected Expression serializeForNullable(
+ Expression inputObject,
+ Expression buffer,
+ TypeRef<?> typeRef,
+ Expression serializer,
+ boolean generateNewMethod,
+ boolean nullable) {
+ if (needWriteRef(typeRef)) {
+ return new If(
+ not(writeRefOrNull(buffer, inputObject)),
+ serializeForNotNull(inputObject, buffer, typeRef, serializer,
generateNewMethod));
+ } else {
+ // if typeToken is not final, ref tracking of subclass will be ignored
too.
+ if (typeRef.isPrimitive()) {
+ return serializeForNotNull(inputObject, buffer, typeRef, serializer,
generateNewMethod);
+ }
+ if (nullable) {
+ Expression action =
+ new ListExpression(
+ new Invoke(buffer, "writeByte",
Literal.ofByte(Fury.NOT_NULL_VALUE_FLAG)),
+ serializeForNotNull(inputObject, buffer, typeRef, serializer,
generateNewMethod));
+ return new If(
+ eqNull(inputObject),
+ new Invoke(buffer, "writeByte", Literal.ofByte(Fury.NULL_FLAG)),
+ action);
+ } else {
+ return serializeForNotNull(inputObject, buffer, typeRef, serializer,
generateNewMethod);
+ }
+ }
+ }
+
protected Expression writeRefOrNull(Expression buffer, Expression object) {
return inlineInvoke(refResolverRef, "writeRefOrNull",
PRIMITIVE_BOOLEAN_TYPE, buffer, object);
}
@@ -1377,11 +1409,6 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
refResolverRef, "tryPreserveRefId", "refId", PRIMITIVE_INT_TYPE,
false, buffer);
}
- protected Expression deserializeFor(
- Expression buffer, TypeRef<?> typeRef, Function<Expression, Expression>
callback) {
- return deserializeFor(buffer, typeRef, callback, null);
- }
-
/**
* Returns an expression that deserialize a nullable
<code>inputObject</code> from <code>buffer
* </code>.
@@ -1406,6 +1433,24 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
}
}
+ protected Expression deserializeForNullable(
+ Expression buffer,
+ TypeRef<?> typeRef,
+ Function<Expression, Expression> callback,
+ boolean nullable) {
+ if (visitFury(f -> f.getClassResolver().needToWriteRef(typeRef))) {
+ return readRef(buffer, callback, () -> deserializeForNotNull(buffer,
typeRef, null));
+ } else {
+ if (typeRef.isPrimitive()) {
+ Expression value = deserializeForNotNull(buffer, typeRef, null);
+ // Should put value expr ahead to avoid generated code in wrong scope.
+ return new ListExpression(value, callback.apply(value));
+ }
+ return readNullable(
+ buffer, typeRef, callback, () -> deserializeForNotNull(buffer,
typeRef, null), nullable);
+ }
+ }
+
private Expression readRef(
Expression buffer,
Function<Expression, Expression> callback,
@@ -1441,6 +1486,26 @@ public abstract class BaseObjectCodecBuilder extends
CodecBuilder {
return new If(notNull, callback.apply(value),
callback.apply(nullValue(typeRef)), false);
}
+ private Expression readNullable(
+ Expression buffer,
+ TypeRef<?> typeRef,
+ Function<Expression, Expression> callback,
+ Supplier<Expression> deserializeForNotNull,
+ boolean nullable) {
+ if (nullable) {
+ Expression notNull =
+ neq(
+ inlineInvoke(buffer, "readByte", PRIMITIVE_BYTE_TYPE),
+ Literal.ofByte(Fury.NULL_FLAG));
+ Expression value = deserializeForNotNull.get();
+ // use false to ignore null.
+ return new If(notNull, callback.apply(value),
callback.apply(nullValue(typeRef)), false);
+ } else {
+ Expression value = deserializeForNotNull.get();
+ return callback.apply(value);
+ }
+ }
+
protected Expression deserializeForNotNull(
Expression buffer, TypeRef<?> typeRef, InvokeHint invokeHint) {
return deserializeForNotNull(buffer, typeRef, null, invokeHint);
diff --git
a/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java
b/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java
index f799a708..33b8bcc0 100644
---
a/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java
+++
b/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java
@@ -341,9 +341,10 @@ public class CompatibleCodecBuilder extends
BaseObjectCodecBuilder {
Expression bean, Expression buffer, FieldInfo fieldInfo) {
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
+ boolean nullable = descriptor.isNullable();
Expression fieldValue = getFieldValue(bean, descriptor);
walkPath.removeLast();
- return serializeFor(fieldValue, buffer, descriptor.getTypeRef());
+ return serializeForNullable(fieldValue, buffer, descriptor.getTypeRef(),
nullable);
}
@Override
@@ -501,11 +502,13 @@ public class CompatibleCodecBuilder extends
BaseObjectCodecBuilder {
private Expression readEmbedTypes4(
Expression bean, Expression buffer, Descriptor descriptor, Expression
partFieldInfo) {
+ boolean nullable = descriptor.isNullable();
Expression deserializeAction =
- deserializeFor(
+ deserializeForNullable(
buffer,
descriptor.getTypeRef(),
- expr -> setFieldValue(bean, descriptor, tryInlineCast(expr,
descriptor.getTypeRef())));
+ expr -> setFieldValue(bean, descriptor, tryInlineCast(expr,
descriptor.getTypeRef())),
+ nullable);
return new ListExpression(
deserializeAction,
new Assign(partFieldInfo, inlineInvoke(buffer, readIntFunc(),
PRIMITIVE_LONG_TYPE)));
@@ -706,11 +709,13 @@ public class CompatibleCodecBuilder extends
BaseObjectCodecBuilder {
private Expression readEmbedTypes8Field(
Expression bean, Expression buffer, Descriptor descriptor, Expression
partFieldInfo) {
+ boolean nullable = descriptor.isNullable();
Expression deserializeAction =
- deserializeFor(
+ deserializeForNullable(
buffer,
descriptor.getTypeRef(),
- expr -> setFieldValue(bean, descriptor, tryInlineCast(expr,
descriptor.getTypeRef())));
+ expr -> setFieldValue(bean, descriptor, tryInlineCast(expr,
descriptor.getTypeRef())),
+ nullable);
return new ListExpression(
deserializeAction,
new Assign(partFieldInfo, inlineInvoke(buffer, readLongFunc(),
PRIMITIVE_LONG_TYPE)));
diff --git
a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java
b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java
index c32a49e1..3429512b 100644
---
a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java
+++
b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java
@@ -204,7 +204,9 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
// `bean` will be replaced by `Reference` to cut-off expr
dependency.
Expression fieldValue = getFieldValue(bean, d);
walkPath.add(d.getDeclaringClass() + d.getName());
- Expression fieldExpr = serializeFor(fieldValue, buffer,
d.getTypeRef());
+ boolean nullable = d.isNullable();
+ Expression fieldExpr =
+ serializeForNullable(fieldValue, buffer, d.getTypeRef(),
nullable);
walkPath.removeLast();
groupExpressions.add(fieldExpr);
}
@@ -554,15 +556,17 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
for (Descriptor d : group) {
ExpressionVisitor.ExprHolder exprHolder =
ExpressionVisitor.ExprHolder.of("bean", bean);
walkPath.add(d.getDeclaringClass() + d.getName());
+ boolean nullable = d.isNullable();
Expression action =
- deserializeFor(
+ deserializeForNullable(
buffer,
d.getTypeRef(),
// `bean` will be replaced by `Reference` to cut-off expr
// dependency.
expr ->
setFieldValue(
- exprHolder.get("bean"), d, tryInlineCast(expr,
d.getTypeRef())));
+ exprHolder.get("bean"), d, tryInlineCast(expr,
d.getTypeRef())),
+ nullable);
walkPath.removeLast();
groupExpressions.add(action);
}
@@ -580,7 +584,8 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
ListExpression groupExpressions = new ListExpression();
// use Reference to cut-off expr dependency.
for (Descriptor d : group) {
- Expression v = deserializeFor(buffer, d.getTypeRef(), expr -> expr);
+ boolean nullable = d.isNullable();
+ Expression v = deserializeForNullable(buffer, d.getTypeRef(), expr ->
expr, nullable);
Expression action = setFieldValue(bean, d, tryInlineCast(v,
d.getTypeRef()));
groupExpressions.add(action);
}
diff --git
a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
index cba248e9..1608c3c5 100644
---
a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
+++
b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
@@ -995,9 +995,7 @@ public abstract class AbstractObjectSerializer<T> extends
Serializer<T> {
this.qualifiedFieldName = d.getDeclaringClass() + "." + d.getName();
this.fieldAccessor = d.getField() != null ?
FieldAccessor.createAccessor(d.getField()) : null;
FuryField furyField = d.getFuryField();
- if (!typeRef.isPrimitive()) {
- nullable = furyField == null || furyField.nullable();
- }
+ nullable = d.isNullable();
if (fury.trackingRef()) {
trackingRef =
furyField != null
diff --git a/java/fury-core/src/main/java/org/apache/fury/type/Descriptor.java
b/java/fury-core/src/main/java/org/apache/fury/type/Descriptor.java
index 0d94854a..05cd7a33 100644
--- a/java/fury-core/src/main/java/org/apache/fury/type/Descriptor.java
+++ b/java/fury-core/src/main/java/org/apache/fury/type/Descriptor.java
@@ -99,6 +99,9 @@ public class Descriptor {
this.writeMethod = writeMethod;
this.typeRef = typeRef;
this.furyField = this.field.getAnnotation(FuryField.class);
+ if (!typeRef.isPrimitive()) {
+ this.nullable = furyField == null || furyField.nullable();
+ }
}
public Descriptor(TypeRef<?> typeRef, String name, int modifier, String
declaringClass) {
@@ -111,6 +114,7 @@ public class Descriptor {
this.readMethod = null;
this.writeMethod = null;
this.furyField = null;
+ this.nullable = !typeRef.isPrimitive();
}
private Descriptor(Field field, Method readMethod) {
@@ -123,6 +127,9 @@ public class Descriptor {
this.writeMethod = null;
this.typeRef = null;
this.furyField = this.field.getAnnotation(FuryField.class);
+ if (!field.getType().isPrimitive()) {
+ this.nullable = furyField == null || furyField.nullable();
+ }
}
private Descriptor(
@@ -143,6 +150,9 @@ public class Descriptor {
this.readMethod = readMethod;
this.writeMethod = writeMethod;
this.furyField = this.field == null ? null :
this.field.getAnnotation(FuryField.class);
+ if (!typeRef.isPrimitive()) {
+ this.nullable = furyField == null || furyField.nullable();
+ }
}
public Descriptor(DescriptorBuilder builder) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]