diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java index add0b97ac30f..4970170fda73 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java @@ -128,14 +128,11 @@ CodePointer getCallWrapperAddress() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) CodePointer getJavaCallAddress(Object instance, boolean nonVirtual) { if (!nonVirtual) { - if (SubstrateOptions.useClosedTypeWorldHubLayout()) { - assert vtableOffset != JNIAccessibleMethod.VTABLE_OFFSET_NOT_YET_COMPUTED; - if (vtableOffset != JNIAccessibleMethod.STATICALLY_BOUND_METHOD) { + assert vtableOffset != JNIAccessibleMethod.VTABLE_OFFSET_NOT_YET_COMPUTED; + if (vtableOffset != JNIAccessibleMethod.STATICALLY_BOUND_METHOD) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { return BarrieredAccess.readWord(instance.getClass(), vtableOffset, NamedLocationIdentity.FINAL_LOCATION); - } - } else { - assert vtableOffset != JNIAccessibleMethod.VTABLE_OFFSET_NOT_YET_COMPUTED; - if (vtableOffset != STATICALLY_BOUND_METHOD) { + } else { long tableStartingOffset = LoadOpenTypeWorldDispatchTableStartingOffset.createOpenTypeWorldLoadDispatchTableStartingOffset(instance.getClass(), interfaceTypeID); return BarrieredAccess.readWord(instance.getClass(), Word.pointer(tableStartingOffset + vtableOffset), NamedLocationIdentity.FINAL_LOCATION); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java index 5e2bd925b4ce..6189187e50fa 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java @@ -106,21 +106,13 @@ private CFunctionPointer invokeTarget(Object obj) { * In case we have both a vtableOffset and a directTarget, the vtable lookup wins. For such * methods, the directTarget is only used when doing an invokeSpecial. */ - if (SubstrateOptions.useClosedTypeWorldHubLayout()) { - CFunctionPointer target; - if (vtableOffset == OFFSET_NOT_YET_COMPUTED) { - throw VMError.shouldNotReachHere("Missed vtableOffset recomputation at image build time"); - } else if (vtableOffset != STATICALLY_BOUND) { + CFunctionPointer target; + if (vtableOffset == OFFSET_NOT_YET_COMPUTED) { + throw VMError.shouldNotReachHere("Missed vtableOffset recomputation at image build time"); + } else if (vtableOffset != STATICALLY_BOUND) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { target = BarrieredAccess.readWord(obj.getClass(), vtableOffset, NamedLocationIdentity.FINAL_LOCATION); } else { - target = directTarget; - } - return target; - } else { - CFunctionPointer target; - if (vtableOffset == OFFSET_NOT_YET_COMPUTED) { - throw VMError.shouldNotReachHere("Missed vtableOffset recomputation at image build time"); - } else if (vtableOffset != STATICALLY_BOUND) { long tableStartingOffset = LoadOpenTypeWorldDispatchTableStartingOffset.createOpenTypeWorldLoadDispatchTableStartingOffset(obj.getClass(), interfaceTypeID); /* @@ -129,11 +121,11 @@ private CFunctionPointer invokeTarget(Object obj) { long methodOffset = tableStartingOffset + vtableOffset; target = BarrieredAccess.readWord(obj.getClass(), Word.pointer(methodOffset), NamedLocationIdentity.FINAL_LOCATION); - } else { - target = directTarget; } - return target; + } else { + target = directTarget; } + return target; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java index ba9cba213e90..2e3b4448c0a4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java @@ -187,12 +187,25 @@ private List generateDispatchTable(HostedType type, int startingIn // include only methods which will be indirect calls includeMethod = m -> { assert !m.isConstructor() : Assertions.errorMessage("Constructors should never be in dispatch tables", m); - return m.implementations.length > 1 || m.wrapped.isVirtualRootMethod(); + if (m.implementations.length > 1) { + return true; + } else { + if (m.wrapped.isVirtualRootMethod()) { + return !m.canBeStaticallyBound(); + } else { + return false; + } + } }; } else { includeMethod = m -> { assert !m.isConstructor() : Assertions.errorMessage("Constructors should never be in dispatch tables", m); - return true; + /* + * We have to use the analysis method's canBeStaticallyBound implementation because + * within HostedMethod we sometimes do additional pruning when operating under the + * close type world assumption. + */ + return !m.getWrapped().canBeStaticallyBound(); }; } var table = type.getWrapped().getOpenTypeWorldDispatchTableMethods().stream().map(hUniverse::lookup).filter(includeMethod).sorted(HostedUniverse.METHOD_COMPARATOR).toList(); @@ -466,18 +479,30 @@ private void buildVTable(HostedClass clazz, Map> vtablesMap, Map usedSlotsMap, Map> vtablesSlots) { + /* + * Methods with 1 implementation do not need a vtable because invokes can be done as direct + * calls without the need for a vtable. Methods with 0 implementations are unreachable. + * + * However, virtual roots (even those with 0 implementations) that cannot be statically + * bound always need a vtable entry. This is because the vtable is used to invoke these + * methods via reflection and/or jni. + */ + Predicate vtableEntryRequired = (hMethod) -> { + if (hMethod.implementations.length > 1) { + return true; + } else { + if (hMethod.wrapped.isVirtualRootMethod()) { + return !hMethod.canBeStaticallyBound(); + } else { + return false; + } + } + }; + for (HostedMethod method : type.getAllDeclaredMethods()) { /* We only need to look at methods that the static analysis registered as invoked. */ if (method.wrapped.isInvoked() || method.wrapped.isImplementationInvoked()) { - /* - * Methods with 1 implementation do not need a vtable because invokes can be done as - * direct calls without the need for a vtable. Methods with 0 implementations are - * unreachable. - * - * Methods manually registered as virtual root methods always need a vtable slot, - * even if there are 0 or 1 implementations. - */ - if (method.implementations.length > 1 || method.wrapped.isVirtualRootMethod()) { + if (vtableEntryRequired.test(method)) { assert !method.isConstructor() : Assertions.errorMessage("Constructors should never be in vtables", method); /* * Find a suitable vtable slot for the method, taking the existing vtable diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index 01532dada7a8..cc2aa5513f10 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -67,7 +67,6 @@ import com.oracle.svm.core.hub.ClassForNameSupportFeature; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.MethodPointer; -import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; import com.oracle.svm.core.reflect.SubstrateAccessor; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; @@ -220,6 +219,9 @@ private SubstrateAccessor createAccessor(AccessorKey key) { if (!targetMethod.canBeStaticallyBound()) { vtableOffset = SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED; } + if (callerSensitiveAdapter) { + VMError.guarantee(vtableOffset == SubstrateMethodAccessor.STATICALLY_BOUND, "Caller sensitive adapters should always be statically bound %s", targetMethod); + } VMError.guarantee(directTarget != null || vtableOffset != SubstrateMethodAccessor.STATICALLY_BOUND, "Must have either a directTarget or a vtableOffset"); if (!targetMethod.isStatic()) { receiverType = target.getDeclaringClass(); @@ -485,7 +487,10 @@ public Object transform(Object receiver, Object originalValue) { SubstrateMethodAccessor accessor = (SubstrateMethodAccessor) receiver; if (accessor.getVTableOffset() == SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED) { - SharedMethod member = ImageSingletons.lookup(ReflectionFeature.class).hostedMetaAccess().lookupJavaMethod(accessor.getMember()); + HostedMethod member = ImageSingletons.lookup(ReflectionFeature.class).hostedMetaAccess().lookupJavaMethod(accessor.getMember()); + if (member.canBeStaticallyBound()) { + return SubstrateMethodAccessor.STATICALLY_BOUND; + } if (SubstrateOptions.useClosedTypeWorldHubLayout()) { return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex(), true); } else {