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 26 package jdk.experimental.value; 27 28 import java.lang.invoke.MethodHandle; 29 import java.lang.invoke.MethodHandles.Lookup; 30 import java.lang.invoke.MethodType; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.Method; 33 import java.lang.reflect.Modifier; 34 import java.util.Arrays; 35 import java.util.Map; 36 import java.util.Objects; 37 import java.util.Optional; 38 import java.util.concurrent.ConcurrentHashMap; 39 import java.util.stream.Stream; 40 41 import jdk.experimental.value.ValueType.ValueHandleKind.ValueHandleKey; 42 import jdk.experimental.bytecode.MacroCodeBuilder.CondKind; 43 import jdk.experimental.bytecode.TypeTag; 44 import jdk.internal.misc.Unsafe; 45 import sun.invoke.util.BytecodeDescriptor; 46 import sun.invoke.util.Wrapper; 47 import valhalla.shady.MinimalValueTypes_1_0; 48 49 // Rough place holder just now... 50 public class ValueType<T> { 51 52 static final Unsafe UNSAFE = Unsafe.getUnsafe(); 53 54 enum ValueHandleKind { 55 BOX, 56 UNBOX, 57 DEFAULT, 58 EQ, 59 HASH, 60 WITHER() { 61 @Override 62 ValueHandleKey key(Object fieldName) { 63 return new ValueHandleKey(this, fieldName); 64 } 65 }, 66 MAKE, 67 NEWARRAY, 68 VALOAD, 69 VASTORE, 70 MULTINEWARRAY() { 71 @Override 72 ValueHandleKey key(Object dims) { 73 return new ValueHandleKey(this, dims); 74 } 75 }; 76 77 ValueHandleKey key() { 78 return new ValueHandleKey(this, null); 79 } 80 81 ValueHandleKey key(Object optArg) { 82 throw new IllegalStateException(); 83 } 84 85 static class ValueHandleKey { 86 ValueHandleKind kind; 87 Optional<Object> optArg; 88 89 ValueHandleKey(ValueHandleKind kind, Object optArg) { 90 this.kind = kind; 91 this.optArg = Optional.ofNullable(optArg); 92 } 93 94 @Override 95 public boolean equals(Object obj) { 96 if (obj instanceof ValueHandleKey) { 97 ValueHandleKey that = (ValueHandleKey)obj; 98 return Objects.equals(kind, that.kind) && 99 Objects.equals(optArg, that.optArg); 100 } else { 101 return false; 102 } 103 } 104 105 @Override 106 public int hashCode() { 107 return Objects.hashCode(kind) * 31 + Objects.hashCode(optArg); 108 } 109 } 110 } 111 112 private static final Lookup IMPL_LOOKUP; 113 114 static { 115 try { 116 Field f = Lookup.class.getDeclaredField("IMPL_LOOKUP"); 117 f.setAccessible(true); 118 IMPL_LOOKUP = (Lookup)f.get(null); 119 } catch (ReflectiveOperationException ex) { 120 throw new AssertionError(ex); 121 } 122 } 123 124 private static final ConcurrentHashMap<Class<?>, ValueType<?>> BOX_TO_VT = new ConcurrentHashMap<>(); 125 126 public static boolean classHasValueType(Class<?> x) { 127 return MinimalValueTypes_1_0.classHasValueType(x); 128 } 129 130 @SuppressWarnings("unchecked") 131 public static <T> ValueType<T> forClass(Class<T> x) { 132 ValueType<T> vt = (ValueType<T>) BOX_TO_VT.get(x); 133 if (vt != null) { 134 return vt; 135 } 136 137 try { 138 Class<T> valueClass = (Class<T>) MinimalValueTypes_1_0.getValueTypeClass(x); 139 vt = new ValueType<T>(x, valueClass); 140 ValueType<T> old = (ValueType<T>) BOX_TO_VT.putIfAbsent(x, vt); 141 if (old != null) { 142 vt = old; 143 } 144 return vt; 145 } 146 catch (ClassNotFoundException cne) { 147 throw new IllegalArgumentException("Class " + x + " not bound to ValueType", cne); 148 } 149 } 150 151 private Class<T> boxClass; 152 private Class<?> valueClass; 153 private Lookup lookup; 154 private Map<ValueHandleKind.ValueHandleKey, MethodHandle> handleMap = new ConcurrentHashMap<>(); 155 156 private ValueType(Class<T> boxClass, Class<T> valueClass) { 157 this.boxClass = boxClass; 158 this.valueClass = valueClass; 159 this.lookup = IMPL_LOOKUP.in(boxClass); 160 } 161 162 public Class<T> boxClass() { 163 return boxClass; 164 } 165 166 public Class<?> sourceClass() { 167 return boxClass(); 168 } 169 170 public Class<?> valueClass() { 171 return valueClass; 172 } 173 174 public Class<?> arrayValueClass() { 175 return arrayValueClass(1); 176 } 177 178 public Class<?> arrayValueClass(int dims) { 179 try { 180 String dimsStr = "[[[[[[[[[[[[[[[["; 181 if (dims < 1 || dims > 16) { 182 throw new IllegalArgumentException("cannot create array class for dimension > 16"); 183 } 184 return Class.forName(dimsStr.substring(0, dims) + "Q" + valueClass().getName() + ";", false, lookup.lookupClass().getClassLoader()); 185 } catch (ClassNotFoundException ex) { 186 throw new IllegalStateException(ex); 187 } 188 } 189 190 public String toString() { 191 return "ValueType boxClass=" + boxClass() + " valueClass=" + valueClass(); 192 } 193 194 public MethodHandle defaultValueConstant() { 195 ValueHandleKey key = ValueHandleKind.DEFAULT.key(); 196 MethodHandle result = handleMap.get(key); 197 if (result == null) { 198 result = MethodHandleBuilder.loadCode(lookup, "default" + sourceClass().getName(), MethodType.methodType(valueClass()), 199 C -> { 200 C.new_(boxClass()).vunbox(valueClass()).vreturn(); 201 }); 202 handleMap.put(key, result); 203 } 204 return result; 205 } 206 207 public MethodHandle substitutabilityTest() { 208 ValueHandleKey key = ValueHandleKind.EQ.key(); 209 MethodHandle result = handleMap.get(key); 210 if (result == null) { 211 result = MethodHandleBuilder.loadCode(lookup, "subTest" + sourceClass().getName(), MethodType.methodType(boolean.class, valueClass(), valueClass()), 212 C -> { 213 for (Field f : valueFields()) { 214 C.vload(0).vgetfield(valueClass(), f.getName(), BytecodeDescriptor.unparse(f.getType())); 215 C.vload(1).vgetfield(valueClass(), f.getName(), BytecodeDescriptor.unparse(f.getType())); 216 if (f.getType().isPrimitive()) { 217 C.ifcmp(TypeTag.I, CondKind.NE, "fail"); 218 } else { 219 C.invokestatic(Objects.class, "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false); 220 C.const_(0).ifcmp(TypeTag.I, CondKind.NE, "fail"); 221 } 222 } 223 C.const_(1); 224 C.ireturn(); 225 C.label("fail"); 226 C.const_(0); 227 C.ireturn(); 228 }); 229 handleMap.put(key, result); 230 } 231 return result; 232 } 233 234 public MethodHandle substitutabilityHashCode() { 235 ValueHandleKey key = ValueHandleKind.HASH.key(); 236 MethodHandle result = handleMap.get(key); 237 if (result == null) { 238 result = MethodHandleBuilder.loadCode(lookup, "subHash" + sourceClass().getName(), MethodType.methodType(int.class, valueClass()), 239 C -> { 240 C.withLocal("res", "I"); 241 C.const_(1).store("res"); 242 for (Field f : valueFields()) { 243 String desc = BytecodeDescriptor.unparse(f.getType()); 244 C.vload(0).vgetfield(valueClass(), f.getName(), desc); 245 C.load("res").const_(31).imul(); 246 if (f.getType().isPrimitive()) { 247 C.invokestatic(Wrapper.asWrapperType(f.getType()), "hashCode", "(" + desc + ")I", false); 248 } else { 249 C.invokestatic(Objects.class, "hashCode", "(Ljava/lang/Object)I", false); 250 } 251 C.iadd().store("res"); 252 } 253 C.load("res").ireturn(); 254 }); 255 handleMap.put(key, result); 256 } 257 return result; 258 } 259 260 //Todo: when 'vwithfield' is ready, this handle could be greatly simplified 261 public MethodHandle findWither(String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { 262 ValueHandleKey key = ValueHandleKind.WITHER.key(name); 263 MethodHandle result = handleMap.get(key); 264 if (result == null) { 265 Field field = boxClass().getDeclaredField(name); 266 if (field == null || !field.getType().equals(type) || 267 (field.getModifiers() & Modifier.STATIC) != 0) { 268 throw new NoSuchFieldException(name); 269 } 270 Class<?> erasedType = type.isPrimitive() ? 271 type : Object.class; 272 Method unsafeMethod = Stream.of(UNSAFE.getClass().getDeclaredMethods()) 273 .filter(m -> m.getName().startsWith("put") && 274 Arrays.asList(m.getParameterTypes()).equals(Arrays.asList(Object.class, long.class, erasedType))) 275 .findFirst().get(); 276 long fieldOffset = UNSAFE.objectFieldOffset(field); 277 result = MethodHandleBuilder.loadCode(lookup, "wither" + sourceClass().getName() + ":" + name, MethodType.methodType(valueClass(), UNSAFE.getClass(), valueClass(), type), 278 C -> { 279 C.withLocal("boxedVal", BytecodeDescriptor.unparse(boxClass())) 280 .load(1) 281 .vbox(boxClass()) 282 .store("boxedVal") 283 .load(0) 284 .load("boxedVal") 285 .const_(fieldOffset) 286 .load(2); 287 MethodType unsafeMT = MethodType.methodType(unsafeMethod.getReturnType(), unsafeMethod.getParameterTypes()); 288 C.invokevirtual(UNSAFE.getClass(), unsafeMethod.getName(), BytecodeDescriptor.unparse(unsafeMT), false) 289 .load("boxedVal") 290 .vunbox(valueClass()) 291 .vreturn(); 292 }).bindTo(UNSAFE); 293 handleMap.put(key, result); 294 } 295 return result; 296 } 297 298 public MethodHandle unbox() { 299 ValueHandleKey key = ValueHandleKind.UNBOX.key(); 300 MethodHandle result = handleMap.get(key); 301 if (result == null) { 302 result = MethodHandleBuilder.loadCode(lookup, "unbox" + sourceClass().getName(), MethodType.methodType(valueClass(), boxClass()), 303 C -> { 304 C.load(0).vunbox(valueClass()).vreturn(); 305 }); 306 handleMap.put(key, result); 307 } 308 return result; 309 } 310 311 public MethodHandle box() { 312 ValueHandleKey key = ValueHandleKind.BOX.key(); 313 MethodHandle result = handleMap.get(key); 314 if (result == null) { 315 result = MethodHandleBuilder.loadCode(lookup, "box" + sourceClass().getName(), MethodType.methodType(boxClass(), valueClass()), 316 C -> { 317 C.vload(0).vbox(boxClass()).areturn(); 318 }); 319 handleMap.put(key, result); 320 } 321 return result; 322 } 323 324 public MethodHandle newArray() { 325 Class<?> arrayValueClass = arrayValueClass(); 326 ValueHandleKey key = ValueHandleKind.NEWARRAY.key(); 327 MethodHandle result = handleMap.get(key); 328 if (result == null) { 329 result = MethodHandleBuilder.loadCode(lookup, "newArray" + sourceClass().getName(), MethodType.methodType(arrayValueClass, int.class), 330 C -> { 331 C.load(0).anewarray(valueClass()).areturn(); 332 }); 333 handleMap.put(key, result); 334 } 335 return result; 336 } 337 338 public MethodHandle arrayGetter() { 339 Class<?> arrayValueClass = arrayValueClass(); 340 ValueHandleKey key = ValueHandleKind.VALOAD.key(); 341 MethodHandle result = handleMap.get(key); 342 if (result == null) { 343 result = MethodHandleBuilder.loadCode(lookup, "arrayGet" + sourceClass().getName(), MethodType.methodType(valueClass(), arrayValueClass, int.class), 344 C -> { 345 C.load(0).load(1).vaload().vreturn(); 346 }); 347 handleMap.put(key, result); 348 } 349 return result; 350 } 351 352 public MethodHandle arraySetter() { 353 Class<?> arrayValueClass = arrayValueClass(); 354 ValueHandleKey key = ValueHandleKind.VASTORE.key(); 355 MethodHandle result = handleMap.get(key); 356 if (result == null) { 357 result = MethodHandleBuilder.loadCode(lookup, "arraySet" + sourceClass().getName(), MethodType.methodType(void.class, arrayValueClass, int.class, valueClass()), 358 C -> { 359 C.load(0).load(1).load(2).vastore().return_(); 360 }); 361 handleMap.put(key, result); 362 } 363 return result; 364 } 365 366 public MethodHandle newMultiArray(int dims) { 367 Class<?> arrayValueClass = arrayValueClass(dims); 368 ValueHandleKey key = ValueHandleKind.MULTINEWARRAY.key(dims); 369 MethodHandle result = handleMap.get(key); 370 Class<?>[] params = new Class<?>[dims]; 371 Arrays.fill(params, int.class); 372 if (result == null) { 373 result = MethodHandleBuilder.loadCode(lookup, "newMultiArray" + sourceClass().getName(), MethodType.methodType(arrayValueClass, params), 374 C -> { 375 for (int i = 0 ; i < dims ; i++) { 376 C.load(i); 377 } 378 C.multianewarray(arrayValueClass, (byte)dims).areturn(); 379 }); 380 handleMap.put(key, result); 381 } 382 return result; 383 } 384 385 private Field[] valueFields() { 386 int valFieldMask = Modifier.PUBLIC | Modifier.FINAL; 387 return Stream.of(sourceClass().getDeclaredFields()) 388 .filter(f -> (f.getModifiers() & (valFieldMask | Modifier.STATIC)) == valFieldMask) 389 .toArray(Field[]::new); 390 } 391 }