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 java.io.IOException;
  26 import java.io.RandomAccessFile;
  27 import java.lang.reflect.Field;
  28 import java.lang.reflect.Method;
  29 import static java.lang.reflect.Modifier.*;
  30 import java.net.URL;
  31 import java.util.Enumeration;
  32 
  33 import jdk.experimental.value.ValueType;
  34 
  35 import jdk.internal.org.objectweb.asm.*;
  36 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  37 
  38 import static jdk.test.lib.Asserts.*;
  39 
  40 /*
  41  * @test DeriveValueTypeCreation
  42  * @summary Derive Value Type creation test
  43  * @library /test/lib
  44  * @modules java.base/jdk.internal.org.objectweb.asm
  45  * @build runtime.valhalla.valuetypes.ValueCapableClass
  46  * @run main/othervm -Xint -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation
  47  * @run main/othervm -Xcomp -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation
  48  */
  49 public class DeriveValueTypeCreation {
  50 
  51     public static final String VCC_CLASSNAME = "runtime.valhalla.valuetypes.ValueCapableClass";
  52 
  53     public static void main(String[] args) {
  54         DeriveValueTypeCreation test = new DeriveValueTypeCreation();
  55         test.run();
  56     }
  57 
  58     public void run() {
  59         loadAndRunTest();
  60         notValueCapableClasses();
  61     }
  62 
  63     void loadAndRunTest() {
  64         Class<?> clazz = null;
  65         try {
  66             clazz = Class.forName(VCC_CLASSNAME, true, getClass().getClassLoader());
  67             clazz.getDeclaredMethod("test").invoke(null);
  68         }
  69         catch (ClassNotFoundException cnfe) { fail("VCC class missing", cnfe); }
  70         catch (NoSuchMethodException nsme) { fail("VCC test method missing", nsme); }
  71         catch (Throwable t) { fail("Failed to invoke VCC.test()", t); }
  72 
  73         checkValueCapableClass(clazz);
  74     }
  75 
  76     void checkValueCapableClass(Class<?> clazz) {
  77         if (!ValueType.classHasValueType(clazz)) {
  78             fail("!classHasValueType: " + clazz);
  79         }
  80 
  81         ValueType<?> vt = ValueType.forClass(clazz);
  82         if (vt == null) {
  83             fail("ValueType.forClass failed");
  84         }
  85 
  86         System.out.println("ValueType: " + vt);
  87 
  88         if (vt.boxClass() != clazz) {
  89             fail("ValueType.boxClass() failed");
  90         }
  91         if (vt.sourceClass() != clazz) {
  92             fail("ValueType.sourceClass() failed");
  93         }
  94 
  95         // DVT class matches our expectations for the current implementation...
  96         Class<?> vtClass = vt.valueClass();
  97         if (!vtClass.getName().equals(clazz.getName() + "$Value")) {
  98             fail("ValueType.valueClass() failed");
  99         }
 100         if (!vtClass.getSuperclass().getName().equals("java.lang.__Value")) {
 101             fail("ValueType.valueClass() isn't a Value Type class");
 102         }
 103 
 104         // Exercise "Class.getSimpleName()", we've cause problems with it before
 105         String sn = vtClass.getSimpleName();
 106         System.out.println("SimpleName: " + sn);
 107 
 108         if (clazz.getClassLoader() != vtClass.getClassLoader()) {
 109             fail("ClassLoader mismatch");
 110         }
 111         if (clazz.getProtectionDomain() != vtClass.getProtectionDomain()) {
 112             fail("ProtectionDomain mismatch");
 113         }
 114     }
 115 
 116     void notValueCapableClasses() {
 117         int vccKlassAccess = ACC_SUPER | ACC_PUBLIC | ACC_FINAL;
 118         int vccFieldAccess = ACC_PUBLIC | ACC_FINAL;
 119         String vccSuperClass = "java/lang/Object";
 120 
 121         // First a control test to check createTestClass is working
 122         try {
 123             Class<?> cls = createTestClass("Control_Case_is_a_VCC", vccKlassAccess, vccSuperClass, "I", vccFieldAccess);
 124             checkValueCapableClass(cls);
 125         }
 126         catch (Exception e) {
 127             fail("Control test failed", e);
 128         }
 129 
 130         testFailCase("Not_a_final_class", ACC_SUPER | ACC_PUBLIC, vccSuperClass, "I", vccFieldAccess, "not a final class");
 131         testFailCase("No_fields", vccKlassAccess, vccSuperClass, null, vccFieldAccess, "has no instance fields");
 132         testFailCase("Not_final_field", vccKlassAccess, vccSuperClass, "I", ACC_PUBLIC, "contains non-final instance field");
 133         testFailCase("Super_not_Object", vccKlassAccess, "java/lang/Throwable", "I", vccFieldAccess, "does not derive from Object");
 134     }
 135 
 136     void testFailCase(String clsName,
 137                       int klassAccess,
 138                       String superKlass,
 139                       String fieldType,
 140                       int fieldAccess,
 141                       String errMsgRequired) {
 142         try {
 143             createTestClass(clsName, klassAccess, superKlass, fieldType, fieldAccess);
 144             fail(clsName + " : failed to fail with Error");
 145         }
 146         catch (ClassNotFoundException cnfe) {
 147             fail(clsName + " : Unexpected ClassNotFoundException", cnfe);
 148         }
 149         catch (Error err) {
 150             if (!err.getMessage().contains(errMsgRequired)) {
 151                 fail(clsName + " : Not the error we were looking for", err);
 152             }
 153         }
 154     }
 155 
 156     Class<?> createTestClass(String name,
 157                              int klassAccess,
 158                              String superKlass,
 159                              String fieldType,
 160                              int fieldAccess) throws ClassNotFoundException {
 161         ClassWriter cw = new ClassWriter(0);
 162         cw.visit(52, klassAccess, name, null, superKlass, null);
 163         cw.visitAnnotation("Ljvm/internal/value/DeriveValueType;", true);
 164         if (fieldType != null) {
 165             cw.visitField(fieldAccess, "x", fieldType, null, null);
 166         }
 167         cw.visitEnd();
 168         return new TestClassLoader(name, cw.toByteArray()).loadClass(name);
 169     }
 170 
 171     class TestClassLoader extends ClassLoader {
 172 
 173         String name;
 174         byte[] classBytes;
 175 
 176         TestClassLoader(String name, byte[] classBytes) {
 177             this.name = name;
 178             this.classBytes = classBytes;
 179         }
 180 
 181         @Override
 182         public Class findClass(String name) throws ClassNotFoundException {
 183             if (this.name.equals(name)) {
 184                 return defineClass(name, classBytes, 0, classBytes.length);
 185             }
 186             throw new ClassNotFoundException();
 187         }
 188     }
 189 
 190 }