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 
  66     static native void checkHiddenClass(Class klass, String sig);
  67     static native void checkHiddenClassArray(Class array, String sig);
  68     static native boolean checkFailed();
  69 
  70     static {
  71         try {
  72             System.loadLibrary("HiddenClassSigTest");
  73         } catch (UnsatisfiedLinkError ule) {
  74             System.err.println("Could not load HiddenClassSigTest library");
  75             System.err.println("java.library.path: "
  76                 + System.getProperty("java.library.path"));
  77             throw ule;
  78         }
  79     }
  80 
  81     static byte[] readClassFile(String classFileName) throws Exception {
  82         File classFile = new File(classFileName);
  83         try (FileInputStream in = new FileInputStream(classFile);
  84              ByteArrayOutputStream out = new ByteArrayOutputStream())
  85         {
  86             int b;
  87             while ((b = in.read()) != -1) {
  88                 out.write(b);
  89             }
  90             return out.toByteArray();
  91         }
  92     }
  93 
  94     static Class<?> defineHiddenClass(String classFileName) throws Exception {
  95         Lookup lookup = MethodHandles.lookup();
  96         byte[] bytes = readClassFile(DIR + File.separator + classFileName);
  97         Class<?> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
  98         return hc;
  99     }
 100 
 101     static void logClassInfo(Class<?> klass) {
 102         log("\n### Testing class: " + klass);
 103         log(LOG_PREFIX + "isHidden:  " + klass.isHidden());
 104         log(LOG_PREFIX + "getName:   " + klass.getName());
 105         log(LOG_PREFIX + "typeName:  " + klass.getTypeName());
 106         log(LOG_PREFIX + "toString:  " + klass.toString());
 107         log(LOG_PREFIX + "toGenStr:  " + klass.toGenericString());
 108         log(LOG_PREFIX + "elem type: " + klass.componentType());
 109     }
 110 
 111     private static final String HC_NAME = "P.Q.HiddenClassSig";
 112     private static final String HC_SUFFIX_REGEX = "0x[0-9a-f]+";
 113     static boolean checkName(Class<?> klass, String name, String toString) {
 114         boolean failed = false;
 115         String regex = "";
 116         Class<?> c = klass;
 117         while (c.isArray()) {
 118             regex = "\\[" + regex;
 119             c = c.componentType();
 120         }
 121         if (klass.isArray()) {
 122             regex += "L" + HC_NAME + "/" + HC_SUFFIX_REGEX + ";";
 123         } else {
 124             regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
 125         }
 126         if (!name.matches(regex)) {
 127             log("Test FAIL: result of Class::getName" + " \"" + name + "\" does not match " + regex);
 128             failed = true;
 129         }
 130         if (!toString.matches("class " + regex)) {
 131             log("Test FAIL: result of Class::toString" + " \"" + name + "\" does not match " + regex);
 132             failed = true;
 133         }
 134         return failed;
 135     }
 136 
 137     static boolean checkTypeName(Class<?> klass, String name) {
 138         boolean failed = false;
 139         String regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
 140         Class<?> c = klass;
 141         while (c.isArray()) {
 142             c = c.componentType();
 143             regex = regex + "\\[\\]";
 144         }
 145         if (!name.matches(regex)) {
 146             log("Test FAIL: result of Class::getTypeName" + " \"" + name + "\" does not match " + regex);
 147             failed = true;
 148         }
 149         return failed;
 150     }
 151 
 152     static boolean checkGenericString(Class<?> klass, String name) {
 153         boolean failed = false;
 154         Class<?> c = klass;
 155         String regex = HC_NAME + "/" + HC_SUFFIX_REGEX + "<T>";
 156         if (!klass.isArray()) {
 157             regex = "class " + regex;
 158         }
 159         while (c.isArray()) {
 160             c = c.componentType();
 161             regex = regex + "\\[\\]";
 162         }
 163         if (!name.matches(regex)) {
 164             log("Test FAIL: result of Class::toGenericString" + " \"" + name + "\" does not match " + regex);
 165             failed = true;
 166         }
 167         return failed;
 168     }
 169 
 170     static boolean checkDescriptorString(Class<?> klass, String name) {
 171         boolean failed = false;
 172         String regex = "L" + HC_NAME.replace('.', '/') + "." + HC_SUFFIX_REGEX + ";";
 173         Class<?> c = klass;
 174         while (c.isArray()) {
 175             regex = "\\[" + regex;
 176             c = c.componentType();
 177         }
 178         if (!name.matches(regex)) {
 179             log("Test FAIL: result of Class::descriptorString" + " \"" + name + "\" does not match " + regex);
 180             failed = true;
 181         }
 182         return failed;
 183     }
 184 
 185     static boolean testClass(Class<?> klass) {
 186         boolean failed = false;
 187         logClassInfo(klass);
 188 
 189         failed |= checkName(klass, klass.getName(), klass.toString());
 190         failed |= checkTypeName(klass, klass.getTypeName());
 191         failed |= checkGenericString(klass, klass.toGenericString());
 192         failed |= checkDescriptorString(klass, klass.descriptorString());
 193 
 194         if (klass.isArray() && klass.isHidden()) {
 195             log("Test FAIL: an array class is never hidden");
 196             failed = true;
 197         }
 198         if (klass.isArray()) {
 199             checkHiddenClassArray(klass, klass.descriptorString());
 200         } else {
 201             checkHiddenClass(klass, klass.descriptorString());
 202         }
 203         return failed;
 204     }
 205 
 206     public static void main(String args[]) throws Exception {
 207         log(LOG_PREFIX + "started");
 208         Class<?> hc = defineHiddenClass(HCName);
 209         String baseName = ("" + hc).substring("class ".length());
 210 
 211         Test<String> t = (Test<String>)hc.newInstance();
 212         String str = t.test("Test generic hidden class");
 213         log(LOG_PREFIX + "hc.test() returned string: " + str);
 214 
 215         boolean failed = testClass(hc);
 216 
 217         Class<?> hcArr = hc.arrayType();
 218         failed |= testClass(hcArr);
 219 
 220         Class<?> hcArrArr = hcArr.arrayType();
 221         failed |= testClass(hcArrArr);
 222 
 223         if (failed) {
 224           throw new RuntimeException("FAIL: failed status from java part");
 225         }
 226         if (checkFailed()) {
 227           throw new RuntimeException("FAIL: failed status from native agent");
 228         }
 229         log(LOG_PREFIX + "finished");
 230     }
 231 }