1 /*
   2  * Copyright (c) 2014, 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 /* @test
  25  * @bug 8032400
  26  * @summary JSR292: invokeSpecial: InternalError attempting to lookup a method
  27  * @modules java.base/jdk.internal.org.objectweb.asm
  28  * @compile -XDignore.symbol.file SpecialStatic.java
  29  * @run junit test.java.lang.invoke.lookup.SpecialStatic
  30  */
  31 package test.java.lang.invoke.lookup;
  32 
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.invoke.MethodType;
  36 import jdk.internal.org.objectweb.asm.*;
  37 import org.junit.Test;
  38 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  39 import static org.junit.Assert.*;
  40 
  41 /**
  42  * Test case:
  43  *   class T1            {        int m() { return 1; }}
  44  *   class T2 extends T1 { static int m() { return 2; }}
  45  *   class T3 extends T2 {        int m() { return 3; }}
  46  *
  47  *   T3::test { invokespecial T1.m() T3 } ==> T1::m
  48  */
  49 public class SpecialStatic {
  50     static class CustomClassLoader extends ClassLoader {
  51         public Class<?> loadClass(String name) throws ClassNotFoundException {
  52             if (findLoadedClass(name) != null) {
  53                 return findLoadedClass(name);
  54             }
  55 
  56             if ("T1".equals(name)) {
  57                 byte[] classFile = dumpT1();
  58                 return defineClass("T1", classFile, 0, classFile.length);
  59             }
  60             if ("T2".equals(name)) {
  61                 byte[] classFile = dumpT2();
  62                 return defineClass("T2", classFile, 0, classFile.length);
  63             }
  64             if ("T3".equals(name)) {
  65                 byte[] classFile = dumpT3();
  66                 return defineClass("T3", classFile, 0, classFile.length);
  67             }
  68 
  69             return super.loadClass(name);
  70         }
  71     }
  72 
  73     private static ClassLoader cl = new CustomClassLoader();
  74     private static Class t1, t3;
  75     static {
  76         try {
  77             t1 = cl.loadClass("T1");
  78             t3 = cl.loadClass("T3");
  79         } catch (ClassNotFoundException e) {
  80             throw new Error(e);
  81         }
  82     }
  83 
  84     public static void main(String[] args) throws Throwable {
  85         SpecialStatic test = new SpecialStatic();
  86         test.testConstant();
  87         test.testFindSpecial();
  88     }
  89 
  90     @Test
  91     public void testConstant() throws Throwable {
  92         MethodHandle mh = (MethodHandle)t3.getDeclaredMethod("getMethodHandle").invoke(null);
  93         int result = (int)mh.invoke(t3.newInstance());
  94         assertEquals(result, 1); // T1.m should be invoked.
  95     }
  96 
  97     @Test
  98     public void testFindSpecial() throws Throwable {
  99         MethodHandles.Lookup lookup = (MethodHandles.Lookup)t3.getDeclaredMethod("getLookup").invoke(null);
 100         MethodHandle mh = lookup.findSpecial(t1, "m", MethodType.methodType(int.class), t3);
 101         int result = (int)mh.invoke(t3.newInstance());
 102         assertEquals(result, 1); // T1.m should be invoked.
 103     }
 104 
 105     public static byte[] dumpT1() {
 106         ClassWriter cw = new ClassWriter(0);
 107         MethodVisitor mv;
 108 
 109         cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T1", null, "java/lang/Object", null);
 110 
 111         mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 112         mv.visitCode();
 113         mv.visitVarInsn(ALOAD, 0);
 114         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 115         mv.visitInsn(RETURN);
 116         mv.visitMaxs(1, 1);
 117         mv.visitEnd();
 118 
 119         mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null);
 120         mv.visitCode();
 121         mv.visitIntInsn(BIPUSH, 1);
 122         mv.visitInsn(IRETURN);
 123         mv.visitMaxs(1, 1);
 124         mv.visitEnd();
 125 
 126         cw.visitEnd();
 127         return cw.toByteArray();
 128     }
 129 
 130     public static byte[] dumpT2() {
 131         ClassWriter cw = new ClassWriter(0);
 132         MethodVisitor mv;
 133 
 134         cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T2", null, "T1", null);
 135 
 136         mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 137         mv.visitCode();
 138         mv.visitVarInsn(ALOAD, 0);
 139         mv.visitMethodInsn(INVOKESPECIAL, "T1", "<init>", "()V", false);
 140         mv.visitInsn(RETURN);
 141         mv.visitMaxs(1, 1);
 142         mv.visitEnd();
 143 
 144         mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()I", null, null);
 145         mv.visitCode();
 146         mv.visitIntInsn(BIPUSH, 2);
 147         mv.visitInsn(IRETURN);
 148         mv.visitMaxs(1, 1);
 149         mv.visitEnd();
 150 
 151         cw.visitEnd();
 152         return cw.toByteArray();
 153     }
 154 
 155     public static byte[] dumpT3() {
 156         ClassWriter cw = new ClassWriter(0);
 157         MethodVisitor mv;
 158 
 159         cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T3", null, "T2", null);
 160 
 161         mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 162         mv.visitCode();
 163         mv.visitVarInsn(ALOAD, 0);
 164         mv.visitMethodInsn(INVOKESPECIAL, "T2", "<init>", "()V", false);
 165         mv.visitInsn(RETURN);
 166         mv.visitMaxs(1, 1);
 167         mv.visitEnd();
 168 
 169         mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null);
 170         mv.visitCode();
 171         mv.visitIntInsn(BIPUSH, 3);
 172         mv.visitInsn(IRETURN);
 173         mv.visitMaxs(1, 1);
 174         mv.visitEnd();
 175 
 176         // getMethodHandle
 177         mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getMethodHandle", "()Ljava/lang/invoke/MethodHandle;", null, null);
 178         mv.visitCode();
 179         mv.visitLdcInsn(new Handle(H_INVOKESPECIAL, "T1", "m", "()I"));
 180         mv.visitInsn(ARETURN);
 181         mv.visitMaxs(1, 0);
 182         mv.visitEnd();
 183 
 184         // getLookup
 185         mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getLookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", null, null);
 186         mv.visitCode();
 187         mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
 188         mv.visitInsn(ARETURN);
 189         mv.visitMaxs(1, 0);
 190         mv.visitEnd();
 191 
 192         cw.visitEnd();
 193         return cw.toByteArray();
 194     }
 195 }