1 /*
   2  * Copyright (c) 2018, 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 
  24 /*
  25  * @test
  26  * @library /test/lib
  27  * @modules java.base/jdk.internal.org.objectweb.asm
  28  * @build  DefineClassTest
  29  * @run testng/othervm DefineClassTest
  30  */
  31 
  32 import java.lang.invoke.MethodHandles;
  33 import java.lang.invoke.MethodHandles.Lookup;
  34 import java.lang.reflect.InvocationTargetException;
  35 import java.lang.reflect.Method;
  36 import java.util.stream.Stream;
  37 
  38 import jdk.internal.org.objectweb.asm.*;
  39 import org.testng.annotations.Test;
  40 
  41 import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*;
  42 import static java.lang.invoke.MethodHandles.Lookup.PRIVATE;
  43 
  44 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  45 import static org.testng.Assert.*;
  46 
  47 public class DefineClassTest {
  48     private static final byte[] bytes = classBytes("Injected");
  49     private static byte[] classBytes(String classname) {
  50         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
  51         MethodVisitor mv;
  52 
  53         cw.visit(V11, ACC_FINAL, classname, null, "java/lang/Object", null);
  54 
  55         {
  56             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  57             mv.visitCode();
  58             mv.visitVarInsn(ALOAD, 0);
  59             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
  60             mv.visitInsn(RETURN);
  61             mv.visitMaxs(0, 0);
  62             mv.visitEnd();
  63         }
  64         {
  65             // access a private member of the nest host class
  66             mv = cw.visitMethod(ACC_PUBLIC, "test", "(LDefineClassTest;)I", null, null);
  67             mv.visitCode();
  68             mv.visitVarInsn(ALOAD, 0);
  69             mv.visitVarInsn(ALOAD, 1);
  70             mv.visitMethodInsn(INVOKEVIRTUAL, "DefineClassTest", "privMethod", "()I");
  71             mv.visitInsn(IRETURN);
  72             mv.visitMaxs(0, 0);
  73             mv.visitEnd();
  74         }
  75         cw.visitEnd();
  76 
  77         return cw.toByteArray();
  78     }
  79 
  80     private int privMethod() { return 1234; }
  81 
  82     @Test
  83     public void defineNestMate() throws Throwable {
  84         // define a nestmate
  85         Lookup lookup = MethodHandles.lookup();
  86         Class<?> c = lookup.defineClass(bytes, NESTMATE);
  87         assertTrue(c.getNestHost() == DefineClassTest.class);
  88         assertTrue(c == Class.forName("Injected"));
  89 
  90         // invoke int test(DefineClassTest o)
  91         int x = testInjectedClass(c);
  92         assertTrue(x == privMethod());
  93 
  94         // dynamic nestmate is not listed in the return array of getNestMembers
  95         assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c));
  96         assertTrue(c.isNestmateOf(DefineClassTest.class));
  97     }
  98 
  99     @Test
 100     public void defineHiddenClass() throws Throwable {
 101         // define a hidden class
 102         Lookup lookup = MethodHandles.lookup();
 103         Class<?> c = lookup.defineClass(bytes, NESTMATE, HIDDEN);
 104         System.out.println(c.getName());
 105         assertTrue(c.getNestHost() == DefineClassTest.class);
 106         assertTrue(c.isHidden());
 107 
 108         // invoke int test(DefineClassTest o)
 109         int x = testInjectedClass(c);
 110         assertTrue(x == privMethod());
 111 
 112         // dynamic nestmate is not listed in the return array of getNestMembers
 113         assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c));
 114         assertTrue(c.isNestmateOf(DefineClassTest.class));
 115     }
 116 
 117     @Test
 118     public void defineWeakClass() throws Throwable {
 119         // define a weak class
 120         Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
 121         Class<?> c = lookup.defineClass(bytes, WEAK);
 122         System.out.println(c.getName());
 123         assertTrue(c.getNestHost() == c);
 124         assertTrue(c.isHidden());
 125     }
 126 
 127     @Test(expectedExceptions = IllegalAccessError.class)
 128     public void definePackageAccessClass() throws Throwable {
 129         Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
 130         Class<?> c = lookup.defineClass(bytes, HIDDEN);
 131         assertTrue(c.getNestHost() == c);
 132         assertTrue(c.isHidden());
 133 
 134         // fail to access DefineClassTest::privMethod method
 135         testInjectedClass(c);
 136     }
 137 
 138     @Test(expectedExceptions = IllegalAccessException.class)
 139     public void noPrivateLookupAccess() throws Throwable {
 140         Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
 141         lookup.defineClass(bytes, NESTMATE);
 142     }
 143 
 144     @Test(expectedExceptions = IllegalAccessException.class)
 145     public void teleportToNestmate() throws Throwable {
 146         Class<?> c = MethodHandles.lookup().defineClass(bytes, NESTMATE, HIDDEN);
 147         assertTrue(c.getNestHost() == DefineClassTest.class);
 148         assertTrue(c.isHidden());
 149 
 150         // Teleport to a nestmate
 151         Lookup lookup =  MethodHandles.lookup().in(c);
 152         assertTrue((lookup.lookupModes() & PRIVATE) == 0);
 153         // fail to define a nestmate
 154         lookup.defineClass(bytes, NESTMATE, HIDDEN);
 155     }
 156 
 157     @Test(expectedExceptions = IllegalArgumentException.class)
 158     public void notSamePackage() throws Throwable {
 159         MethodHandles.lookup().defineClass(classBytes("p/Injected"), NESTMATE);
 160     }
 161 
 162     /*
 163      * invoke int test(DefineClassTest o) method defined in the injected class
 164      */
 165     private int testInjectedClass(Class<?> c) throws Throwable {
 166         try {
 167             Method m = c.getMethod("test", DefineClassTest.class);
 168             return (int) m.invoke(c.newInstance(), this);
 169         } catch (InvocationTargetException e) {
 170             throw e.getCause();
 171         }
 172     }
 173 }
 174 
 175