Skip to content

Commit b3d69e4

Browse files
committed
Restore public access from inherited classes when public access is enabled.
1 parent 676ed13 commit b3d69e4

File tree

6 files changed

+156
-6
lines changed

6 files changed

+156
-6
lines changed

truffle/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
55
## Version 25.0
66
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.
77
* GR-61493 Added `RootNode.prepareForCall` which allows root nodes to prepare themselves for use as a call target (or to validate whether they can be used as a call target).
8+
* GR-63075 Java host interop again inherits public method methods from non-public base classes if public access is enabled. This was originally changed in 24.1.
89

910
## Version 24.2.0
1011
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.

truffle/src/com.oracle.truffle.api.test/src/META-INF/native-image/reflect-config.json

+12
Original file line numberDiff line numberDiff line change
@@ -271,5 +271,17 @@
271271
"methods": [
272272
{ "name": "getIsolate", "parameterTypes": ["java.lang.Object"] }
273273
]
274+
},
275+
{
276+
"name": "com.oracle.truffle.api.test.host.GR63075Test$BaseClass",
277+
"allPublicMethods": true
278+
},
279+
{
280+
"name": "com.oracle.truffle.api.test.host.GR63075Test$SubClass",
281+
"allPublicMethods": true
282+
},
283+
{
284+
"name": "com.oracle.truffle.api.test.host.GR63075Test$NonPublicSubClass",
285+
"allPublicMethods": true
274286
}
275287
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.test.host;
42+
43+
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.assertNull;
45+
46+
import org.graalvm.polyglot.Context;
47+
import org.graalvm.polyglot.HostAccess;
48+
import org.graalvm.polyglot.HostAccess.Export;
49+
import org.graalvm.polyglot.Value;
50+
import org.junit.Test;
51+
52+
public class GR63075Test {
53+
54+
static class BaseClass {
55+
56+
@Export
57+
public String publicAbstractMethod() {
58+
return "publicAbstractMethod";
59+
}
60+
61+
}
62+
63+
public static class SubClass extends BaseClass {
64+
65+
@Export
66+
public String publicMethod() {
67+
return "publicMethod";
68+
}
69+
70+
}
71+
72+
static class NonPublicSubClass extends BaseClass {
73+
74+
public String publicMethod() {
75+
return "publicMethod";
76+
}
77+
78+
}
79+
80+
@Test
81+
public void testPublicAccess() {
82+
try (Context c = Context.newBuilder().allowHostAccess(HostAccess.ALL).build()) {
83+
SubClass b = new SubClass();
84+
Value v = c.asValue(b);
85+
86+
assertEquals("publicMethod", v.invokeMember("publicMethod").asString());
87+
assertEquals("publicAbstractMethod", v.invokeMember("publicAbstractMethod").asString());
88+
}
89+
}
90+
91+
@Test
92+
public void testNonPublicAccess() {
93+
try (Context c = Context.newBuilder().allowHostAccess(HostAccess.ALL).build()) {
94+
NonPublicSubClass b = new NonPublicSubClass();
95+
Value v = c.asValue(b);
96+
97+
assertNull(v.getMember("publicMethod"));
98+
assertNull(v.getMember("publicAbstractMethod"));
99+
}
100+
}
101+
102+
@Test
103+
public void testExplicitAccess() {
104+
try (Context c = Context.newBuilder().allowHostAccess(HostAccess.EXPLICIT).build()) {
105+
SubClass b = new SubClass();
106+
Value v = c.asValue(b);
107+
108+
assertEquals("publicMethod", v.invokeMember("publicMethod").asString());
109+
assertNull(v.getMember("publicAbstractMethod"));
110+
}
111+
}
112+
113+
}

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/VisibilityTest.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,8 @@ public void testStaticMethodNotInherited() throws InteropException {
256256
}
257257

258258
@Test
259-
public void testPublicClassBridgeMethod() {
260-
// GR-42882: public bridge method B1.run() for A3.run() no longer exposed.
261-
// invokeRun(new B1(), A3.class);
262-
Assert.assertFalse(INTEROP.isMemberExisting(asTruffleObject(new B1()), "run"));
259+
public void testPublicClassBridgeMethod() throws InteropException {
260+
invokeRun(new B1(), A3.class);
263261
}
264262

265263
@Test

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
import static org.junit.Assert.assertTrue;
5353
import static org.junit.Assert.fail;
5454

55-
import java.awt.*;
55+
import java.awt.Color;
5656
import java.awt.image.BufferedImage;
5757
import java.lang.reflect.Method;
5858
import java.nio.ByteBuffer;

truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostClassDesc.java

+27-1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ private static void collectPublicMethods(HostClassCache hostAccess, Class<?> typ
217217
Class<?> startType) {
218218
boolean isPublicType = getLookupForPublicClass(type, hostAccess) != null && !Proxy.isProxyClass(type);
219219
boolean includeInherited = hostAccess.allowsPublicAccess || hostAccess.allowsAccessInheritance;
220+
List<Method> bridgeMethods = null;
220221
/**
221222
* If we do not allow inheriting access, we discover all accessible methods through the
222223
* public methods of this class. That way, (possibly inaccessible) method overrides hide
@@ -231,8 +232,25 @@ private static void collectPublicMethods(HostClassCache hostAccess, Class<?> typ
231232
} else if (getLookupForPublicClass(declaringClass, hostAccess) == null && !Proxy.isProxyClass(declaringClass)) {
232233
// the declaring class and the method itself must be public and accessible
233234
continue;
235+
} else if (m.isBridge() && hostAccess.allowsPublicAccess && hostAccess.allowsAccess(m)) {
236+
/*
237+
* Bridge methods for varargs methods generated by javac may not have the
238+
* varargs modifier, so we must not use the bridge method in that case since
239+
* it would be then treated as non-varargs.
240+
*
241+
* As a workaround, stash away all bridge methods and only consider them at
242+
* the end if no equivalent public non-bridge method was found.
243+
*/
244+
if (bridgeMethods == null) {
245+
bridgeMethods = new ArrayList<>();
246+
}
247+
bridgeMethods.add(m);
234248
} else if (m.isBridge()) {
235-
// skip all bridge methods
249+
/*
250+
* GR-42882] Without public access we skip all bridge methods. This is
251+
* needed due to inconsistent behavior between javac and JDT. JDT does not
252+
* inherit exported methods for bridge methods.
253+
*/
236254
continue;
237255
}
238256
if (hostAccess.allowsAccess(m)) {
@@ -255,6 +273,14 @@ private static void collectPublicMethods(HostClassCache hostAccess, Class<?> typ
255273
}
256274
}
257275
}
276+
// Add bridge methods for public methods inherited from non-public superclasses.
277+
// See https://bugs.openjdk.java.net/browse/JDK-6342411
278+
if (bridgeMethods != null && !bridgeMethods.isEmpty()) {
279+
for (Method m : bridgeMethods) {
280+
assert hostAccess.allowsAccess(m); // already checked above
281+
collectPublicMethod(hostAccess, methodMap, staticMethodMap, visited, m);
282+
}
283+
}
258284
}
259285

260286
private static void collectPublicMethod(HostClassCache hostAccess, Map<String, HostMethodDesc> methodMap, Map<String, HostMethodDesc> staticMethodMap, Map<Object, Object> visited, Method m) {

0 commit comments

Comments
 (0)