1 /*
   2  * Copyright (c) 2019, 2020, 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.misc
  28  *          jdk.compiler
  29  * @compile HiddenClassSigTest.java
  30  * @run main/othervm/native -agentlib:HiddenClassSigTest P.Q.HiddenClassSigTest
  31  */
  32 
  33 package P.Q;
  34 
  35 import java.io.ByteArrayOutputStream;
  36 import java.io.File;
  37 import java.io.FileInputStream;
  38 
  39 import java.lang.invoke.MethodHandles;
  40 import java.lang.invoke.MethodHandles.Lookup;
  41 
  42 import jdk.test.lib.Utils;
  43 import jdk.test.lib.compiler.InMemoryJavaCompiler;
  44 
  45 
  46 interface Test<T> {
  47     String test(T t);
  48 }
  49 
  50 class HiddenClassSig<T> implements Test<T> {
  51     private String realTest() { return "HiddenClassSig: "; }
  52 
  53     public String test(T t) {
  54         String str = realTest();
  55         return str + t.toString();
  56     }
  57 }
  58 
  59 public class HiddenClassSigTest {
  60     private static void log(String str) { System.out.println(str); }
  61 
  62     private static final String HCName = "P/Q/HiddenClassSig.class";
  63     private static final String DIR = Utils.TEST_CLASSES;
  64     private static final String LOG_PREFIX = "HiddenClassSigTest: ";
  65     private static final String ARR_PREFIX = "[[L";
  66     private static final String ARR_POSTFIX = "[][]";
  67     private static final String CLASS_PREFIX = "class ";
  68 
  69     static native void checkHiddenClass(Class klass, String sig);
  70     static native void checkHiddenClassArray(Class array, String sig);
  71     static native boolean checkFailed();
  72 
  73     static {
  74         try {
  75             System.loadLibrary("HiddenClassSigTest");
  76         } catch (UnsatisfiedLinkError ule) {
  77             System.err.println("Could not load HiddenClassSigTest library");
  78             System.err.println("java.library.path: "
  79                 + System.getProperty("java.library.path"));
  80             throw ule;
  81         }
  82     }
  83 
  84     static byte[] readClassFile(String classFileName) throws Exception {
  85         File classFile = new File(classFileName);
  86         try (FileInputStream in = new FileInputStream(classFile);
  87              ByteArrayOutputStream out = new ByteArrayOutputStream())
  88         {
  89             int b;
  90             while ((b = in.read()) != -1) {
  91                 out.write(b);
  92             }
  93             return out.toByteArray();
  94         }
  95     }
  96 
  97     static Class<?> defineHiddenClass(String classFileName) throws Exception {
  98         Lookup lookup = MethodHandles.lookup();
  99         byte[] bytes = readClassFile(DIR + File.separator + classFileName);
 100         Class<?> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
 101         return hc;
 102     }
 103 
 104     static void logClassInfo(Class<?> klass) {
 105         log("\n### Testing class: " + klass);
 106         log(LOG_PREFIX + "isHidden:  " + klass.isHidden());
 107         log(LOG_PREFIX + "getName:   " + klass.getName());
 108         log(LOG_PREFIX + "typeName:  " + klass.getTypeName());
 109         log(LOG_PREFIX + "toString:  " + klass.toString());
 110         log(LOG_PREFIX + "toGenStr:  " + klass.toGenericString());
 111         log(LOG_PREFIX + "elem type: " + klass.componentType());
 112     }
 113 
 114     static boolean checkName(String name, String expName, String msgPart) {
 115         boolean failed = false;
 116         if (!name.equals(expName)) {
 117             log("Test FAIL: result of " + msgPart + " does not match expectation");
 118             failed = true;
 119         }
 120         return failed;
 121     }
 122 
 123     static boolean checkNameHas(String name, String expNamePart, String msgPart) {
 124         boolean failed = false;
 125         if (name.indexOf(expNamePart) < 0) {
 126             log("Test FAIL: result of " + msgPart + " does not match expectation");
 127             failed = true;
 128         }
 129         return failed;
 130     }
 131 
 132     static boolean checkArray(Class<?> arrClass) {
 133         boolean failed = false;
 134         Class<?> elemClass = arrClass.componentType();
 135 
 136         String arrName = arrClass.getName();
 137         String arrType = arrClass.getTypeName();
 138         String arrStr = arrClass.toString().substring(CLASS_PREFIX.length());
 139         String arrGen = arrClass.toGenericString();
 140 
 141         String elemName = elemClass.getName();
 142         String elemType = elemClass.getTypeName();
 143         String elemStr = elemClass.toString().substring(CLASS_PREFIX.length());
 144         String elemGen = elemClass.toGenericString();
 145 
 146         if (elemClass.isHidden()) {
 147             elemGen = elemGen.substring(CLASS_PREFIX.length());
 148         }
 149         failed |= checkNameHas(arrName, elemName, "klass.getName()");
 150         failed |= checkNameHas(arrStr, elemStr, "klass.toString()");
 151         failed |= checkNameHas(arrType, elemType, "klass.getTypeName()");
 152         failed |= checkNameHas(arrGen, elemGen, "klass.getGenericString()");
 153         return failed;
 154     }
 155 
 156     static boolean testClass(Class<?> klass, int arrLevel, String baseName) {
 157         boolean failed = false;
 158         boolean isHidden = (arrLevel == 0);
 159         String prefix = (arrLevel == 0) ? "" : ARR_PREFIX.substring(2 - arrLevel);
 160         String postfix = (arrLevel == 0) ? "" : ";";
 161 
 162         logClassInfo(klass);
 163 
 164         String expName = ("" + klass).substring(CLASS_PREFIX.length());
 165         String expType = baseName;
 166         String expStr = CLASS_PREFIX + prefix + baseName + postfix;
 167         String expGen = baseName + "<T>" + ARR_POSTFIX.substring(0, 2*arrLevel);
 168 
 169         if (arrLevel > 0) {
 170             expType = expType + ARR_POSTFIX.substring(0, 2*arrLevel); 
 171         } else {
 172             expGen = CLASS_PREFIX + expGen; 
 173         }
 174         failed |= checkName(klass.getName(), expName, "klass.getName()");
 175         failed |= checkName(klass.getTypeName(), expType, "klass.getTypeName()");
 176         failed |= checkName(klass.toString(), expStr, "klass.toString()");
 177         failed |= checkName(klass.toGenericString(), expGen, "klass.toGenericString()");
 178 
 179         if (klass.isHidden() != isHidden) {
 180             log("Test FAIL: result of klass.isHidden() does not match expectation");
 181             failed = true;
 182         }
 183         String sig = hcSignature(klass);
 184         if (arrLevel == 0) {
 185             checkHiddenClass(klass, sig);
 186         } else {
 187             failed |= checkArray(klass);
 188             checkHiddenClassArray(klass, sig);
 189         }
 190         return failed;
 191     }
 192 
 193     static String hcSignature(Class<?> klass) {
 194         boolean isArray = klass.isArray();
 195         String sig = klass.getName();
 196         String prefix  = isArray ? "" : "L";
 197         String postfix = isArray ? "" : ";";
 198         int idx = sig.indexOf("/");
 199 
 200         sig = prefix + sig.substring(0, idx).replace('.', '/')
 201                       + "."
 202                       + sig.substring(idx + 1, sig.length())
 203                       + postfix;
 204         return sig;
 205     }
 206 
 207     public static void main(String args[]) throws Exception {
 208         log(LOG_PREFIX + "started");
 209         Class<?> hc = defineHiddenClass(HCName);
 210         String baseName = ("" + hc).substring("class ".length());
 211 
 212         Test<String> t = (Test<String>)hc.newInstance();
 213         String str = t.test("Test generic hidden class");
 214         log(LOG_PREFIX + "hc.test() returned string: " + str);
 215 
 216         boolean failed = testClass(hc, 0, baseName);
 217 
 218         Class<?> hcArr = hc.arrayType();
 219         failed |= testClass(hcArr, 1, baseName);
 220 
 221         Class<?> hcArrArr = hcArr.arrayType();
 222         failed |= testClass(hcArrArr, 2, baseName);
 223 
 224         if (failed) {
 225           throw new RuntimeException("FAIL: failed status from java part");
 226         }
 227         if (checkFailed()) {
 228           throw new RuntimeException("FAIL: failed status from native agent");
 229         }
 230         log(LOG_PREFIX + "finished");
 231     }
 232 }