1 /*
   2  * Copyright (c) 2017, 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 package runtime.valhalla.valuetypes;
  24 
  25 import jdk.incubator.mvt.ValueType;
  26 import java.lang.reflect.Field;
  27 import java.lang.reflect.InvocationTargetException;
  28 import java.lang.reflect.Method;
  29 import static java.lang.reflect.Modifier.*;
  30 import java.net.URL;
  31 import java.util.ArrayList;
  32 import java.util.HashMap;
  33 
  34 import jdk.experimental.bytecode.*;
  35 
  36 import jdk.internal.org.objectweb.asm.*;
  37 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  38 
  39 import static jdk.test.lib.Asserts.*;
  40 
  41 /*
  42  * @test DeriveValueTypeCreation
  43  * @summary Derive Value Type creation test
  44  * @library /test/lib
  45  * @compile DeriveValueTypeCreation.java
  46  * @modules java.base/jdk.experimental.bytecode
  47  *          java.base/jdk.internal.org.objectweb.asm
  48  *          jdk.incubator.mvt
  49  * @build runtime.valhalla.valuetypes.ValueCapableClass
  50  * @run main/othervm -Xint -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation
  51  * @run main/othervm -Xcomp -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation
  52  */
  53 public class DeriveValueTypeCreation {
  54 
  55     public static final String VCC_CLASSNAME = "runtime.valhalla.valuetypes.ValueCapableClass";
  56     public static final String DVT_SUFFIX = "$Value";
  57 
  58     public static final int TEST_DEF_CLASS_ACCESS = ACC_SUPER | ACC_PUBLIC | ACC_FINAL;
  59     public static final int TEST_DEF_FIELD_ACCESS = ACC_PUBLIC | ACC_FINAL;
  60     public static final String OBJECT_CLASS_DESC  = "java/lang/Object";
  61 
  62     public static void main(String[] args) {
  63         DeriveValueTypeCreation test = new DeriveValueTypeCreation();
  64         test.run();
  65     }
  66 
  67     public void run() {
  68         loadAndRunTest();
  69         notValueCapableClasses();
  70         loadDvtFirst();
  71     }
  72 
  73     void loadAndRunTest() {
  74         Class<?> clazz = null;
  75         try {
  76             clazz = Class.forName(VCC_CLASSNAME, true, getClass().getClassLoader());
  77             clazz.getDeclaredMethod("test").invoke(null);
  78         }
  79         catch (ClassNotFoundException cnfe) { fail("VCC class missing", cnfe); }
  80         catch (NoSuchMethodException nsme) { fail("VCC test method missing", nsme); }
  81         catch (Throwable t) { fail("Failed to invoke VCC.test()", t); }
  82 
  83         checkValueCapableClass(clazz);
  84     }
  85 
  86     void checkValueCapableClass(Class<?> clazz) {
  87         if (!ValueType.classHasValueType(clazz)) {
  88             fail("!classHasValueType: " + clazz);
  89         }
  90 
  91         ValueType<?> vt = ValueType.forClass(clazz);
  92         if (vt == null) {
  93             fail("ValueType.forClass failed");
  94         }
  95 
  96         System.out.println("ValueType: " + vt);
  97 
  98         if (vt.boxClass() != clazz) {
  99             fail("ValueType.boxClass() failed");
 100         }
 101 
 102         // DVT class matches our expectations for the current implementation...
 103         Class<?> vtClass = vt.valueClass();
 104         if (!vtClass.getName().equals(clazz.getName() + DVT_SUFFIX)) {
 105             fail("ValueType.valueClass() failed");
 106         }
 107         if (!vtClass.getSuperclass().getName().equals("java.lang.__Value")) {
 108             fail("ValueType.valueClass() isn't a Value Type class");
 109         }
 110 
 111         // Exercise "Class.getSimpleName()", we've cause problems with it before
 112         String sn = vtClass.getSimpleName();
 113         System.out.println("SimpleName: " + sn);
 114 
 115         if (clazz.getClassLoader() != vtClass.getClassLoader()) {
 116             fail("ClassLoader mismatch");
 117         }
 118         if (clazz.getProtectionDomain() != vtClass.getProtectionDomain()) {
 119             fail("ProtectionDomain mismatch");
 120         }
 121     }
 122 
 123     void notValueCapableClasses() {
 124         // First a control test to check createTestVccClass is working
 125         try {
 126             Class<?> cls = createTestVccClass("Control_Case_is_a_VCC", TEST_DEF_CLASS_ACCESS, OBJECT_CLASS_DESC, "I", TEST_DEF_FIELD_ACCESS);
 127             checkValueCapableClass(cls);
 128         }
 129         catch (Exception e) {
 130             fail("Control test failed", e);
 131         }
 132 
 133         testFailCase("Not_a_final_class", ACC_SUPER | ACC_PUBLIC, OBJECT_CLASS_DESC, "I", TEST_DEF_FIELD_ACCESS, "not a final class");
 134         testFailCase("No_fields", TEST_DEF_CLASS_ACCESS, OBJECT_CLASS_DESC, null, TEST_DEF_FIELD_ACCESS, "has no instance fields");
 135         testFailCase("Not_final_field", TEST_DEF_CLASS_ACCESS, OBJECT_CLASS_DESC, "I", ACC_PUBLIC, "contains non-final instance field");
 136         testFailCase("Super_not_Object", TEST_DEF_CLASS_ACCESS, "java/lang/Throwable", "I", TEST_DEF_FIELD_ACCESS, "does not derive from Object");
 137     }
 138 
 139     void testFailCase(String clsName,
 140                       int klassAccess,
 141                       String superKlass,
 142                       String fieldType,
 143                       int fieldAccess,
 144                       String errMsgRequired) {
 145         try {
 146             createTestVccClass(clsName, klassAccess, superKlass, fieldType, fieldAccess);
 147             fail(clsName + " : failed to fail with Error");
 148         }
 149         catch (ClassNotFoundException cnfe) {
 150             fail(clsName + " : Unexpected ClassNotFoundException", cnfe);
 151         }
 152         catch (Error err) {
 153             if (!err.getMessage().contains(errMsgRequired)) {
 154                 fail(clsName + " : Not the error we were looking for", err);
 155             }
 156         }
 157     }
 158 
 159     byte[] createTestVccClassBytes(String name,
 160                                 boolean vccAnnotation) {
 161         return createTestVccClassBytes(name, TEST_DEF_CLASS_ACCESS, vccAnnotation, OBJECT_CLASS_DESC, "I", TEST_DEF_FIELD_ACCESS);
 162     }
 163 
 164     byte[] createTestVccClassBytes(String name,
 165                                 int klassAccess,
 166                                 String superKlass,
 167                                 String fieldType,
 168                                 int fieldAccess)  {
 169         return createTestVccClassBytes(name, klassAccess, true, superKlass, fieldType, fieldAccess);
 170     }
 171 
 172     byte[] createTestVccClassBytes(String name,
 173                                 int klassAccess,
 174                                 boolean vccAnnotation,
 175                                 String superKlass,
 176                                 String fieldType,
 177                                 int fieldAccess)  {
 178         ClassWriter cw = new ClassWriter(0);
 179         cw.visit(52, klassAccess, name, null, superKlass, null);
 180         if (vccAnnotation ) {
 181             cw.visitAnnotation("Ljdk/incubator/mvt/ValueCapableClass;", true);
 182         }
 183         if (fieldType != null) {
 184             cw.visitField(fieldAccess, "x", fieldType, null, null);
 185         }
 186         cw.visitEnd();
 187         return cw.toByteArray();
 188     }
 189 
 190     Class<?> createTestVccClass(String name,
 191                              int klassAccess,
 192                              String superKlass,
 193                              String fieldType,
 194                              int fieldAccess) throws ClassNotFoundException {
 195         return new TestClassLoader(name,
 196                                    createTestVccClassBytes(name, klassAccess, superKlass, fieldType, fieldAccess)).loadClass(name);
 197     }
 198 
 199     class TestClassLoader extends ClassLoader {
 200 
 201         HashMap<String, byte[]> namedBytes = new HashMap<>();
 202         ArrayList<String> findNames = new ArrayList<String>();
 203 
 204         TestClassLoader() {}
 205         TestClassLoader(String name, byte[] classBytes) {
 206             addNamedBytes(name, classBytes);
 207         }
 208 
 209         void addNamedBytes(String name, byte[] classBytes) {
 210             namedBytes.put(name, classBytes);
 211         }
 212 
 213         @Override
 214         public Class findClass(String name) throws ClassNotFoundException {
 215             byte[] classBytes = null;
 216             synchronized (findNames) {
 217                 findNames.add(name);
 218                 classBytes = namedBytes.get(name);
 219             }
 220             if (classBytes != null) {
 221                 return defineClass(name, classBytes, 0, classBytes.length);
 222             }
 223             throw new ClassNotFoundException(name);
 224         }
 225 
 226         public ArrayList<String> getFindNames() {
 227             return findNames;
 228         }
 229     }
 230 
 231     void loadDvtFirst() {
 232         try {
 233             loadDvtFirstNoVcc();
 234             loadDvtFirstNotVcc();
 235             loadDvtFirstBadVcc();
 236             loadDvtFirstVcc();
 237         } catch (Throwable t) {
 238             fail("loadDvtFirst failed", t);
 239         }
 240     }
 241 
 242     void loadDvtFirstNoVcc() throws Throwable {
 243         String vccName = "TestNoVcc";
 244         try {
 245             newDvtUserInstance(vccName, null, false);
 246         } catch (NoClassDefFoundError ncdfe) {}
 247         try {
 248             newDvtUserInstance(vccName, null, true);
 249         } catch (NoClassDefFoundError ncdfe) {}
 250     }
 251 
 252     void loadDvtFirstNotVcc() throws Throwable {
 253         String vccName = "TestNotVcc";
 254         byte[] vccBytes = createTestVccClassBytes(vccName, false);
 255         try {
 256             newDvtUserInstance(vccName, vccBytes, false);
 257         } catch (NoClassDefFoundError ncdfe) {}
 258         try {
 259             newDvtUserInstance(vccName, vccBytes, true);
 260         } catch (NoClassDefFoundError ncdfe) {}
 261     }
 262 
 263     void loadDvtFirstBadVcc() throws Throwable {
 264         String vccName = "TestBadVcc";
 265         byte[] vccBytes = createTestVccClassBytes(vccName, TEST_DEF_CLASS_ACCESS,
 266                                                   true, OBJECT_CLASS_DESC, "I",
 267                                                   ACC_PUBLIC);
 268         try {
 269             newDvtUserInstance(vccName, vccBytes, false);
 270         } catch (IncompatibleClassChangeError icce) {}
 271         try {
 272             newDvtUserInstance(vccName, vccBytes, true);
 273         } catch (IncompatibleClassChangeError icce) {}
 274     }
 275 
 276     void loadDvtFirstVcc() throws Throwable {
 277         String vccName = "TestValidVcc";
 278         byte[] vccBytes = createTestVccClassBytes(vccName, TEST_DEF_CLASS_ACCESS,
 279                                                   true, OBJECT_CLASS_DESC, "I",
 280                                                   TEST_DEF_FIELD_ACCESS);
 281         newDvtUserInstance(vccName, vccBytes, false);
 282         newDvtUserInstance(vccName, vccBytes, true);
 283     }
 284 
 285     void newDvtUserInstance(String vccName, byte[] vccBytes, boolean withField) throws Throwable {
 286         TestClassLoader cl = new TestClassLoader();
 287         if (vccBytes != null) {
 288             cl.addNamedBytes(vccName, vccBytes);
 289         }
 290         String dvtUserName = "UseValidDvt";
 291         String dvtName = vccName + DVT_SUFFIX;
 292         String dvtFieldDesc = "Q" + dvtName + ";";
 293         String dvtClassDesc = ";" + dvtFieldDesc;
 294         byte[] classBytes = createTestDvtUserClassBytes(dvtUserName, dvtClassDesc, (withField) ? dvtFieldDesc : null);
 295         cl.addNamedBytes(dvtUserName, classBytes);
 296         try {
 297             Class.forName(dvtUserName, true, cl).getDeclaredConstructor().newInstance();
 298         } catch (InvocationTargetException ite) { throw ite.getTargetException(); }
 299     }
 300 
 301     byte[] createTestDvtUserClassBytes(String className, String dvtDesc, String dvtFieldDesc) {
 302         BasicClassBuilder builder = new BasicClassBuilder(className, 53, 1)
 303             .withFlags(Flag.ACC_PUBLIC)
 304             .withSuperclass("java/lang/Object")
 305             .withMethod("<init>", "()V", M ->
 306                 M.withFlags(Flag.ACC_PUBLIC).withCode(TypedCodeBuilder::new, C ->
 307                     C
 308                     .load(0).invokespecial("java/lang/Object", "<init>", "()V", false)
 309                     .iconst_1().anewarray(dvtDesc).pop()
 310                     .return_()));
 311         if (dvtFieldDesc != null) {
 312             builder.withField("dvtField", dvtFieldDesc);
 313         }
 314         return builder.build();
 315     }
 316 }