f6d7e30b84
Reviewed-by: asotona
134 lines
5.3 KiB
Java
134 lines
5.3 KiB
Java
/*
|
|
* Copyright (c) 2021, 2023, 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.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8266925
|
|
* @summary hidden class members can't be statically invocable
|
|
* @modules java.base/jdk.internal.misc
|
|
* @enablePreview
|
|
* @build java.base/*
|
|
* @run testng StaticInvocableTest
|
|
*/
|
|
|
|
import java.lang.classfile.ClassFile;
|
|
import java.lang.constant.ClassDesc;
|
|
import java.lang.constant.MethodTypeDesc;
|
|
import java.lang.invoke.MethodHandle;
|
|
import java.lang.invoke.MethodHandles.Lookup;
|
|
import java.lang.invoke.MethodType;
|
|
import java.lang.invoke.LookupHelper;
|
|
import java.lang.reflect.AccessFlag;
|
|
import org.testng.annotations.Test;
|
|
|
|
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
|
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
|
import static java.lang.constant.ConstantDescs.CD_Object;
|
|
import static java.lang.constant.ConstantDescs.CD_int;
|
|
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
|
import static java.lang.constant.ConstantDescs.MTD_void;
|
|
|
|
public class StaticInvocableTest {
|
|
public static void main(String[] args) throws Throwable {
|
|
StaticInvocableTest test = new StaticInvocableTest();
|
|
test.testJavaLang();
|
|
test.testJavaUtil();
|
|
test.testJdkInternalMisc();
|
|
test.testJavaLangInvoke();
|
|
test.testProhibitedJavaPkg();
|
|
System.out.println("TEST PASSED");
|
|
}
|
|
|
|
// Test hidden classes from different packages
|
|
// (see j.l.i.InvokerBytecodeGenerator::isStaticallyInvocable).
|
|
@Test public void testJavaLang() throws Throwable { test("java/lang"); }
|
|
@Test public void testJavaUtil() throws Throwable { test("java/util"); }
|
|
@Test public void testJdkInternalMisc() throws Throwable { test("jdk/internal/misc"); }
|
|
@Test public void testJavaLangInvoke() throws Throwable { test("java/lang/invoke"); }
|
|
@Test public void testProhibitedJavaPkg() throws Throwable {
|
|
try {
|
|
test("java/prohibited");
|
|
} catch (IllegalArgumentException e) {
|
|
return;
|
|
}
|
|
throw new RuntimeException("Expected SecurityException");
|
|
}
|
|
|
|
private static void test(String pkg) throws Throwable {
|
|
byte[] bytes = dumpClass(pkg);
|
|
Lookup lookup;
|
|
if (pkg.equals("java/prohibited")) {
|
|
StaticInvocableTest sampleclass = new StaticInvocableTest();
|
|
lookup = LookupHelper.newLookup(sampleclass.getClass());
|
|
} else if (pkg.equals("java/lang")) {
|
|
lookup = LookupHelper.newLookup(Object.class);
|
|
} else if (pkg.equals("java/util")) {
|
|
lookup = LookupHelper.newLookup(java.util.ArrayList.class);
|
|
} else if (pkg.equals("jdk/internal/misc")) {
|
|
lookup = LookupHelper.newLookup(jdk.internal.misc.Signal.class);
|
|
} else if (pkg.equals("java/lang/invoke")) {
|
|
lookup = LookupHelper.newLookup(java.lang.invoke.CallSite.class);
|
|
} else {
|
|
throw new RuntimeException("Unexpected pkg: " + pkg);
|
|
}
|
|
|
|
// Define hidden class
|
|
Lookup l = lookup.defineHiddenClass(bytes, true);
|
|
|
|
MethodType t = MethodType.methodType(Object.class, int.class);
|
|
MethodHandle target = l.findStatic(l.lookupClass(), "get", t);
|
|
|
|
// Wrap target into LF (convert) to get "target" referenced from LF
|
|
MethodHandle wrappedMH = target.asType(MethodType.methodType(Object.class, Integer.class));
|
|
|
|
// Invoke enough times to provoke LF compilation to bytecode.
|
|
for (int i = 0; i<100; i++) {
|
|
Object r = wrappedMH.invokeExact((Integer)1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Constructs bytecode for the following class:
|
|
* public class pkg.MyClass {
|
|
* MyClass() {}
|
|
* public Object get(int i) { return null; }
|
|
* }
|
|
*/
|
|
public static byte[] dumpClass(String pkg) {
|
|
return ClassFile.of().build(ClassDesc.of(pkg.replace('/', '.'), "MyClass"), clb -> {
|
|
clb.withSuperclass(CD_Object);
|
|
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
|
clb.withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
|
|
cob.aload(0);
|
|
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
|
cob.return_();
|
|
});
|
|
clb.withMethodBody("get", MethodTypeDesc.of(CD_Object, CD_int),
|
|
ACC_PUBLIC | ACC_STATIC, cob -> {
|
|
cob.aconst_null();
|
|
cob.areturn();
|
|
});
|
|
});
|
|
}
|
|
}
|