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