From 1d5f5420fd57256e423ceaec10eb5fac4d47ad16 Mon Sep 17 00:00:00 2001 From: Christian Humer Date: Wed, 15 Jul 2026 17:05:29 +0200 Subject: [PATCH] Preinitialize inherited Truffle library dispatches --- .../svm/truffle/TruffleBaseFeature.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index d6ad186297cb..7c379b469381 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -268,6 +268,8 @@ public boolean getAsBoolean() { private AnalysisMetaAccess metaAccess; private GraalGraphObjectReplacer graalGraphObjectReplacer; private final EconomicSet> registeredClasses = EconomicSet.create(); + private final EconomicSet> registeredExportLibraryClasses = EconomicSet.create(); + private final Set> pendingExportLibraryReceiverClasses = ConcurrentHashMap.newKeySet(); private final Map, PossibleReplaceCandidatesSubtypeHandler> subtypeChecks = new HashMap<>(); private boolean profilingEnabled; private Field uncachedDispatchField; @@ -818,6 +820,13 @@ public void duringAnalysis(DuringAnalysisAccess a) { initializeTruffleLibrariesAtBuildTime(access, type); initializeDynamicObjectLayouts(type); } + /* Resolve reachable export subclasses once they are known to be instantiated. */ + for (Class receiverClass : pendingExportLibraryReceiverClasses) { + AnalysisType type = access.getMetaAccess().lookupJavaType(receiverClass); + if (type.isInstantiated() && pendingExportLibraryReceiverClasses.remove(receiverClass)) { + initializeTruffleLibraryReceiverAtBuildTime(receiverClass); + } + } access.rescanRoot(layoutInfoMapField, scanReason); access.rescanRoot(layoutMapField, scanReason); access.rescanRoot(libraryFactoryCacheField, scanReason); @@ -983,12 +992,40 @@ private void initializeTruffleLibrariesAtBuildTime(DuringAnalysisAccessImpl acce access.rescanField(factory, uncachedDispatchField, scanReason); } if (AnnotationUtil.isAnnotationPresent(type, ExportLibrary.class) || AnnotationUtil.isAnnotationPresent(type, ExportLibrary.Repeat.class)) { - /* Eagerly resolve receiver type. */ - invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "lookup", - Collections.singleton(Class.class), type.getJavaClass()); + Class receiverClass = type.getJavaClass(); + if (registeredExportLibraryClasses.add(receiverClass)) { + access.registerSubtypeReachabilityHandler(this::registerConcreteTruffleLibraryReceiver, receiverClass); + if (hasExplicitReceiverExport(type)) { + /* + * Exports with an explicit receiver can be used as dynamic dispatch targets + * without the export class being instantiated. + */ + initializeTruffleLibraryReceiverAtBuildTime(receiverClass); + } + } + } + } + + private static boolean hasExplicitReceiverExport(AnalysisType type) { + for (ExportLibrary export : AnnotationUtil.getAnnotationsByType(type, ExportLibrary.class, ExportLibrary.Repeat.class, ExportLibrary.Repeat::value)) { + if (export.receiverType() != Void.class) { + return true; + } + } + return false; + } + + private void registerConcreteTruffleLibraryReceiver(@SuppressWarnings("unused") DuringAnalysisAccess access, Class receiverClass) { + if (!Modifier.isAbstract(receiverClass.getModifiers())) { + pendingExportLibraryReceiverClasses.add(receiverClass); } } + private static void initializeTruffleLibraryReceiverAtBuildTime(Class receiverClass) { + invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "lookup", + Collections.singleton(Class.class), receiverClass); + } + private final EconomicSet> dynamicObjectClasses = EconomicSet.create(); private void initializeDynamicObjectLayouts(AnalysisType type) {