Skip to content

Commit 1768167

Browse files
committed
[GR-64583] Fix no unwrapping in interop read array element.
PullRequest: graal/20668
2 parents 293a883 + 7b61670 commit 1768167

File tree

12 files changed

+250
-134
lines changed

12 files changed

+250
-134
lines changed

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -82,6 +82,8 @@
8282
import com.oracle.truffle.espresso.meta.InteropKlassesDispatch;
8383
import com.oracle.truffle.espresso.meta.Meta;
8484
import com.oracle.truffle.espresso.meta.MetaUtil;
85+
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNode;
86+
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNodeGen;
8587
import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethod;
8688
import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethodNodeGen;
8789
import com.oracle.truffle.espresso.nodes.interop.LookupFieldNode;
@@ -94,7 +96,6 @@
9496
import com.oracle.truffle.espresso.runtime.EspressoException;
9597
import com.oracle.truffle.espresso.runtime.EspressoFunction;
9698
import com.oracle.truffle.espresso.runtime.GuestAllocator;
97-
import com.oracle.truffle.espresso.runtime.InteropUtils;
9899
import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics;
99100
import com.oracle.truffle.espresso.runtime.dispatch.staticobject.BaseInterop;
100101
import com.oracle.truffle.espresso.runtime.dispatch.staticobject.EspressoInterop;
@@ -177,13 +178,14 @@ static Object doShared(Klass receiver, String member,
177178
@Bind Node node,
178179
@Cached @Shared InlinedBranchProfile error,
179180
@Bind("getLang(lib)") @SuppressWarnings("unused") EspressoLanguage language) throws UnknownIdentifierException {
180-
return readMember(receiver, member, LookupFieldNodeGen.getUncached(), LookupDeclaredMethodNodeGen.getUncached(), node, error, lib, language);
181+
return readMember(receiver, member, LookupFieldNodeGen.getUncached(), LookupDeclaredMethodNodeGen.getUncached(), InteropUnwrapNodeGen.getUncached(), node, error, lib, language);
181182
}
182183

