4242import com .oracle .svm .core .SubstrateUtil ;
4343import com .oracle .svm .core .hub .RuntimeClassLoading ;
4444import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
45+ import com .oracle .svm .hosted .OpenTypeWorldFeature ;
4546import com .oracle .svm .hosted .imagelayer .LayeredDispatchTableFeature ;
4647
4748import jdk .graal .compiler .debug .Assertions ;
@@ -182,15 +183,40 @@ private boolean verifyOpenTypeWorldDispatchTables() {
182183 }
183184
184185 private List <HostedMethod > generateITable (HostedType type ) {
185- return generateDispatchTable (type , 0 );
186+ return generateDispatchTable (type , List . of () );
186187 }
187188
188- private List <HostedMethod > generateDispatchTable (HostedType type , int startingIndex ) {
189+ /**
190+ * Tries to find an existing parent slot with an identical signature. If successful, this
191+ * method's index can be assigned to the same slot and no new dispatch slot is needed.
192+ */
193+ private static boolean findAndLinkToParentSlot (HostedMethod hMethod , List <HostedMethod > parentSlots ) {
194+ for (int i = 0 ; i < parentSlots .size (); i ++) {
195+ HostedMethod candidate = parentSlots .get (i );
196+ if (OpenTypeWorldFeature .matchingSignature (hMethod , candidate )) {
197+ assert candidate .computedVTableIndex == i : candidate .computedVTableIndex ;
198+ installVTableIndex (hMethod , candidate .computedVTableIndex );
199+ return true ;
200+ }
201+ }
202+ return false ;
203+ }
204+
205+ private static void installVTableIndex (HostedMethod hMethod , int index ) {
206+ assert hMethod .computedVTableIndex == HostedMethod .MISSING_VTABLE_IDX : hMethod .computedVTableIndex ;
207+ hMethod .computedVTableIndex = index ;
208+ }
209+
210+ private List <HostedMethod > generateDispatchTable (HostedType type , List <HostedMethod > parentClassTable ) {
189211 Predicate <HostedMethod > includeMethod ;
190212 if (openHubUtils .filterVTableMethods (type )) {
191213 // include only methods which will be indirect calls
192214 includeMethod = m -> {
193215 assert !m .isConstructor () : Assertions .errorMessage ("Constructors should never be in dispatch tables" , m );
216+ if (findAndLinkToParentSlot (m , parentClassTable )) {
217+ // a prior slot has been found
218+ return false ;
219+ }
194220 if (m .implementations .length > 1 ) {
195221 return true ;
196222 } else {
@@ -204,6 +230,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
204230 } else {
205231 includeMethod = m -> {
206232 assert !m .isConstructor () : Assertions .errorMessage ("Constructors should never be in dispatch tables" , m );
233+ if (findAndLinkToParentSlot (m , parentClassTable )) {
234+ // a prior slot has been found
235+ return false ;
236+ }
207237 /*
208238 * We have to use the analysis method's canBeStaticallyBound implementation because
209239 * within HostedMethod we sometimes do additional pruning when operating under the
@@ -214,11 +244,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
214244 }
215245 var table = type .getWrapped ().getOpenTypeWorldDispatchTableMethods ().stream ().map (hUniverse ::lookup ).filter (includeMethod ).sorted (HostedUniverse .METHOD_COMPARATOR ).toList ();
216246
217- int index = startingIndex ;
247+ int index = parentClassTable . size () ;
218248 for (HostedMethod typeMethod : table ) {
219249 assert typeMethod .getDeclaringClass ().equals (type ) : typeMethod ;
220- assert typeMethod .computedVTableIndex == HostedMethod .MISSING_VTABLE_IDX : typeMethod .computedVTableIndex ;
221- typeMethod .computedVTableIndex = index ;
250+ installVTableIndex (typeMethod , index );
222251 index ++;
223252 }
224253
@@ -229,40 +258,42 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
229258 return table ;
230259 }
231260
232- private void generateOpenTypeWorldDispatchTable (HostedInstanceClass type , Map <HostedType , List <HostedMethod >> dispatchTablesMap , HostedMethod invalidDispatchTableEntryHandler ) {
261+ private void generateOpenTypeWorldDispatchTable (HostedInstanceClass type , Map <HostedType , List <HostedMethod >> classTablesMap , HostedMethod invalidDispatchTableEntryHandler ) {
233262 var superClass = type .getSuperclass ();
234- List <HostedMethod > parentClassTable = superClass == null ? List .of () : dispatchTablesMap .get (superClass );
235- List <HostedMethod > classTableWithoutSuper = generateDispatchTable (type , parentClassTable . size () );
236- List <HostedMethod > resultClassTableMethods ;
263+ List <HostedMethod > parentClassTable = superClass == null ? List .of () : classTablesMap .get (superClass );
264+ List <HostedMethod > classTableWithoutSuper = generateDispatchTable (type , parentClassTable );
265+ List <HostedMethod > classTableMethods ;
237266 if (!classTableWithoutSuper .isEmpty ()) {
238- resultClassTableMethods = new ArrayList <>(parentClassTable );
239- resultClassTableMethods .addAll (classTableWithoutSuper );
267+ classTableMethods = new ArrayList <>(parentClassTable );
268+ classTableMethods .addAll (classTableWithoutSuper );
240269 } else {
241270 /*
242271 * If the type doesn't declare any new methods, then we can use the parent's class
243272 * table.
244273 */
245- resultClassTableMethods = parentClassTable ;
274+ classTableMethods = parentClassTable ;
246275 }
247- dispatchTablesMap .put (type , resultClassTableMethods );
276+ classTablesMap .put (type , classTableMethods );
248277
249278 if (!type .isAbstract ()) {
250279 // create concrete dispatch classes
251- List <HostedMethod > aggregatedTable = new ArrayList <>(resultClassTableMethods );
280+ List <HostedMethod > aggregatedTable = new ArrayList <>(classTableMethods );
252281 HostedType [] interfaces = type .typeCheckInterfaceOrder ;
253282 type .itableStartingOffsets = new int [interfaces .length ];
254- int currentITableOffset = resultClassTableMethods .size ();
283+ int currentITableOffset = classTableMethods .size ();
255284 for (int i = 0 ; i < interfaces .length ; i ++) {
256285 HostedType interfaceType = interfaces [i ];
257- List <HostedMethod > interfaceMethods = dispatchTablesMap .get (interfaceType );
286+ List <HostedMethod > interfaceMethods = classTablesMap .get (interfaceType );
258287
259288 type .itableStartingOffsets [i ] = currentITableOffset ;
260289 aggregatedTable .addAll (interfaceMethods );
261290 currentITableOffset += interfaceMethods .size ();
262291 }
263292 type .openTypeWorldDispatchTables = new HostedMethod [aggregatedTable .size ()];
264293 type .openTypeWorldDispatchTableSlotTargets = aggregatedTable .toArray (HostedMethod []::new );
294+
265295 boolean [] validTarget = new boolean [aggregatedTable .size ()];
296+ Set <HostedMethod > seenResolvedMethods = SubstrateUtil .assertionsEnabled () ? new HashSet <>() : null ;
266297 for (int i = 0 ; i < aggregatedTable .size (); i ++) {
267298 HostedMethod method = aggregatedTable .get (i );
268299 /*
@@ -275,6 +306,13 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
275306 if (resolvedMethod != null ) {
276307 targetMethod = resolvedMethod ;
277308 validTarget [i ] = true ;
309+ if (seenResolvedMethods != null && i < classTableMethods .size ()) {
310+ /*
311+ * Check that each resolved method within the class table is unique
312+ */
313+ var added = seenResolvedMethods .add (resolvedMethod );
314+ assert added : Assertions .errorMessage ("Multiple slots with same resolution method" , resolvedMethod );
315+ }
278316 }
279317 }
280318
@@ -289,7 +327,7 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
289327 assert !type .isInterface ();
290328 List <HostedMethod > sourceTable ;
291329 if (type .isAbstract ()) {
292- sourceTable = resultClassTableMethods ;
330+ sourceTable = classTableMethods ;
293331 } else {
294332 sourceTable = Arrays .asList (type .openTypeWorldDispatchTableSlotTargets );
295333 }
@@ -306,13 +344,16 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
306344
307345 for (HostedType subType : type .subTypes ) {
308346 if (subType instanceof HostedInstanceClass instanceClass && openHubUtils .shouldIncludeType (subType )) {
309- generateOpenTypeWorldDispatchTable (instanceClass , dispatchTablesMap , invalidDispatchTableEntryHandler );
347+ generateOpenTypeWorldDispatchTable (instanceClass , classTablesMap , invalidDispatchTableEntryHandler );
310348 }
311349 }
312350 }
313351
314352 private void buildOpenTypeWorldDispatchTables () {
315- Map <HostedType , List <HostedMethod >> dispatchTablesMap = new HashMap <>();
353+ /*
354+ * Map from type to class table (i.e. the type's vtable w/o any appended itables).
355+ */
356+ Map <HostedType , List <HostedMethod >> classTablesMap = new HashMap <>();
316357
317358 for (HostedType type : hUniverse .getTypes ()) {
318359 /*
@@ -321,7 +362,7 @@ private void buildOpenTypeWorldDispatchTables() {
321362 */
322363 if (type .isInterface () && openHubUtils .shouldIncludeType (type )) {
323364 List <HostedMethod > itable = generateITable (type );
324- dispatchTablesMap .put (type , itable );
365+ classTablesMap .put (type , itable );
325366 if (RuntimeClassLoading .isSupported ()) {
326367 type .cremaOpenTypeWorldDispatchTables = new HostedMethod [itable .size ()];
327368 for (int i = 0 ; i < itable .size (); i ++) {
@@ -332,7 +373,7 @@ private void buildOpenTypeWorldDispatchTables() {
332373 }
333374
334375 HostedMethod invalidDispatchTableEntryHandler = hMetaAccess .lookupJavaMethod (InvalidMethodPointerHandler .INVALID_VTABLE_ENTRY_HANDLER_METHOD );
335- generateOpenTypeWorldDispatchTable ((HostedInstanceClass ) hUniverse .objectType (), dispatchTablesMap , invalidDispatchTableEntryHandler );
376+ generateOpenTypeWorldDispatchTable ((HostedInstanceClass ) hUniverse .objectType (), classTablesMap , invalidDispatchTableEntryHandler );
336377
337378 int [] emptyITableOffsets = new int [0 ];
338379 var objectType = hUniverse .getObjectClass ();
0 commit comments