From 6b0241257c77f952cb7d0018d2fbf661e9d6da02 Mon Sep 17 00:00:00 2001 From: Shawn Date: Fri, 21 Jul 2023 15:28:08 +0800 Subject: [PATCH] [Java] Support jit for non public classes (#719) * support jit for non pblic classes * make guava serializers jit async * lint code * add private bean jit tests and package level bean test * add private bean jit tests and package level bean test --- .../fury-core/src/main/java/io/fury/Fury.java | 2 +- .../java/io/fury/builder/CodecBuilder.java | 4 +- .../io/fury/serializer/CodegenSerializer.java | 17 +----- .../io/fury/serializer/GuavaSerializers.java | 32 ++++++++--- .../src/test/java/io/fury/FuryTest.java | 53 +++++++++++++++---- 5 files changed, 72 insertions(+), 36 deletions(-) diff --git a/java/fury-core/src/main/java/io/fury/Fury.java b/java/fury-core/src/main/java/io/fury/Fury.java index 0cead7c686..55c03cb61f 100644 --- a/java/fury-core/src/main/java/io/fury/Fury.java +++ b/java/fury-core/src/main/java/io/fury/Fury.java @@ -123,6 +123,7 @@ private Fury(FuryBuilder builder, ClassLoader classLoader) { } else { this.refResolver = new NoRefResolver(); } + jitContext = new JITContext(this); enumStringResolver = new EnumStringResolver(); classResolver = new ClassResolver(this); classResolver.initialize(); @@ -132,7 +133,6 @@ private Fury(FuryBuilder builder, ClassLoader classLoader) { nativeObjects = new ArrayList<>(); generics = new Generics(this); stringSerializer = new StringSerializer(this); - jitContext = new JITContext(this); LOG.info("Created new fury {}", this); } 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 edbb03391c..b78cae037d 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 @@ -113,7 +113,7 @@ protected Expression getFieldValue(Expression inputBeanExpr, Descriptor descript Modifier.isPublic(getRawType(fieldType).getModifiers()), "Field type should be public for codegen-based access"); String fieldName = descriptor.getName(); - if (duplicatedFields.contains(fieldName)) { + if (duplicatedFields.contains(fieldName) || !Modifier.isPublic(beanClass.getModifiers())) { return unsafeAccessField(inputBeanExpr, beanClass, descriptor); } // public field or non-private non-java field access field directly. @@ -200,7 +200,7 @@ protected Expression setFieldValue(Expression bean, Descriptor d, Expression val if (value instanceof Inlineable) { ((Inlineable) value).inline(); } - if (duplicatedFields.contains(fieldName)) { + if (duplicatedFields.contains(fieldName) || !Modifier.isPublic(beanClass.getModifiers())) { return unsafeSetField(bean, d, value); } if (!Modifier.isFinal(d.getModifiers()) && Modifier.isPublic(d.getModifiers())) { diff --git a/java/fury-core/src/main/java/io/fury/serializer/CodegenSerializer.java b/java/fury-core/src/main/java/io/fury/serializer/CodegenSerializer.java index 645c2ad28f..f5e856ac10 100644 --- a/java/fury-core/src/main/java/io/fury/serializer/CodegenSerializer.java +++ b/java/fury-core/src/main/java/io/fury/serializer/CodegenSerializer.java @@ -18,10 +18,8 @@ package io.fury.serializer; -import static io.fury.type.TypeUtils.getRawType; import static io.fury.util.Utils.checkArgument; -import com.google.common.reflect.TypeToken; import io.fury.Fury; import io.fury.builder.CodecUtils; import io.fury.builder.Generated; @@ -37,19 +35,8 @@ public final class CodegenSerializer { public static boolean supportCodegenForJavaSerialization(Class cls) { - return isJavaPojo(TypeToken.of(cls)); - } - - private static boolean isJavaPojo(TypeToken type) { - Class rawType = getRawType(type); - // since we need to access class in generated code in our package, the class must be public. - // TODO support default access-level class jit. - if (Modifier.isPublic(rawType.getModifiers())) { - // bean class can be static nested class, but can't be a non-static inner class - return rawType.getEnclosingClass() == null || Modifier.isStatic(rawType.getModifiers()); - } else { - return false; - } + // bean class can be static nested class, but can't be a non-static inner class + return cls.getEnclosingClass() == null || Modifier.isStatic(cls.getModifiers()); } @SuppressWarnings("unchecked") diff --git a/java/fury-core/src/main/java/io/fury/serializer/GuavaSerializers.java b/java/fury-core/src/main/java/io/fury/serializer/GuavaSerializers.java index 9cf240f619..ac6a975837 100644 --- a/java/fury-core/src/main/java/io/fury/serializer/GuavaSerializers.java +++ b/java/fury-core/src/main/java/io/fury/serializer/GuavaSerializers.java @@ -44,23 +44,31 @@ public class GuavaSerializers { abstract static class GuavaCollectionSerializer extends CollectionSerializer { - private final ReplaceResolveSerializer serializer; + private ReplaceResolveSerializer serializer; public GuavaCollectionSerializer(Fury fury, Class cls) { super(fury, cls, false, false); fury.getClassResolver().setSerializer(cls, this); - serializer = new ReplaceResolveSerializer(fury, cls); + } + + protected ReplaceResolveSerializer getOrCreateSerializer() { + // reduce cost of fury creation, ReplaceResolveSerializer will jit-generate serializer. + ReplaceResolveSerializer serializer = this.serializer; + if (serializer == null) { + this.serializer = serializer = new ReplaceResolveSerializer(fury, type); + } + return serializer; } @SuppressWarnings("unchecked") @Override public T read(MemoryBuffer buffer) { - return (T) serializer.read(buffer); + return (T) getOrCreateSerializer().read(buffer); } @Override public void write(MemoryBuffer buffer, T value) { - serializer.write(buffer, value); + getOrCreateSerializer().write(buffer, value); } @Override @@ -122,23 +130,31 @@ protected T xnewInstance(Collection collection) { } abstract static class GuavaMapSerializer extends MapSerializer { - private final ReplaceResolveSerializer serializer; + private ReplaceResolveSerializer serializer; public GuavaMapSerializer(Fury fury, Class cls) { super(fury, cls, false, false); fury.getClassResolver().setSerializer(cls, this); - serializer = new ReplaceResolveSerializer(fury, cls); + } + + protected ReplaceResolveSerializer getOrCreateSerializer() { + // reduce cost of fury creation, ReplaceResolveSerializer will jit-generate serializer. + ReplaceResolveSerializer serializer = this.serializer; + if (serializer == null) { + this.serializer = serializer = new ReplaceResolveSerializer(fury, type); + } + return serializer; } @SuppressWarnings("unchecked") @Override public T read(MemoryBuffer buffer) { - return (T) serializer.read(buffer); + return (T) getOrCreateSerializer().read(buffer); } @Override public void write(MemoryBuffer buffer, T value) { - serializer.write(buffer, value); + getOrCreateSerializer().write(buffer, value); } @Override diff --git a/java/fury-core/src/test/java/io/fury/FuryTest.java b/java/fury-core/src/test/java/io/fury/FuryTest.java index 82a0ded473..74935338b1 100644 --- a/java/fury-core/src/test/java/io/fury/FuryTest.java +++ b/java/fury-core/src/test/java/io/fury/FuryTest.java @@ -72,8 +72,8 @@ public class FuryTest extends FuryTestBase { @DataProvider(name = "languageConfig") - public static Object[] languageConfig() { - return new Object[] {Language.JAVA, Language.PYTHON}; + public static Object[][] languageConfig() { + return new Object[][] {{Language.JAVA}, {Language.PYTHON}}; } @Test(dataProvider = "crossLanguageReferenceTrackingConfig") @@ -275,22 +275,55 @@ private static class Inner { } } - @Test(dataProvider = "referenceTrackingConfig") - public void testSerializePrivateBean(boolean referenceTracking) { + @Test + public void testSerializePrivateBean() { Fury fury = - Fury.builder() - .withLanguage(Language.JAVA) - .withRefTracking(referenceTracking) - .disableSecureMode() - .build(); + Fury.builder().withLanguage(Language.JAVA).withCodegen(false).disableSecureMode().build(); Outer outer = new Outer(); outer.inner = new Outer.Inner(); fury.deserialize(fury.serialize(outer)); - assertTrue(fury.getClassResolver().getSerializer(Outer.class) instanceof Generated); + assertTrue(fury.getClassResolver().getSerializer(Outer.class) instanceof ObjectSerializer); assertTrue( fury.getClassResolver().getSerializer(Outer.Inner.class) instanceof ObjectSerializer); } + @Test + public void testSerializePrivateBeanJIT() { + Fury fury = + Fury.builder().withLanguage(Language.JAVA).withCodegen(true).disableSecureMode().build(); + Outer outer = new Outer(); + outer.inner = new Outer.Inner(); + fury.deserialize(fury.serialize(outer)); + assertTrue(fury.getClassResolver().getSerializer(Outer.class) instanceof Generated); + assertTrue(fury.getClassResolver().getSerializer(Outer.Inner.class) instanceof Generated); + } + + @Data + public static class PackageLevelBean { + public long f1; + private long f2; + } + + @Test + public void testSerializePackageLevelBean() { + Fury fury = + Fury.builder().withLanguage(Language.JAVA).withCodegen(false).disableSecureMode().build(); + PackageLevelBean o = new PackageLevelBean(); + o.f1 = 10; + o.f2 = 1; + serDeCheckSerializer(fury, o, "Object"); + } + + @Test + public void testSerializePackageLevelBeanJIT() { + Fury fury = + Fury.builder().withLanguage(Language.JAVA).withCodegen(true).disableSecureMode().build(); + PackageLevelBean o = new PackageLevelBean(); + o.f1 = 10; + o.f2 = 1; + serDeCheckSerializer(fury, o, "PackageLevelBean"); + } + static class B { int f1; }