Skip to content

Commit 60b1ae5

Browse files
committed
[GR-52713] JDWP fix for reading 'this' and args for intrinsified methods.
PullRequest: graal/17525
2 parents a65ec78 + 7197c2a commit 60b1ae5

File tree

6 files changed

+225
-15
lines changed

6 files changed

+225
-15
lines changed

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,10 +833,6 @@ public String getSignatureAsString() {
833833
return getRawSignature().toString();
834834
}
835835

836-
public KlassRef[] getParameters() {
837-
return resolveParameterKlasses();
838-
}
839-
840836
@TruffleBoundary
841837
public Object invokeMethod(Object callee, Object[] args) {
842838
if (isConstructor()) {

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

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
import com.oracle.truffle.espresso.EspressoScope;
4141
import com.oracle.truffle.espresso.classfile.attributes.Local;
4242
import com.oracle.truffle.espresso.descriptors.ByteSequence;
43+
import com.oracle.truffle.espresso.descriptors.Signatures;
4344
import com.oracle.truffle.espresso.descriptors.Symbol;
4445
import com.oracle.truffle.espresso.descriptors.Utf8ConstantTable;
45-
import com.oracle.truffle.espresso.impl.Klass;
4646
import com.oracle.truffle.espresso.impl.Method;
4747

4848
@GenerateWrapper
@@ -100,34 +100,31 @@ private Object getScopeSlowPath(MaterializedFrame frame) {
100100
int localCount = hasReceiver ? 1 : 0;
101101
localCount += method.getParameterCount();
102102

103-
Klass[] parameters = (Klass[]) method.getParameters();
104103
Utf8ConstantTable utf8Constants = method.getLanguage().getUtf8ConstantTable();
105104
int startslot = 0;
106105

107106
if (hasReceiver) {
108107
// include 'this' and method arguments if not already included
109108
if (!slotToLocal.containsKey(startslot)) {
110-
constructedLiveLocals.add(new Local(utf8Constants.getOrCreate(Symbol.Name.thiz), utf8Constants.getOrCreate(method.getDeclaringKlass().getType()), 0, 65536, 0));
109+
constructedLiveLocals.add(new Local(utf8Constants.getOrCreate(Symbol.Name.thiz), utf8Constants.getOrCreate(method.getDeclaringKlass().getType()), 0, 0xffff, 0));
111110
} else {
112111
constructedLiveLocals.add(slotToLocal.get(startslot));
113112
}
114113
slotToLocal.remove(startslot);
115114
startslot++;
116115
}
117-
116+
Symbol<Symbol.Type>[] parsedSignature = method.getParsedSignature();
118117
// include method parameters if not already included
119118
for (int i = startslot; i < localCount; i++) {
120-
Klass param = hasReceiver ? parameters[i - 1] : parameters[i];
119+
Symbol<Symbol.Type> paramType = hasReceiver ? Signatures.parameterType(parsedSignature, i - 1) : Signatures.parameterType(parsedSignature, i);
121120
if (!slotToLocal.containsKey(i)) {
122-
constructedLiveLocals.add(new Local(utf8Constants.getOrCreate(ByteSequence.create("param_" + (i))), utf8Constants.getOrCreate(param.getType()), 0, 65536, i));
121+
constructedLiveLocals.add(new Local(utf8Constants.getOrCreate(ByteSequence.create("arg_" + i)), utf8Constants.getOrCreate(paramType), 0, 0xffff, i));
123122
slotToLocal.remove(i);
124123
}
125124
}
126-
for (Local local : slotToLocal.values()) {
127-
// add non-parameters last
128-
constructedLiveLocals.add(local);
129-
}
130-
liveLocals = constructedLiveLocals.toArray(new Local[constructedLiveLocals.size()]);
125+
// add non-parameters last
126+
constructedLiveLocals.addAll(slotToLocal.values());
127+
liveLocals = constructedLiveLocals.toArray(Local.EMPTY_ARRAY);
131128
}
132129
return EspressoScope.createVariables(liveLocals, frame, method.getName());
133130
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@
2727

2828
import com.oracle.truffle.api.frame.Frame;
2929
import com.oracle.truffle.api.frame.VirtualFrame;
30+
import com.oracle.truffle.api.interop.NodeLibrary;
31+
import com.oracle.truffle.api.library.ExportLibrary;
32+
import com.oracle.truffle.api.library.ExportMessage;
3033
import com.oracle.truffle.api.nodes.Node;
3134
import com.oracle.truffle.espresso.impl.Method;
3235
import com.oracle.truffle.espresso.meta.EspressoError;
3336
import com.oracle.truffle.espresso.perf.DebugCounter;
3437
import com.oracle.truffle.espresso.substitutions.JavaSubstitution;
3538
import com.oracle.truffle.espresso.vm.VM;
3639

40+
@ExportLibrary(NodeLibrary.class)
3741
public final class IntrinsicSubstitutorNode extends EspressoInstrumentableRootNodeImpl {
3842
@Child private JavaSubstitution substitution;
3943

@@ -95,4 +99,16 @@ public int getBci(Frame frame) {
9599
return 0;
96100
}
97101
}
102+
103+
@ExportMessage
104+
@SuppressWarnings("static-method")
105+
public boolean hasScope(@SuppressWarnings("unused") Frame frame) {
106+
return true;
107+
}
108+
109+
@ExportMessage
110+
@SuppressWarnings("static-method")
111+
public Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) {
112+
return new SubstitutionScope(frame.getArguments(), getMethodVersion());
113+
}
98114
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@
2525

2626
import com.oracle.truffle.api.frame.Frame;
2727
import com.oracle.truffle.api.frame.VirtualFrame;
28+
import com.oracle.truffle.api.interop.NodeLibrary;
29+
import com.oracle.truffle.api.library.ExportLibrary;
30+
import com.oracle.truffle.api.library.ExportMessage;
2831
import com.oracle.truffle.espresso.impl.Method;
2932
import com.oracle.truffle.espresso.substitutions.CallableFromNative;
3033
import com.oracle.truffle.espresso.vm.VM;
3134

35+
@ExportLibrary(NodeLibrary.class)
3236
final class IntrinsifiedNativeMethodNode extends EspressoInstrumentableRootNodeImpl {
3337
@Child private CallableFromNative nativeMethod;
3438
private final Object env;
@@ -58,4 +62,16 @@ Object execute(VirtualFrame frame) {
5862
public int getBci(Frame frame) {
5963
return VM.EspressoStackElement.NATIVE_BCI;
6064
}
65+
66+
@ExportMessage
67+
@SuppressWarnings("static-method")
68+
public boolean hasScope(@SuppressWarnings("unused") Frame frame) {
69+
return true;
70+
}
71+
72+
@ExportMessage
73+
@SuppressWarnings("static-method")
74+
public Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) {
75+
return new SubstitutionScope(frame.getArguments(), getMethodVersion());
76+
}
6177
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@
2929
import com.oracle.truffle.api.frame.VirtualFrame;
3030
import com.oracle.truffle.api.interop.ArityException;
3131
import com.oracle.truffle.api.interop.InteropLibrary;
32+
import com.oracle.truffle.api.interop.NodeLibrary;
3233
import com.oracle.truffle.api.interop.TruffleObject;
3334
import com.oracle.truffle.api.interop.UnsupportedMessageException;
3435
import com.oracle.truffle.api.interop.UnsupportedTypeException;
36+
import com.oracle.truffle.api.library.ExportLibrary;
37+
import com.oracle.truffle.api.library.ExportMessage;
3538
import com.oracle.truffle.api.nodes.ExplodeLoop;
3639
import com.oracle.truffle.espresso.descriptors.Signatures;
3740
import com.oracle.truffle.espresso.descriptors.Symbol;
@@ -49,6 +52,7 @@
4952
/**
5053
* Represents a native Java method.
5154
*/
55+
@ExportLibrary(NodeLibrary.class)
5256
final class NativeMethodNode extends EspressoInstrumentableRootNodeImpl {
5357

5458
private final TruffleObject boundNative;
@@ -151,4 +155,16 @@ private Object processResult(JniEnv env, Object result) {
151155
public int getBci(Frame frame) {
152156
return VM.EspressoStackElement.NATIVE_BCI;
153157
}
158+
159+
@ExportMessage
160+
@SuppressWarnings("static-method")
161+
public boolean hasScope(@SuppressWarnings("unused") Frame frame) {
162+
return true;
163+
}
164+
165+
@ExportMessage
166+
@SuppressWarnings("static-method")
167+
public Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) {
168+
return new SubstitutionScope(frame.getArguments(), getMethodVersion());
169+
}
154170
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.espresso.nodes;
24+
25+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
26+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
27+
import com.oracle.truffle.api.TruffleLanguage;
28+
import com.oracle.truffle.api.interop.InteropLibrary;
29+
import com.oracle.truffle.api.interop.TruffleObject;
30+
import com.oracle.truffle.api.interop.UnknownIdentifierException;
31+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
32+
import com.oracle.truffle.api.library.ExportLibrary;
33+
import com.oracle.truffle.api.library.ExportMessage;
34+
import com.oracle.truffle.espresso.EspressoLanguage;
35+
import com.oracle.truffle.espresso.classfile.ConstantPool;
36+
import com.oracle.truffle.espresso.classfile.attributes.MethodParametersAttribute;
37+
import com.oracle.truffle.espresso.descriptors.Symbol;
38+
import com.oracle.truffle.espresso.impl.Method;
39+
40+
@ExportLibrary(InteropLibrary.class)
41+
final class SubstitutionScope implements TruffleObject {
42+
@CompilationFinal(dimensions = 1) private final Object[] args;
43+
private final Method method;
44+
private String[] paramNames;
45+
46+
SubstitutionScope(Object[] arguments, Method.MethodVersion method) {
47+
this.args = arguments;
48+
this.method = method.getMethod();
49+
}
50+
51+
@ExportMessage
52+
@SuppressWarnings("static-method")
53+
boolean hasMembers() {
54+
return true;
55+
}
56+
57+
@ExportMessage
58+
@SuppressWarnings("static-method")
59+
boolean isScope() {
60+
return true;
61+
}
62+
63+
@ExportMessage
64+
@SuppressWarnings("static-method")
65+
boolean hasLanguage() {
66+
return true;
67+
}
68+
69+
@ExportMessage
70+
@SuppressWarnings("static-method")
71+
Class<? extends TruffleLanguage<?>> getLanguage() {
72+
return EspressoLanguage.class;
73+
}
74+
75+
@ExportMessage
76+
@TruffleBoundary
77+
Object readMember(String member) throws UnknownIdentifierException {
78+
try {
79+
int index = Integer.parseInt(member);
80+
if (index < 0 || index >= args.length) {
81+
throw UnknownIdentifierException.create(member);
82+
}
83+
return args[index];
84+
} catch (NumberFormatException e) {
85+
// OK, see if we have parameter names as a fallback.
86+
// The main use case which is JDWP doesn't use anything but slots,
87+
// so this branch should rarely be taken.
88+
String[] names = paramNames;
89+
if (names == null) {
90+
names = paramNames = fetchNames();
91+
}
92+
for (int i = 0; i < names.length; i++) {
93+
if (names[i].equals(member)) {
94+
return args[i];
95+
}
96+
}
97+
throw UnknownIdentifierException.create(member);
98+
}
99+
}
100+
101+
private String[] fetchNames() {
102+
MethodParametersAttribute methodParameters = (MethodParametersAttribute) method.getAttribute(Symbol.Name.MethodParameters);
103+
104+
if (methodParameters == null) {
105+
return new String[0];
106+
}
107+
// verify parameter attribute first
108+
MethodParametersAttribute.Entry[] entries = methodParameters.getEntries();
109+
int cpLength = method.getConstantPool().length();
110+
for (MethodParametersAttribute.Entry entry : entries) {
111+
int nameIndex = entry.getNameIndex();
112+
if (nameIndex < 0 || nameIndex >= cpLength) {
113+
return new String[0];
114+
}
115+
if (nameIndex != 0 && method.getConstantPool().tagAt(nameIndex) != ConstantPool.Tag.UTF8) {
116+
return new String[0];
117+
}
118+
}
119+
String[] result = new String[entries.length];
120+
for (int i = 0; i < entries.length; i++) {
121+
MethodParametersAttribute.Entry entry = entries[i];
122+
// For a 0 index, give an empty name.
123+
String name;
124+
if (entry.getNameIndex() != 0) {
125+
name = method.getConstantPool().symbolAt(entry.getNameIndex(), "parameter name").toString();
126+
} else {
127+
name = "";
128+
}
129+
result[i] = name;
130+
}
131+
return result;
132+
}
133+
134+
@ExportMessage
135+
@SuppressWarnings("static-method")
136+
Object getMembers(@SuppressWarnings("unused") boolean includeInternal) throws UnsupportedMessageException {
137+
throw UnsupportedMessageException.create();
138+
}
139+
140+
@ExportMessage
141+
@TruffleBoundary
142+
boolean isMemberReadable(String member) {
143+
try {
144+
int index = Integer.parseInt(member);
145+
return 0 <= index && index < args.length;
146+
} catch (NumberFormatException e) {
147+
// OK, see if we have parameter names as a fallback.
148+
// The main use case which is JDWP doesn't use anything but slots,
149+
// so this branch should rarely be taken.
150+
String[] names = paramNames;
151+
if (names == null) {
152+
names = paramNames = fetchNames();
153+
}
154+
for (String name : names) {
155+
if (name.equals(member)) {
156+
return true;
157+
}
158+
}
159+
return false;
160+
}
161+
}
162+
163+
@ExportMessage
164+
@SuppressWarnings("static-method")
165+
Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
166+
return method.getNameAsString();
167+
}
168+
169+
}

0 commit comments

Comments
 (0)