Path: blob/master/test/jdk/java/lang/invoke/DefineClassTest.java
41149 views
/*1* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* @test24* @modules java.base/java.lang:open25* java.base/jdk.internal.org.objectweb.asm26* @run testng/othervm test.DefineClassTest27* @summary Basic test for java.lang.invoke.MethodHandles.Lookup.defineClass28*/2930package test;3132import java.lang.invoke.MethodHandles.Lookup;33import static java.lang.invoke.MethodHandles.*;34import static java.lang.invoke.MethodHandles.Lookup.*;35import java.net.URL;36import java.net.URLClassLoader;37import java.nio.file.Files;38import java.nio.file.Path;39import java.nio.file.Paths;4041import jdk.internal.org.objectweb.asm.ClassWriter;42import jdk.internal.org.objectweb.asm.MethodVisitor;43import static jdk.internal.org.objectweb.asm.Opcodes.*;4445import org.testng.annotations.Test;46import static org.testng.Assert.*;4748public class DefineClassTest {49private static final String THIS_PACKAGE = DefineClassTest.class.getPackageName();5051/**52* Test that a class has the same class loader, and is in the same package and53* protection domain, as a lookup class.54*/55void testSameAbode(Class<?> clazz, Class<?> lc) {56assertTrue(clazz.getClassLoader() == lc.getClassLoader());57assertEquals(clazz.getPackageName(), lc.getPackageName());58assertTrue(clazz.getProtectionDomain() == lc.getProtectionDomain());59}6061/**62* Tests that a class is discoverable by name using Class.forName and63* lookup.findClass64*/65void testDiscoverable(Class<?> clazz, Lookup lookup) throws Exception {66String cn = clazz.getName();67ClassLoader loader = clazz.getClassLoader();68assertTrue(Class.forName(cn, false, loader) == clazz);69assertTrue(lookup.findClass(cn) == clazz);70}7172/**73* Basic test of defineClass to define a class in the same package as test.74*/75@Test76public void testDefineClass() throws Exception {77final String CLASS_NAME = THIS_PACKAGE + ".Foo";78Lookup lookup = lookup();79Class<?> clazz = lookup.defineClass(generateClass(CLASS_NAME));8081// test name82assertEquals(clazz.getName(), CLASS_NAME);8384// test loader/package/protection-domain85testSameAbode(clazz, lookup.lookupClass());8687// test discoverable88testDiscoverable(clazz, lookup);8990// attempt defineClass again91try {92lookup.defineClass(generateClass(CLASS_NAME));93assertTrue(false);94} catch (LinkageError expected) { }95}9697/**98* Test public/package/protected/private access from class defined with defineClass.99*/100@Test101public void testAccess() throws Exception {102final String THIS_CLASS = this.getClass().getName();103final String CLASS_NAME = THIS_PACKAGE + ".Runner";104Lookup lookup = lookup();105106// public107byte[] classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method1");108testInvoke(lookup.defineClass(classBytes));109110// package111classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method2");112testInvoke(lookup.defineClass(classBytes));113114// protected (same package)115classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method3");116testInvoke(lookup.defineClass(classBytes));117118// private119classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method4");120Class<?> clazz = lookup.defineClass(classBytes);121Runnable r = (Runnable) clazz.newInstance();122try {123r.run();124assertTrue(false);125} catch (IllegalAccessError expected) { }126}127128public static void method1() { }129static void method2() { }130protected static void method3() { }131private static void method4() { }132133void testInvoke(Class<?> clazz) throws Exception {134Object obj = clazz.newInstance();135((Runnable) obj).run();136}137138/**139* Test that defineClass does not run the class initializer140*/141@Test142public void testInitializerNotRun() throws Exception {143final String THIS_CLASS = this.getClass().getName();144final String CLASS_NAME = THIS_PACKAGE + ".ClassWithClinit";145146byte[] classBytes = generateClassWithInitializer(CLASS_NAME, THIS_CLASS, "fail");147Class<?> clazz = lookup().defineClass(classBytes);148149// trigger initializer to run150try {151clazz.newInstance();152assertTrue(false);153} catch (ExceptionInInitializerError e) {154assertTrue(e.getCause() instanceof IllegalCallerException);155}156}157158static void fail() { throw new IllegalCallerException(); }159160161/**162* Test defineClass to define classes in a package containing classes with163* different protection domains.164*/165@Test166public void testTwoProtectionDomains() throws Exception {167Path here = Paths.get("");168169// p.C1 in one exploded directory170Path dir1 = Files.createTempDirectory(here, "classes");171Path p = Files.createDirectory(dir1.resolve("p"));172Files.write(p.resolve("C1.class"), generateClass("p.C1"));173URL url1 = dir1.toUri().toURL();174175// p.C2 in another exploded directory176Path dir2 = Files.createTempDirectory(here, "classes");177p = Files.createDirectory(dir2.resolve("p"));178Files.write(p.resolve("C2.class"), generateClass("p.C2"));179URL url2 = dir2.toUri().toURL();180181// load p.C1 and p.C2182ClassLoader loader = new URLClassLoader(new URL[] { url1, url2 });183Class<?> target1 = Class.forName("p.C1", false, loader);184Class<?> target2 = Class.forName("p.C2", false, loader);185assertTrue(target1.getClassLoader() == loader);186assertTrue(target1.getClassLoader() == loader);187assertNotEquals(target1.getProtectionDomain(), target2.getProtectionDomain());188189// protection domain 1190Lookup lookup1 = privateLookupIn(target1, lookup());191192Class<?> clazz = lookup1.defineClass(generateClass("p.Foo"));193testSameAbode(clazz, lookup1.lookupClass());194testDiscoverable(clazz, lookup1);195196// protection domain 2197Lookup lookup2 = privateLookupIn(target2, lookup());198199clazz = lookup2.defineClass(generateClass("p.Bar"));200testSameAbode(clazz, lookup2.lookupClass());201testDiscoverable(clazz, lookup2);202}203204/**205* Test defineClass defining a class to the boot loader206*/207@Test208public void testBootLoader() throws Exception {209Lookup lookup = privateLookupIn(Thread.class, lookup());210assertTrue(lookup.getClass().getClassLoader() == null);211212Class<?> clazz = lookup.defineClass(generateClass("java.lang.Foo"));213assertEquals(clazz.getName(), "java.lang.Foo");214testSameAbode(clazz, Thread.class);215testDiscoverable(clazz, lookup);216}217218@Test(expectedExceptions = { IllegalArgumentException.class })219public void testWrongPackage() throws Exception {220lookup().defineClass(generateClass("other.C"));221}222223@Test(expectedExceptions = { IllegalAccessException.class })224public void testNoPackageAccess() throws Exception {225Lookup lookup = lookup().dropLookupMode(PACKAGE);226lookup.defineClass(generateClass(THIS_PACKAGE + ".C"));227}228229@Test(expectedExceptions = { ClassFormatError.class })230public void testTruncatedClassFile() throws Exception {231lookup().defineClass(new byte[0]);232}233234@Test(expectedExceptions = { NullPointerException.class })235public void testNull() throws Exception {236lookup().defineClass(null);237}238239@Test(expectedExceptions = { NoClassDefFoundError.class })240public void testLinking() throws Exception {241lookup().defineClass(generateNonLinkableClass(THIS_PACKAGE + ".NonLinkableClass"));242}243244@Test(expectedExceptions = { IllegalArgumentException.class })245public void testModuleInfo() throws Exception {246lookup().defineClass(generateModuleInfo());247}248249/**250* Generates a class file with the given class name251*/252byte[] generateClass(String className) {253ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS254+ ClassWriter.COMPUTE_FRAMES);255cw.visit(V9,256ACC_PUBLIC + ACC_SUPER,257className.replace(".", "/"),258null,259"java/lang/Object",260null);261262// <init>263MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);264mv.visitVarInsn(ALOAD, 0);265mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);266mv.visitInsn(RETURN);267mv.visitMaxs(0, 0);268mv.visitEnd();269270cw.visitEnd();271return cw.toByteArray();272}273274/**275* Generate a class file with the given class name. The class implements Runnable276* with a run method to invokestatic the given targetClass/targetMethod.277*/278byte[] generateRunner(String className,279String targetClass,280String targetMethod) throws Exception {281282ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS283+ ClassWriter.COMPUTE_FRAMES);284cw.visit(V9,285ACC_PUBLIC + ACC_SUPER,286className.replace(".", "/"),287null,288"java/lang/Object",289new String[] { "java/lang/Runnable" });290291// <init>292MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);293mv.visitVarInsn(ALOAD, 0);294mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);295mv.visitInsn(RETURN);296mv.visitMaxs(0, 0);297mv.visitEnd();298299// run()300String tc = targetClass.replace(".", "/");301mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);302mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);303mv.visitInsn(RETURN);304mv.visitMaxs(0, 0);305mv.visitEnd();306307cw.visitEnd();308return cw.toByteArray();309}310311/**312* Generate a class file with the given class name. The class will initializer313* to invokestatic the given targetClass/targetMethod.314*/315byte[] generateClassWithInitializer(String className,316String targetClass,317String targetMethod) throws Exception {318319ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS320+ ClassWriter.COMPUTE_FRAMES);321cw.visit(V9,322ACC_PUBLIC + ACC_SUPER,323className.replace(".", "/"),324null,325"java/lang/Object",326null);327328// <init>329MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);330mv.visitVarInsn(ALOAD, 0);331mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);332mv.visitInsn(RETURN);333mv.visitMaxs(0, 0);334mv.visitEnd();335336// <clinit>337String tc = targetClass.replace(".", "/");338mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);339mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);340mv.visitInsn(RETURN);341mv.visitMaxs(0, 0);342mv.visitEnd();343344cw.visitEnd();345return cw.toByteArray();346}347348/**349* Generates a non-linkable class file with the given class name350*/351byte[] generateNonLinkableClass(String className) {352ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS353+ ClassWriter.COMPUTE_FRAMES);354cw.visit(V14,355ACC_PUBLIC + ACC_SUPER,356className.replace(".", "/"),357null,358"MissingSuperClass",359null);360361// <init>362MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);363mv.visitVarInsn(ALOAD, 0);364mv.visitMethodInsn(INVOKESPECIAL, "MissingSuperClass", "<init>", "()V", false);365mv.visitInsn(RETURN);366mv.visitMaxs(0, 0);367mv.visitEnd();368369cw.visitEnd();370return cw.toByteArray();371}372373/**374* Generates a class file with the given class name375*/376byte[] generateModuleInfo() {377ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS378+ ClassWriter.COMPUTE_FRAMES);379cw.visit(V14,380ACC_MODULE,381"module-info",382null,383null,384null);385386cw.visitEnd();387return cw.toByteArray();388}389390private int nextNumber() {391return ++nextNumber;392}393394private int nextNumber;395}396397398