diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupMode.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupMode.java new file mode 100644 index 000000000000..c11ab6b177b1 --- /dev/null +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupMode.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.espresso.shared.lookup; + +import com.oracle.truffle.espresso.classfile.Constants; +import com.oracle.truffle.espresso.shared.meta.ModifiersProvider; + +/** + * Provides a simple way of filtering elements with {@linkplain ModifiersProvider modifiers} (such + * as classes, methods or fields), based on their declared flags. + */ +public final class LookupMode { + public static final LookupMode ALL = new LookupMode(0, 0); + public static final LookupMode INSTANCE_ONLY = new LookupMode(0, Constants.ACC_STATIC); + public static final LookupMode STATIC_ONLY = new LookupMode(Constants.ACC_STATIC, 0); + public static final LookupMode NON_STATIC_NON_PRIVATE = new LookupMode(0, Constants.ACC_STATIC | Constants.ACC_PRIVATE); + public static final LookupMode PUBLIC_NON_STATIC = new LookupMode(Constants.ACC_PUBLIC, Constants.ACC_STATIC); + + private final int positives; + private final int negatives; + + /** + * Creates a new element filter. + * + * @param positives The flags that need to be set + * @param negatives The flags that must not be set. + */ + public LookupMode(int positives, int negatives) { + assert (positives & negatives) == 0 : "Empty mode: +" + Integer.toHexString(positives) + " and -" + Integer.toHexString(negatives); + this.positives = positives; + this.negatives = negatives; + } + + /** + * Returns whether the given element is accepted by {@code this} filter. If {@code m} is + * {@code null}, then this method returns {@code false}. + */ + public boolean include(ModifiersProvider m) { + if (m == null) { + return false; + } + int flags = m.getModifiers(); + return hasPositives(flags) && hasNoNegatives(flags); + } + + /** + * Combines this filter with another. + *

+ * Calling the resulting {@link #include(ModifiersProvider e)} is equivalent to + * {@code this.include(e) && other.include(e)}. + */ + public LookupMode combine(LookupMode other) { + int newPositives = this.positives | other.negatives; + int newNegatives = this.negatives | other.negatives; + if (newPositives == positives && newNegatives == negatives) { + return this; + } + if (newPositives == other.positives && newNegatives == other.negatives) { + return other; + } + return new LookupMode(newPositives, newNegatives); + } + + private boolean hasPositives(int flags) { + return (flags & positives) == positives; + } + + private boolean hasNoNegatives(int flags) { + return (flags & negatives) == 0; + } +} diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupSuccessInvocationFailure.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupSuccessInvocationFailure.java new file mode 100644 index 000000000000..e1fb102a2cb9 --- /dev/null +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/LookupSuccessInvocationFailure.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.espresso.shared.lookup; + +import java.io.Serial; + +import com.oracle.truffle.espresso.shared.meta.MethodAccess; + +/** + * Checked exception thrown by the {@link MethodLookup} helper class when method lookup succeeds, + * but direct invocation of the resulting method should trigger + * {@link IncompatibleClassChangeError}. + *

+ * Use of a checked exceptions should ensure that the result is not accidentally passed along or + * invoked without consideration. + */ +public final class LookupSuccessInvocationFailure extends Exception { + @Serial private static final long serialVersionUID = 1794882806666028197L; + + LookupSuccessInvocationFailure(MethodAccess m) { + super(null, null); + this.m = m; + } + + private final transient MethodAccess m; + + /** + * Obtain the result of method lookup. + */ + @SuppressWarnings("unchecked") + public > M getResult() { + return (M) m; + } + + @SuppressWarnings("sync-override") + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/MethodLookup.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/MethodLookup.java new file mode 100644 index 000000000000..2d1856c12d84 --- /dev/null +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/lookup/MethodLookup.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.espresso.shared.lookup; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.oracle.truffle.espresso.classfile.ParserKlass; +import com.oracle.truffle.espresso.classfile.descriptors.Name; +import com.oracle.truffle.espresso.classfile.descriptors.ParserSymbols; +import com.oracle.truffle.espresso.classfile.descriptors.Signature; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol; +import com.oracle.truffle.espresso.shared.meta.MethodAccess; +import com.oracle.truffle.espresso.shared.meta.TypeAccess; +import com.oracle.truffle.espresso.shared.resolver.CallKind; + +/** + * This class provides implementations of method lookups as defined by the Java Virtual Machine + * Specifications. + *

+ * Some of these methods may throw a checked {@link LookupSuccessInvocationFailure}. This indicates + * that, while lookup succeeded, the result of that lookup is not applicable for a + * {@link CallKind#DIRECT direct} invocation, and should fail with + * {@link IncompatibleClassChangeError} if ever directly invoked. + */ +public final class MethodLookup { + // region public interface + + /** + * Lookup in the declared methods of {@code type} for a method with the specified {@code name} + * and {@code signature}. + */ + public static , M extends MethodAccess> M lookupDeclaredMethod(C type, Symbol name, Symbol signature, LookupMode lookup) { + return lookupDeclaredMethodImpl(type, name, signature, lookup); + } + + /** + * Lookup in the declared methods of {@code type} for a + * {@link MethodAccess#isDeclaredSignaturePolymorphic() signature polymorphic declared method} + * with the specified {@code name}. + */ + public static , M extends MethodAccess> M lookupDeclaredSignaturePolymorphicMethod(C type, Symbol name, LookupMode lookup) { + return lookupDeclaredSignaturePolymorphicMethodImpl(type, name, lookup); + } + + /** + * Performs {@code method lookup} on this class for the given method name and method signature, + * according to JVMS-5.4.3.3. + * + * @see TypeAccess#lookupMethod(Symbol, Symbol) + */ + public static , M extends MethodAccess> M lookupMethod(C type, Symbol name, Symbol signature, LookupMode lookup) + throws LookupSuccessInvocationFailure { + return lookupMethodImpl(type, name, signature, lookup); + } + + /** + * Performs {@code interface method lookup} on this class for the given method name and method + * signature, according to JVMS-5.4.3.4. + * + * @see TypeAccess#lookupInterfaceMethod(Symbol, Symbol) + */ + public static , M extends MethodAccess> M lookupInterfaceMethod(C type, Symbol name, Symbol signature) + throws LookupSuccessInvocationFailure { + return lookupInterfaceMethodImpl(type, name, signature); + } + + /** + * Returns the set {@code S} of {@code maximally-specific} methods from the given set. + *

+ * This means that, for every method {@code C.m} in {@code S}, there is no method from the + * original set {@code C'.m'} such that {@code C} is a super type of {@code C'}. + */ + public static , M extends MethodAccess> Set resolveMaximallySpecific(Iterable candidates) { + Set currentMaxs = new HashSet<>(); + for (M candidate : candidates) { + if (isLocalMax(candidate, currentMaxs)) { + currentMaxs.add(candidate); + } + } + return currentMaxs; + } + + // endregion public interface + + // region IMPL + + private MethodLookup() { + } + + private static , M extends MethodAccess> M lookupDeclaredMethodImpl(C type, Symbol name, Symbol signature, LookupMode lookup) { + M polymorphic = lookupDeclaredSignaturePolymorphicMethodImpl(type, name, lookup); + if (polymorphic != null) { + return polymorphic.findSignaturePolymorphicIntrinsic(signature); + } + return lookupInList(type.getDeclaredMethodsList(), name, signature, lookup); + } + + private static , M extends MethodAccess> M lookupInDeclaredAndImplicit(C type, Symbol name, Symbol signature, LookupMode lookup) { + // First, try to locate method in this type's declared methods. + M declared = lookupDeclaredMethodImpl(type, name, signature, lookup); + if (declared != null) { + return declared; + } + + // If the miranda methods are readily available, try to locate the method in it. + List mirandas = type.getImplicitInterfaceMethodsList(); + if (mirandas != null) { + M inMirandas = lookupInList(mirandas, name, signature, lookup); + if (inMirandas != null) { + return inMirandas; + } + } + return null; + } + + private static , M extends MethodAccess> M lookupMethodImpl(C type, Symbol name, Symbol signature, LookupMode lookup) + throws LookupSuccessInvocationFailure { + M declared = lookupInDeclaredAndImplicit(type, name, signature, lookup); + if (declared != null) { + return declared; + } + + // Delegate to super type. + C current = type.getSuperClass(); + while (current != null) { + M fromSuper = lookupInDeclaredAndImplicit(current, name, signature, lookup); + if (fromSuper != null) { + return fromSuper; + } + current = current.getSuperClass(); + } + + /* + * Note: In the case where the implicit interface methods are available for the entire super + * type hierarchy and the lookup is failing, this call is unnecessary. + * + * This should be fine as: + * + * - If in the hierarchy, even one type does not have its implicit interface methods + * available, then this class becomes necessary. + * + * - Most lookups usually succeed, and will not reach here. + */ + return lookupMethodInInterfaces(type, name, signature); + } + + private static , M extends MethodAccess> M lookupDeclaredSignaturePolymorphicMethodImpl(C type, Symbol name, LookupMode lookup) { + if (!ParserKlass.isSignaturePolymorphicHolderType(type.getSymbolicType())) { + return null; + } + for (M m : type.getDeclaredMethodsList()) { + if (lookup.include(m) && m.isDeclaredSignaturePolymorphic()) { + if (name == m.getSymbolicName()) { + return m; + } + } + } + return null; + } + + private static , M extends MethodAccess> M lookupInterfaceMethodImpl(C type, Symbol name, Symbol signature) + throws LookupSuccessInvocationFailure { + assert type.isInterface(); + // Simple case: This class declares the requested method + M declaredMethod = lookupDeclaredMethod(type, name, signature, LookupMode.ALL); + if (declaredMethod != null) { + return declaredMethod; + } + + // Look in the methods of java.lang.Object + C objectType = type.getSuperClass(); + assert objectType.getSymbolicType() == ParserSymbols.ParserTypes.java_lang_Object : "An interface should always declare j.l.Object as its super class."; + M objectMethod = lookupDeclaredMethod(objectType, name, signature, LookupMode.PUBLIC_NON_STATIC); + if (objectMethod != null) { + return objectMethod; + } + + // If miranda methods are immediately available, do a linear search. + List implicitMethods = type.getImplicitInterfaceMethodsList(); + if (implicitMethods != null) { + return lookupInList(implicitMethods, name, signature, LookupMode.ALL); + } else { + return lookupMethodInInterfaces(type, name, signature); + } + } + + private static , M extends MethodAccess> M lookupMethodInInterfaces(C type, Symbol name, Symbol signature) + throws LookupSuccessInvocationFailure { + // Do lookup in super interfaces + Set candidates = new HashSet<>(); + for (C superInterface : getTransitiveSuperInterfaces(type)) { + M candidate = lookupDeclaredMethodImpl(superInterface, name, signature, LookupMode.NON_STATIC_NON_PRIVATE); + if (candidate != null) { + candidates.add(candidate); + } + } + if (candidates.isEmpty()) { + return null; + } + if (candidates.size() == 1) { + return candidates.iterator().next(); + } + + // Search for the maximally-specific methods + Set maximallySpecific = resolveMaximallySpecific(candidates); + + if (maximallySpecific.size() == 1) { + return maximallySpecific.iterator().next(); + } + + // Search for a unique non-abstract maximally-specific method. + M uniqueNonAbstract = null; + for (M candidate : maximallySpecific) { + if (!candidate.isAbstract()) { + if (uniqueNonAbstract != null) { + // Not unique: chose one arbitrarily, and notify that direct invocation should + // fail. + throw new LookupSuccessInvocationFailure(candidate); + } + uniqueNonAbstract = candidate; + } + } + if (uniqueNonAbstract != null) { + return uniqueNonAbstract; + } + // Only abstract maximally-specific methods. Choose one arbitrarily. + M result = maximallySpecific.iterator().next(); + assert result.isAbstract(); + return result; + } + + /** + * Compares the declaring classes of both given methods, and returns the method whose declaring + * class is a sub-type of the other's, or {@code null} if the declaring classes are from + * unrelated hierarchies. + */ + private static , M extends MethodAccess> M mostSpecific(M m1, M m2) { + if (m2.getDeclaringClass().isAssignableFrom(m1.getDeclaringClass())) { + return m1; + } + if (m1.getDeclaringClass().isAssignableFrom(m2.getDeclaringClass())) { + return m2; + } + return null; + } + + /** + * This method compares the given {@code candidate} method with each method in the + * {@code currentMaxs} set to determine if it is a local maximum with respect to the set of + * current local maximums ({@code currentMaxs}): + *

+ */ + private static , M extends MethodAccess> boolean isLocalMax(M candidate, Set currentMaxs) { + Iterator maxIterator = currentMaxs.iterator(); + while (maxIterator.hasNext()) { + M currentMax = maxIterator.next(); + M mostSpecific = mostSpecific(candidate, currentMax); + if (mostSpecific == candidate) { + /* candidate's is a subtype of currentMax's: it is more specific. */ + maxIterator.remove(); + } else if (mostSpecific == currentMax) { + /* + * candidate's is a super-type of currentMax's: it is less specific and can be + * discarded. + */ + return false; + } else { + + /* + * If candidate and currentMax are unrelated, there is nothing to do but compare the + * candidate to the next current maximally-specific method. + */ + assert mostSpecific == null; + } + } + /* + * No method in the currentMaxs was more specific: Our candidate is therefore a new + * maximally-specific method (wrt the current position in the candidates set). + */ + return true; + } + + private static > Set getTransitiveSuperInterfaces(C type) { + HashSet result = new HashSet<>(); + C current = type; + while (current != null) { + for (C interfaceClass : current.getSuperInterfacesList()) { + collectInterfaces(interfaceClass, result); + } + current = current.getSuperClass(); + } + return result; + } + + private static > void collectInterfaces(C interfaceClass, HashSet result) { + if (result.add(interfaceClass)) { + for (C superInterface : interfaceClass.getSuperInterfacesList()) { + collectInterfaces(superInterface, result); + } + } + } + + private static > M lookupInList(List methods, Symbol name, Symbol signature, LookupMode lookup) { + for (M m : methods) { + if (lookup.include(m)) { + if (name == m.getSymbolicName() && signature == m.getSymbolicSignature()) { + return m; + } + } + } + return null; + } + // endregion IMPL +} diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/MethodAccess.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/MethodAccess.java index 9d8ee8a0c0cf..42d799a3c058 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/MethodAccess.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/MethodAccess.java @@ -24,7 +24,10 @@ */ package com.oracle.truffle.espresso.shared.meta; +import com.oracle.truffle.espresso.classfile.ClassfileParser; +import com.oracle.truffle.espresso.classfile.Constants; import com.oracle.truffle.espresso.classfile.ExceptionHandler; +import com.oracle.truffle.espresso.classfile.ParserMethod; import com.oracle.truffle.espresso.classfile.attributes.CodeAttribute; import com.oracle.truffle.espresso.classfile.descriptors.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; @@ -79,9 +82,26 @@ default Symbol[] getParsedSymbolicSignature(SymbolPool symbolPool) { *

* Note that this may return false for instantiations of such signature polymorphic method * returned by {@link #createSignaturePolymorphicIntrinsic(Symbol)}. + * + * @implNote If this method was derived from the result of {@link ClassfileParser}, then this + * can simply be implemented by checking that the + * {@link Constants#ACC_SIGNATURE_POLYMORPHIC signature polymorphic} flag is set in + * the {@link ParserMethod#getFlags() parser flags}. */ boolean isDeclaredSignaturePolymorphic(); + /** + * Tries to locate an instantiation of this {@linkplain #isDeclaredSignaturePolymorphic() + * signature polymorphic declared method} for the given {@code signature}, or creates one if not + * found. + * + * @implNote This method can be implemented by using the helper method + * {@link MethodHandleIntrinsics#findIntrinsic(MethodAccess, Symbol, RuntimeAccess)}. + * Doing so requires a valid implementation for + * {@link #createSignaturePolymorphicIntrinsic(Symbol)} + */ + M findSignaturePolymorphicIntrinsic(Symbol signature); + /** * Instantiates a {@linkplain #isDeclaredSignaturePolymorphic() signature polymorphic} method * for a specific signature. diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/TypeAccess.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/TypeAccess.java index 743ecd3969f9..19a3df598628 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/TypeAccess.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/meta/TypeAccess.java @@ -24,12 +24,20 @@ */ package com.oracle.truffle.espresso.shared.meta; +import java.util.List; + import com.oracle.truffle.espresso.classfile.ConstantPool; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Type; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; +import com.oracle.truffle.espresso.shared.lookup.LookupSuccessInvocationFailure; +import com.oracle.truffle.espresso.shared.lookup.MethodLookup; import com.oracle.truffle.espresso.shared.resolver.LinkResolver; +import com.oracle.truffle.espresso.shared.vtable.PartialType; +import com.oracle.truffle.espresso.shared.vtable.Tables; +import com.oracle.truffle.espresso.shared.vtable.VTable; /** * Represents a {@link Class}, and provides access to various lookups and runtime metadata. @@ -64,6 +72,11 @@ public interface TypeAccess, M extends MethodAcces */ C getSuperClass(); + /** + * Returns the direct super interfaces of this class. + */ + List getSuperInterfacesList(); + /** * Returns the host class of this VM-anonymous class, or {@code null} if this class is not a * VM-anonymous class. @@ -99,6 +112,79 @@ public interface TypeAccess, M extends MethodAcces */ F lookupField(Symbol name, Symbol type); + /** + * Returns a list reflecting all the methods and constructors declared by this type. + */ + List getDeclaredMethodsList(); + + /** + * Returns a list containing all the interface methods that can be called on this type, that are + * not declared by this type or its super types. + *

+ * Such methods are sometimes called {@code miranda methods}. + *

+ * This method may return {@code null} if obtaining these methods is not trivial. + * + * @see Tables#getImplicitInterfaceMethods() + * @implNote If this type's VTable was created using + * {@link VTable#create(PartialType, boolean, boolean, boolean)}, this method can + * simply return the result of {@link Tables#getImplicitInterfaceMethods()} + */ + List getImplicitInterfaceMethodsList(); + + /** + * Returns the {@link MethodAccess method} appearing in this type's virtual table at index + * {@code vtableIndex}. + *

+ * If {@code vtableIndex} is not within bounds of this type's virtual table length, this method + * should return {@code null}. + */ + M lookupVTableEntry(int vtableIndex); + + /** + * @return {@code true} if {@code other} is a subtype of {@code this}, {@code false} otherwise. + */ + boolean isAssignableFrom(C other); + + /** + * @return {@code true} if this class represents the {@link Object} class, {@code false} + * otherwise. + */ + default boolean isJavaLangObject() { + return getSuperClass() == null; + } + + /** + * Whether this class extends the "magic accessor". + */ + boolean isMagicAccessor(); + + /** + * The {@link ConstantPool} associated with this class. + */ + ConstantPool getConstantPool(); + + /** + * Resolves a class in the runtime constant pool of this type, then returns it. Further calls to + * this method with the same cpi should not trigger class loading. Resolution errors should not + * be saved in the constant pool. + * + * @param cpi The constant pool index in which to find the class constant + * @throws IllegalArgumentException If there is no + * {@link com.oracle.truffle.espresso.classfile.ConstantPool.Tag#CLASS} in the + * constant pool at index {@code cpi}. + */ + C resolveClassConstantInPool(int cpi); + + /** + * If this type declares a method with the given method {@code name} and {@code signature}, + * returns that method. Returns {@code null} otherwise. + */ + @SuppressWarnings("unchecked") + default M lookupDeclaredMethod(Symbol name, Symbol signature) { + return MethodLookup.lookupDeclaredMethod((C) this, name, signature, LookupMode.ALL); + } + /** * Performs method lookup on this class for the given method name and method signature, * according to JVMS-5.4.3.3. @@ -126,8 +212,8 @@ public interface TypeAccess, M extends MethodAcces *

    *
  • If the maximally-specific superinterface methods of {@link TypeAccess C} for the name and * signature specified by the method reference include exactly one method that is not - * {@link ModifiersProvider#isAbstract() abstract} , then this method is chosen and method - * lookup succeeds.
  • + * {@link ModifiersProvider#isAbstract() abstract}, then this method is chosen and method lookup + * succeeds. *
  • Otherwise, if any superinterface of {@link TypeAccess C} declares a method with the name * and signature specified that is neither {@link ModifiersProvider#isPrivate() private} nor * {@link ModifiersProvider#isStatic() static}, one of these is arbitrarily chosen and method @@ -136,17 +222,23 @@ public interface TypeAccess, M extends MethodAcces *
* */ - M lookupMethod(Symbol name, Symbol signature); + @SuppressWarnings("unchecked") + default M lookupMethod(Symbol name, Symbol signature) throws LookupSuccessInvocationFailure { + return MethodLookup.lookupMethod((C) this, name, signature, LookupMode.ALL); + } /** * Same as {@link #lookupMethod(Symbol, Symbol)}, but ignores * {@link ModifiersProvider#isStatic() static} methods. */ - M lookupInstanceMethod(Symbol name, Symbol signature); + @SuppressWarnings("unchecked") + default M lookupInstanceMethod(Symbol name, Symbol signature) throws LookupSuccessInvocationFailure { + return MethodLookup.lookupMethod((C) this, name, signature, LookupMode.INSTANCE_ONLY); + } /** * Performs interface method lookup on this class for the given method name and method - * signature, according to JVMS-5.4.3.3. + * signature, according to JVMS-5.4.3.4. *

*

    *
  • This lookup does not need to throw {@link ErrorType#IncompatibleClassChangeError} if this @@ -166,55 +258,17 @@ public interface TypeAccess, M extends MethodAcces *
  • Otherwise, method lookup fails and returns {@code null}.
  • *
*/ - M lookupInterfaceMethod(Symbol name, Symbol signature); - - /** - * Returns the {@link MethodAccess method} appearing in this type's virtual table at index - * {@code vtableIndex}. - *

- * If {@code vtableIndex} is not within bounds of this type's virtual table length, this method - * should return {@code null}. - */ - M lookupVTableEntry(int vtableIndex); + @SuppressWarnings("unchecked") + default M lookupInterfaceMethod(Symbol name, Symbol signature) throws LookupSuccessInvocationFailure { + return MethodLookup.lookupInterfaceMethod((C) this, name, signature); + } /** * Attempts to find a signature polymorphic method declared in this class as described in * JVMS-5.4.3.3 & 2.9.3. If no such method is found, returns null. */ - M lookupDeclaredSignaturePolymorphicMethod(Symbol name); - - /** - * @return {@code true} if {@code other} is a subtype of {@code this}, {@code false} otherwise. - */ - boolean isAssignableFrom(C other); - - /** - * @return {@code true} if this class represents the {@link Object} class, {@code false} - * otherwise. - */ - default boolean isJavaLangObject() { - return getSuperClass() == null; + @SuppressWarnings("unchecked") + default M lookupDeclaredSignaturePolymorphicMethod(Symbol name) { + return MethodLookup.lookupDeclaredSignaturePolymorphicMethod((C) this, name, LookupMode.ALL); } - - /** - * Whether this class extends the "magic accessor". - */ - boolean isMagicAccessor(); - - /** - * The {@link ConstantPool} associated with this class. - */ - ConstantPool getConstantPool(); - - /** - * Resolves a class in the runtime constant pool of this type, then returns it. Further calls to - * this method with the same cpi should not trigger class loading. Resolution errors should not - * be saved in the constant pool. - * - * @param cpi The constant pool index in which to find the class constant - * @throws IllegalArgumentException If there is no - * {@link com.oracle.truffle.espresso.classfile.ConstantPool.Tag#CLASS} in the - * constant pool at index {@code cpi}. - */ - C resolveClassConstantInPool(int cpi); } diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/resolver/LinkResolver.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/resolver/LinkResolver.java index f9397d2d6832..f477ae2825bc 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/resolver/LinkResolver.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/resolver/LinkResolver.java @@ -28,6 +28,7 @@ import com.oracle.truffle.espresso.classfile.descriptors.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Type; +import com.oracle.truffle.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.truffle.espresso.shared.meta.ErrorType; import com.oracle.truffle.espresso.shared.meta.FieldAccess; import com.oracle.truffle.espresso.shared.meta.MethodAccess; @@ -171,7 +172,7 @@ public static , C extends TypeAccess, R runtime, C accessingKlass, Symbol name, Symbol signature, C symbolicHolder, boolean interfaceLookup, - boolean accessCheck, boolean loadingConstraints) { + boolean accessCheck, boolean loadingConstraints) throws LookupSuccessInvocationFailure { return resolveMethodSymbolImpl(runtime, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints, true); } @@ -196,9 +197,11 @@ public static , C extends TypeAccess, R runtime, C accessingKlass, Symbol name, Symbol signature, C symbolicHolder, boolean interfaceLookup, - boolean accessCheck, boolean loadingConstraints) { + boolean accessCheck, boolean loadingConstraints) throws LookupSuccessInvocationFailure { try { return resolveMethodSymbolImpl(runtime, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints, false); + } catch (LookupSuccessInvocationFailure e) { + throw e; } catch (Throwable e) { if (runtime.getErrorType(e) != null) { throw runtime.fatal(e, "No exception was expected"); @@ -391,7 +394,7 @@ private static , C extends TypeAccess, private static , C extends TypeAccess, M extends MethodAccess, F extends FieldAccess> M resolveMethodSymbolImpl(R runtime, C accessingKlass, Symbol name, Symbol signature, C symbolicHolder, - boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints, boolean throwExceptions) { + boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints, boolean throwExceptions) throws LookupSuccessInvocationFailure { M resolved; if (interfaceLookup != symbolicHolder.isInterface()) { if (throwExceptions) { @@ -552,7 +555,18 @@ private static , C extends TypeAccess, currentKlass.getSuperClass() != null && symbolicHolder != currentKlass.getSuperClass() && symbolicHolder.isAssignableFrom(currentKlass)) { - resolved = currentKlass.getSuperClass().lookupInstanceMethod(resolved.getSymbolicName(), resolved.getSymbolicSignature()); + try { + resolved = currentKlass.getSuperClass().lookupInstanceMethod(resolved.getSymbolicName(), resolved.getSymbolicSignature()); + } catch (LookupSuccessInvocationFailure e) { + if (throwExceptions) { + throw runtime.throwError(ErrorType.IncompatibleClassChangeError, + "Unable to find a unique non-abstract maximally-specific instance method for an INVOKESPECIAL call-site: '%s.%s%s'", + resolved.getDeclaringClass().getJavaName(), + resolved.getSymbolicName(), + resolved.getSymbolicSignature()); + } + return null; + } } } callKind = CallKind.DIRECT; diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/verifier/MethodVerifier.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/verifier/MethodVerifier.java index 7c3a9051d0af..6678849f9614 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/verifier/MethodVerifier.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/verifier/MethodVerifier.java @@ -267,6 +267,7 @@ import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols; import com.oracle.truffle.espresso.classfile.descriptors.Validation; import com.oracle.truffle.espresso.classfile.descriptors.ValidationException; +import com.oracle.truffle.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.truffle.espresso.shared.meta.FieldAccess; import com.oracle.truffle.espresso.shared.meta.KnownTypes; import com.oracle.truffle.espresso.shared.meta.MemberAccess; @@ -2188,7 +2189,6 @@ private boolean checkHostAccess(Operand methodHolder) { } // Helper methods - private void checkProtectedMember(Operand stackOp, Symbol holderType, int memberIndex, boolean method) { /* * 4.10.1.8. @@ -2214,7 +2214,12 @@ private void checkProtectedMember(Operand stackOp, Symbol hold /* Non-failing method lookup. */ Symbol name = pool.methodName(memberIndex); Symbol methodSignature = pool.methodSignature(memberIndex); - member = holderOp.getKlass(this).lookupMethod(name, methodSignature); + try { + member = holderOp.getKlass(this).lookupMethod(name, methodSignature); + } catch (LookupSuccessInvocationFailure e) { + // We are not planning to invoke, so we can safely ignore that hint. + member = e. getResult(); + } } else { /* Non-failing field lookup. */ Symbol fieldName = pool.fieldName(memberIndex); diff --git a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/vtable/VTable.java b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/vtable/VTable.java index cd95b89572be..79f0bd4c7d33 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/vtable/VTable.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/vtable/VTable.java @@ -24,19 +24,20 @@ */ package com.oracle.truffle.espresso.shared.vtable; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; +import java.util.Set; import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Equivalence; import org.graalvm.collections.MapCursor; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; +import com.oracle.truffle.espresso.shared.lookup.MethodLookup; import com.oracle.truffle.espresso.shared.meta.FieldAccess; import com.oracle.truffle.espresso.shared.meta.MethodAccess; import com.oracle.truffle.espresso.shared.meta.TypeAccess; @@ -412,32 +413,19 @@ private PartialMethod resolveImpl(MethodKey k, Builder b, bool private PartialMethod resolveConcrete() { M candidate = null; for (Location loc : vLocations) { - candidate = mostSpecific(loc.value, candidate, true); + if (candidate == null) { + candidate = loc.value; + } else { + // Here, the classes are known to be from the same hierarchy, so a single + // type check is needed. + candidate = loc.value.getDeclaringClass().isAssignableFrom(candidate.getDeclaringClass()) ? candidate : loc.value; + } } return candidate; } private PartialMethod resolveMaximallySpecific() { - EconomicSet maximallySpecific = EconomicSet.create(Equivalence.IDENTITY); - locationLoop: // - for (Location loc : iLocations) { - Iterator iter = maximallySpecific.iterator(); - while (iter.hasNext()) { - M next = iter.next(); - M mostSpecific = mostSpecific(loc.value, next, false); - if (mostSpecific == next) { - // An existing declaring class was already more specific. - continue locationLoop; - } else if (mostSpecific == loc.value) { - // The current declaring class is more specific: replace. - iter.remove(); - } else { - assert mostSpecific == null; - // The declaring classes are unrelated - } - } - maximallySpecific.add(loc.value); - } + Set maximallySpecific = MethodLookup.resolveMaximallySpecific(new IMethodsList()); M nonAbstractMaximallySpecific = null; for (M m : maximallySpecific) { if (!m.isAbstract()) { @@ -463,24 +451,6 @@ private PartialMethod resolveMaximallySpecific() { return maximallySpecific.iterator().next(); } - private M mostSpecific(M m1, M m2, boolean totalOrder) { - assert m1 != null; - if (m2 == null) { - return m1; - } - if (m2.getDeclaringClass().isAssignableFrom(m1.getDeclaringClass())) { - return m1; - } - if (totalOrder) { - assert m1.getDeclaringClass().isAssignableFrom(m2.getDeclaringClass()); - return m2; - } - if (m1.getDeclaringClass().isAssignableFrom(m2.getDeclaringClass())) { - return m2; - } - return null; - } - private class Location implements Comparable { private final M value; private final int index; @@ -495,6 +465,18 @@ public int compareTo(Location o) { return Integer.compare(this.index, o.index); } } + + private final class IMethodsList extends AbstractList { + @Override + public M get(int index) { + return iLocations.get(index).value; + } + + @Override + public int size() { + return iLocations.size(); + } + } } private enum LocationKind { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/cds/Writer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/cds/Writer.java index b6e595fbdd04..087338f056b1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/cds/Writer.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/cds/Writer.java @@ -43,7 +43,6 @@ import com.oracle.truffle.espresso.impl.ClassRegistry; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Klass.LookupMode; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ModuleTable; import com.oracle.truffle.espresso.impl.PackageTable; @@ -52,6 +51,7 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.OS; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.vm.InterpreterToVM; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java index c9bd500968aa..9a235a634642 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ArrayKlass.java @@ -130,9 +130,9 @@ public Field[] getDeclaredFields() { } @Override - public Method lookupMethod(Symbol methodName, Symbol signature, LookupMode mode) { + public Method lookupMethod(Symbol methodName, Symbol signature) { KLASS_LOOKUP_METHOD_COUNT.inc(); - return getSuperKlass().lookupMethod(methodName, signature, mode); + return getSuperKlass().lookupMethod(methodName, signature); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/EspressoMethodTableBuilder.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/EspressoMethodTableBuilder.java index b23695a5cd36..fa74676cc48e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/EspressoMethodTableBuilder.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/EspressoMethodTableBuilder.java @@ -189,7 +189,7 @@ private static Method.MethodVersion[] toEspressoVTable(List m : table) { Method entry = m.asMethodAccess(); if (m.isSelectionFailure()) { - entry = new Method(entry).setPoisonPill(); + entry = entry.forFailing(); } itable[itableIndex] = entry.getMethodVersion(); itableIndex++; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index fc934f7681eb..bd2a83288ef2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -26,7 +26,10 @@ import static com.oracle.truffle.espresso.vm.InterpreterToVM.instanceOf; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.function.IntFunction; import org.graalvm.collections.EconomicSet; @@ -103,7 +106,8 @@ import com.oracle.truffle.espresso.runtime.dispatch.staticobject.InteropLookupAndInvoke; import com.oracle.truffle.espresso.runtime.dispatch.staticobject.InteropLookupAndInvokeFactory; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; -import com.oracle.truffle.espresso.shared.meta.SignaturePolymorphicIntrinsic; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; +import com.oracle.truffle.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.truffle.espresso.shared.meta.TypeAccess; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.vm.InterpreterToVM; @@ -1366,30 +1370,6 @@ public StaticObject allocateReferenceArray(int length, IntFunction // region Lookup - public enum LookupMode { - ALL(true, true), - INSTANCE_ONLY(true, false), - STATIC_ONLY(false, true); - - private final boolean instances; - private final boolean statics; - - LookupMode(boolean instances, boolean statics) { - this.instances = instances; - this.statics = statics; - } - - public boolean include(Member m) { - if (m == null) { - return false; - } - if (statics && m.isStatic()) { - return true; - } - return instances && !m.isStatic(); - } - } - public final Field requireDeclaredField(Symbol fieldName, Symbol fieldType) { Field obj = lookupDeclaredField(fieldName, fieldType); if (obj == null) { @@ -1487,20 +1467,10 @@ public final Method requireDeclaredMethod(Symbol methodName, Symbol methodName, Symbol signature) { - return lookupDeclaredMethod(methodName, signature, LookupMode.ALL); - } - - @ExplodeLoop public final Method lookupDeclaredMethod(Symbol methodName, Symbol signature, LookupMode lookupMode) { - KLASS_LOOKUP_DECLARED_METHOD_COUNT.inc(); - // TODO(peterssen): Improve lookup performance. - for (Method method : getDeclaredMethods()) { - if (lookupMode.include(method)) { - if (methodName.equals(method.getName()) && signature.equals(method.getRawSignature())) { - return method; - } - } + Method result = lookupDeclaredMethod(methodName, signature); + if (lookupMode.include(result)) { + return result; } return null; } @@ -1555,17 +1525,6 @@ protected static int fastLookupImpl(Klass target, ObjectKlass.KlassVersion[] kla return -1; // not found } - /** - * Give the accessing klass if there is a chance the method to be resolved is a method handle - * intrinsics. - */ - public abstract Method lookupMethod(Symbol methodName, Symbol signature, LookupMode lookupMode); - - @Override - public final Method lookupMethod(Symbol methodName, Symbol signature) { - return lookupMethod(methodName, signature, LookupMode.ALL); - } - public final Method vtableLookup(int vtableIndex) { if (this instanceof ObjectKlass) { return ((ObjectKlass) this).vtableLookupImpl(vtableIndex); @@ -1578,41 +1537,6 @@ public final Method vtableLookup(int vtableIndex) { return null; } - public Method lookupSignaturePolymorphicMethod(Symbol methodName, Symbol signature, LookupMode lookupMode) { - Method m = lookupSignaturePolymorphicDeclaredMethod(methodName, lookupMode); - if (m != null) { - return findMethodHandleIntrinsic(m, signature); - } - return null; - } - - public Method lookupSignaturePolymorphicDeclaredMethod(Symbol methodName, LookupMode lookupMode) { - for (Method m : getDeclaredMethods()) { - if (lookupMode.include(m)) { - if (m.getName() == methodName && m.isDeclaredSignaturePolymorphic()) { - return m; - } - } - } - return null; - } - - @Override - public Method lookupDeclaredSignaturePolymorphicMethod(Symbol methodName) { - return lookupSignaturePolymorphicDeclaredMethod(methodName, LookupMode.ALL); - } - - @TruffleBoundary - private Method findMethodHandleIntrinsic(Method m, Symbol signature) { - assert m.isDeclaredSignaturePolymorphic(); - SignaturePolymorphicIntrinsic iid = SignaturePolymorphicIntrinsic.getId(m); - Symbol sig = signature; - if (iid.isStaticSignaturePolymorphic()) { - sig = getSignatures().toBasic(signature, true); - } - return m.findIntrinsic(sig); - } - /** * Returns the access flags provided by the .class file, e.g. ignores inner class access flags. */ @@ -1884,16 +1808,70 @@ public final Klass getSuperClass() { } @Override - public final Method lookupInterfaceMethod(Symbol methodName, Symbol methodSignature) { + public List getSuperInterfacesList() { + return Arrays.asList(getSuperInterfaces()); + } + + @Override + public List getDeclaredMethodsList() { + return Method.versionsToMethodList(this.getDeclaredMethodVersions()); + } + + @Override + public List getImplicitInterfaceMethodsList() { + if (isInterface()) { + return null; + } if (this instanceof ObjectKlass) { - return ((ObjectKlass) this).resolveInterfaceMethod(methodName, methodSignature); + return Method.versionsToMethodList(((ObjectKlass) this).getMirandaMethods()); } - return null; + return Collections.emptyList(); + } + + @TruffleBoundary + public final Method lookupDeclaredMethod(Symbol methodName, Symbol signature) { + KLASS_LOOKUP_DECLARED_METHOD_COUNT.inc(); + return TypeAccess.super.lookupDeclaredMethod(methodName, signature); } @Override + @TruffleBoundary + public Method lookupDeclaredSignaturePolymorphicMethod(Symbol methodName) { + KLASS_LOOKUP_DECLARED_METHOD_COUNT.inc(); + return TypeAccess.super.lookupDeclaredSignaturePolymorphicMethod(methodName); + } + + @Override + @TruffleBoundary + public Method lookupMethod(Symbol methodName, Symbol signature) { + try { + KLASS_LOOKUP_METHOD_COUNT.inc(); + return TypeAccess.super.lookupMethod(methodName, signature); + } catch (LookupSuccessInvocationFailure e) { + return e. getResult().forFailing(); + } + } + + @Override + @TruffleBoundary public final Method lookupInstanceMethod(Symbol methodName, Symbol methodSignature) { - return lookupMethod(methodName, methodSignature, LookupMode.INSTANCE_ONLY); + try { + KLASS_LOOKUP_METHOD_COUNT.inc(); + return TypeAccess.super.lookupInstanceMethod(methodName, methodSignature); + } catch (LookupSuccessInvocationFailure e) { + return e. getResult().forFailing(); + } + } + + @Override + @TruffleBoundary + public final Method lookupInterfaceMethod(Symbol methodName, Symbol methodSignature) { + try { + KLASS_LOOKUP_METHOD_COUNT.inc(); + return TypeAccess.super.lookupInterfaceMethod(methodName, methodSignature); + } catch (LookupSuccessInvocationFailure e) { + return e. getResult().forFailing(); + } } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java index d24a13026af2..815ba663b80f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java @@ -54,8 +54,10 @@ import java.io.PrintStream; import java.lang.invoke.VarHandle; import java.lang.reflect.Modifier; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -135,6 +137,7 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.shared.meta.ErrorType; import com.oracle.truffle.espresso.shared.meta.MethodAccess; import com.oracle.truffle.espresso.shared.meta.ModifiersProvider; @@ -228,6 +231,23 @@ private Method(Method method, CodeAttribute split) { this.isLeaf = getContext().getClassHierarchyOracle().createLeafAssumptionForNewMethod(this); } + public static List versionsToMethodList(Method.MethodVersion[] versions) { + if (versions == null) { + return Collections.emptyList(); + } + return new AbstractList<>() { + @Override + public Method get(int index) { + return versions[index].getMethod(); + } + + @Override + public int size() { + return versions.length; + } + }; + } + public Method identity() { return proxy; } @@ -753,7 +773,7 @@ public static Method getHostReflectiveConstructorRoot(StaticObject seed, Meta me Klass holder = meta.java_lang_reflect_Constructor_clazz.getObject(rootMethod).getMirrorKlass(meta); Symbol signature = rebuildConstructorSignature(meta, rootMethod); assert signature != null; - Method method = holder.lookupDeclaredMethod(Names._init_, signature, Klass.LookupMode.INSTANCE_ONLY); + Method method = holder.lookupDeclaredMethod(Names._init_, signature, LookupMode.INSTANCE_ONLY); assert method != null; // remember the mapping for the next query meta.HIDDEN_CONSTRUCTOR_KEY.setHiddenObject(rootMethod, method); @@ -791,23 +811,10 @@ public static ByteSequence getSignatureFromGuestDescription(@JavaType(Class[].cl // Polymorphic signature method 'creation' - Method findIntrinsic(Symbol signature) { - return getContext().getMethodHandleIntrinsics().findIntrinsic(this, signature, getContext()); - } - - @Override - public boolean isDeclaredSignaturePolymorphic() { - return (getModifiers() & ACC_SIGNATURE_POLYMORPHIC) != 0; - } - public int getVTableIndex() { return getMethodVersion().getVTableIndex(); } - void setITableIndex(int i) { - getMethodVersion().setITableIndex(i); - } - public int getITableIndex() { return getMethodVersion().getITableIndex(); } @@ -820,9 +827,19 @@ public boolean isVirtualCall() { return !isStatic() && !isConstructor() && !isPrivate() && !getDeclaringKlass().isInterface(); } - public Method setPoisonPill() { - getMethodVersion().setPoisonPill(); - return this; + public Method forFailing() { + if (isProxy()) { + getMethodVersion().setPoisonPill(); + return this; + } + Method m = new Method(this); + m.getMethodVersion().setPoisonPill(); + if (this.hasVTableIndex()) { + m.getMethodVersion().setVTableIndex(this.getVTableIndex()); + } else { + m.getMethodVersion().setITableIndex(this.getITableIndex()); + } + return m; } public String report(int curBCI) { @@ -1179,45 +1196,6 @@ public StaticObject apply(int j) { return instance; } - /** - * Returns the maximally specific method between the two given methods. If they are both - * maximally-specific, returns a proxy of the second, to which a poison pill has been set. - *

- * Determining maximally specific method works as follow: - *

  • If both methods are abstract, return any of the two. - *
  • If exactly one is non-abstract, return it. - *
  • If both are non-abstract, check if one of the declaring class subclasses the other. If - * that is the case, return the method that is lower in the hierarchy. Otherwise, return a - * freshly spawned proxy method pointing to either of them, which is set to fail on invocation. - */ - public static MethodVersion resolveMaximallySpecific(Method m1, Method m2) { - ObjectKlass k1 = m1.getDeclaringKlass(); - ObjectKlass k2 = m2.getDeclaringKlass(); - if (k1.isAssignableFrom(k2)) { - return m2.getMethodVersion(); - } else if (k2.isAssignableFrom(k1)) { - return m1.getMethodVersion(); - } else { - boolean b1 = m1.isAbstract(); - boolean b2 = m2.isAbstract(); - if (b1 && b2) { - return m1.getMethodVersion(); - } - if (b1) { - return m2.getMethodVersion(); - } - if (b2) { - return m1.getMethodVersion(); - } - // JVM specs: - // Can *declare* ambiguous default method (in bytecodes only, javac wouldn't compile - // it). (5.4.3.3.) - // - // But if you try to *use* them, specs dictate to fail. (6.5.invoke{virtual,interface}) - return new Method(m2).setPoisonPill().getMethodVersion(); - } - } - // region MethodAccess impl @Override @@ -1237,6 +1215,11 @@ public boolean isAbstract() { return super.isAbstract(); } + @Override + public boolean isDeclaredSignaturePolymorphic() { + return ((rawFlags & ACC_SIGNATURE_POLYMORPHIC)) != 0; + } + @Override public boolean shouldSkipLoadingConstraints() { return isPolySignatureIntrinsic(); @@ -1274,6 +1257,12 @@ public boolean hasPoisonPill() { return getMethodVersion().poisonPill; } + @Override + @TruffleBoundary + public Method findSignaturePolymorphicIntrinsic(Symbol signature) { + return getContext().getMethodHandleIntrinsics().findIntrinsic(this, signature, getContext()); + } + // endregion MethodAccess impl // region MethodRef impl @@ -1766,9 +1755,8 @@ public int getITableIndex() { return itableIndex; } - public Method.MethodVersion setPoisonPill() { + private void setPoisonPill() { poisonPill = true; - return this; } public ExceptionHandler[] getExceptionHandlers() { @@ -1901,7 +1889,7 @@ private CallTarget findCallTarget() { * The method was obtained through a regular lookup (since it is in the declared * methods). Delegate it to a polysignature method lookup. */ - target = declaringKlass.lookupSignaturePolymorphicMethod(getName(), getRawSignature(), Klass.LookupMode.ALL).getCallTarget(); + target = getMethod().findSignaturePolymorphicIntrinsic(getRawSignature()).getCallTarget(); } if (target == null) { @@ -1928,7 +1916,7 @@ private CallTarget lookupWithNativePrefixes() { } // before using the prefix-stripped method name, make sure we have a // non-native wrapper method, if not we can't link - Method wrapperMethod = getDeclaringKlass().lookupDeclaredMethod(resolvedName, getRawSignature(), isStatic() ? Klass.LookupMode.STATIC_ONLY : Klass.LookupMode.INSTANCE_ONLY); + Method wrapperMethod = getDeclaringKlass().lookupDeclaredMethod(resolvedName, getRawSignature(), isStatic() ? LookupMode.STATIC_ONLY : LookupMode.INSTANCE_ONLY); if (wrapperMethod == null || wrapperMethod.isNative()) { return null; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java index fe88147d0a3a..d162e7b11352 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java @@ -25,7 +25,6 @@ import static com.oracle.truffle.espresso.classfile.Constants.ACC_FINALIZER; import static com.oracle.truffle.espresso.classfile.Constants.ACC_SUPER; import static com.oracle.truffle.espresso.classfile.Constants.JVM_ACC_WRITTEN_FLAGS; -import static com.oracle.truffle.espresso.classfile.ParserKlass.isSignaturePolymorphicHolderType; import java.io.PrintStream; import java.lang.ref.WeakReference; @@ -83,7 +82,6 @@ import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols; import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names; -import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; import com.oracle.truffle.espresso.impl.ModuleTable.ModuleEntry; import com.oracle.truffle.espresso.impl.PackageTable.PackageEntry; import com.oracle.truffle.espresso.meta.EspressoError; @@ -1097,100 +1095,6 @@ int findVirtualMethodIndex(Symbol methodName, Symbol signature, return -1; } - @TruffleBoundary - public Method resolveInterfaceMethod(Symbol methodName, Symbol signature) { - assert isInterface(); - /* - * 2. Otherwise, if C declares a method with the name and descriptor specified by the - * interface method reference, method lookup succeeds. - */ - for (Method m : getDeclaredMethods()) { - if (methodName == m.getName() && signature == m.getRawSignature()) { - return m; - } - } - /* - * 3. Otherwise, if the class Object declares a method with the name and descriptor - * specified by the interface method reference, which has its ACC_PUBLIC flag set and does - * not have its ACC_STATIC flag set, method lookup succeeds. - */ - assert getSuperKlass().getType() == Types.java_lang_Object; - Method m = getSuperKlass().lookupDeclaredMethod(methodName, signature); - if (m != null && m.isPublic() && !m.isStatic()) { - return m; - } - - Method resolved = null; - /* - * Interfaces are sorted, superinterfaces first; traverse in reverse order to get - * maximally-specific first. - */ - for (int i = getiKlassTable().length - 1; i >= 0; i--) { - ObjectKlass superInterf = getiKlassTable()[i].getKlass(); - for (Method.MethodVersion superMVersion : superInterf.getInterfaceMethodsTable()) { - Method superM = superMVersion.getMethod(); - /* - * Methods in superInterf.getInterfaceMethodsTable() are all non-static non-private - * methods declared in superInterf. - */ - if (methodName == superM.getName() && signature == superM.getRawSignature()) { - if (resolved == null) { - resolved = superM; - } else { - /* - * 4. Otherwise, if the maximally-specific superinterface methods - * (§5.4.3.3) of C for the name and descriptor specified by the method - * reference include exactly one method that does not have its ACC_ABSTRACT - * flag set, then this method is chosen and method lookup succeeds. - * - * 5. Otherwise, if any superinterface of C declares a method with the name - * and descriptor specified by the method reference that has neither its - * ACC_PRIVATE flag nor its ACC_STATIC flag set, one of these is arbitrarily - * chosen and method lookup succeeds. - */ - resolved = Method.resolveMaximallySpecific(resolved, superM).getMethod(); - if (resolved.getITableIndex() == -1) { - /* - * Multiple maximally specific: this method has a poison pill. - * - * NOTE: Since java 9, we can invokespecial interface methods (ie: a - * call directly to the resolved method, rather than after an interface - * lookup). We are looking up a method taken from the implemented - * interface (and not from a currently non-existing itable of the - * implementing interface). This difference, and the possibility of - * invokespecial, means that we cannot return the looked up method - * directly in case of multiple maximally specific method. thus, we - * spawn a new proxy method, attached to no method table, just to fail - * if invokespecial. - */ - assert (resolved.identity() == superM.identity()); - resolved.setITableIndex(superM.getITableIndex()); - } - } - } - } - } - return resolved; - } - - @Override - public Method lookupMethod(Symbol methodName, Symbol signature, LookupMode lookupMode) { - KLASS_LOOKUP_METHOD_COUNT.inc(); - Method method = lookupDeclaredMethod(methodName, signature, lookupMode); - if (method == null) { - // Implicit interface methods. - method = lookupMirandas(methodName, signature); - } - if (method == null && isSignaturePolymorphicHolderType(getType())) { - method = lookupSignaturePolymorphicMethod(methodName, signature, lookupMode); - } - if (method == null && getSuperKlass() != null) { - CompilerAsserts.partialEvaluationConstant(this); - method = getSuperKlass().lookupMethod(methodName, signature, lookupMode); - } - return method; - } - public Field[] getFieldTable() { if (!getContext().advancedRedefinitionEnabled()) { return fieldTable; @@ -1235,19 +1139,6 @@ public Field[] getStaticFieldTable() { } } - private Method lookupMirandas(Symbol methodName, Symbol signature) { - if (getMirandaMethods() == null) { - return null; - } - for (Method.MethodVersion miranda : getMirandaMethods()) { - Method method = miranda.getMethod(); - if (method.getName() == methodName && method.getRawSignature() == signature) { - return method; - } - } - return null; - } - void print(PrintStream out) { out.println(getType()); for (Method m : getDeclaredMethods()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/PrimitiveKlass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/PrimitiveKlass.java index 9f4e35fcd254..6ffa0c7aad10 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/PrimitiveKlass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/PrimitiveKlass.java @@ -26,9 +26,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.classfile.JavaKind; -import com.oracle.truffle.espresso.classfile.descriptors.Name; -import com.oracle.truffle.espresso.classfile.descriptors.Signature; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool; import com.oracle.truffle.espresso.impl.ModuleTable.ModuleEntry; import com.oracle.truffle.espresso.impl.ObjectKlass.KlassVersion; @@ -108,11 +105,6 @@ public Method.MethodVersion[] getDeclaredMethodVersions() { return Method.EMPTY_VERSION_ARRAY; } - @Override - public Method lookupMethod(Symbol methodName, Symbol signature, LookupMode lookupMode) { - return null; - } - @Override public Field[] getDeclaredFields() { return Field.EMPTY_ARRAY; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java index 58d612671c5b..3e38dcac05d2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java @@ -111,6 +111,7 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.substitutions.CallableFromNative; import com.oracle.truffle.espresso.substitutions.GenerateNativeEnv; import com.oracle.truffle.espresso.substitutions.Inject; @@ -448,7 +449,7 @@ private WeakHandles methodIds() { if (fieldType != null) { // Lookup only if name and type are known symbols. klass.safeInitialize(); - field = klass.lookupField(fieldName, fieldType, Klass.LookupMode.INSTANCE_ONLY); + field = klass.lookupField(fieldName, fieldType, LookupMode.INSTANCE_ONLY); assert field == null || field.getType().equals(fieldType); } } @@ -492,7 +493,7 @@ private WeakHandles methodIds() { Klass klass = clazz.getMirrorKlass(getMeta()); klass.safeInitialize(); // Lookup only if name and type are known symbols. - field = klass.lookupField(fieldName, fieldType, Klass.LookupMode.STATIC_ONLY); + field = klass.lookupField(fieldName, fieldType, LookupMode.STATIC_ONLY); assert field == null || field.getType().equals(fieldType); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index 9c791ca429bc..01df9528ded8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -2321,6 +2321,7 @@ public BaseQuickNode generifyInlinedMethodNode(int top, int opcode, int curBCI, resolvedInvoke = getResolvedInvoke(opcode, readOriginalCPI(curBCI)); } else { assert !resolvedCall.getResolvedMethod().isInvokeIntrinsic() : "An inlined method may never be an invokeGeneric."; + assert resolvedCall.getCallKind() != CallKind.ITABLE_LOOKUP : "A bytecode-inlined method may not be from an interface dispatch."; resolvedInvoke = new ResolvedInvoke(resolvedCall, null); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/ReferenceProcessCache.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/ReferenceProcessCache.java index 18c4ab3352f7..f5e33da20593 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/ReferenceProcessCache.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/ReferenceProcessCache.java @@ -39,6 +39,7 @@ import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.threads.ThreadState; import com.oracle.truffle.espresso.threads.Transition; @@ -135,7 +136,7 @@ private static Klass findSharedSecrets(EspressoContext context) { private static Field findJlaField(Klass sharedSecrets) { for (Symbol type : JAVA_LANG_ACCESS_TYPES) { - Field f = sharedSecrets.lookupField(Names.javaLangAccess, type, Klass.LookupMode.STATIC_ONLY); + Field f = sharedSecrets.lookupField(Names.javaLangAccess, type, LookupMode.STATIC_ONLY); if (f != null) { return f; } @@ -145,7 +146,7 @@ private static Field findJlaField(Klass sharedSecrets) { private static Method findRunFinalizer(EspressoContext context) { for (Symbol signature : RUN_FINALIZER_SIGNATURES) { - Method m = context.getMeta().java_lang_ref_Finalizer.lookupMethod(Names.runFinalizer, signature, Klass.LookupMode.INSTANCE_ONLY); + Method m = context.getMeta().java_lang_ref_Finalizer.lookupMethod(Names.runFinalizer, signature); if (m != null) { return m; } @@ -157,9 +158,9 @@ private static Method findProcessPendingReferences(EspressoContext context) { Method processPendingReferenceMethod; if (context.getJavaVersion().java8OrEarlier()) { processPendingReferenceMethod = context.getMeta().java_lang_ref_Reference.lookupDeclaredMethod(Names.tryHandlePending, Signatures._boolean_boolean, - Klass.LookupMode.STATIC_ONLY); + LookupMode.STATIC_ONLY); } else { - processPendingReferenceMethod = context.getMeta().java_lang_ref_Reference.lookupDeclaredMethod(Names.processPendingReferences, Signatures._void, Klass.LookupMode.STATIC_ONLY); + processPendingReferenceMethod = context.getMeta().java_lang_ref_Reference.lookupDeclaredMethod(Names.processPendingReferences, Signatures._void, LookupMode.STATIC_ONLY); } if (processPendingReferenceMethod == null) { throw EspressoError.shouldNotReachHere("Could not find pending reference processing method."); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java index ee606dea2413..c2dbfc470c99 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java @@ -55,6 +55,7 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.threads.ThreadState; import com.oracle.truffle.espresso.threads.Transition; import com.oracle.truffle.espresso.vm.VM; @@ -440,7 +441,7 @@ public static final class BuiltinExceptionTypeConverter implements InternalTypeC public BuiltinExceptionTypeConverter(ObjectKlass klass) { this.exceptionKlass = klass; - this.messageConstructor = klass.lookupDeclaredMethod(Names._init_, Signatures._void_String, Klass.LookupMode.INSTANCE_ONLY); + this.messageConstructor = klass.lookupDeclaredMethod(Names._init_, Signatures._void_String, LookupMode.INSTANCE_ONLY); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoLinkResolver.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoLinkResolver.java index ff4d987c341c..1ce1be165bca 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoLinkResolver.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoLinkResolver.java @@ -29,6 +29,7 @@ import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.truffle.espresso.shared.resolver.CallSiteType; import com.oracle.truffle.espresso.shared.resolver.FieldAccessType; import com.oracle.truffle.espresso.shared.resolver.LinkResolver; @@ -63,14 +64,22 @@ public static Method resolveMethodSymbol(EspressoContext ctx, Klass accessingKla Symbol name, Symbol signature, Klass symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { - return LinkResolver.resolveMethodSymbol(ctx, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + try { + return LinkResolver.resolveMethodSymbol(ctx, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + } catch (LookupSuccessInvocationFailure e) { + return e. getResult().forFailing(); + } } public static Method resolveMethodSymbolOrNull(EspressoContext ctx, Klass accessingKlass, Symbol name, Symbol signature, Klass symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { - return LinkResolver.resolveMethodSymbolOrNull(ctx, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + try { + return LinkResolver.resolveMethodSymbolOrNull(ctx, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + } catch (LookupSuccessInvocationFailure e) { + return e. getResult().forFailing(); + } } public static ResolvedCall resolveCallSiteOrThrow(EspressoContext ctx, Klass currentKlass, Method symbolicResolution, CallSiteType callSiteType, Klass symbolicHolder) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/polyglot/Target_com_oracle_truffle_espresso_polyglot_TypeLiteral.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/polyglot/Target_com_oracle_truffle_espresso_polyglot_TypeLiteral.java index af10c006f10a..a124439b5102 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/polyglot/Target_com_oracle_truffle_espresso_polyglot_TypeLiteral.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/polyglot/Target_com_oracle_truffle_espresso_polyglot_TypeLiteral.java @@ -39,6 +39,7 @@ import com.oracle.truffle.espresso.nodes.interop.GetTypeLiteralNode; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.shared.lookup.LookupMode; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; @@ -126,14 +127,14 @@ private EspressoType extractEspressoType(StaticObject type, EspressoContext cont } private static Klass rawType(StaticObject type) { - Method method = type.getKlass().lookupDeclaredMethod(Names.getRawType, Signatures.Class, Klass.LookupMode.INSTANCE_ONLY); + Method method = type.getKlass().lookupDeclaredMethod(Names.getRawType, Signatures.Class, LookupMode.INSTANCE_ONLY); assert method != null; StaticObject rawGuestClass = (StaticObject) method.invokeDirectVirtual(type); return rawGuestClass.getMirrorKlass(); } private EspressoType[] typeArguments(StaticObject type, EspressoContext context) { - Method method = type.getKlass().lookupDeclaredMethod(Names.getActualTypeArguments, Signatures.Type_array, Klass.LookupMode.INSTANCE_ONLY); + Method method = type.getKlass().lookupDeclaredMethod(Names.getActualTypeArguments, Signatures.Type_array, LookupMode.INSTANCE_ONLY); assert method != null; StaticObject typesArray = (StaticObject) method.invokeDirectVirtual(type); StaticObject[] types = typesArray.unwrap(context.getLanguage()); diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java index 598df1c099d5..8ac78719189c 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java @@ -39,6 +39,7 @@ import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; +import com.oracle.svm.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.svm.espresso.shared.meta.MethodAccess; import jdk.vm.ci.meta.JavaKind; @@ -101,7 +102,12 @@ static InterpreterResolvedJavaMethod toJVMCI(Executable executable) { sb.append('V'); } Symbol signature = SymbolsSupport.getSignatures().lookupValidSignature(sb.toString()); - return holder.lookupMethod(name, signature); + try { + return holder.lookupMethod(name, signature); + } catch (LookupSuccessInvocationFailure e) { + // GR-70938 + return e.getResult(); + } } static JavaType toJavaType(Symbol typeSymbol) { diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java index 36c5d7380a8f..4b832ecb4ce1 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java @@ -336,6 +336,11 @@ public final boolean isCallerSensitive() { return (flags & ACC_CALLER_SENSITIVE) != 0; } + @Override + public InterpreterResolvedJavaMethod findSignaturePolymorphicIntrinsic(Symbol methodSignature) { + return (InterpreterResolvedJavaMethod) CremaSupport.singleton().findMethodHandleIntrinsic(this, methodSignature); + } + @Override public final boolean isDeclaredSignaturePolymorphic() { // Note: might not be true for the instantiation of polymorphic signature intrinsics. diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java index 020e428e52b6..6985455920b2 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java @@ -53,6 +53,8 @@ * closed world e.g. instantiable, instantiated, effectively final ... */ public abstract class InterpreterResolvedJavaType implements ResolvedJavaType, CremaTypeAccess { + public static final InterpreterResolvedJavaType[] EMPTY_ARRAY = new InterpreterResolvedJavaType[0]; + private final Symbol type; protected final Class clazz; private final JavaConstant clazzConstant; diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java index 2a243181f986..b827458cac87 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java @@ -25,8 +25,8 @@ package com.oracle.svm.interpreter.metadata; import static com.oracle.svm.core.BuildPhaseProvider.AfterAnalysis; -import static com.oracle.svm.espresso.classfile.ParserKlass.isSignaturePolymorphicHolderType; +import java.util.Arrays; import java.util.List; import org.graalvm.nativeimage.Platform; @@ -36,14 +36,12 @@ import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.crema.CremaSupport; import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ParserKlass; import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; import com.oracle.svm.espresso.classfile.descriptors.Name; -import com.oracle.svm.espresso.classfile.descriptors.Signature; import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; @@ -352,10 +350,26 @@ public final InterpreterResolvedJavaType findLeastCommonAncestor(InterpreterReso } @Override - public final InterpreterResolvedJavaType getSuperClass() { + public final InterpreterResolvedObjectType getSuperClass() { return this.superclass; } + @Override + public final List getSuperInterfacesList() { + return Arrays.asList(getInterfaces()); + } + + @Override + public List getDeclaredMethodsList() { + return Arrays.asList(declaredMethods); + } + + @Override + public List getImplicitInterfaceMethodsList() { + // GR-70607: get mirandas. + return null; + } + @Override public final InterpreterResolvedJavaType getHostType() { throw VMError.unimplemented("getHostType"); @@ -386,79 +400,4 @@ public final InterpreterResolvedJavaField lookupField(Symbol name, Symbol< return null; } - @Override - public final InterpreterResolvedJavaMethod lookupMethod(Symbol name, Symbol signature) { - InterpreterResolvedObjectType current = this; - InterpreterResolvedJavaMethod method = current.lookupDeclaredMethod(name, signature); - if (method != null) { - return method; - } - - current = current.getSuperclass(); - while (current != null) { - method = current.lookupDeclaredMethod(name, signature); - if (method != null) { - return method; - } - current = current.getSuperclass(); - } - return null; - } - - private InterpreterResolvedJavaMethod lookupDeclaredMethod(Symbol name, Symbol signature) { - if (isSignaturePolymorphicHolderType(getSymbolicType())) { - InterpreterResolvedJavaMethod method = lookupSignaturePolymorphicMethod(name, signature); - if (method != null) { - return method; - } - } - for (InterpreterResolvedJavaMethod method : this.declaredMethods) { - if (name.equals(method.getSymbolicName()) && signature.equals(method.getSymbolicSignature())) { - return method; - } - } - return null; - } - - private InterpreterResolvedJavaMethod lookupSignaturePolymorphicMethod(Symbol methodName, Symbol signature) { - InterpreterResolvedJavaMethod m = lookupDeclaredSignaturePolymorphicMethod(methodName); - if (m != null) { - return (InterpreterResolvedJavaMethod) CremaSupport.singleton().findMethodHandleIntrinsic(m, signature); - } - return null; - } - - @Override - public final InterpreterResolvedJavaMethod lookupInstanceMethod(Symbol name, Symbol signature) { - InterpreterResolvedObjectType current = this; - while (current != null) { - for (InterpreterResolvedJavaMethod method : current.declaredMethods) { - if (!method.isStatic() && name.equals(method.getSymbolicName()) && signature.equals(method.getSymbolicSignature())) { - return method; - } - } - current = current.getSuperclass(); - } - return null; - } - - @Override - public final InterpreterResolvedJavaMethod lookupInterfaceMethod(Symbol name, Symbol signature) { - assert isInterface(); - InterpreterResolvedJavaMethod result = lookupDeclaredMethod(name, signature); - if (result != null) { - return result; - } - throw VMError.unimplemented("lookupInterfaceMethod"); - } - - @Override - public InterpreterResolvedJavaMethod lookupDeclaredSignaturePolymorphicMethod(Symbol methodName) { - for (InterpreterResolvedJavaMethod m : getDeclaredMethods()) { - if (m.getSymbolicName() == methodName && m.isDeclaredSignaturePolymorphic()) { - return m; - } - } - return null; - } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java index 67bed668e834..ae4027117a8f 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java @@ -25,6 +25,7 @@ package com.oracle.svm.interpreter.metadata; import java.lang.reflect.Modifier; +import java.util.Collections; import java.util.List; import com.oracle.svm.core.hub.registry.SymbolsSupport; @@ -121,8 +122,13 @@ public ResolvedJavaType getSuperclass() { } @Override - public ResolvedJavaType[] getInterfaces() { - return new ResolvedJavaType[0]; + public InterpreterResolvedJavaType[] getInterfaces() { + return InterpreterResolvedJavaType.EMPTY_ARRAY; + } + + @Override + public List getSuperInterfacesList() { + return Collections.emptyList(); } @Override @@ -135,6 +141,16 @@ public InterpreterResolvedJavaMethod[] getDeclaredMethods(boolean link) { return InterpreterResolvedJavaMethod.EMPTY_ARRAY; } + @Override + public List getDeclaredMethodsList() { + return Collections.emptyList(); + } + + @Override + public List getImplicitInterfaceMethodsList() { + return null; + } + @Override public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { return InterpreterResolvedJavaField.EMPTY_ARRAY; diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaLinkResolver.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaLinkResolver.java index 8687cd09456b..4e0f69d204a0 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaLinkResolver.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaLinkResolver.java @@ -28,6 +28,7 @@ import com.oracle.svm.espresso.classfile.descriptors.Signature; import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; +import com.oracle.svm.espresso.shared.lookup.LookupSuccessInvocationFailure; import com.oracle.svm.espresso.shared.resolver.CallSiteType; import com.oracle.svm.espresso.shared.resolver.FieldAccessType; import com.oracle.svm.espresso.shared.resolver.LinkResolver; @@ -67,14 +68,24 @@ public static InterpreterResolvedJavaMethod resolveMethodSymbol(CremaRuntimeAcce Symbol name, Symbol signature, InterpreterResolvedJavaType symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { - return LinkResolver.resolveMethodSymbol(runtime, accessingClass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + try { + return LinkResolver.resolveMethodSymbol(runtime, accessingClass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + } catch (LookupSuccessInvocationFailure e) { + // GR-70938 Somehow communicate to the caller this info. + return e.getResult(); + } } public static InterpreterResolvedJavaMethod resolveMethodSymbolOrNull(CremaRuntimeAccess runtime, InterpreterResolvedJavaType accessingClass, Symbol name, Symbol signature, InterpreterResolvedJavaType symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { - return LinkResolver.resolveMethodSymbolOrNull(runtime, accessingClass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + try { + return LinkResolver.resolveMethodSymbolOrNull(runtime, accessingClass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + } catch (LookupSuccessInvocationFailure e) { + // GR-70938 Somehow communicate to the caller this info. + return e.getResult(); + } } public static ResolvedCall resolveCallSiteOrThrow(