1 /*
   2  * Copyright (c) 2016, 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 /testlibrary /
  44  * @build runtime.valhalla.valuetypes.ValueCapableClass
  45  * @run main/othervm -Xint -noverify runtime.valhalla.valuetypes.DeriveValueTypeCreation
  46  */
  47 public class DeriveValueTypeCreation {
  48 
  49     public static final String VCC_CLASSNAME = "runtime.valhalla.valuetypes.ValueCapableClass";
  50 
  51     static final boolean MVT_1_0 = true; // "Some restrictions apply"
  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             error("!classHasValueType: " + clazz);
  79         }
  80 
  81         ValueType<?> vt = ValueType.forClass(clazz);
  82         if (vt == null) {
  83             error("ValueType.forClass failed");
  84         }
  85 
  86         System.out.println("ValueType: " + vt);
  87 
  88         if (vt.boxClass() != clazz) {
  89             error("ValueType.boxClass() failed");
  90         }
  91         if (vt.sourceClass() != clazz) {
  92             error("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             error("ValueType.valueClass() failed");
  99         }
 100         if (!vtClass.getSuperclass().getName().equals("java.lang.__Value")) {
 101             error("ValueType.valueClass() isn't a Value Type class");
 102         }
 103         if (MVT_1_0) {
 104             if (!valhalla.shady.MinimalValueTypes_1_0.isValueType(vtClass)) {
 105                 error("ValueType.valueClass() isn't a Value Type class according to MVT1.0");
 106             }
 107         }
 108 
 109         // Exercise "Class.getSimpleName()", we've cause problems with it before
 110         String sn = vtClass.getSimpleName();
 111         System.out.println("SimpleName: " + sn);
 112 
 113         if (clazz.getClassLoader() != vtClass.getClassLoader()) {
 114             error("ClassLoader mismatch");
 115         }
 116         if (clazz.getProtectionDomain() != vtClass.getProtectionDomain()) {
 117             error("ProtectionDomain mismatch");
 118         }
 119     }
 120 
 121     void notValueCapableClasses() {
 122         int vccKlassAccess = ACC_SUPER | ACC_PUBLIC | ACC_FINAL;
 123         int vccFieldAccess = ACC_PUBLIC | ACC_FINAL;
 124         String vccSuperClass = "java/lang/Object";
 125 
 126         // First a control test to check createTestClass is working
 127         try {
 128             Class<?> cls = createTestClass("Control_Case_is_a_VCC", vccKlassAccess, vccSuperClass, "I", vccFieldAccess);
 129             checkValueCapableClass(cls);
 130         }
 131         catch (Exception e) {
 132             fail("Control test failed", e);
 133         }
 134 
 135         testFailCase("Not_a_final_class", ACC_SUPER | ACC_PUBLIC, vccSuperClass, "I", vccFieldAccess, "not a final class");
 136         testFailCase("No_fields", vccKlassAccess, vccSuperClass, null, vccFieldAccess, "has no instance fields");
 137         testFailCase("Not_final_field", vccKlassAccess, vccSuperClass, "I", ACC_PUBLIC, "contains non-final instance field");
 138         testFailCase("Super_not_Object", vccKlassAccess, "java/lang/Throwable", "I", vccFieldAccess, "does not derive from Object");
 139         if (MVT_1_0) {
 140             String notSupported = "do not support";
 141             testFailCase("Only_Primitve_Fields_String", vccKlassAccess, vccSuperClass, "Ljava/lang/String;", vccFieldAccess, notSupported);
 142             testFailCase("Only_Primitve_Fields_Array", vccKlassAccess, vccSuperClass, "[I", vccFieldAccess, notSupported);
 143         }
 144     }
 145 
 146     void testFailCase(String clsName,
 147                       int klassAccess,
 148                       String superKlass,
 149                       String fieldType,
 150                       int fieldAccess,
 151                       String errMsgRequired) {
 152         try {
 153             createTestClass(clsName, klassAccess, superKlass, fieldType, fieldAccess);
 154             error(clsName + " : failed to fail with Error");
 155         }
 156         catch (ClassNotFoundException cnfe) {
 157             fail(clsName + " : Unexpected ClassNotFoundException", cnfe);
 158         }
 159         catch (Error err) {
 160             if (!err.getMessage().contains(errMsgRequired)) {
 161                 fail(clsName + " : Not the error we were looking for", err);
 162             }
 163         }
 164     }
 165 
 166     Class<?> createTestClass(String name,
 167                              int klassAccess,
 168                              String superKlass,
 169                              String fieldType,
 170                              int fieldAccess) throws ClassNotFoundException {
 171         ClassWriter cw = new ClassWriter(0);
 172         cw.visit(52, klassAccess, name, null, superKlass, null);
 173         cw.visitAnnotation("Ljvm/internal/value/DeriveValueType;", true);
 174         if (fieldType != null) {
 175             cw.visitField(fieldAccess, "x", fieldType, null, null);
 176         }
 177         cw.visitEnd();
 178         return new TestClassLoader(name, cw.toByteArray()).loadClass(name);
 179     }
 180 
 181     class TestClassLoader extends ClassLoader {
 182 
 183         String name;
 184         byte[] classBytes;
 185 
 186         TestClassLoader(String name, byte[] classBytes) {
 187             this.name = name;
 188             this.classBytes = classBytes;
 189         }
 190 
 191         @Override
 192         public Class findClass(String name) throws ClassNotFoundException {
 193             if (this.name.equals(name)) {
 194                 return defineClass(name, classBytes, 0, classBytes.length);
 195             }
 196             throw new ClassNotFoundException();
 197         }
 198     }
 199 
 200     /*
 201        TODO: Current repo state doesn't pick up the common test/lib Asserts
 202        remove this implementation when fixed
 203      */
 204     public static void fail(String msg, Throwable thr) { // Missing Asserts.fail(String, Throwable)
 205         throw new RuntimeException(msg, thr);
 206     }
 207     public static void error(String msg) { // Missing Asserts.error(String)
 208         throw new RuntimeException(msg);
 209     }
 210 }