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