183184
@Specialization
184185
static Object readMember(Klass receiver, String member,
185186
@Shared("lookupField") @Cached LookupFieldNode lookupFieldNode,
186187
@Shared("lookupMethod") @Cached LookupDeclaredMethod lookupMethod,
188+
@Cached InteropUnwrapNode unwrapNode,
187189
@Bind Node node,
188190
@Cached @Shared InlinedBranchProfile error,
189191
@CachedLibrary("receiver") InteropLibrary lib,
@@ -194,7 +196,7 @@ static Object readMember(Klass receiver, String member,
194196
if (field != null) {
195197
Object result = field.get(receiver.tryInitializeAndGetStatics());
196198
if (result instanceof StaticObject) {
197-
result = InteropUtils.unwrap(language, (StaticObject) result, meta);
199+
result = unwrapNode.execute(result);
198200
}
199201
return result;
200202
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) 2025, 2025, 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.interop;
24+
25+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
26+
import com.oracle.truffle.api.dsl.Bind;
27+
import com.oracle.truffle.api.dsl.Cached;
28+
import com.oracle.truffle.api.dsl.Fallback;
29+
import com.oracle.truffle.api.dsl.GenerateUncached;
30+
import com.oracle.truffle.api.dsl.ImportStatic;
31+
import com.oracle.truffle.api.dsl.Specialization;
32+
import com.oracle.truffle.api.nodes.Node;
33+
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
34+
import com.oracle.truffle.espresso.EspressoLanguage;
35+
import com.oracle.truffle.espresso.impl.Klass;
36+
import com.oracle.truffle.espresso.meta.Meta;
37+
import com.oracle.truffle.espresso.nodes.EspressoNode;
38+
import com.oracle.truffle.espresso.runtime.EspressoContext;
39+
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
40+
import com.oracle.truffle.espresso.vm.VM;
41+
42+
@GenerateUncached
43+
@ImportStatic(EspressoContext.class)
44+
public abstract class InteropUnwrapNode extends EspressoNode {
45+
46+
public abstract Object execute(Object object);
47+
48+
@Specialization(guards = {"!object.isForeignObject()", "object.getKlass() == cachedKlass"}, limit = "3")
49+
@SuppressWarnings("unused")
50+
static Object doStaticObjectCached(StaticObject object,
51+
@Bind Node node,
52+
@Bind("get(node).getMeta()") Meta meta,
53+
@Cached("object.getKlass()") Klass cachedKlass,
54+
@Cached("meta.isBoxed(cachedKlass)") boolean needsUnboxing,
55+
@Cached("maybeForeignException(cachedKlass, meta)") boolean foreignException) {
56+
assert (cachedKlass == null) == StaticObject.isNull(object);
57+
if (cachedKlass == null) { // is StaticObject.NULL
58+
return object;
59+
} else if (needsUnboxing) {
60+
return unboxBoundary(EspressoContext.get(node).getMeta(), object);
61+
} else if (foreignException) {
62+
return unwrapForeignException(object, EspressoContext.get(node).getMeta());
63+
} else {
64+
return object;
65+
}
66+
}
67+
68+
@Specialization(guards = "!object.isForeignObject()", replaces = "doStaticObjectCached")
69+
static Object doStaticObjectGeneric(StaticObject object,
70+
@Bind Node node,
71+
@Bind("get(node).getMeta()") Meta meta,
72+
@Cached InlinedBranchProfile seenNull,
73+
@Cached InlinedBranchProfile seenBoxed,
74+
@Cached InlinedBranchProfile seenForeignException,
75+
@Cached InlinedBranchProfile seenRegular) {
76+
Klass klass = object.getKlass();
77+
assert (klass == null) == StaticObject.isNull(object);
78+
if (klass == null) { // is StaticObject.NULL
79+
seenNull.enter(node);
80+
return object;
81+
} else if (meta.isBoxed(klass)) {
82+
seenBoxed.enter(node);
83+
return unboxBoundary(meta, object);
84+
} else if (maybeForeignException(klass, meta)) {
85+
seenForeignException.enter(node);
86+
return unwrapForeignException(object, meta);
87+
} else {
88+
seenRegular.enter(node);
89+
return object;
90+
}
91+
}
92+
93+
@Specialization(guards = "object.isForeignObject()")
94+
static Object doStaticObjectForeign(StaticObject object, @Bind Node node) {
95+
return object.rawForeignObject(EspressoLanguage.get(node));
96+
}
97+
98+
@Fallback
99+
public static Object doOther(Object object) {
100+
return object;
101+
}
102+
103+
@TruffleBoundary
104+
private static Object unboxBoundary(Meta meta, StaticObject object) {
105+
// meta.unboxGuest would need to be properly optimized as well
106+
return meta.unboxGuest(object);
107+
}
108+
109+
static boolean maybeForeignException(Klass klass, Meta meta) {
110+
return klass != null && meta.polyglot != null && meta.java_lang_Throwable.isAssignableFrom(klass);
111+
}
112+
113+
private static Object unwrapForeignException(StaticObject object, Meta meta) {
114+
assert meta.java_lang_Throwable.isAssignableFrom(object.getKlass());
115+
if (meta.HIDDEN_FRAMES.getHiddenObject(object) == VM.StackTrace.FOREIGN_MARKER_STACK_TRACE) {
116+
return meta.java_lang_Throwable_backtrace.getObject(object).rawForeignObject(meta.getLanguage());
117+
}
118+
return object;
119+
}
120+
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/InvokeEspressoNode.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -49,7 +49,7 @@
4949
public abstract class InvokeEspressoNode extends EspressoNode {
5050
static final int LIMIT = 4;
5151

52-
public final Object execute(Method method, Object receiver, Object[] arguments, boolean argsConverted) throws ArityException, UnsupportedTypeException {
52+
public final Object execute(Method method, Object receiver, Object[] arguments, boolean argsConverted, InteropUnwrapNode unwrapNode) throws ArityException, UnsupportedTypeException {
5353
Method.MethodVersion resolutionSeed = method.getMethodVersion();
5454
if (!resolutionSeed.getRedefineAssumption().isValid()) {
5555
// OK, we know it's a removed method then
@@ -67,7 +67,7 @@ public final Object execute(Method method, Object receiver, Object[] arguments,
6767
* Invariant: Foreign objects are always wrapped when coming into Espresso and unwrapped
6868
* when going out.
6969
*/
70-
return InteropUtils.unwrap(language, result, meta);
70+
return unwrapNode.execute(result);
7171
} catch (EspressoException e) {
7272
/*
7373
* Invariant: Foreign exceptions are always unwrapped when going out of Espresso.
@@ -78,8 +78,8 @@ public final Object execute(Method method, Object receiver, Object[] arguments,
7878
}
7979
}
8080

81-
public final Object execute(Method method, Object receiver, Object[] arguments) throws ArityException, UnsupportedTypeException {
82-
return execute(method, receiver, arguments, false);
81+
public final Object execute(Method method, Object receiver, Object[] arguments, InteropUnwrapNode unwrapNode) throws ArityException, UnsupportedTypeException {
82+
return execute(method, receiver, arguments, false, unwrapNode);
8383
}
8484

8585
static DirectCallNode createDirectCallNode(CallTarget callTarget) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/LookupAndInvokeKnownMethodNode.java

+21-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -117,45 +117,51 @@ Object doInterfaceCached(StaticObject receiver, Method resolutionSeed, Object[]
117117
@Cached("resolutionSeed") Method cachedSeed,
118118
@Cached("receiver.getKlass()") Klass cachedKlass,
119119
@Cached("interfaceLookup(receiver, resolutionSeed)") Method m,
120-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
120+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
121+
@Cached InteropUnwrapNode unwrapNode) {
121122
assert m.getParameterCount() == arguments.length;
122-
return invoke(invoke, m, receiver, arguments);
123+
return invoke(invoke, m, receiver, arguments, unwrapNode);
123124
}
124125

125126
@Specialization(guards = {"resolutionSeed == cachedSeed", "!cachedSeed.getDeclaringKlass().isInterface()", "receiver.getKlass() == cachedKlass"}, limit = "LIMIT")
126127
Object doVirtualCached(StaticObject receiver, Method resolutionSeed, Object[] arguments,
127128
@Cached("resolutionSeed") Method cachedSeed,
128129
@Cached("receiver.getKlass()") Klass cachedKlass,
129130
@Cached("virtualLookup(receiver, resolutionSeed)") Method m,
130-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
131+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
132+
@Cached InteropUnwrapNode unwrapNode) {
131133
assert m.getParameterCount() == arguments.length;
132-
return invoke(invoke, m, receiver, arguments);
134+
return invoke(invoke, m, receiver, arguments, unwrapNode);
133135
}
134136

135137
@Specialization(guards = {"resolutionSeed == cachedSeed", "cachedSeed.getDeclaringKlass().isInterface()"}, replaces = {"doInterfaceCachedNoArg", "doInterfaceCached"}, limit = "1")
136138
Object doInterfaceUncached(StaticObject receiver, Method resolutionSeed, Object[] arguments,
137139
@Cached("resolutionSeed") Method cachedSeed,
138-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
139-
return invoke(invoke, interfaceLookup(receiver, cachedSeed), receiver, arguments);
140+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
141+
@Cached InteropUnwrapNode unwrapNode) {
142+
return invoke(invoke, interfaceLookup(receiver, cachedSeed), receiver, arguments, unwrapNode);
140143
}
141144

142145
@Specialization(guards = {"resolutionSeed == cachedSeed", "!cachedSeed.getDeclaringKlass().isInterface()"}, replaces = {"doVirtualCachedNoArg", "doVirtualCached"}, limit = "1")
143146
Object doVirtualUncached(StaticObject receiver, Method resolutionSeed, Object[] arguments,
144147
@Cached("resolutionSeed") Method cachedSeed,
145-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
146-
return invoke(invoke, virtualLookup(receiver, cachedSeed), receiver, arguments);
148+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
149+
@Cached InteropUnwrapNode unwrapNode) {
150+
return invoke(invoke, virtualLookup(receiver, cachedSeed), receiver, arguments, unwrapNode);
147151
}
148152

149153
@Specialization(guards = {"resolutionSeed.getDeclaringKlass().isInterface()"}, replaces = {"doInterfaceUncached"})
150154
Object doInterfaceUnknown(StaticObject receiver, Method resolutionSeed, Object[] arguments,
151-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
152-
return invoke(invoke, interfaceLookup(receiver, resolutionSeed), receiver, arguments);
155+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
156+
@Cached InteropUnwrapNode unwrapNode) {
157+
return invoke(invoke, interfaceLookup(receiver, resolutionSeed), receiver, arguments, unwrapNode);
153158
}
154159

155160
@Specialization(guards = {"!resolutionSeed.getDeclaringKlass().isInterface()"}, replaces = {"doVirtualUncached"})
156161
Object doVirtualUnknown(StaticObject receiver, Method resolutionSeed, Object[] arguments,
157-
@Cached.Exclusive @Cached InvokeEspressoNode invoke) {
158-
return invoke(invoke, virtualLookup(receiver, resolutionSeed), receiver, arguments);
162+
@Cached.Exclusive @Cached InvokeEspressoNode invoke,
163+
@Cached InteropUnwrapNode unwrapNode) {
164+
return invoke(invoke, virtualLookup(receiver, resolutionSeed), receiver, arguments, unwrapNode);
159165
}
160166

161167
Method interfaceLookup(StaticObject receiver, Method resolutionSeed) {
@@ -168,9 +174,9 @@ Method virtualLookup(StaticObject receiver, Method resolutionSeed) {
168174
return EspressoInterop.getInteropKlass(receiver).vtableLookup(resolutionSeed.getVTableIndex());
169175
}
170176

171-
private static Object invoke(InvokeEspressoNode invoke, Method m, StaticObject receiver, Object[] arguments) {
177+
private static Object invoke(InvokeEspressoNode invoke, Method m, StaticObject receiver, Object[] arguments, InteropUnwrapNode unwrapNode) {
172178
try {
173-
return invoke.execute(m, receiver, arguments);
179+
return invoke.execute(m, receiver, arguments, unwrapNode);
174180
} catch (ArityException | UnsupportedTypeException e) {
175181
CompilerDirectives.transferToInterpreterAndInvalidate();
176182
throw EspressoError.shouldNotReachHere(e);

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoFunction.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
3030
import com.oracle.truffle.api.library.ExportLibrary;
3131
import com.oracle.truffle.api.library.ExportMessage;
3232
import com.oracle.truffle.espresso.impl.Method;
33+
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNode;
3334
import com.oracle.truffle.espresso.nodes.interop.InvokeEspressoNode;
3435
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
3536

@@ -60,7 +61,8 @@ public boolean isExecutable() {
6061

6162
@ExportMessage
6263
public Object execute(Object[] args,
63-
@Cached InvokeEspressoNode invoke) throws ArityException, UnsupportedTypeException {
64-
return invoke.execute(m, receiver, args);
64+
@Cached InvokeEspressoNode invoke,
65+
@Cached InteropUnwrapNode unwrapNode) throws ArityException, UnsupportedTypeException {
66+
return invoke.execute(m, receiver, args, unwrapNode);
6567
}
6668
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/InteropUtils.java

+1-34
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -64,39 +64,6 @@ public static boolean isAtMostFloat(Klass klass) {
6464
return klass == meta.java_lang_Byte || klass == meta.java_lang_Short || klass == meta.java_lang_Float;
6565
}
6666

67-
public static Object unwrap(EspressoLanguage language, StaticObject object, Meta meta) {
68-
if (meta.isBoxed(object.getKlass())) {
69-
return meta.unboxGuest(object);
70-
}
71-
if (object.isForeignObject()) {
72-
return object.rawForeignObject(language);
73-
}
74-
// We need to unwrap foreign exceptions which are stored in guest throwable backtrace.
75-
// They only exist if polyglot is in use though.
76-
if (meta.polyglot == null || StaticObject.isNull(object)) {
77-
return object;
78-
}
79-
if (meta.java_lang_Throwable.isAssignableFrom(object.getKlass())) {
80-
return unwrapForeignException(object, meta);
81-
}
82-
return object;
83-
}
84-
85-
private static Object unwrapForeignException(StaticObject object, Meta meta) {
86-
assert meta.java_lang_Throwable.isAssignableFrom(object.getKlass());
87-
if (meta.HIDDEN_FRAMES.getHiddenObject(object) == VM.StackTrace.FOREIGN_MARKER_STACK_TRACE) {
88-
return meta.java_lang_Throwable_backtrace.getObject(object).rawForeignObject(meta.getLanguage());
89-
}
90-
return object;
91-
}
92-
93-
public static Object unwrap(EspressoLanguage language, Object object, Meta meta) {
94-
if (object instanceof StaticObject) {
95-
return unwrap(language, (StaticObject) object, meta);
96-
}
97-
return object;
98-
}
99-
10067
public static boolean isForeignException(EspressoException e) {
10168
assert e != null;
10269
StaticObject guestException = e.getGuestException();

0 commit comments

Comments
 (0)