Skip to content

Commit a8efbf7

Browse files
committed
[GR-57119] Espresso: Unify resolution paths into a LinkResolver class.
PullRequest: graal/18865
2 parents 2a99d81 + 99eb05e commit a8efbf7

14 files changed

+735
-433
lines changed

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/classfile/constantpool/ClassMethodRefConstant.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.truffle.espresso.meta.EspressoError;
3838
import com.oracle.truffle.espresso.meta.Meta;
3939
import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
40+
import com.oracle.truffle.espresso.resolver.LinkResolver;
4041
import com.oracle.truffle.espresso.runtime.EspressoContext;
4142

4243
public interface ClassMethodRefConstant extends MethodRefConstant {
@@ -50,10 +51,6 @@ default Tag tag() {
5051
return Tag.METHOD_REF;
5152
}
5253

53-
default MHInvokeGenericNode.MethodHandleInvoker invoker() {
54-
return null;
55-
}
56-
5754
final class Indexes extends MethodRefConstant.Indexes implements ClassMethodRefConstant, Resolvable {
5855
Indexes(int classIndex, int nameAndTypeIndex) {
5956
super(classIndex, nameAndTypeIndex);
@@ -172,28 +169,16 @@ final class Indexes extends MethodRefConstant.Indexes implements ClassMethodRefC
172169
@Override
173170
public ResolvedConstant resolve(RuntimeConstantPool pool, int thisIndex, ObjectKlass accessingKlass) {
174171
METHODREF_RESOLVE_COUNT.inc();
175-
176172
EspressoContext context = pool.getContext();
177-
Klass holderKlass = getResolvedHolderKlass(accessingKlass, pool);
178-
179173
Meta meta = context.getMeta();
180-
if (holderKlass.isInterface()) {
181-
throw meta.throwExceptionWithMessage(meta.java_lang_IncompatibleClassChangeError, meta.toGuestString(getName(pool)));
182-
}
183174

175+
Klass holderKlass = getResolvedHolderKlass(accessingKlass, pool);
184176
Symbol<Name> name = getName(pool);
185177
Symbol<Signature> signature = getSignature(pool);
186178

187-
Method method = holderKlass.lookupMethod(name, signature);
188-
if (method == null) {
189-
throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, meta.toGuestString(holderKlass.getNameAsString() + "." + getName(pool) + signature));
190-
}
191-
192-
MemberRefConstant.doAccessCheck(accessingKlass, holderKlass, method, meta);
179+
Method method = LinkResolver.resolveSymbol(meta, accessingKlass, name, signature, holderKlass, false, true, true);
193180

194-
if (!method.isPolySignatureIntrinsic()) {
195-
method.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), method.getDeclaringKlass().getDefiningClassLoader());
196-
} else if (method.isInvokeIntrinsic()) {
181+
if (method.isInvokeIntrinsic()) {
197182
MHInvokeGenericNode.MethodHandleInvoker invoker = MHInvokeGenericNode.linkMethod(meta.getLanguage(), meta, accessingKlass, method, name, signature);
198183
return new ResolvedWithInvoker(method, invoker);
199184
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/classfile/constantpool/InterfaceMethodRefConstant.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
import com.oracle.truffle.espresso.impl.Method;
3636
import com.oracle.truffle.espresso.impl.ObjectKlass;
3737
import com.oracle.truffle.espresso.meta.EspressoError;
38-
import com.oracle.truffle.espresso.meta.Meta;
39-
import com.oracle.truffle.espresso.runtime.EspressoContext;
38+
import com.oracle.truffle.espresso.resolver.LinkResolver;
4039

4140
public interface InterfaceMethodRefConstant extends MethodRefConstant {
4241

@@ -110,30 +109,12 @@ final class Indexes extends MethodRefConstant.Indexes implements InterfaceMethod
110109
@Override
111110
public ResolvedConstant resolve(RuntimeConstantPool pool, int thisIndex, ObjectKlass accessingKlass) {
112111
METHODREF_RESOLVE_COUNT.inc();
113-
EspressoContext context = pool.getContext();
114-
Meta meta = context.getMeta();
115112

116113
Klass holderInterface = getResolvedHolderKlass(accessingKlass, pool);
117-
118114
Symbol<Name> name = getName(pool);
119-
120-
// 1. If C is not an interface, interface method resolution throws an
121-
// IncompatibleClassChangeError.
122-
if (!holderInterface.isInterface()) {
123-
throw meta.throwExceptionWithMessage(meta.java_lang_IncompatibleClassChangeError, "Found class " + holderInterface.getExternalName() + ", but interface was expected");
124-
}
125-
126115
Symbol<Signature> signature = getSignature(pool);
127116

128-
Method method = ((ObjectKlass) holderInterface).resolveInterfaceMethod(name, signature);
129-
130-
if (method == null) {
131-
throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, meta.toGuestString(name));
132-
}
133-
134-
MemberRefConstant.doAccessCheck(accessingKlass, holderInterface, method, meta);
135-
136-
method.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), method.getDeclaringKlass().getDefiningClassLoader());
117+
Method method = LinkResolver.resolveSymbol(pool.getContext().getMeta(), accessingKlass, name, signature, holderInterface, true, true, true);
137118

138119
return new Resolved(method);
139120
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/classfile/constantpool/MethodRefConstant.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.oracle.truffle.espresso.classfile.ConstantPool;
2626
import com.oracle.truffle.espresso.descriptors.Symbol;
2727
import com.oracle.truffle.espresso.descriptors.Symbol.Signature;
28+
import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
2829
import com.oracle.truffle.espresso.perf.DebugCounter;
2930

3031
public interface MethodRefConstant extends MemberRefConstant {
@@ -41,6 +42,10 @@ default Symbol<Signature> getSignature(ConstantPool pool) {
4142
return (Symbol<Signature>) getDescriptor(pool);
4243
}
4344

45+
default MHInvokeGenericNode.MethodHandleInvoker invoker() {
46+
return null;
47+
}
48+
4449
abstract class Indexes extends MemberRefConstant.Indexes implements MethodRefConstant {
4550
Indexes(int classIndex, int nameAndTypeIndex) {
4651
super(classIndex, nameAndTypeIndex);

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java

Lines changed: 20 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,6 @@
315315
import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute;
316316
import com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute;
317317
import com.oracle.truffle.espresso.classfile.constantpool.ClassConstant;
318-
import com.oracle.truffle.espresso.classfile.constantpool.ClassMethodRefConstant;
319318
import com.oracle.truffle.espresso.classfile.constantpool.DoubleConstant;
320319
import com.oracle.truffle.espresso.classfile.constantpool.DynamicConstant;
321320
import com.oracle.truffle.espresso.classfile.constantpool.FloatConstant;
@@ -376,6 +375,10 @@
376375
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeVirtualQuickNode;
377376
import com.oracle.truffle.espresso.nodes.quick.invoke.inline.InlinedMethodNode;
378377
import com.oracle.truffle.espresso.perf.DebugCounter;
378+
import com.oracle.truffle.espresso.resolver.CallKind;
379+
import com.oracle.truffle.espresso.resolver.CallSiteType;
380+
import com.oracle.truffle.espresso.resolver.LinkResolver;
381+
import com.oracle.truffle.espresso.resolver.ResolvedCall;
379382
import com.oracle.truffle.espresso.runtime.EspressoContext;
380383
import com.oracle.truffle.espresso.runtime.EspressoException;
381384
import com.oracle.truffle.espresso.runtime.EspressoExitException;
@@ -2394,141 +2397,38 @@ private int quickenArrayStore(final VirtualFrame frame, int top, int curBCI, int
23942397
// endregion quickenForeign
23952398

23962399
private InvokeQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowBytecodeInlining) {
2397-
Method resolved = resolutionSeed;
2398-
int resolvedOpCode = opcode;
2399-
switch (opcode) {
2400-
case INVOKESTATIC:
2401-
// Otherwise, if the resolved method is an instance method, the invokestatic
2402-
// instruction throws an IncompatibleClassChangeError.
2403-
if (!resolved.isStatic()) {
2404-
enterLinkageExceptionProfile();
2405-
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected static method '%s.%s%s'",
2406-
resolved.getDeclaringKlass().getName(),
2407-
resolved.getName(),
2408-
resolved.getRawSignature());
2409-
}
2410-
break;
2411-
case INVOKEINTERFACE:
2412-
// Otherwise, if the resolved method is static or (jdk8 or earlier) private, the
2413-
// invokeinterface instruction throws an IncompatibleClassChangeError.
2414-
if (resolved.isStatic() ||
2415-
(getMethod().getContext().getJavaVersion().java8OrEarlier() && resolved.isPrivate())) {
2416-
enterLinkageExceptionProfile();
2417-
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance method '%s.%s%s'",
2418-
resolved.getDeclaringKlass().getName(),
2419-
resolved.getName(),
2420-
resolved.getRawSignature());
2421-
}
2422-
if (resolved.getITableIndex() < 0) {
2423-
if (resolved.isPrivate()) {
2424-
assert getJavaVersion().java9OrLater();
2425-
// Interface private methods do not appear in itables.
2426-
resolvedOpCode = INVOKESPECIAL;
2427-
} else {
2428-
// Can happen in old classfiles that calls j.l.Object on interfaces.
2429-
resolvedOpCode = INVOKEVIRTUAL;
2430-
}
2431-
}
2432-
break;
2433-
case INVOKEVIRTUAL:
2434-
// Otherwise, if the resolved method is a class (static) method, the invokevirtual
2435-
// instruction throws an IncompatibleClassChangeError.
2436-
if (resolved.isStatic()) {
2437-
enterLinkageExceptionProfile();
2438-
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'",
2439-
resolved.getDeclaringKlass().getName(),
2440-
resolved.getName(),
2441-
resolved.getRawSignature());
2442-
}
2443-
if (resolved.isFinalFlagSet() || resolved.getDeclaringKlass().isFinalFlagSet() || resolved.isPrivate()) {
2444-
resolvedOpCode = INVOKESPECIAL;
2445-
}
2446-
break;
2447-
case INVOKESPECIAL:
2448-
// Otherwise, if the resolved method is an instance initialization method, and the
2449-
// class in which it is declared is not the class symbolically referenced by the
2450-
// instruction, a NoSuchMethodError is thrown.
2451-
if (resolved.isConstructor()) {
2452-
if (resolved.getDeclaringKlass().getName() != getConstantPool().methodAt(cpi).getHolderKlassName(getConstantPool())) {
2453-
enterLinkageExceptionProfile();
2454-
throw throwBoundary(getMethod().getMeta().java_lang_NoSuchMethodError,
2455-
"%s.%s%s",
2456-
resolved.getDeclaringKlass().getName(),
2457-
resolved.getName(),
2458-
resolved.getRawSignature());
2459-
}
2460-
}
2461-
// Otherwise, if the resolved method is a class (static) method, the invokespecial
2462-
// instruction throws an IncompatibleClassChangeError.
2463-
if (resolved.isStatic()) {
2464-
enterLinkageExceptionProfile();
2465-
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'",
2466-
resolved.getDeclaringKlass().getName(),
2467-
resolved.getName(),
2468-
resolved.getRawSignature());
2469-
}
2470-
// If all of the following are true, let C be the direct superclass of the current
2471-
// class:
2472-
//
2473-
// * The resolved method is not an instance initialization method (&sect;2.9).
2474-
//
2475-
// * If the symbolic reference names a class (not an interface), then that class is
2476-
// a superclass of the current class.
2477-
//
2478-
// * The ACC_SUPER flag is set for the class file (&sect;4.1). In Java SE 8 and
2479-
// above, the Java Virtual Machine considers the ACC_SUPER flag to be set in every
2480-
// class file, regardless of the actual value of the flag in the class file and the
2481-
// version of the class file.
2482-
if (!resolved.isConstructor()) {
2483-
ObjectKlass declaringKlass = getMethod().getDeclaringKlass();
2484-
Klass symbolicRef = ((MethodRefConstant.Indexes) getConstantPool().methodAt(cpi)).getResolvedHolderKlass(declaringKlass, getConstantPool());
2485-
if (!symbolicRef.isInterface() &&
2486-
symbolicRef != declaringKlass &&
2487-
declaringKlass.getSuperKlass() != null &&
2488-
symbolicRef != declaringKlass.getSuperKlass() &&
2489-
symbolicRef.isAssignableFrom(declaringKlass)) {
2490-
resolved = declaringKlass.getSuperKlass().lookupMethod(resolved.getName(), resolved.getRawSignature(), Klass.LookupMode.INSTANCE_ONLY);
2491-
}
2492-
}
2493-
break;
2494-
default:
2495-
CompilerDirectives.transferToInterpreterAndInvalidate();
2496-
throw EspressoError.unimplemented("Quickening for " + Bytecodes.nameOf(opcode));
2497-
}
2400+
2401+
Klass symbolicRef = ((MethodRefConstant.Indexes) getConstantPool().methodAt(cpi)).getResolvedHolderKlass(getDeclaringKlass(), getConstantPool());
2402+
ResolvedCall resolvedCall = LinkResolver.resolveCallSite(getMeta(), getDeclaringKlass(), resolutionSeed, CallSiteType.fromOpCode(opcode), symbolicRef);
2403+
2404+
Method resolved = resolvedCall.getResolvedMethod();
2405+
CallKind callKind = resolvedCall.getCallKind();
24982406

24992407
// Skip inlined nodes if instrumentation is live.
25002408
// Lock must be owned for correctness.
25012409
assert lockIsHeld();
25022410
boolean tryBytecodeLevelInlining = this.instrumentation == null && allowBytecodeInlining;
25032411
if (tryBytecodeLevelInlining) {
2504-
var node = InlinedMethodNode.createFor(resolved, top, resolvedOpCode, curBCI, statementIndex);
2412+
InlinedMethodNode node = InlinedMethodNode.createFor(resolvedCall, top, opcode, curBCI, statementIndex);
25052413
if (node != null) {
25062414
return node;
25072415
}
25082416
}
25092417

2510-
InvokeQuickNode invoke;
25112418
if (resolved.isPolySignatureIntrinsic()) {
2512-
MethodHandleInvoker invoker = null;
2513-
if ((resolvedOpCode == INVOKEVIRTUAL || resolvedOpCode == INVOKESPECIAL) && (getConstantPool().resolvedMethodRefAt(getDeclaringKlass(), cpi) instanceof ClassMethodRefConstant methodRef)) {
2514-
// There might be an invoker if it's an InvokeGeneric
2515-
invoker = methodRef.invoker();
2516-
}
2517-
invoke = new InvokeHandleNode(resolved, invoker, top, curBCI);
2419+
MethodHandleInvoker invoker = getConstantPool().resolvedMethodRefAt(getDeclaringKlass(), cpi).invoker();
2420+
assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && resolved.isInvokeIntrinsic());
2421+
return new InvokeHandleNode(resolved, invoker, top, curBCI);
25182422
} else {
25192423
// @formatter:off
2520-
switch (resolvedOpCode) {
2521-
case INVOKESTATIC : invoke = new InvokeStaticQuickNode(resolved, top, curBCI); break;
2522-
case INVOKEINTERFACE : invoke = new InvokeInterfaceQuickNode(resolved, top, curBCI); break;
2523-
case INVOKEVIRTUAL : invoke = new InvokeVirtualQuickNode(resolved, top, curBCI); break;
2524-
case INVOKESPECIAL : invoke = new InvokeSpecialQuickNode(resolved, top, curBCI); break;
2525-
default :
2526-
CompilerDirectives.transferToInterpreterAndInvalidate();
2527-
throw EspressoError.unimplemented("Quickening for " + Bytecodes.nameOf(resolvedOpCode));
2528-
}
2424+
return switch (callKind) {
2425+
case STATIC -> new InvokeStaticQuickNode(resolved, top, curBCI);
2426+
case ITABLE_LOOKUP -> new InvokeInterfaceQuickNode(resolved, top, curBCI);
2427+
case VTABLE_LOOKUP -> new InvokeVirtualQuickNode(resolved, top, curBCI);
2428+
case DIRECT -> new InvokeSpecialQuickNode(resolved, top, curBCI);
2429+
};
25292430
// @formatter:on
25302431
}
2531-
return invoke;
25322432
}
25332433

