|
37 | 37 | import java.util.stream.Collectors; |
38 | 38 | import java.util.stream.Stream; |
39 | 39 |
|
| 40 | +import org.graalvm.collections.EconomicSet; |
| 41 | +import org.graalvm.compiler.debug.GraalError; |
40 | 42 | import org.graalvm.compiler.java.LambdaUtils; |
41 | 43 |
|
42 | 44 | import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; |
@@ -329,7 +331,7 @@ InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize, |
329 | 331 | superResult = superResult.max(processInterfaces(clazz, memoize, earlyClassInitializerAnalyzedClasses)); |
330 | 332 |
|
331 | 333 | if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) { |
332 | | - if (!Proxy.isProxyClass(clazz) || areAllInterfaceMethodsSafeToInitializeAtBuildTime(clazz)) { |
| 334 | + if (!Proxy.isProxyClass(clazz) || !implementsRunTimeInitializedInterface(clazz)) { |
333 | 335 | forceInitializeHosted(clazz, "proxy/lambda classes with interfaces initialized at build time are also initialized at build time", false); |
334 | 336 | return InitKind.BUILD_TIME; |
335 | 337 | } |
@@ -377,14 +379,29 @@ InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize, |
377 | 379 | } |
378 | 380 |
|
379 | 381 | /** |
380 | | - * Interfaces are only safe to initialize at build time if no interface method references a type |
381 | | - * that {@linkplain #shouldInitializeAtRuntime should be initialized at run time}. |
| 382 | + * Interfaces must be initialized at run time if any interface method references a type that |
| 383 | + * {@linkplain #shouldInitializeAtRuntime should be initialized at run time}. |
382 | 384 | */ |
383 | | - private boolean areAllInterfaceMethodsSafeToInitializeAtBuildTime(Class<?> clazz) { |
384 | | - var interfaces = Arrays.stream(clazz.getInterfaces()); |
385 | | - var methods = interfaces.flatMap(c -> Arrays.stream(c.getDeclaredMethods())); |
| 385 | + private boolean implementsRunTimeInitializedInterface(Class<?> clazz) { |
| 386 | + EconomicSet<Class<?>> visited = EconomicSet.create(); |
| 387 | + return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited)); |
| 388 | + } |
| 389 | + |
| 390 | + private boolean shouldInitializeInterfaceAtRunTime(Class<?> clazz, EconomicSet<Class<?>> visited) { |
| 391 | + if (visited.contains(clazz)) { |
| 392 | + // already visited |
| 393 | + return false; |
| 394 | + } |
| 395 | + GraalError.guarantee(clazz.isInterface(), "expected interface, got %s", clazz); |
| 396 | + visited.add(clazz); |
| 397 | + var methods = Arrays.stream(clazz.getDeclaredMethods()); |
386 | 398 | var types = methods.flatMap(m -> Stream.concat(Stream.of(m.getReturnType()), Arrays.stream(m.getParameterTypes()))); |
387 | | - return types.noneMatch(this::shouldInitializeAtRuntime); |
| 399 | + // check for initialize at run time |
| 400 | + if (types.anyMatch(this::shouldInitializeAtRuntime)) { |
| 401 | + return true; |
| 402 | + } |
| 403 | + // iterate super interfaces recursively |
| 404 | + return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited)); |
388 | 405 | } |
389 | 406 |
|
390 | 407 | private InitKind processInterfaces(Class<?> clazz, boolean memoizeEager, Set<Class<?>> earlyClassInitializerAnalyzedClasses) { |
|
0 commit comments