42
42
import com .oracle .svm .core .SubstrateUtil ;
43
43
import com .oracle .svm .core .hub .RuntimeClassLoading ;
44
44
import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
45
+ import com .oracle .svm .hosted .OpenTypeWorldFeature ;
45
46
import com .oracle .svm .hosted .imagelayer .LayeredDispatchTableFeature ;
46
47
47
48
import jdk .graal .compiler .debug .Assertions ;
@@ -182,15 +183,40 @@ private boolean verifyOpenTypeWorldDispatchTables() {
182
183
}
183
184
184
185
private List <HostedMethod > generateITable (HostedType type ) {
185
- return generateDispatchTable (type , 0 );
186
+ return generateDispatchTable (type , List . of () );
186
187
}
187
188
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 ) {
189
211
Predicate <HostedMethod > includeMethod ;
190
212
if (openHubUtils .filterVTableMethods (type )) {
191
213
// include only methods which will be indirect calls
192
214
includeMethod = m -> {
193
215
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
+ }
194
220
if (m .implementations .length > 1 ) {
195
221
return true ;
196
222
} else {
@@ -204,6 +230,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
204
230
} else {
205
231
includeMethod = m -> {
206
232
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
+ }
207
237
/*
208
238
* We have to use the analysis method's canBeStaticallyBound implementation because
209
239
* within HostedMethod we sometimes do additional pruning when operating under the
@@ -214,11 +244,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
214
244
}
215
245
var table = type .getWrapped ().getOpenTypeWorldDispatchTableMethods ().stream ().map (hUniverse ::lookup ).filter (includeMethod ).sorted (HostedUniverse .METHOD_COMPARATOR ).toList ();
216
246
217
- int index = startingIndex ;
247
+ int index = parentClassTable . size () ;
218
248
for (HostedMethod typeMethod : table ) {
219
249
assert typeMethod .getDeclaringClass ().equals (type ) : typeMethod ;
220
- assert typeMethod .computedVTableIndex == HostedMethod .MISSING_VTABLE_IDX : typeMethod .computedVTableIndex ;
221
- typeMethod .computedVTableIndex = index ;
250
+ installVTableIndex (typeMethod , index );
222
251
index ++;
223
252
}
224
253
@@ -229,40 +258,42 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
229
258
return table ;
230
259
}
231
260
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 ) {
233
262
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 ;
237
266
if (!classTableWithoutSuper .isEmpty ()) {
238
- resultClassTableMethods = new ArrayList <>(parentClassTable );
239
- resultClassTableMethods .addAll (classTableWithoutSuper );
267
+ classTableMethods = new ArrayList <>(parentClassTable );
268
+ classTableMethods .addAll (classTableWithoutSuper );
240
269
} else {
241
270
/*
242
271
* If the type doesn't declare any new methods, then we can use the parent's class
243
272
* table.
244
273
*/
245
- resultClassTableMethods = parentClassTable ;
274
+ classTableMethods = parentClassTable ;
246
275
}
247
- dispatchTablesMap .put (type , resultClassTableMethods );
276
+ classTablesMap .put (type , classTableMethods );
248
277
249
278
if (!type .isAbstract ()) {
250
279
// create concrete dispatch classes
251
- List <HostedMethod > aggregatedTable = new ArrayList <>(resultClassTableMethods );
280
+ List <HostedMethod > aggregatedTable = new ArrayList <>(classTableMethods );
252
281
HostedType [] interfaces = type .typeCheckInterfaceOrder ;
253
282
type .itableStartingOffsets = new int [interfaces .length ];
254
- int currentITableOffset = resultClassTableMethods .size ();
283
+ int currentITableOffset = classTableMethods .size ();
255
284
for (int i = 0 ; i < interfaces .length ; i ++) {
256
285
HostedType interfaceType = interfaces [i ];
257
- List <HostedMethod > interfaceMethods = dispatchTablesMap .get (interfaceType );
286
+ List <HostedMethod > interfaceMethods = classTablesMap .get (interfaceType );
258
287
259
288
type .itableStartingOffsets [i ] = currentITableOffset ;
260
289
aggregatedTable .addAll (interfaceMethods );
261
290
currentITableOffset += interfaceMethods .size ();
262
291
}
263
292
type .openTypeWorldDispatchTables = new HostedMethod [aggregatedTable .size ()];
264
293
type .openTypeWorldDispatchTableSlotTargets = aggregatedTable .toArray (HostedMethod []::new );
294
+
265
295
boolean [] validTarget = new boolean [aggregatedTable .size ()];
296
+ Set <HostedMethod > seenResolvedMethods = SubstrateUtil .assertionsEnabled () ? new HashSet <>() : null ;
266
297
for (int i = 0 ; i < aggregatedTable .size (); i ++) {
267
298
HostedMethod method = aggregatedTable .get (i );
268
299
/*
@@ -275,6 +306,13 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
275
306
if (resolvedMethod != null ) {
276
307
targetMethod = resolvedMethod ;
277
308
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
+ }
278
316
}
279
317
}
280
318
@@ -289,7 +327,7 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
289
327
assert !type .isInterface ();
290
328
List <HostedMethod > sourceTable ;
291
329
if (type .isAbstract ()) {
292
- sourceTable = resultClassTableMethods ;
330
+ sourceTable = classTableMethods ;
293
331
} else {
294
332
sourceTable = Arrays .asList (type .openTypeWorldDispatchTableSlotTargets );
295
333
}
@@ -306,13 +344,16 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
306
344
307
345
for (HostedType subType : type .subTypes ) {
308
346
if (subType instanceof HostedInstanceClass instanceClass && openHubUtils .shouldIncludeType (subType )) {
309
- generateOpenTypeWorldDispatchTable (instanceClass , dispatchTablesMap , invalidDispatchTableEntryHandler );
347
+ generateOpenTypeWorldDispatchTable (instanceClass , classTablesMap , invalidDispatchTableEntryHandler );
310
348
}
311
349
}
312
350
}
313
351
314
352
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 <>();
316
357
317
358
for (HostedType type : hUniverse .getTypes ()) {
318
359
/*
@@ -321,7 +362,7 @@ private void buildOpenTypeWorldDispatchTables() {
321
362
*/
322
363
if (type .isInterface () && openHubUtils .shouldIncludeType (type )) {
323
364
List <HostedMethod > itable = generateITable (type );
324
- dispatchTablesMap .put (type , itable );
365
+ classTablesMap .put (type , itable );
325
366
if (RuntimeClassLoading .isSupported ()) {
326
367
type .cremaOpenTypeWorldDispatchTables = new HostedMethod [itable .size ()];
327
368
for (int i = 0 ; i < itable .size (); i ++) {
@@ -332,7 +373,7 @@ private void buildOpenTypeWorldDispatchTables() {
332
373
}
333
374
334
375
HostedMethod invalidDispatchTableEntryHandler = hMetaAccess .lookupJavaMethod (InvalidMethodPointerHandler .INVALID_VTABLE_ENTRY_HANDLER_METHOD );
335
- generateOpenTypeWorldDispatchTable ((HostedInstanceClass ) hUniverse .objectType (), dispatchTablesMap , invalidDispatchTableEntryHandler );
376
+ generateOpenTypeWorldDispatchTable ((HostedInstanceClass ) hUniverse .objectType (), classTablesMap , invalidDispatchTableEntryHandler );
336
377
337
378
int [] emptyITableOffsets = new int [0 ];
338
379
var objectType = hUniverse .getObjectClass ();
0 commit comments