1 /*
   2  * Copyright (c) 2019, 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  HiddenNestmateTest
  29  * @run testng/othervm HiddenNestmateTest
  30  */
  31 
  32 import java.lang.invoke.*;
  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 import java.util.Arrays;
  38 
  39 import jdk.internal.org.objectweb.asm.*;
  40 import org.testng.annotations.Test;
  41 
  42 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
  43 import static java.lang.invoke.MethodHandles.Lookup.*;
  44 
  45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  46 import static org.testng.Assert.*;
  47 
  48 public class HiddenNestmateTest {
  49     private static final byte[] bytes = classBytes("HiddenInjected");
  50 
  51     private static void assertNestmate(Lookup lookup) {
  52         assertTrue((lookup.lookupModes() & PRIVATE) != 0);
  53         assertTrue((lookup.lookupModes() & MODULE) != 0);
  54 
  55         Class<?> hiddenClass = lookup.lookupClass();
  56         Class<?> nestHost = hiddenClass.getNestHost();
  57         assertTrue(hiddenClass.isHiddenClass());
  58         assertTrue(nestHost == MethodHandles.lookup().lookupClass());
  59 
  60         // hidden nestmate is not listed in the return array of getNestMembers
  61         assertTrue(Stream.of(nestHost.getNestMembers()).noneMatch(k -> k == hiddenClass));
  62         assertTrue(hiddenClass.isNestmateOf(lookup.lookupClass()));
  63         assertTrue(Arrays.equals(hiddenClass.getNestMembers(), nestHost.getNestMembers()));
  64     }
  65 
  66     @Test
  67     public void hiddenClass() throws Throwable {
  68         // define a hidden class
  69         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
  70         Class<?> c = lookup.lookupClass();
  71         assertTrue((lookup.lookupModes() & PRIVATE) != 0);
  72         assertTrue((lookup.lookupModes() & MODULE) != 0);
  73         assertTrue(c.getNestHost() == c);  // host of its own nest
  74         assertTrue(c.isHiddenClass());
  75 
  76         // invoke int test(HiddenNestmateTest o) via MethodHandle
  77         MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
  78         MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
  79         try {
  80             int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this);
  81             throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
  82         } catch (IllegalAccessError e) {}
  83 
  84         // invoke int test(HiddenNestmateTest o)
  85         try {
  86             int x1 = testInjectedClass(c);
  87             throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
  88         } catch (IllegalAccessError e) {}
  89     }
  90 
  91     @Test
  92     public void hiddenNestmate() throws Throwable {
  93         // define a hidden nestmate class
  94         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
  95         Class<?> c = lookup.lookupClass();
  96         assertNestmate(lookup);
  97 
  98         // invoke int test(HiddenNestmateTest o) via MethodHandle
  99         MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
 100         MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
 101         int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this);
 102         assertTrue(x == privMethod());
 103 
 104         // invoke int test(HiddenNestmateTest o)
 105         int x1 = testInjectedClass(c);
 106         assertTrue(x1 == privMethod());
 107     }
 108 
 109     @Test
 110     public void hiddenStrongClass() throws Throwable {
 111         // define a hidden class strongly referenced by the class loader
 112         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, STRONG);
 113         assertNestmate(lookup);
 114     }
 115 
 116     @Test(expectedExceptions = IllegalAccessException.class)
 117     public void noPrivateLookupAccess() throws Throwable {
 118         Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
 119         lookup.defineHiddenClass(bytes, false, NESTMATE);
 120     }
 121 
 122     public void teleportToNestmate() throws Throwable {
 123         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
 124         assertNestmate(lookup);
 125 
 126         // Teleport to a hidden nestmate
 127         Lookup lc =  MethodHandles.lookup().in(lookup.lookupClass());
 128         assertTrue((lc.lookupModes() & PRIVATE) != 0);
 129         Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
 130         assertNestmate(lc2);
 131     }
 132 
 133     @Test(expectedExceptions = IllegalArgumentException.class)
 134     public void notSamePackage() throws Throwable {
 135         MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE);
 136     }
 137 
 138     /*
 139      * invoke int test(HiddenNestmateTest o) method defined in the injected class
 140      */
 141     private int testInjectedClass(Class<?> c) throws Throwable {
 142         try {
 143             Method m = c.getMethod("test", HiddenNestmateTest.class);
 144             return (int) m.invoke(c.newInstance(), this);
 145         } catch (InvocationTargetException e) {
 146             throw e.getCause();
 147         }
 148     }
 149 
 150     private static byte[] classBytes(String classname) {
 151         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 152         MethodVisitor mv;
 153 
 154         cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null);
 155 
 156         {
 157             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 158             mv.visitCode();
 159             mv.visitVarInsn(ALOAD, 0);
 160             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
 161             mv.visitInsn(RETURN);
 162             mv.visitMaxs(0, 0);
 163             mv.visitEnd();
 164         }
 165         {
 166             // access a private member of the nest host class
 167             mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null);
 168             mv.visitCode();
 169             mv.visitVarInsn(ALOAD, 0);
 170             mv.visitVarInsn(ALOAD, 1);
 171             mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I");
 172             mv.visitInsn(IRETURN);
 173             mv.visitMaxs(0, 0);
 174             mv.visitEnd();
 175         }
 176         cw.visitEnd();
 177 
 178         return cw.toByteArray();
 179     }
 180 
 181     private int privMethod() { return 1234; }
 182 }