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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package valhalla.shady; 26 27 import jdk.experimental.bytecode.BasicClassBuilder; 28 import jdk.internal.misc.Unsafe; 29 import jdk.internal.misc.VM; 30 import sun.security.action.GetPropertyAction; 31 32 import java.io.BufferedOutputStream; 33 import java.io.File; 34 import java.io.IOException; 35 import java.io.OutputStream; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.security.ProtectionDomain; 40 import java.util.Properties; 41 42 import static jdk.internal.org.objectweb.asm.Opcodes.*; 43 44 import jdk.internal.misc.JavaLangAccess; 45 import jdk.internal.misc.SharedSecrets; 46 public class MinimalValueTypes_1_0 { 47 48 public static final int V53_1 = 1 << 16 | 53; 49 public static final int ACC_VALUE = ACC_NATIVE; 50 public static final String OBJECT_CLASS_DESC = "java/lang/Object"; 51 public static final String VALUE_CLASS_DESC = "java/lang/__Value"; 52 53 public static final String DERIVE_VALUE_TYPE_DESC = "Ljvm/internal/value/ValueCapableClass;"; 54 public static final String DERIVE_VT_CLASSNAME_POSTFIX = "$Value"; 55 public static final int DERIVE_VT_CLASS_ACCESS = ACC_PUBLIC|ACC_SUPER|ACC_FINAL|ACC_VALUE|ACC_SYNTHETIC; 56 57 public static final boolean DUMP_CLASS_FILES; 58 private static final boolean VALUE_TYPE_ENABLED; 59 private static final JavaLangAccess JLA; 60 61 static { 62 // Use same property as in j.l.invoke.MethodHandleStatics 63 Properties props = GetPropertyAction.privilegedGetProperties(); 64 DUMP_CLASS_FILES = Boolean.parseBoolean( 65 props.getProperty("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES")); 66 67 VALUE_TYPE_ENABLED = Boolean.parseBoolean( 68 props.getProperty("valhalla.enableValueType")); 69 70 JLA = SharedSecrets.getJavaLangAccess(); 71 } 72 73 public static boolean isValueTypeEnabled() { 74 return VALUE_TYPE_ENABLED; 75 } 76 77 public static String getValueTypeClassName(ValueTypeDesc valueTypeDesc) { 78 return getValueTypeClassName(valueTypeDesc.getName()); 79 } 80 81 public static String getValueTypeClassName(String vccName) { 82 return vccName + DERIVE_VT_CLASSNAME_POSTFIX; 83 } 84 85 public static String getValueCapableClassName(String valName) { 86 return valName.substring(0, valName.length() - DERIVE_VT_CLASSNAME_POSTFIX.length()); 87 } 88 89 public static boolean isValueType(Class<?> dvt) { 90 return (dvt.getModifiers() & ACC_VALUE) != 0; 91 } 92 93 public static boolean isValueCapable(Class<?> vcc) { 94 return vcc.getDeclaredAnnotation(jvm.internal.value.ValueCapableClass.class) != null; 95 } 96 97 public static Class<?> getValueCapableClass(Class<?> dvt) { 98 if (!isValueType(dvt)) { 99 throw new IllegalArgumentException(dvt + " is not a derived value type"); 100 } 101 102 Class<?> c = Class.forName(dvt.getModule(), getValueCapableClassName(dvt.getName())); 103 if (c == null || !isValueCapable(c)) { 104 throw new InternalError(dvt + " not bound to ValueType"); 105 } 106 return c; 107 } 108 109 public static Class<?> getValueTypeClass(Class<?> vcc) { 110 if (!isValueCapable(vcc)) { 111 throw new IllegalArgumentException(vcc + " is not a value capable class"); 112 } 113 return loadValueTypeClass(vcc, getValueTypeClassName(vcc.getName())); 114 } 115 116 public static Class<?> loadValueTypeClass(Class<?> vcc, String className) { 117 if (!isValueCapable(vcc)) { 118 throw new IllegalArgumentException(vcc.getName() + " is not a value capable class"); 119 } 120 return JLA.loadValueTypeClass(vcc.getModule(), vcc.getClassLoader(), className); 121 } 122 123 /** 124 * This method is invoked by the VM. 125 * 126 * @param fds : name/sig pairs 127 * @param fmods : field modifiers 128 */ 129 public static String createDerivedValueType(String vccInternalClassName, 130 ClassLoader cl, 131 ProtectionDomain pd, 132 String[] fds, 133 int[] fmods) { 134 String vtInternalClassName = getValueTypeClassName(vccInternalClassName); 135 ValueTypeDesc valueTypeDesc = new ValueTypeDesc(vccInternalClassName, fds, fmods); 136 byte[] valueTypeBytes = createValueType(valueTypeDesc); 137 Class<?> vtClass = Unsafe.getUnsafe().defineClass(vtInternalClassName, valueTypeBytes, 0, valueTypeBytes.length, cl, pd); 138 return vtInternalClassName; 139 } 140 141 public static byte[] createValueType(ValueTypeDesc valueTypeDesc) { 142 143 String valueTypeClassName = getValueTypeClassName(valueTypeDesc); 144 145 BasicClassBuilder builder = new BasicClassBuilder(mangleValueClassName(valueTypeClassName), 53, 1) 146 .withFlags(DERIVE_VT_CLASS_ACCESS) 147 .withSuperclass(mangleValueClassName(VALUE_CLASS_DESC)); 148 149 ValueTypeDesc.Field[] fields = valueTypeDesc.getFields(); 150 for (ValueTypeDesc.Field field : fields) { 151 builder.withField(field.name, field.type, F -> F.withFlags(field.modifiers)); 152 } 153 154 byte[] newBytes = builder.build(); 155 maybeDump(valueTypeClassName, newBytes); 156 return newBytes; 157 } 158 159 /** debugging flag for saving generated class files */ 160 private static final Path DUMP_CLASS_FILES_DIR; 161 162 static { 163 if (DUMP_CLASS_FILES) { 164 try { 165 Path dumpDir = Paths.get("DUMP_CLASS_FILES"); 166 Files.createDirectories(dumpDir); 167 DUMP_CLASS_FILES_DIR = dumpDir; 168 } catch (Exception e) { 169 throw new InternalError(e); 170 } 171 } else { 172 DUMP_CLASS_FILES_DIR = null; 173 } 174 } 175 176 public static void maybeDump(final String className, final byte[] classFile) { 177 if (DUMP_CLASS_FILES_DIR != null) { 178 java.security.AccessController.doPrivileged( 179 new java.security.PrivilegedAction<>() { 180 public Void run() { 181 String dumpName = className; 182 //dumpName = dumpName.replace('/', '-'); 183 Path dumpFile = DUMP_CLASS_FILES_DIR.resolve(dumpName + ".class"); 184 File root = dumpFile.getParent().toFile(); 185 if (!root.exists() && !root.mkdirs()) { 186 throw new IllegalStateException("Could not create dump file directory: " + root); 187 } 188 System.out.println("dump: " + dumpFile); 189 try (OutputStream os = Files.newOutputStream(dumpFile); 190 BufferedOutputStream bos = new BufferedOutputStream(os)) { 191 bos.write(classFile); 192 } catch (IOException ex) { 193 throw new InternalError(ex); 194 } 195 return null; 196 } 197 }); 198 199 } 200 } 201 202 private final native Class<?> getDerivedValueType(Class<?> ofClass); 203 204 public static Class<?> getValueClass() { 205 return VALUE_TYPE_ENABLED ? ValueClassHolder.CLASS_OBJECT : null; 206 } 207 208 public static String mangleValueClassName(String name) { 209 return ";Q" + name + ";"; 210 } 211 212 /* 213 * A holder class that references __Value to be loaded only when MVT is enabled. 214 * 215 * This holder class would fail verification if loaded if MVT is not enabled. 216 */ 217 private static class ValueClassHolder { 218 static final Class<?> CLASS_OBJECT = 219 (Class<?>)(Object)__Value.class; //hack around static type-system checks 220 } 221 }