|
39 | 39 | import java.util.Map;
|
40 | 40 | import java.util.Objects;
|
41 | 41 | import java.util.Set;
|
| 42 | +import java.util.concurrent.ConcurrentHashMap; |
42 | 43 | import java.util.function.BiConsumer;
|
43 | 44 | import java.util.function.Consumer;
|
44 | 45 | import java.util.function.Function;
|
|
59 | 60 | import com.oracle.graal.pointsto.heap.ImageHeapConstant;
|
60 | 61 | import com.oracle.graal.pointsto.heap.ImageHeapScanner;
|
61 | 62 | import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
|
| 63 | +import com.oracle.graal.pointsto.meta.AnalysisElement; |
| 64 | +import com.oracle.graal.pointsto.meta.AnalysisElement.ElementNotification; |
| 65 | +import com.oracle.graal.pointsto.meta.AnalysisElement.MethodOverrideReachableNotification; |
| 66 | +import com.oracle.graal.pointsto.meta.AnalysisElement.SubtypeReachableNotification; |
62 | 67 | import com.oracle.graal.pointsto.meta.AnalysisField;
|
63 | 68 | import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
|
64 | 69 | import com.oracle.graal.pointsto.meta.AnalysisMethod;
|
@@ -358,14 +363,13 @@ public SVMHost getHostVM() {
|
358 | 363 | public static class BeforeAnalysisAccessImpl extends AnalysisAccessBase implements Feature.BeforeAnalysisAccess {
|
359 | 364 |
|
360 | 365 | private final NativeLibraries nativeLibraries;
|
361 |
| - private final ReachabilityHandler reachabilityHandler; |
362 | 366 | private final ClassForNameSupport classForNameSupport;
|
| 367 | + private final Map<Consumer<DuringAnalysisAccess>, ElementNotification> reachabilityNotifications = new ConcurrentHashMap<>(); |
363 | 368 |
|
364 | 369 | public BeforeAnalysisAccessImpl(FeatureHandler featureHandler, ImageClassLoader imageClassLoader, Inflation bb, NativeLibraries nativeLibraries,
|
365 | 370 | DebugContext debugContext) {
|
366 | 371 | super(featureHandler, imageClassLoader, bb, debugContext);
|
367 | 372 | this.nativeLibraries = nativeLibraries;
|
368 |
| - this.reachabilityHandler = new ConcurrentReachabilityHandler(); |
369 | 373 | this.classForNameSupport = ClassForNameSupport.currentLayer();
|
370 | 374 | }
|
371 | 375 |
|
@@ -469,22 +473,80 @@ public void registerHierarchyForReflectiveInstantiation(Class<?> c) {
|
469 | 473 |
|
470 | 474 | @Override
|
471 | 475 | public void registerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Object... elements) {
|
472 |
| - reachabilityHandler.registerReachabilityHandler(this, callback, elements); |
| 476 | + /* |
| 477 | + * All callback->notification pairs are tracked by the reachabilityNotifications map to |
| 478 | + * prevent registering the same callback multiple times. The notifications are also |
| 479 | + * tracked by each AnalysisElement, i.e., each trigger, and are removed as soon as they |
| 480 | + * are notified. |
| 481 | + */ |
| 482 | + ElementNotification notification = reachabilityNotifications.computeIfAbsent(callback, ElementNotification::new); |
| 483 | + |
| 484 | + if (notification.isNotified()) { |
| 485 | + /* Already notified from an earlier registration, nothing to do. */ |
| 486 | + return; |
| 487 | + } |
| 488 | + |
| 489 | + for (Object trigger : elements) { |
| 490 | + AnalysisElement analysisElement = switch (trigger) { |
| 491 | + case Class<?> clazz -> getMetaAccess().lookupJavaType(clazz); |
| 492 | + case Field field -> getMetaAccess().lookupJavaField(field); |
| 493 | + case Executable executable -> getMetaAccess().lookupJavaMethod(executable); |
| 494 | + default -> throw UserError.abort("'registerReachabilityHandler' called with an element that is not a Class, Field, or Executable: %s", |
| 495 | + trigger.getClass().getTypeName()); |
| 496 | + }; |
| 497 | + |
| 498 | + analysisElement.registerReachabilityNotification(notification); |
| 499 | + if (analysisElement.isTriggered()) { |
| 500 | + /* |
| 501 | + * Element already triggered, just notify the callback. At this point we could |
| 502 | + * just notify the callback and bail out, but, for debugging, it may be useful |
| 503 | + * to execute the notification for each trigger. Note that although the |
| 504 | + * notification can be shared between multiple triggers the notification |
| 505 | + * mechanism ensures that the callback itself is only executed once. |
| 506 | + */ |
| 507 | + analysisElement.notifyReachabilityCallback(getUniverse(), notification); |
| 508 | + } |
| 509 | + } |
473 | 510 | }
|
474 | 511 |
|
475 | 512 | @Override
|
476 | 513 | public void registerMethodOverrideReachabilityHandler(BiConsumer<DuringAnalysisAccess, Executable> callback, Executable baseMethod) {
|
477 |
| - reachabilityHandler.registerMethodOverrideReachabilityHandler(this, callback, baseMethod); |
| 514 | + AnalysisMethod baseAnalysisMethod = getMetaAccess().lookupJavaMethod(baseMethod); |
| 515 | + MethodOverrideReachableNotification notification = new MethodOverrideReachableNotification(callback); |
| 516 | + baseAnalysisMethod.registerOverrideReachabilityNotification(notification); |
| 517 | + |
| 518 | + /* |
| 519 | + * Notify for already reachable overrides. When a new override becomes reachable all |
| 520 | + * installed reachability callbacks in the supertypes declaring the method are |
| 521 | + * triggered. |
| 522 | + */ |
| 523 | + for (AnalysisMethod override : reachableMethodOverrides(baseAnalysisMethod)) { |
| 524 | + notification.notifyCallback(getUniverse(), override); |
| 525 | + } |
478 | 526 | }
|
479 | 527 |
|
480 | 528 | @Override
|
481 | 529 | public void registerSubtypeReachabilityHandler(BiConsumer<DuringAnalysisAccess, Class<?>> callback, Class<?> baseClass) {
|
482 |
| - reachabilityHandler.registerSubtypeReachabilityHandler(this, callback, baseClass); |
| 530 | + AnalysisType baseType = getMetaAccess().lookupJavaType(baseClass); |
| 531 | + SubtypeReachableNotification notification = new SubtypeReachableNotification(callback); |
| 532 | + baseType.registerSubtypeReachabilityNotification(notification); |
| 533 | + |
| 534 | + /* |
| 535 | + * Notify for already reachable subtypes. When a new type becomes reachable all |
| 536 | + * installed reachability callbacks in the supertypes are triggered. |
| 537 | + */ |
| 538 | + for (AnalysisType subtype : reachableSubtypes(baseType)) { |
| 539 | + notification.notifyCallback(getUniverse(), subtype); |
| 540 | + } |
483 | 541 | }
|
484 | 542 |
|
485 | 543 | @Override
|
486 | 544 | public void registerClassInitializerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Class<?> clazz) {
|
487 |
| - reachabilityHandler.registerClassInitializerReachabilityHandler(this, callback, clazz); |
| 545 | + /* |
| 546 | + * In our current static analysis implementations, there is no difference between the |
| 547 | + * reachability of a class and the reachability of its class initializer. |
| 548 | + */ |
| 549 | + registerReachabilityHandler(callback, clazz); |
488 | 550 | }
|
489 | 551 |
|
490 | 552 | @Override
|
|
0 commit comments