From b9e1b1912736036cd9f702f98706f84d8f22b69f Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 12 Oct 2023 01:57:37 +0800 Subject: [PATCH] [Java] Fix private record JIT (#1004) * fix private record jit * add codegen param * lint code --- .../RecordSerializersTest.java | 12 +++++ .../fury/builder/BaseObjectCodecBuilder.java | 35 --------------- .../java/io/fury/builder/CodecBuilder.java | 45 ++++++++++++++++++- .../java/io/fury/util/unsafe/_JDKAccess.java | 6 ++- 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/integration_tests/latest_jdk_tests/src/test/java/io/fury/integration_tests/RecordSerializersTest.java b/integration_tests/latest_jdk_tests/src/test/java/io/fury/integration_tests/RecordSerializersTest.java index 967a1ac2ab..5bd744821d 100644 --- a/integration_tests/latest_jdk_tests/src/test/java/io/fury/integration_tests/RecordSerializersTest.java +++ b/integration_tests/latest_jdk_tests/src/test/java/io/fury/integration_tests/RecordSerializersTest.java @@ -219,4 +219,16 @@ public void testPrivateRecords(boolean codegen) { Assert.assertEquals(fury.deserialize(bytes), o2); } } + + @Test(dataProvider = "codegen") + public void testPrivateRecord(boolean codegen) { + Fury fury = Fury.builder().withCodegen(codegen).build(); + fury.register(PrivateRecord.class); + byte[] serialized = fury.serialize(new PrivateRecord("foo")); // fails + Object deserialized = fury.deserialize(serialized); + System.out.println(deserialized); + } + + private record PrivateRecord(String foo) { + } } diff --git a/java/fury-core/src/main/java/io/fury/builder/BaseObjectCodecBuilder.java b/java/fury-core/src/main/java/io/fury/builder/BaseObjectCodecBuilder.java index 9bcd43161b..cbf122c9c8 100644 --- a/java/fury-core/src/main/java/io/fury/builder/BaseObjectCodecBuilder.java +++ b/java/fury-core/src/main/java/io/fury/builder/BaseObjectCodecBuilder.java @@ -82,7 +82,6 @@ import io.fury.serializer.Serializer; import io.fury.serializer.Serializers; import io.fury.serializer.StringSerializer; -import io.fury.type.FinalObjectTypeStub; import io.fury.type.TypeUtils; import io.fury.util.ReflectionUtils; import io.fury.util.StringUtils; @@ -946,40 +945,6 @@ private Expression writeContainerElement( return new ListExpression(elem, write); } - protected Expression tryInlineCast(Expression expression, TypeToken targetType) { - return tryCastIfPublic(expression, targetType, true); - } - - protected Expression tryCastIfPublic(Expression expression, TypeToken targetType) { - return tryCastIfPublic(expression, targetType, false); - } - - protected Expression tryCastIfPublic( - Expression expression, TypeToken targetType, boolean inline) { - if (getRawType(targetType) == FinalObjectTypeStub.class) { - // final field doesn't exist in this class, skip cast. - return expression; - } - if (inline) { - if (ReflectionUtils.isPublic(targetType) - && !expression.type().wrap().isSubtypeOf(targetType.wrap())) { - return new Cast(expression, targetType); - } else { - return expression; - } - } - return tryCastIfPublic(expression, targetType, "castedValue"); - } - - protected Expression tryCastIfPublic( - Expression expression, TypeToken targetType, String valuePrefix) { - if (ReflectionUtils.isPublic(targetType) - && !expression.type().wrap().isSubtypeOf(targetType.wrap())) { - return new Cast(expression, targetType, valuePrefix); - } - return expression; - } - /** * Return an expression to write a map to buffer. This expression can have better * efficiency for final key/value type. For final key/value type, it doesn't have to write class diff --git a/java/fury-core/src/main/java/io/fury/builder/CodecBuilder.java b/java/fury-core/src/main/java/io/fury/builder/CodecBuilder.java index db76656c7b..871f64beb5 100644 --- a/java/fury-core/src/main/java/io/fury/builder/CodecBuilder.java +++ b/java/fury-core/src/main/java/io/fury/builder/CodecBuilder.java @@ -16,6 +16,7 @@ package io.fury.builder; +import static io.fury.codegen.Expression.Invoke.inlineInvoke; import static io.fury.type.TypeUtils.OBJECT_ARRAY_TYPE; import static io.fury.type.TypeUtils.OBJECT_TYPE; import static io.fury.type.TypeUtils.PRIMITIVE_BOOLEAN_TYPE; @@ -45,6 +46,7 @@ import io.fury.resolver.ClassInfo; import io.fury.resolver.ClassInfoHolder; import io.fury.type.Descriptor; +import io.fury.type.FinalObjectTypeStub; import io.fury.util.Platform; import io.fury.util.ReflectionUtils; import io.fury.util.StringUtils; @@ -115,6 +117,40 @@ public CodecBuilder(CodegenContext ctx, TypeToken beanType) { /** Returns an expression that serialize java bean of type {@link CodecBuilder#beanClass}. */ public abstract Expression buildEncodeExpression(); + protected Expression tryInlineCast(Expression expression, TypeToken targetType) { + return tryCastIfPublic(expression, targetType, true); + } + + protected Expression tryCastIfPublic(Expression expression, TypeToken targetType) { + return tryCastIfPublic(expression, targetType, false); + } + + protected Expression tryCastIfPublic( + Expression expression, TypeToken targetType, boolean inline) { + if (getRawType(targetType) == FinalObjectTypeStub.class) { + // final field doesn't exist in this class, skip cast. + return expression; + } + if (inline) { + if (ReflectionUtils.isPublic(targetType) + && !expression.type().wrap().isSubtypeOf(targetType.wrap())) { + return new Cast(expression, targetType); + } else { + return expression; + } + } + return tryCastIfPublic(expression, targetType, "castedValue"); + } + + protected Expression tryCastIfPublic( + Expression expression, TypeToken targetType, String valuePrefix) { + if (ReflectionUtils.isPublic(targetType) + && !expression.type().wrap().isSubtypeOf(targetType.wrap())) { + return new Cast(expression, targetType, valuePrefix); + } + return expression; + } + // left null check in sub class encode method to reduce data dependence. private final boolean fieldNullable = false; @@ -212,7 +248,14 @@ private Expression getRecordFieldValue(Expression inputBeanExpr, Descriptor desc ref = new Reference(key, getterType); fieldMap.put(key, ref); } - return new Invoke(ref, methodInfo.f1, fieldType, fieldNullable, inputBeanExpr); + if (!fieldType.isPrimitive()) { + Expression v = inlineInvoke(ref, methodInfo.f1, OBJECT_TYPE, fieldNullable, inputBeanExpr); + TypeToken publicSuperType = + ReflectionUtils.getPublicSuperType(descriptor.getTypeToken()); + return new Cast(v, publicSuperType, fieldName); + } else { + return new Invoke(ref, methodInfo.f1, fieldType, fieldNullable, inputBeanExpr); + } } } diff --git a/java/fury-core/src/main/java/io/fury/util/unsafe/_JDKAccess.java b/java/fury-core/src/main/java/io/fury/util/unsafe/_JDKAccess.java index cc605962f7..b2b75ed1f2 100644 --- a/java/fury-core/src/main/java/io/fury/util/unsafe/_JDKAccess.java +++ b/java/fury-core/src/main/java/io/fury/util/unsafe/_JDKAccess.java @@ -261,7 +261,11 @@ public static T makeFunction(Lookup lookup, MethodHandle handle, Class fu } public static Tuple2, String> getterMethodInfo(Class type) { - return methodMap.get(type); + Tuple2, String> info = methodMap.get(type); + if (info == null) { + return Tuple2.of(Function.class, "apply"); + } + return info; } public static Object makeGetterFunction(