From d658f771480950571237258776dfc26718433ed8 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 2 Apr 2024 15:34:42 +0300 Subject: [PATCH] No build time init of classes used in `UnsafeAccessedFieldBuildItem` Use the Feature classloader instead of the default one to prevent classes being loaded to register fields as unsafe accessed to end up being build time initialized. Similarly ensure that the registration is done after class initialization configuration to prevent classes from being configured for runtime initialization because of the unsafe access registration. --- .../steps/NativeImageFeatureStep.java | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 7fff7025f304e..28e1c088653e6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -78,28 +78,6 @@ public void write(String s, byte[] bytes) { MethodCreator beforeAn = file.getMethodCreator("beforeAnalysis", "V", BEFORE_ANALYSIS_ACCESS); TryBlock overallCatch = beforeAn.tryBlock(); - ResultHandle beforeAnalysisParam = beforeAn.getMethodParam(0); - - MethodCreator registerAsUnsafeAccessed = file - .getMethodCreator("registerAsUnsafeAccessed", void.class, Feature.BeforeAnalysisAccess.class) - .setModifiers(Modifier.PRIVATE | Modifier.STATIC); - for (UnsafeAccessedFieldBuildItem unsafeAccessedField : unsafeAccessedFields) { - TryBlock tc = registerAsUnsafeAccessed.tryBlock(); - ResultHandle declaringClassHandle = tc.invokeStaticMethod( - ofMethod(Class.class, "forName", Class.class, String.class), - tc.load(unsafeAccessedField.getDeclaringClass())); - ResultHandle fieldHandle = tc.invokeVirtualMethod( - ofMethod(Class.class, "getDeclaredField", Field.class, String.class), declaringClassHandle, - tc.load(unsafeAccessedField.getFieldName())); - tc.invokeInterfaceMethod( - ofMethod(Feature.BeforeAnalysisAccess.class, "registerAsUnsafeAccessed", void.class, Field.class), - registerAsUnsafeAccessed.getMethodParam(0), fieldHandle); - CatchBlockCreator cc = tc.addCatch(Throwable.class); - cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); - } - registerAsUnsafeAccessed.returnVoid(); - overallCatch.invokeStaticMethod(registerAsUnsafeAccessed.getMethodDescriptor(), beforeAnalysisParam); - overallCatch.invokeStaticMethod(BUILD_TIME_INITIALIZATION, overallCatch.marshalAsArray(String.class, overallCatch.load(""))); // empty string means initialize everything @@ -179,6 +157,35 @@ public void write(String s, byte[] bytes) { overallCatch.invokeStaticMethod(runtimeReinitializedClasses.getMethodDescriptor()); } + // Ensure registration of fields being accessed through unsafe is done last to ensure that the class + // initialization configuration is done first. Registering the fields before configuring class initialization + // may results in classes being marked for runtime initialization even if not explicitly requested. + if (!unsafeAccessedFields.isEmpty()) { + ResultHandle beforeAnalysisParam = beforeAn.getMethodParam(0); + MethodCreator registerAsUnsafeAccessed = file + .getMethodCreator("registerAsUnsafeAccessed", void.class, Feature.BeforeAnalysisAccess.class) + .setModifiers(Modifier.PRIVATE | Modifier.STATIC); + ResultHandle thisClass = registerAsUnsafeAccessed.loadClassFromTCCL(GRAAL_FEATURE); + ResultHandle cl = registerAsUnsafeAccessed + .invokeVirtualMethod(ofMethod(Class.class, "getClassLoader", ClassLoader.class), thisClass); + for (UnsafeAccessedFieldBuildItem unsafeAccessedField : unsafeAccessedFields) { + TryBlock tc = registerAsUnsafeAccessed.tryBlock(); + ResultHandle declaringClassHandle = tc.invokeStaticMethod( + ofMethod(Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class), + tc.load(unsafeAccessedField.getDeclaringClass()), tc.load(false), cl); + ResultHandle fieldHandle = tc.invokeVirtualMethod( + ofMethod(Class.class, "getDeclaredField", Field.class, String.class), declaringClassHandle, + tc.load(unsafeAccessedField.getFieldName())); + tc.invokeInterfaceMethod( + ofMethod(Feature.BeforeAnalysisAccess.class, "registerAsUnsafeAccessed", void.class, Field.class), + registerAsUnsafeAccessed.getMethodParam(0), fieldHandle); + CatchBlockCreator cc = tc.addCatch(Throwable.class); + cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); + } + registerAsUnsafeAccessed.returnVoid(); + overallCatch.invokeStaticMethod(registerAsUnsafeAccessed.getMethodDescriptor(), beforeAnalysisParam); + } + CatchBlockCreator print = overallCatch.addCatch(Throwable.class); print.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), print.getCaughtException());