25342434
@TruffleBoundary

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/invoke/inline/ConditionalInlinedMethodNode.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323

2424
package com.oracle.truffle.espresso.nodes.quick.invoke.inline;
2525

26-
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKEINTERFACE;
27-
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKESPECIAL;
28-
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKESTATIC;
29-
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKEVIRTUAL;
30-
3126
import com.oracle.truffle.api.CompilerDirectives;
3227
import com.oracle.truffle.api.frame.VirtualFrame;
3328
import com.oracle.truffle.espresso.impl.Method;
@@ -36,6 +31,7 @@
3631
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeSpecialQuickNode;
3732
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeStaticQuickNode;
3833
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeVirtualQuickNode;
34+
import com.oracle.truffle.espresso.resolver.ResolvedCall;
3935

4036
public class ConditionalInlinedMethodNode extends InlinedMethodNode {
4137

@@ -50,9 +46,9 @@ public interface Recipes {
5046
@Child protected InvokeQuickNode fallbackNode;
5147
private final InlinedMethodPredicate condition;
5248

53-
public ConditionalInlinedMethodNode(Method.MethodVersion inlinedMethod, int top, int opcode, int callerBCI, int statementIndex, Recipes recipes, InlinedMethodPredicate condition) {
54-
super(inlinedMethod, top, opcode, callerBCI, statementIndex, null);
55-
this.fallbackNode = getFallback(inlinedMethod.getMethod(), top, callerBCI, opcode);
49+
public ConditionalInlinedMethodNode(ResolvedCall resolvedCall, int top, int opcode, int callerBCI, int statementIndex, Recipes recipes, InlinedMethodPredicate condition) {
50+
super(resolvedCall.getResolvedMethod().getMethodVersion(), top, opcode, callerBCI, statementIndex, null);
51+
this.fallbackNode = getFallback(resolvedCall, top, callerBCI);
5652
this.condition = condition;
5753
this.recipes = recipes;
5854
}
@@ -83,13 +79,13 @@ public static InlinedMethodNode getDefinitiveNode(Recipes recipes,
8379
return replacement;
8480
}
8581

86-
static InvokeQuickNode getFallback(Method inlinedMethod, int top, int callerBCI, int opcode) {
82+
static InvokeQuickNode getFallback(ResolvedCall resolvedCall, int top, int callerBCI) {
8783
// @formatter:off
88-
switch (opcode) {
89-
case INVOKESTATIC : return new InvokeStaticQuickNode(inlinedMethod, top, callerBCI);
90-
case INVOKESPECIAL : return new InvokeSpecialQuickNode(inlinedMethod, top, callerBCI);
91-
case INVOKEVIRTUAL : return new InvokeVirtualQuickNode(inlinedMethod, top, callerBCI);
92-
case INVOKEINTERFACE : // fallback.
84+
switch (resolvedCall.getCallKind()) {
85+
case STATIC : return new InvokeStaticQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
86+
case DIRECT : return new InvokeSpecialQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
87+
case VTABLE_LOOKUP : return new InvokeVirtualQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
88+
case ITABLE_LOOKUP : // fallback.
9389
default :
9490
CompilerDirectives.transferToInterpreterAndInvalidate();
9591
throw EspressoError.unimplemented("Conditional bytecode-level inlining only available for invokestatic, invokespecial and invokevirtual");

0 commit comments

Comments
 (0)