OpenJDK / valhalla / valhalla
changeset 47252:e99c25c2f4a6 mvt
import changes from mvt/jdk
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/Class.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/Class.java Tue Sep 26 16:13:10 2017 +0200 @@ -81,6 +81,9 @@ import sun.security.util.SecurityConstants; import sun.reflect.annotation.*; import sun.reflect.misc.ReflectUtil; +import valhalla.shady.MinimalValueTypes_1_0; + +import static valhalla.shady.MinimalValueTypes_1_0.ACC_VALUE; /** * Instances of the class {@code Class} represent classes and @@ -289,7 +292,11 @@ public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); - return forName0(className, true, ClassLoader.getClassLoader(caller), caller); + Class<?> c = forName0(className, true, ClassLoader.getClassLoader(caller), caller); + if (c.isValueClass()) { + throw new ClassNotFoundException(className + " is a derived value class"); + } + return c; } @@ -372,13 +379,17 @@ } } } - return forName0(name, initialize, loader, caller); + Class<?> c = forName0(name, initialize, loader, caller); + if (c.isValueClass()) { + throw new ClassNotFoundException(name + " is a derived value class"); + } + return c; } /** Called after security check for system loader access checks have been made. */ - private static native Class<?> forName0(String name, boolean initialize, - ClassLoader loader, - Class<?> caller) + static native Class<?> forName0(String name, boolean initialize, + ClassLoader loader, + Class<?> caller) throws ClassNotFoundException; @@ -447,10 +458,38 @@ cl = module.getClassLoader(); } + Class<?> c; if (cl != null) { - return cl.loadClass(module, name); + c = cl.loadClass(module, name); } else { - return BootLoader.loadClass(module, name); + c = BootLoader.loadClass(module, name); + } + + return c != null && !c.isValueClass() ? c : null; + } + + boolean isValueClass() { + // ensure that system properties have been initialized before + // loading MinimalValueTypes_1_0 class. + if (VM.initLevel() < 1 || !MinimalValueTypes_1_0.isValueTypeEnabled()) + return false; + + Class<?> c = this; + while (c.isArray()) { + c = c.getComponentType(); + } + + // For now, check if it is a subtype of __Value + Class<?> valueBaseClass = MinimalValueTypes_1_0.getValueClass(); + return valueBaseClass != null && c != valueBaseClass && valueBaseClass.isAssignableFrom(c); + } + + private void ensureNotValueClass() { + // temporary workaround until compiler/valhalla/valuetypes/ValueTypeBenchTest.java + // is updated to do reflection on VCC instead of VVT declared with __ByValue + if (this.isValueClass() && !Boolean.parseBoolean(VM.getSavedProperty("jdk.lang.reflect.DVT"))) { + throw new UnsupportedOperationException("cannot reflect on derived value type " + + this.getName()); } } @@ -512,6 +551,8 @@ public T newInstance() throws InstantiationException, IllegalAccessException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false); @@ -1246,6 +1287,8 @@ // Perform access check final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass(); + enclosingCandidate.ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { enclosingCandidate.checkMemberAccess(sm, Member.DECLARED, @@ -1402,6 +1445,8 @@ // Perform access check final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass(); + enclosingCandidate.ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { enclosingCandidate.checkMemberAccess(sm, Member.DECLARED, @@ -1698,6 +1743,8 @@ */ @CallerSensitive public Class<?>[] getClasses() { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false); @@ -1767,6 +1814,8 @@ */ @CallerSensitive public Field[] getFields() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); @@ -1857,6 +1906,8 @@ */ @CallerSensitive public Method[] getMethods() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); @@ -1896,6 +1947,8 @@ */ @CallerSensitive public Constructor<?>[] getConstructors() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); @@ -1949,6 +2002,8 @@ @CallerSensitive public Field getField(String name) throws NoSuchFieldException, SecurityException { + ensureNotValueClass(); + Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2058,6 +2113,8 @@ @CallerSensitive public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { + ensureNotValueClass(); + Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2103,6 +2160,8 @@ public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); @@ -2149,6 +2208,8 @@ */ @CallerSensitive public Class<?>[] getDeclaredClasses() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), false); @@ -2201,6 +2262,8 @@ */ @CallerSensitive public Field[] getDeclaredFields() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); @@ -2262,6 +2325,8 @@ */ @CallerSensitive public Method[] getDeclaredMethods() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); @@ -2310,6 +2375,8 @@ */ @CallerSensitive public Constructor<?>[] getDeclaredConstructors() throws SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); @@ -2361,6 +2428,8 @@ @CallerSensitive public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException { + ensureNotValueClass(); + Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2425,6 +2494,8 @@ @CallerSensitive public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { + ensureNotValueClass(); + Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2504,6 +2575,8 @@ public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { + ensureNotValueClass(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
--- a/src/java.base/share/classes/java/lang/System.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/System.java Tue Sep 26 16:13:10 2017 +0200 @@ -2185,6 +2185,19 @@ public Stream<ModuleLayer> layers(ClassLoader loader) { return ModuleLayer.layers(loader); } + public Class<?> loadValueTypeClass(Module module, ClassLoader cl, String name) { + try { + // VM support to define DVT + Class<?> c = Class.forName0(name, false, cl, Object.class); + // catch if the given name is not the name of a DVT class + if (!c.isValueClass()) { + throw new InternalError(c.getName() + " not a value type"); + } + return c; + } catch (ClassNotFoundException e) { + throw new InternalError(e); + } + } }); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/java/lang/__Value.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Base class of all value types in prototype implementation. + */ +__ByValue final public class __Value { + + /** + * Returns the runtime class of this {@code Object}. The returned + * {@code Class} object is the object that is locked by {@code + * static synchronized} methods of the represented class. + * + * <p><b>The actual result type is {@code Class<? extends |X|>} + * where {@code |X|} is the erasure of the static type of the + * expression on which {@code getClass} is called.</b> For + * example, no cast is required in this code fragment:</p> + * + * <p> + * {@code Number n = 0; }<br> + * {@code Class<? extends Number> c = n.getClass(); } + * </p> + * + * @return The {@code Class} object that represents the runtime + * class of this object. + * @jls 15.8.2 Class Literals + */ + public final native Class<?> getClass(); + + /** + * Returns a hash code value for the object. This method is + * supported for the benefit of hash tables such as those provided by + * {@link java.util.HashMap}. + * <p> + * The general contract of {@code hashCode} is: + * <ul> + * <li>Whenever it is invoked on the same object more than once during + * an execution of a Java application, the {@code hashCode} method + * must consistently return the same integer, provided no information + * used in {@code equals} comparisons on the object is modified. + * This integer need not remain consistent from one execution of an + * application to another execution of the same application. + * <li>If two objects are equal according to the {@code equals(Object)} + * method, then calling the {@code hashCode} method on each of + * the two objects must produce the same integer result. + * <li>It is <em>not</em> required that if two objects are unequal + * according to the {@link java.lang.Object#equals(java.lang.Object)} + * method, then calling the {@code hashCode} method on each of the + * two objects must produce distinct integer results. However, the + * programmer should be aware that producing distinct integer results + * for unequal objects may improve the performance of hash tables. + * </ul> + * <p> + * As much as is reasonably practical, the hashCode method defined + * by class {@code Object} does return distinct integers for + * distinct objects. (The hashCode may or may not be implemented + * as some function of an object's memory address at some point + * in time.) + * + * @return a hash code value for this object. + * @see java.lang.Object#equals(java.lang.Object) + * @see java.lang.System#identityHashCode + */ + public int hashCode() { + throw new UnsupportedOperationException(); + } + + /** + * Indicates whether some other object is "equal to" this one. + * <p> + * The {@code equals} method implements an equivalence relation + * on non-null object references: + * <ul> + * <li>It is <i>reflexive</i>: for any non-null reference value + * {@code x}, {@code x.equals(x)} should return + * {@code true}. + * <li>It is <i>symmetric</i>: for any non-null reference values + * {@code x} and {@code y}, {@code x.equals(y)} + * should return {@code true} if and only if + * {@code y.equals(x)} returns {@code true}. + * <li>It is <i>transitive</i>: for any non-null reference values + * {@code x}, {@code y}, and {@code z}, if + * {@code x.equals(y)} returns {@code true} and + * {@code y.equals(z)} returns {@code true}, then + * {@code x.equals(z)} should return {@code true}. + * <li>It is <i>consistent</i>: for any non-null reference values + * {@code x} and {@code y}, multiple invocations of + * {@code x.equals(y)} consistently return {@code true} + * or consistently return {@code false}, provided no + * information used in {@code equals} comparisons on the + * objects is modified. + * <li>For any non-null reference value {@code x}, + * {@code x.equals(null)} should return {@code false}. + * </ul> + * <p> + * The {@code equals} method for class {@code Object} implements + * the most discriminating possible equivalence relation on objects; + * that is, for any non-null reference values {@code x} and + * {@code y}, this method returns {@code true} if and only + * if {@code x} and {@code y} refer to the same object + * ({@code x == y} has the value {@code true}). + * <p> + * Note that it is generally necessary to override the {@code hashCode} + * method whenever this method is overridden, so as to maintain the + * general contract for the {@code hashCode} method, which states + * that equal objects must have equal hash codes. + * + * @param obj the reference object with which to compare. + * @return {@code true} if this object is the same as the obj + * argument; {@code false} otherwise. + * @see #hashCode() + * @see java.util.HashMap + */ + public boolean equals(Object obj) { + throw new UnsupportedOperationException(); + } + + /** + * Returns a string representation of the object. In general, the + * {@code toString} method returns a string that + * "textually represents" this object. The result should + * be a concise but informative representation that is easy for a + * person to read. + * It is recommended that all subclasses override this method. + * <p> + * The {@code toString} method for class {@code Object} + * returns a string consisting of the name of the class of which the + * object is an instance, the at-sign character `{@code @}', and + * the unsigned hexadecimal representation of the hash code of the + * object. In other words, this method returns a string equal to the + * value of: + * <blockquote> + * <pre> + * getClass().getName() + '@' + Integer.toHexString(hashCode()) + * </pre></blockquote> + * + * @return a string representation of the object. + */ + public String toString() { + throw new UnsupportedOperationException(); + } +}
--- a/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Tue Sep 26 16:13:10 2017 +0200 @@ -114,7 +114,10 @@ BoundMethodHandle bindArgumentD(int pos, double value) { return editor().bindArgumentD(this, pos, value); } - + /*non-public*/ + BoundMethodHandle bindArgumentQ(int pos, Object value, MethodHandle unbox) { + return editor().bindArgumentQ(this, pos, value, unbox); + } @Override BoundMethodHandle rebind() { if (!tooComplex()) { @@ -699,6 +702,7 @@ // for each type, emit copyWithExtendT() for (BasicType type : BasicType.ARG_TYPES) { + if (type == Q_TYPE) continue; int ord = type.ordinal(); char btChar = type.basicTypeChar(); mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
--- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Tue Sep 26 16:13:10 2017 +0200 @@ -25,6 +25,7 @@ package java.lang.invoke; +import java.lang.invoke.LambdaForm.BasicType; import java.util.Map; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; @@ -45,6 +46,7 @@ ArrayList<String> names = new ArrayList<>(); HashSet<String> dedupSet = new HashSet<>(); for (LambdaForm.BasicType type : LambdaForm.BasicType.values()) { + if (type == BasicType.Q_TYPE) continue; LambdaForm zero = LambdaForm.zeroForm(type); String name = zero.kind.defaultLambdaName + "_" + zero.returnType().basicTypeChar();
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Sep 26 16:13:10 2017 +0200 @@ -294,7 +294,7 @@ return invokerClass; } - private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { + static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); try { member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
--- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Tue Sep 26 16:13:10 2017 +0200 @@ -29,14 +29,18 @@ import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; +import sun.security.action.GetBooleanAction; +import valhalla.shady.MinimalValueTypes_1_0; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; +import java.security.AccessController; import java.util.Arrays; import java.util.HashMap; +import java.util.PropertyPermission; import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; @@ -141,6 +145,7 @@ J_TYPE('J', long.class, Wrapper.LONG), F_TYPE('F', float.class, Wrapper.FLOAT), D_TYPE('D', double.class, Wrapper.DOUBLE), // all primitive types + Q_TYPE('Q', MinimalValueTypes_1_0.getValueClass(), Wrapper.OBJECT), // all reference types V_TYPE('V', void.class, Wrapper.VOID); // not valid in all contexts static final BasicType[] ALL_TYPES = BasicType.values(); @@ -189,6 +194,8 @@ case 'S': case 'C': return I_TYPE; + case 'Q': + return MethodHandleStatics.VALHALLA_ENABLE_VALUE_LFORMS ? Q_TYPE : L_TYPE; default: throw newInternalError("Unknown type char: '"+type+"'"); } @@ -198,7 +205,11 @@ return basicType(c); } static BasicType basicType(Class<?> type) { - if (!type.isPrimitive()) return L_TYPE; + if (!type.isPrimitive()) { + return MethodHandleStatics.VALHALLA_ENABLE_VALUE_LFORMS && MinimalValueTypes_1_0.isValueType(type) ? + Q_TYPE : + L_TYPE; + } return basicType(Wrapper.forPrimitiveType(type)); } static BasicType[] basicTypes(String types) { @@ -245,10 +256,10 @@ } static boolean isBasicTypeChar(char c) { - return "LIJFDV".indexOf(c) >= 0; + return "LIJFDQV".indexOf(c) >= 0; } static boolean isArgBasicTypeChar(char c) { - return "LIJFD".indexOf(c) >= 0; + return "LIJFDQ".indexOf(c) >= 0; } static { assert(checkBasicType()); } @@ -635,6 +646,18 @@ return names.length - arity; } + /** + * Does any 'name' in this lambda form contain Q-types? + */ + boolean containsValues() { + for (Name name : names) { + if (name.type == Q_TYPE) { + return true; + } + } + return false; + } + /** Return the method type corresponding to my basic type signature. */ MethodType methodType() { Class<?>[] ptypes = new Class<?>[arity]; @@ -854,7 +877,17 @@ MethodType invokerType = methodType(); assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType)); try { - vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType); + if (!containsValues()) { + //no Q-types, proceed as usual + vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType); + } else { + if (!MethodHandleStatics.VALHALLA_ENABLE_VALUE_LFORMS) { + //should not get here! + throw new IllegalStateException(); + } + //some Q-types were found - use alternate bytecode generator + vmentry = LambdaFormBuilder.generateCustomizedCode(this, invokerType); + } if (TRACE_INTERPRETER) traceInterpreter("compileToBytecode", this); isCompiled = true;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import jdk.experimental.bytecode.*; +import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind; +import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind; +import jdk.experimental.value.MethodHandleBuilder; +import sun.invoke.util.VerifyType; +import sun.invoke.util.Wrapper; +import valhalla.shady.MinimalValueTypes_1_0; + +import java.lang.invoke.LambdaForm.BasicType; +import java.lang.invoke.LambdaForm.Name; +import java.lang.invoke.MethodHandles.Lookup; +import java.util.Arrays; +import java.util.stream.Stream; + +import static java.lang.invoke.LambdaForm.BasicType.L_TYPE; +import static java.lang.invoke.LambdaForm.BasicType.V_TYPE; +import static java.lang.invoke.LambdaForm.BasicType.basicType; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField; +import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic; +import static java.lang.invoke.MethodHandleStatics.PROFILE_GWT; +import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL; +import static java.lang.invoke.MethodHandleStatics.newInternalError; +import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.EQ; +import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.NE; + +/** + * Utility class for spinning classfiles associated with lambda forms. + */ +class LambdaFormBuilder extends MethodHandleBuilder { + + private static final String OBJ = "java/lang/Object"; + private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$Value$"; + private static final String DEFAULT_CLASS = "MH"; + private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; + private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V"; + + private static final String MH = "java/lang/invoke/MethodHandle"; + private static final String MHARY2 = "[[L" + MH + ";"; + + static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { + String invokerName = form.lambdaName(); + int p = invokerName.indexOf('.'); + boolean overrideNames = p != -1; + String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName; + String className = overrideNames ? + CLASS_PREFIX + invokerName.substring(0, p) : + CLASS_PREFIX + DEFAULT_CLASS; + if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) { + // When DUMP_CLASS_FILES is true methodName will have a unique id + className = className + "_" + methodName; + } + return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(), + M -> new LambdaFormCodeBuilder(form, invokerType, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType), + C -> new LambdaFormBuilder(C, form).generateLambdaFormBody()); + } + + LambdaFormCodeBuilder builder; + LambdaForm lambdaForm; + + LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm) { + this.builder = builder; + this.lambdaForm = lambdaForm; + } + + /** Generates code to check that actual receiver and LambdaForm matches */ + private boolean checkActualReceiver() { + // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0 + builder.dup().load(0).invokestatic(MethodHandleImpl.class, "assertSame", LLV_SIG, false); + return true; + } + + void generateLambdaFormBody() { + if (lambdaForm.customized != null && MethodHandleBuilder.ENABLE_POOL_PATCHES) { + // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute + // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. + // It enables more efficient code generation in some situations, since embedded constants + // are compile-time constants for JIT compiler. + builder.ldc(lambdaForm.customized) + .checkcast(MethodHandle.class); + assert(checkActualReceiver()); // generates runtime check + builder.store(0); + } + + // iterate over the form's names, generating bytecode instructions for each + // start iterating at the first name following the arguments + Name onStack = null; + for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { + Name name = lambdaForm.names[i]; + + if (onStack != null && onStack.type != V_TYPE) { + // non-void: actually assign + builder.store(fromBasicType(onStack.type), onStack.index()); + } + onStack = name; // unless otherwise modified below + MemberName member = name.function.member(); + MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); + switch (intr) { + case SELECT_ALTERNATIVE: { + assert lambdaForm.isSelectAlternative(i); + if (PROFILE_GWT) { + assert(name.arguments[0] instanceof Name && + ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean")); + builder.method().withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, InvokerBytecodeGenerator.INJECTEDPROFILE_SIG); + } + onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); + i++; // skip MH.invokeBasic of the selectAlternative result + continue; + + } + + case LOOP: { + assert lambdaForm.isLoop(i); + onStack = emitLoop(i); + i += 2; // jump to the end of the LOOP idiom + continue; + } + case IDENTITY: { + assert (name.arguments.length == 1); + builder.pushArguments(name, 0); + continue; + } + case ZERO: { + assert (name.arguments.length == 0); + assert (name.type != BasicType.Q_TYPE); + builder.ldc(name.type.basicTypeWrapper().zero()); + continue; + } + // TODO: case GUARD_WITH_CATCH: + // TODO: case TRY_FINALLY: + // TODO: case NEW_ARRAY: + // TODO: case ARRAY_LOAD: + // TODO: case ARRAY_STORE: + // TODO: case ARRAY_LENGTH: + } + if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) { + builder.invokeStaticName(member, name); + } else { + builder.invokeName(name); + } + } + builder.return_(onStack); + } + + private Name emitLoop(int pos) { + Name args = lambdaForm.names[pos]; + Name invoker = lambdaForm.names[pos+1]; + Name result = lambdaForm.names[pos+2]; + + // extract clause and loop-local state types + // find the type info in the loop invocation + BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0]; + Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes). + filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new); + + Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1]; + localTypes[0] = MethodHandleImpl.LoopClauses.class; + System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length); + + final int clauseDataIndex = builder.extendLocalsMap(localTypes); + final int firstLoopStateIndex = clauseDataIndex + 1; + + Class<?> returnType = result.function.resolvedHandle().type().returnType(); + MethodType loopType = args.function.resolvedHandle().type() + .dropParameterTypes(0,1) + .changeReturnType(returnType); + MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes); + MethodType predType = loopHandleType.changeReturnType(boolean.class); + MethodType finiType = loopHandleType; + + final int nClauses = loopClauseTypes.length; + + // indices to invoker arguments to load method handle arrays + final int inits = 1; + final int steps = 2; + final int preds = 3; + final int finis = 4; + + // PREINIT: + builder.pushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]) + .getfield(MethodHandleImpl.LoopClauses.class, "clauses", MHARY2) + .store(TypeTag.A, clauseDataIndex); + + // INIT: + for (int c = 0, state = 0; c < nClauses; ++c) { + MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass()); + builder.invokeLoopHandle(inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); + if (cInitType.returnType() != void.class) { + builder.store(fromClass(cInitType.returnType()), firstLoopStateIndex + state); + ++state; + } + } + + // LOOP: + String loopLabel = builder.label(); + String doneLabel = builder.label(); + builder.label(loopLabel); + + String val = null; + for (int c = 0, s = 0; c < nClauses; ++c) { + MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); + boolean isVoid = (stepType.returnType() == void.class); + + // invoke loop step + builder.invokeLoopHandle(steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); + if (!isVoid) { + builder.store(fromClass(stepType.returnType()), firstLoopStateIndex + s); + ++s; + } + + String nextLabel = builder.label(); + + // invoke loop predicate + builder.invokeLoopHandle(preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex) + .emitCondJump(Opcode.IFEQ, NE, nextLabel) + .invokeLoopHandle(finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex) + .goto_(doneLabel) + .label(nextLabel); + } + builder.goto_(loopLabel) + .label(doneLabel); + + return result; + } + + /** + * Emit bytecode for the selectAlternative idiom. + * + * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): + * <blockquote><pre>{@code + * Lambda(a0:L,a1:I)=>{ + * t2:I=foo.test(a1:I); + * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); + * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} + * }</pre></blockquote> + */ + private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { + assert InvokerBytecodeGenerator.isStaticallyInvocable(invokeBasicName); + + Name receiver = (Name) invokeBasicName.arguments[0]; + + String fallbackLabel = builder.label(); + String doneLabel = builder.label(); + + builder.pushArgument(selectAlternativeName, 0) // load test result + .emitCondJump(Opcode.IFEQ, EQ, fallbackLabel) // if_icmp L_fallback + .pushArgument(selectAlternativeName, 1) // get 2nd argument of selectAlternative + .store(TypeTag.A, receiver.index()) // store the MH in the receiver slot + .invokeStaticName(invokeBasicName) // invoke selectAlternativeName.arguments[1] + .goto_(doneLabel) + .label(fallbackLabel) + .pushArgument(selectAlternativeName, 2) // get 3rd argument of selectAlternative + .store(TypeTag.A, receiver.index()) // store the MH in the receiver slot + .invokeStaticName(invokeBasicName) // invoke selectAlternativeName.arguments[2] + .label(doneLabel); + + return invokeBasicName; // return what's on stack + } + + static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder<LambdaFormCodeBuilder> { + + LambdaForm lambdaForm; + MethodType invokerType; + int maxLocals; + int[] localsMap; + private MethodBuilder<Class<?>, String, byte[]> methodBuilder; + private int labelCount = 0; + + public LambdaFormCodeBuilder(LambdaForm form, MethodType invokerType, MethodBuilder<Class<?>, String, byte[]> methodBuilder) { + super(methodBuilder); + if (form.forceInline) { + methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;"); + } + methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;") + .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;"); + this.lambdaForm = form; + this.invokerType = invokerType; + this.maxLocals = form.names.length; + this.localsMap = computeLocalsMap(form); + this.methodBuilder = methodBuilder; + } + + static int[] computeLocalsMap(LambdaForm lform) { + int localsMapSize = lform.names.length; + int[] localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots + for (int i = 0, index = 0; i < localsMap.length; i++) { + localsMap[i] = index; + if (i < lform.names.length) { + BasicType type = lform.names[i].type(); + index += type.basicTypeSlots(); + } + } + return localsMap; + } + + @Override + public LambdaFormCodeBuilder load(int index) { + return load(tagOfLocal(index), index); + } + + @Override + public LambdaFormCodeBuilder store(int index) { + return store(tagOfLocal(index), index); + } + + @Override + public LambdaFormCodeBuilder load(TypeTag type, int n) { + return super.load(type, localsMap[n]); + } + + @Override + public LambdaFormCodeBuilder store(TypeTag type, int n) { + return super.store(type, localsMap[n]); + } + + /** + * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. + */ + private void return_(Name onStack) { + // return statement + Class<?> rclass = invokerType.returnType(); + BasicType rtype = lambdaForm.returnType(); + assert(rtype == basicType(rclass)); // must agree + if (rtype == V_TYPE) { + // void + return_(); + // it doesn't matter what rclass is; the JVM will discard any value + } else { + LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; + + // put return value on the stack if it is not already there + if (rn != onStack) { + load(lambdaForm.result); + } + + coerce(rtype, rclass, rn); + + // generate actual return statement + return_(fromBasicType(rtype)); + } + } + + /** + * Emit an implicit conversion for an argument which must be of the given pclass. + * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface. + * + * @param ptype type of value present on stack + * @param pclass type of value required on stack + * @param arg compile-time representation of value on stack (Node, constant) or null if none + */ + private LambdaFormCodeBuilder coerce(BasicType ptype, Class<?> pclass, Object arg) { + assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller + if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) + return this; // nothing to do + switch (ptype) { + case L_TYPE: + if (VerifyType.isNullConversion(Object.class, pclass, false)) { + if (PROFILE_LEVEL > 0) + coerce(Object.class, arg); + return this; + } + coerce(pclass, arg); + return this; + case I_TYPE: + if (!VerifyType.isNullConversion(int.class, pclass, false)) + conv(fromBasicType(ptype), fromBasicType(BasicType.basicType(pclass))); + return this; + case Q_TYPE: + if (!MinimalValueTypes_1_0.isValueType(pclass)) { + vbox(Object.class); + return this; + } + assert pclass == arg.getClass(); + return this; //assume they are equal + } + throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass); + } + + private LambdaFormCodeBuilder coerce(Class<?> cls, Object arg) { + Name writeBack = null; // local to write back result + if (arg instanceof Name) { + Name n = (Name) arg; + if (cls.isAssignableFrom(typeOfLocal(n.index()))) + return this; // this cast was already performed + if (lambdaForm.useCount(n) > 1) { + // This guy gets used more than once. + writeBack = n; + } + } + if (InvokerBytecodeGenerator.isStaticallyNameable(cls)) { + checkcast(cls); + } else { + ldc(cls) + .checkcast(Class.class) + .swap() + .invokevirtual(Class.class, "cast", LL_SIG, false); + if (Object[].class.isAssignableFrom(cls)) + checkcast(Object[].class); + else if (PROFILE_LEVEL > 0) + checkcast(Object.class); + } + if (writeBack != null) { + dup().store(TypeTag.A, writeBack.index()); + } + return this; + } + + LambdaFormCodeBuilder invokeStaticName(Name name) { + return invokeStaticName(name.function.member(), name); + } + + /** + * Emit an invoke for the given name, using the MemberName directly. + */ + LambdaFormCodeBuilder invokeStaticName(MemberName member, Name name) { + assert(member.equals(name.function.member())); + Class<?> defc = member.getDeclaringClass(); + String mname = member.getName(); + String mtype; + byte refKind = member.getReferenceKind(); + if (refKind == REF_invokeSpecial) { + // in order to pass the verifier, we need to convert this to invokevirtual in all cases + assert(member.canBeStaticallyBound()) : member; + refKind = REF_invokeVirtual; + } + + assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual)); + + // push arguments + pushArguments(name); + + // invocation + if (member.isMethod()) { + mtype = member.getMethodType().toMethodDescriptorString(); + invoke(invKindFromRefKind(refKind), defc, mname, mtype, + member.getDeclaringClass().isInterface()); + } else { + mtype = MethodType.toFieldDescriptorString(member.getFieldType()); + getfield(fieldKindFromRefKind(refKind), defc, mname, mtype); + } + // Issue a type assertion for the result, so we can avoid casts later. + if (name.type == L_TYPE) { + Class<?> rtype = member.getInvocationType().returnType(); + assert(!rtype.isPrimitive()); + } + return this; + } + + /** + * Emit an invoke for the given name. + */ + LambdaFormCodeBuilder invokeName(Name name) { + //assert(!isLinkerMethodInvoke(name)); // should use the static path for these + if (true) { + // push receiver + MethodHandle target = name.function.resolvedHandle(); + assert(target != null) : name.exprString(); + ldc(target); + coerce(MethodHandle.class, target); + } + + // push arguments + pushArguments(name); + + // invocation + MethodType type = name.function.methodType(); + invokevirtual(MethodHandle.class, "invokeBasic", type.basicType().toMethodDescriptorString(), false); + return this; + } + + private LambdaFormCodeBuilder pushArguments(Name args) { + return pushArguments(args, 0); + } + + private LambdaFormCodeBuilder pushArguments(Name args, int start) { + for (int i = start; i < args.arguments.length; i++) { + pushArgument(args, i); + } + return this; + } + + private LambdaFormCodeBuilder pushArgument(Name name, int paramIndex) { + Object arg = name.arguments[paramIndex]; + Class<?> ptype = name.function.methodType().parameterType(paramIndex); + return pushArgument(ptype, arg); + } + + private LambdaFormCodeBuilder pushArgument(Class<?> ptype, Object arg) { + BasicType bptype = basicType(ptype); + if (arg instanceof Name) { + Name n = (Name) arg; + load(fromBasicType(n.type), n.index()); + coerce(n.type, ptype, n); + } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { + ldc(arg); + } else { + if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { + ldc(arg); + } else { + ldc(arg); + coerce(L_TYPE, ptype, arg); + } + } + return this; + } + + private LambdaFormCodeBuilder invokeLoopHandle(int handles, int clause, Name args, boolean pushLocalState, + MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot, + int firstLoopStateSlot) { + // load handle for clause + load(TypeTag.A, clauseDataSlot) + .ldc(handles - 1) + .aaload() + .ldc(clause) + .aaload(); + + // load loop state (preceding the other arguments) + if (pushLocalState) { + for (int s = 0; s < loopLocalStateTypes.length; ++s) { + load(fromClass(loopLocalStateTypes[s]), firstLoopStateSlot + s); + } + } + // load loop args (skip 0: method handle) + return pushArguments(args, 1) + .invokevirtual(MethodHandle.class, "invokeBasic", type.toMethodDescriptorString(), false); + } + + MethodBuilder<Class<?>, String, byte[]> method() { + return methodBuilder; + } + + String label() { + return "label" + labelCount++; + } + + private int extendLocalsMap(Class<?>[] types) { + int firstSlot = localsMap.length - 1; + localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length); + int index = localsMap[firstSlot - 1] + 1; + int lastSlots = 0; + for (int i = 0; i < types.length; ++i) { + localsMap[firstSlot + i] = index; + lastSlots = BasicType.basicType(types[i]).basicTypeSlots(); + index += lastSlots; + } + localsMap[localsMap.length - 1] = index - lastSlots; + maxLocals = types.length; + return firstSlot; + } + + Class<?> typeOfLocal(int index) { + return typeHelper.symbol(descOfLocal(index)); + } + + TypeTag tagOfLocal(int index) { + return typeHelper.tag(descOfLocal(index)); + } + + String descOfLocal(int index) { + return state.locals.get(localsMap[index]); + } + } + + /*** Utility methods ***/ + + static TypeTag fromBasicType(BasicType type) { + switch (type) { + case I_TYPE: return TypeTag.I; + case J_TYPE: return TypeTag.J; + case F_TYPE: return TypeTag.F; + case D_TYPE: return TypeTag.D; + case L_TYPE: return TypeTag.A; + case V_TYPE: return TypeTag.V; + case Q_TYPE: return TypeTag.Q; + default: + throw new InternalError("unknown type: " + type); + } + } + + static InvocationKind invKindFromRefKind(int refKind) { + switch (refKind) { + case REF_invokeVirtual: return InvocationKind.INVOKEVIRTUAL; + case REF_invokeStatic: return InvocationKind.INVOKESTATIC; + case REF_invokeSpecial: return InvocationKind.INVOKESPECIAL; + case REF_invokeInterface: return InvocationKind.INVOKEINTERFACE; + } + throw new InternalError("refKind="+refKind); + } + + static FieldAccessKind fieldKindFromRefKind(int refKind) { + switch (refKind) { + case REF_getField: + case REF_putField: return FieldAccessKind.INSTANCE; + case REF_getStatic: + case REF_putStatic: return FieldAccessKind.STATIC; + } + throw new InternalError("refKind="+refKind); + } + + static TypeTag fromClass(Class<?> cls) { + return fromBasicType(BasicType.basicType(cls)); + } +}
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Tue Sep 26 16:13:10 2017 +0200 @@ -27,6 +27,7 @@ import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Collections; @@ -384,9 +385,47 @@ return BoundMethodHandle.speciesData(lambdaForm); } private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { - return oldSpeciesData().extendWith(type); + return (type != Q_TYPE) ? + oldSpeciesData().extendWith(type) : + oldSpeciesData().extendWith(L_TYPE); // Carrier_Q(value, unbox MH) } + /** + * Instances of this class are used to carry bound value types instances in BMHs. A bound value-type instance + * is represented as a (boxed value, unbox method handle) pair. + */ + static final class Carrier_Q { + + final Object boxedValue; + final MethodHandle unboxHandle; + + Carrier_Q(Object boxedValue, MethodHandle unboxHandle) { + this.boxedValue = boxedValue; + this.unboxHandle = unboxHandle; + } + + static final NamedFunction BOX_VALUE_GETTER; + static final NamedFunction UNBOX_HANDLE_GETTER; + + static { + try { + Lookup lookup = Lookup.IMPL_LOOKUP; + BOX_VALUE_GETTER = new NamedFunction(lookup.findGetter(Carrier_Q.class, "boxedValue", Object.class)); + UNBOX_HANDLE_GETTER = new NamedFunction(lookup.findGetter(Carrier_Q.class, "unboxHandle", MethodHandle.class)); + } catch (Throwable ex) { + throw new IllegalStateException(ex); + } + } + + } + + BoundMethodHandle bindArgumentQ(BoundMethodHandle mh, int pos, Object value, MethodHandle unbox) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = Q_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendL(type2, form2, new Carrier_Q(value, unbox)); + } BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = L_TYPE; @@ -447,7 +486,8 @@ buf.startEdit(); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); + BasicType ptype = lambdaForm.parameterType(pos); + BoundMethodHandle.SpeciesData newData = newSpeciesData(ptype); Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values Name newBaseAddress; NamedFunction getter = newData.getterFunction(oldData.fieldCount()); @@ -458,7 +498,26 @@ buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); - buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); + if (ptype == Q_TYPE) { + // Q-types are stored in boxed form. Unboxing step is required. + int exprPos = lambdaForm.arity(); + int CARRIER_Q = exprPos++; + int UNBOX_MH = exprPos++; + int BOXED_VALUE = exprPos++; + int UNBOXED_VALUE = exprPos++; + NamedFunction carriedQGetter = newData.getterFunction(oldData.fieldCount()); + buf.insertExpression(CARRIER_Q, new Name(carriedQGetter, newBaseAddress)); + NamedFunction unboxGetter = Carrier_Q.UNBOX_HANDLE_GETTER; + buf.insertExpression(UNBOX_MH, new Name(unboxGetter, buf.name(CARRIER_Q))); + NamedFunction boxedValue = Carrier_Q.BOX_VALUE_GETTER; + buf.insertExpression(BOXED_VALUE, new Name(boxedValue, buf.name(CARRIER_Q))); + MethodType unboxInvokerType = MethodType.methodType(Q_TYPE.basicTypeClass(), L_TYPE.basicTypeClass()); + Name unbox = new Name(unboxInvokerType, buf.name(UNBOX_MH), buf.name(BOXED_VALUE)); + buf.insertExpression(UNBOXED_VALUE, unbox); + buf.replaceParameterByCopy(pos, UNBOXED_VALUE); + } else { + buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); + } } else { // cannot bind the MH arg itself, unless oldData is empty assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Sep 26 16:13:10 2017 +0200 @@ -38,6 +38,8 @@ import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; +import valhalla.shady.MinimalValueTypes_1_0; +import valhalla.shady.ValueTypeHolder; import java.lang.reflect.Array; import java.util.Arrays; @@ -432,6 +434,8 @@ assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)); // caller responsibility if (dst == void.class) return dst; + ValueTypeHolder<?> srcVT = MinimalValueTypes_1_0.findValueType(src); + ValueTypeHolder<?> dstVT = MinimalValueTypes_1_0.findValueType(dst); MethodHandle fn; if (src.isPrimitive()) { if (src == void.class) { @@ -454,6 +458,13 @@ fn = MethodHandleImpl.makePairwiseConvert(fn, mt, /*strict=*/ false); } } + } else if (srcVT != null) { + if (srcVT.boxClass().equals(dst) || dst.equals(Object.class)) { + //box + fn = srcVT.box(); + } else { + throw new IllegalArgumentException("Cannot box value " + src + " to class " + dst); + } } else if (dst.isPrimitive()) { Wrapper wdst = Wrapper.forPrimitiveType(dst); if (monobox || src == wdst.wrapperType()) { @@ -467,6 +478,13 @@ ? ValueConversions.unboxWiden(wdst) : ValueConversions.unboxCast(wdst)); } + } else if (dstVT != null) { + if (dstVT.boxClass().equals(src) || src.equals(Object.class)) { + //unbox + fn = dstVT.unbox(); + } else { + throw new IllegalArgumentException("Cannot unbox class " + src + " to value " + dst); + } } else { // Simple reference conversion. // Note: Do not check for a class hierarchy relation @@ -1789,6 +1807,8 @@ Wrapper w = Wrapper.forPrimitiveType(returnType); return ValueConversions.unboxExact(w); } + } else if (MinimalValueTypes_1_0.isValueType(returnType)) { + return MinimalValueTypes_1_0.findValueType(returnType).unbox(); } else { return MethodHandles.identity(Object.class); }
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Tue Sep 26 16:13:10 2017 +0200 @@ -26,9 +26,12 @@ package java.lang.invoke; import jdk.internal.misc.Unsafe; +import sun.security.action.GetBooleanAction; import sun.security.action.GetPropertyAction; +import java.security.AccessController; import java.util.Properties; +import java.util.PropertyPermission; /** * This class consists exclusively of static names internal to the @@ -56,6 +59,11 @@ static final boolean VAR_HANDLE_GUARDS; static final int MAX_ARITY; + //Valhalla flags + + /** should we specialize value lambda forms? **/ + static final boolean VALHALLA_ENABLE_VALUE_LFORMS; + static { Properties props = GetPropertyAction.privilegedGetProperties(); DEBUG_METHOD_HANDLE_NAMES = Boolean.parseBoolean( @@ -90,6 +98,9 @@ if (CUSTOMIZE_THRESHOLD < -1 || CUSTOMIZE_THRESHOLD > 127) { throw newInternalError("CUSTOMIZE_THRESHOLD should be in [-1...127] range"); } + + //Valhalla flags + VALHALLA_ENABLE_VALUE_LFORMS = Boolean.parseBoolean(props.getProperty("valhalla.enableValueLambdaForms", "true")); } /** Tell if any of the debugging switches are turned on.
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Tue Sep 26 16:13:10 2017 +0200 @@ -36,6 +36,8 @@ import sun.invoke.util.Wrapper; import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; +import valhalla.shady.MinimalValueTypes_1_0; +import valhalla.shady.ValueTypeHolder; import java.lang.invoke.LambdaForm.BasicType; import java.lang.reflect.Constructor; @@ -1263,6 +1265,12 @@ throw new NoSuchMethodException("no constructor for array class: " + refc.getName()); } String name = "<init>"; + if (MinimalValueTypes_1_0.isValueType(refc)) { + //shady: The findConstructor method of Lookup will expose all accessible constructors of the original + //value-capable class, for both the Q-type and the legacy L-type. The return type of a method handle produced + //by findConstructor will be identical with the lookup class, even if it is a Q-type. + refc = MinimalValueTypes_1_0.getValueCapableClass(refc); + } MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type); return getDirectConstructor(refc, ctor); } @@ -1417,8 +1425,12 @@ * @see #findVarHandle(Class, String, Class) */ public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { - MemberName field = resolveOrFail(REF_getField, refc, name, type); - return getDirectField(REF_getField, refc, field); + if (MinimalValueTypes_1_0.isValueType(refc)) { + return MinimalValueTypes_1_0.findValueType(refc).findGetter(this, name, type); + } else { + MemberName field = resolveOrFail(REF_getField, refc, name, type); + return getDirectField(REF_getField, refc, field); + } } /** @@ -2526,12 +2538,14 @@ */ public static MethodHandle arrayConstructor(Class<?> arrayClass) throws IllegalArgumentException { - if (!arrayClass.isArray()) { - throw newIllegalArgumentException("not an array class: " + arrayClass.getName()); + ValueTypeHolder<?> compValue = valueComponent(arrayClass); + if (compValue != null) { + return compValue.newArray(); + } else { + MethodHandle ani = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_Array_newInstance). + bindTo(arrayClass.getComponentType()); + return ani.asType(ani.type().changeReturnType(arrayClass)); } - MethodHandle ani = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_Array_newInstance). - bindTo(arrayClass.getComponentType()); - return ani.asType(ani.type().changeReturnType(arrayClass)); } /** @@ -2546,7 +2560,10 @@ */ public static MethodHandle arrayLength(Class<?> arrayClass) throws IllegalArgumentException { - return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.LENGTH); + ValueTypeHolder<?> compValue = valueComponent(arrayClass); + return (compValue != null) ? + compValue.arrayLength() : + MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.LENGTH); } /** @@ -2561,7 +2578,10 @@ */ public static MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException { - return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.GET); + ValueTypeHolder<?> compValue = valueComponent(arrayClass); + return (compValue != null) ? + compValue.arrayGetter() : + MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.GET); } /** @@ -2576,7 +2596,24 @@ */ public static MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException { - return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.SET); + ValueTypeHolder<?> compValue = valueComponent(arrayClass); + return (compValue != null) ? + compValue.arraySetter() : + MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.SET); + } + + @SuppressWarnings("unchecked") + private static <Z> ValueTypeHolder<Z> valueComponent(Class<Z> arrayClass) { + if (!arrayClass.isArray()) { + throw newIllegalArgumentException("not an array class: " + arrayClass.getName()); + } + + Class<?> comp = arrayClass.getComponentType(); + if (MinimalValueTypes_1_0.isValueType(comp)) { + return (ValueTypeHolder<Z>) MinimalValueTypes_1_0.findValueType(comp); + } else { + return null; + } } /** @@ -3293,6 +3330,8 @@ if (w.zero().equals(value)) return zero(w, type); return insertArguments(identity(type), 0, value); + } else if (MinimalValueTypes_1_0.isValueType(type)) { + return insertArguments(identity(type), 0, value); } else { if (value == null) return zero(Wrapper.OBJECT, type); @@ -3340,7 +3379,13 @@ */ public static MethodHandle zero(Class<?> type) { Objects.requireNonNull(type); - return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); + if (type.isPrimitive()) { + return zero(Wrapper.forPrimitiveType(type), type); + } else if (MinimalValueTypes_1_0.isValueType(type)) { + return MinimalValueTypes_1_0.findValueType(type).defaultValueConstant(); + } else { + return zero(Wrapper.OBJECT, type); + } } private static MethodHandle identityOrVoid(Class<?> type) { @@ -3370,9 +3415,13 @@ private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.COUNT]; private static MethodHandle makeIdentity(Class<?> ptype) { - MethodType mtype = methodType(ptype, ptype); - LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype)); - return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY); + if (!MinimalValueTypes_1_0.isValueType(ptype)) { + MethodType mtype = MethodType.methodType(ptype, ptype); + LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype)); + return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY); + } else { + return MinimalValueTypes_1_0.findValueType(ptype).identity(); + } } private static MethodHandle zero(Wrapper btw, Class<?> rtype) { @@ -3444,6 +3493,12 @@ Class<?> ptype = ptypes[pos+i]; if (ptype.isPrimitive()) { result = insertArgumentPrimitive(result, pos, ptype, value); + } else if (MinimalValueTypes_1_0.isValueType(ptype)) { + Class<?> vcc = MinimalValueTypes_1_0.getValueCapableClass(ptype); + Objects.requireNonNull(value); // throw NPE if needed + value = vcc.cast(value); // throw CCE if needed + MethodHandle unbox = MinimalValueTypes_1_0.findValueType(ptype).unbox(); + result = result.bindArgumentQ(pos, value, unbox); } else { value = ptype.cast(value); // throw CCE if needed result = result.bindArgumentL(pos, value);
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java Tue Sep 26 16:13:10 2017 +0200 @@ -40,6 +40,7 @@ import sun.invoke.util.BytecodeDescriptor; import static java.lang.invoke.MethodHandleStatics.*; import sun.invoke.util.VerifyType; +import valhalla.shady.MinimalValueTypes_1_0; /** * A method type represents the arguments and return type accepted and @@ -525,8 +526,10 @@ /*non-public*/ Class<?> leadingReferenceParameter() { Class<?> ptype; if (ptypes.length == 0 || - (ptype = ptypes[0]).isPrimitive()) + (ptype = ptypes[0]).isPrimitive() || + MinimalValueTypes_1_0.isValueType(ptype)) { throw newIllegalArgumentException("no leading reference parameter"); + } return ptype; }
--- a/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Tue Sep 26 16:13:10 2017 +0200 @@ -27,8 +27,12 @@ import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; +import sun.security.action.GetBooleanAction; +import valhalla.shady.MinimalValueTypes_1_0; import java.lang.ref.SoftReference; +import java.security.AccessController; +import java.util.PropertyPermission; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; @@ -171,7 +175,7 @@ Class<?>[] bpts = epts; for (int i = 0; i < epts.length; i++) { Class<?> pt = epts[i]; - if (pt != Object.class) { + if (pt != Object.class && !MinimalValueTypes_1_0.isValueType(pt)) { ++pac; Wrapper w = Wrapper.forPrimitiveType(pt); if (w.isDoubleWord()) ++lac; @@ -185,7 +189,7 @@ pslotCount += lac; // #slots = #args + #longs Class<?> rt = erasedType.returnType(); Class<?> bt = rt; - if (rt != Object.class) { + if (rt != Object.class && !MinimalValueTypes_1_0.isValueType(rt)) { ++prc; // even void.class counts as a prim here Wrapper w = Wrapper.forPrimitiveType(rt); if (w.isDoubleWord()) ++lrc; @@ -359,7 +363,9 @@ */ static Class<?> canonicalize(Class<?> t, int how) { Class<?> ct; - if (t == Object.class) { + if (t == Object.class || + (MethodHandleStatics.VALHALLA_ENABLE_VALUE_LFORMS && + t == MinimalValueTypes_1_0.getValueClass())) { // no change, ever } else if (!t.isPrimitive()) { switch (how) { @@ -369,7 +375,10 @@ break; case RAW_RETURN: case ERASE: - return Object.class; + return (MethodHandleStatics.VALHALLA_ENABLE_VALUE_LFORMS && + MinimalValueTypes_1_0.isValueType(t)) ? + MinimalValueTypes_1_0.getValueClass() : + Object.class; } } else if (t == void.class) { // no change, usually
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/AbstractBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public class AbstractBuilder<S, T, E, D extends AbstractBuilder<S, T, E, D>> { + protected final PoolHelper<S, T, E> poolHelper; + protected final TypeHelper<S, T> typeHelper; + + AbstractBuilder(PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) { + this.poolHelper = pool; + this.typeHelper = typeHelper; + } + + @SuppressWarnings("unchecked") + D thisBuilder() { + return (D) this; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/AnnotationsBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.function.Consumer; + +public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> { + + GrowableByteBuffer annoAttribute; + int nannos; + + AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) { + super(poolHelper, typeHelper); + this.annoAttribute = new GrowableByteBuffer(); + annoAttribute.writeChar(0); + } + + public enum Kind { + RUNTIME_VISIBLE, + RUNTIME_INVISIBLE; + } + + enum Tag { + B('B'), + C('C'), + D('D'), + F('F'), + I('I'), + J('J'), + S('S'), + Z('Z'), + STRING('s'), + ENUM('e'), + CLASS('c'), + ANNO('@'), + ARRAY('['); + + char tagChar; + + Tag(char tagChar) { + this.tagChar = tagChar; + } + } + + AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) { + annoAttribute.writeChar(poolHelper.putType(annoType)); + int offset = annoAttribute.offset; + annoAttribute.writeChar(0); + if (annotationBuilder != null) { + AnnotationElementBuilder _builder = new AnnotationElementBuilder(); + int nelems = _builder.withElements(annotationBuilder); + patchCharAt(offset, nelems); + } + nannos++; + return this; + } + + byte[] build() { + patchCharAt(0, nannos); + return annoAttribute.bytes(); + } + + private void patchCharAt(int offset, int newChar) { + int prevOffset = annoAttribute.offset; + try { + annoAttribute.offset = offset; + annoAttribute.writeChar(newChar); + } finally { + annoAttribute.offset = prevOffset; + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + static Consumer NO_BUILDER = + new Consumer() { + @Override + public void accept(Object o) { + //do nothing + } + }; + + class AnnotationElementBuilder { + + int nelems; + + public AnnotationElementBuilder withString(String name, String s) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writeStringValue(s); + return this; + } + + private void writeStringValue(String s) { + annoAttribute.writeByte(Tag.STRING.tagChar); + annoAttribute.writeChar(poolHelper.putUtf8(s)); + nelems++; + } + + public AnnotationElementBuilder withClass(String name, T s) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writeClassValue(s); + return this; + } + + private void writeClassValue(T s) { + annoAttribute.writeByte(Tag.CLASS.tagChar); + annoAttribute.writeChar(poolHelper.putType(s)); + nelems++; + } + + public AnnotationElementBuilder withEnum(String name, T enumType, int constant) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writeEnumValue(enumType, constant); + return this; + } + + private void writeEnumValue(T enumType, int constant) { + annoAttribute.writeByte(Tag.ENUM.tagChar); + annoAttribute.writeChar(poolHelper.putType(enumType)); + annoAttribute.writeChar(constant); + nelems++; + } + + public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writeAnnotationValue(annoType, annotationBuilder); + return this; + } + + private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) { + annoAttribute.writeByte(Tag.ANNO.tagChar); + annoAttribute.writeChar(poolHelper.putType(annoType)); + int offset = annoAttribute.offset; + annoAttribute.writeChar(0); + int nelems = withNestedElements(annotationBuilder); + patchCharAt(offset, nelems); + this.nelems++; + } + + public AnnotationElementBuilder withPrimitive(String name, char c) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.C, c); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, short s) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.S, s); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, byte b) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.B, b); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, int i) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.I, i); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, float f) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.F, f); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, long l) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.J, l); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, double d) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.D, d); + return this; + } + + public AnnotationElementBuilder withPrimitive(String name, boolean b) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writePrimitiveValue(Tag.Z, b); + return this; + } + + void writePrimitiveValue(Tag tag, Object v) { + annoAttribute.writeByte(tag.tagChar); + annoAttribute.writeChar(poolHelper.putValue(v)); + nelems++; + } + + AnnotationElementBuilder withStrings(String name, String... ss) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(ss.length); + for (String s : ss) { + writeStringValue(s); + } + return this; + } + + @SuppressWarnings("unchecked") + AnnotationElementBuilder withClasses(String name, T... cc) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(cc.length); + for (T c : cc) { + writeClassValue(c); + } + return this; + } + + AnnotationElementBuilder withEnums(String name, T enumType, int... constants) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(constants.length); + for (int c : constants) { + writeEnumValue(enumType, c); + } + return this; + } + + @SuppressWarnings("unchecked") + public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(annotationBuilders.length); + for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) { + writeAnnotationValue(annoType, annotationBuilder); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, char... cc) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(cc.length); + for (char c : cc) { + writePrimitiveValue(Tag.C, c); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, short... ss) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(ss.length); + for (short s : ss) { + writePrimitiveValue(Tag.S, s); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, byte... bb) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(bb.length); + for (byte b : bb) { + writePrimitiveValue(Tag.B, b); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, int... ii) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(ii.length); + for (int i : ii) { + writePrimitiveValue(Tag.I, i); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, float... ff) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(ff.length); + for (float f : ff) { + writePrimitiveValue(Tag.F, f); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, long... ll) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(ll.length); + for (long l : ll) { + writePrimitiveValue(Tag.J, l); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, double... dd) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(dd.length); + for (double d : dd) { + writePrimitiveValue(Tag.D, d); + } + return this; + } + + public AnnotationElementBuilder withPrimitives(String name, boolean... bb) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(bb.length); + for (boolean b : bb) { + writePrimitiveValue(Tag.Z, b); + } + return this; + } + + int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) { + return withElements(new AnnotationElementBuilder(), annotationBuilder); + } + + int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) { + return withElements(this, annotationBuilder); + } + + private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) { + annotationBuilder.accept(builder); + return builder.nelems; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/AttributeBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public class AttributeBuilder<S, T, E, D extends AttributeBuilder<S, T, E, D>> + extends AbstractBuilder<S, T, E, D> { + + protected int nattrs; + protected GrowableByteBuffer attributes = new GrowableByteBuffer(); + + public AttributeBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) { + super(poolHelper, typeHelper); + } + + public D withAttribute(CharSequence name, byte[] bytes) { + attributes.writeChar(poolHelper.putUtf8(name)); + attributes.writeInt(bytes.length); + attributes.writeBytes(bytes); + nattrs++; + return thisBuilder(); + } + + public <Z> D withAttribute(CharSequence name, Z attr, AttributeWriter<S, T, E, Z> attrWriter) { + attributes.writeChar(poolHelper.putUtf8(name)); + int offset = attributes.offset; + attributes.writeInt(0); + attrWriter.write(attr, poolHelper, attributes); + int len = attributes.offset - offset - 4; + attributes.withOffset(offset, buf -> buf.writeInt(len)); + nattrs++; + return thisBuilder(); + } + + public interface AttributeWriter<S, T, E, A> { + void write(A attr, PoolHelper<S, T, E> poolHelper, GrowableByteBuffer buf); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/BasicClassBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,815 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import jdk.experimental.bytecode.AnnotationsBuilder.Kind; +import jdk.experimental.bytecode.MacroCodeBuilder.CondKind; +import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind; +import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Iterator; + +public class BasicClassBuilder extends ClassBuilder<String, String, BasicClassBuilder> { + + public BasicClassBuilder(String thisClass, int majorVersion, int minorVersion) { + this(); + withMinorVersion(minorVersion); + withMajorVersion(majorVersion); + withThisClass(thisClass); + } + + public BasicClassBuilder() { + super(new BasicPoolHelper(), new BasicTypeHelper()); + } + + public static class BasicPoolHelper implements PoolHelper<String, String, byte[]> { + GrowableByteBuffer pool = new GrowableByteBuffer(); + //Map<PoolKey, PoolKey> indicesMap = new HashMap<>(); + int currentIndex = 1; + + PoolKey[] table = new PoolKey[0x10]; + int nelems; + + private void dble() { + PoolKey[] oldtable = table; + table = new PoolKey[oldtable.length * 2]; + int n = 0; + for (int i = oldtable.length; --i >= 0; ) { + PoolKey e = oldtable[i]; + if (e != null) { + table[getIndex(e)] = e; + n++; + } + } + // We don't need to update nelems for shared inherited scopes, + // since that gets handled by leave(). + nelems = n; + } + + /** + * Look for slot in the table. + * We use open addressing with double hashing. + */ + int getIndex(PoolKey e) { + int hashMask = table.length - 1; + int h = e.hashCode(); + int i = h & hashMask; + // The expression below is always odd, so it is guaranteed + // to be mutually prime with table.length, a power of 2. + int x = hashMask - ((h + (h >> 16)) << 1); + for (; ; ) { + PoolKey e2 = table[i]; + if (e2 == null) + return i; + else if (e.hash == e2.hash) + return i; + i = (i + x) & hashMask; + } + } + + public void enter(PoolKey e) { + if (nelems * 3 >= (table.length - 1) * 2) + dble(); + int hash = getIndex(e); + PoolKey old = table[hash]; + if (old == null) { + nelems++; + } + e.next = old; + table[hash] = e; + } + + protected PoolKey lookup(PoolKey other) { + PoolKey e = table[getIndex(other)]; + while (e != null && !e.equals(other)) + e = e.next; + return e; + } + + + PoolKey key = new PoolKey(); + + protected static class PoolKey { + PoolTag tag; + Object o1; + Object o2; + Object o3; + int size = -1; + int hash; + int index = -1; + PoolKey next; + + void setUtf8(CharSequence s) { + tag = PoolTag.CONSTANT_UTF8; + o1 = s; + size = 1; + hash = tag.tag | (s.hashCode() << 1); + } + + void setClass(String clazz) { + tag = PoolTag.CONSTANT_CLASS; + o1 = clazz; + size = 1; + hash = tag.tag | (clazz.hashCode() << 1); + } + + void setNameAndType(CharSequence name, String type) { + tag = PoolTag.CONSTANT_NAMEANDTYPE; + o1 = name; + o2 = type; + size = 2; + hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1); + } + + void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) { + tag = poolTag; + o1 = owner; + o2 = name; + o3 = type; + size = 3; + hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1); + } + + void setString(String s) { + tag = PoolTag.CONSTANT_STRING; + o1 = s; + size = 1; + hash = tag.tag | (s.hashCode() << 1); + } + + void setInteger(Integer i) { + tag = PoolTag.CONSTANT_INTEGER; + o1 = i; + size = 1; + hash = tag.tag | (i.hashCode() << 1); + } + + void setFloat(Float f) { + tag = PoolTag.CONSTANT_FLOAT; + o1 = f; + size = 1; + hash = tag.tag | (f.hashCode() << 1); + } + + void setLong(Long l) { + tag = PoolTag.CONSTANT_LONG; + o1 = l; + size = 1; + hash = tag.tag | (l.hashCode() << 1); + } + + void setDouble(Double d) { + tag = PoolTag.CONSTANT_DOUBLE; + o1 = d; + size = 1; + hash = tag.tag | (d.hashCode() << 1); + } + + void setMethodHandle(MethodHandle mh) { + tag = PoolTag.CONSTANT_METHODHANDLE; + o1 = mh; + size = 1; + hash = tag.tag | (mh.hashCode() << 1); + } + + void setMethodType(MethodType mt) { + tag = PoolTag.CONSTANT_METHODTYPE; + o1 = mt; + size = 1; + hash = tag.tag | (mt.hashCode() << 1); + } + + @Override + public boolean equals(Object obj) { + PoolKey that = (PoolKey) obj; + if (tag != that.tag) return false; + switch (size) { + case 1: + if (!o1.equals(that.o1)) { + return false; + } + break; + case 2: + if (!o2.equals(that.o2) || !o1.equals(that.o1)) { + return false; + } + break; + case 3: + if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) { + return false; + } + break; + } + return true; + } + + @Override + public int hashCode() { + return hash; + } + + PoolKey dup() { + PoolKey poolKey = new PoolKey(); + poolKey.tag = tag; + poolKey.size = size; + poolKey.hash = hash; + poolKey.o1 = o1; + poolKey.o2 = o2; + poolKey.o3 = o3; + return poolKey; + } + + void at(int index) { + this.index = index; + } + } + + @Override + public int putClass(String symbol) { + key.setClass(symbol); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + int utf8_idx = putUtf8(symbol); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_CLASS.tag); + pool.writeChar(utf8_idx); + } + return poolKey.index; + } + + @Override + public int putFieldRef(String owner, CharSequence name, String type) { + return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type); + } + + @Override + public int putMethodRef(String owner, CharSequence name, String type, boolean isInterface) { + return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF, + owner, name, type); + } + + int putMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) { + key.setMemberRef(poolTag, owner, name, type); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + int owner_idx = putClass(owner); + int nameAndType_idx = putNameAndType(name, type); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(poolTag.tag); + pool.writeChar(owner_idx); + pool.writeChar(nameAndType_idx); + } + return poolKey.index; + } + + @Override + public int putValue(Object v) { + if (v instanceof Class<?>) { + return putClass(((Class<?>) v).getName().replaceAll("\\.", "/")); + } else if (v instanceof String) { + key.setString((String) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + int utf8_index = putUtf8((String) v); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_STRING.tag); + pool.writeChar(utf8_index); + } + return poolKey.index; + } else if (v instanceof Integer) { + key.setInteger((Integer) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_INTEGER.tag); + pool.writeInt((Integer) v); + } + return poolKey.index; + } else if (v instanceof Float) { + key.setFloat((Float) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_FLOAT.tag); + pool.writeFloat((Float) v); + } + return poolKey.index; + } else if (v instanceof Double) { + key.setDouble((Double) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag); + pool.writeDouble((Double) v); + currentIndex++; + } + return poolKey.index; + } else if (v instanceof Long) { + key.setLong((Long) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_LONG.tag); + pool.writeLong((Long) v); + currentIndex++; + } + return poolKey.index; + } else if (v instanceof MethodHandle) { + key.setMethodHandle((MethodHandle) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + MethodHandle mh = (MethodHandle) v; + Member member = memberFromHandle(mh); + int member_ref_idx = putMemberRef(tagForMember(member), + member.getDeclaringClass().getSimpleName(), + member.getName(), + typeFromHandle(mh)); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag); + pool.writeByte(kindForMember(member)); + pool.writeChar(member_ref_idx); + } + return poolKey.index; + } else if (v instanceof MethodType) { + key.setMethodType((MethodType) v); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + MethodType mt = (MethodType) v; + int methdodType_idx = putUtf8(mt.toMethodDescriptorString()); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag); + pool.writeChar(methdodType_idx); + } + return poolKey.index; + } else { + throw new UnsupportedOperationException("Unsupported object class: " + v.getClass().getName()); + } + } + + private String typeFromHandle(MethodHandle mh) { + return null; + } //TODO + + private int kindForMember(Member member) { + return 0; + } //TODO + + private PoolTag tagForMember(Member member) { + return null; //TODO + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private Member memberFromHandle(MethodHandle mh) { + Class<Member>[] targets = new Class[]{Method.class, Field.class, Constructor.class}; + for (Class<Member> target : targets) { + try { + return MethodHandles.reflectAs(target, mh); + } catch (ClassCastException ex) { + //swallow + } + } + throw new UnsupportedOperationException("Cannot crack method handle!"); + } + + @Override + public int putInvokeDynamic(CharSequence invokedName, String invokedType, int bsmKind, String bsmClass, CharSequence bsmName, String bsmType, Object... staticArgs) { + return 0; //TODO + } + + @Override + public int putMethodType(String s) { + return 0; //TODO + } + + @Override + public int putHandle(int refKind, String owner, CharSequence name, String type) { + return 0; //TODO + } + + @Override + public int putType(String s) { + return putUtf8(s); + } + + public int putUtf8(CharSequence s) { + key.setUtf8(s); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_UTF8.tag); + putUTF8Internal(s); + } + return poolKey.index; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param s a String whose UTF8 encoded length must be less than 65536. + * @return this byte vector. + */ + void putUTF8Internal(final CharSequence s) { + int charLength = s.length(); + if (charLength > 65535) { + throw new IllegalArgumentException(); + } + // optimistic algorithm: instead of computing the byte length and then + // serializing the string (which requires two loops), we assume the byte + // length is equal to char length (which is the most frequent case), and + // we start serializing the string right away. During the serialization, + // if we find that this assumption is wrong, we continue with the + // general method. + pool.writeChar(charLength); + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + pool.writeByte((byte) c); + } else { + encodeUTF8(s, i, 65535); + break; + } + } + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. The string length is encoded in two + * bytes before the encoded characters, if there is space for that (i.e. if + * this.length - i - 2 >= 0). + * + * @param s the String to encode. + * @param i the index of the first character to encode. The previous + * characters are supposed to have already been encoded, using + * only one byte per character. + * @param maxByteLength the maximum byte length of the encoded string, including the + * already encoded characters. + * @return this byte vector. + */ + void encodeUTF8(final CharSequence s, int i, int maxByteLength) { + int charLength = s.length(); + int byteLength = i; + char c; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > maxByteLength) { + throw new IllegalArgumentException(); + } + int byteLengthFinal = byteLength; + pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal)); + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + pool.writeChar((byte) c); + } else if (c > '\u07FF') { + pool.writeChar((byte) (0xE0 | c >> 12 & 0xF)); + pool.writeChar((byte) (0x80 | c >> 6 & 0x3F)); + pool.writeChar((byte) (0x80 | c & 0x3F)); + } else { + pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F)); + pool.writeChar((byte) (0x80 | c & 0x3F)); + } + } + } + + int putNameAndType(CharSequence name, String type) { + key.setNameAndType(name, type); + PoolKey poolKey = lookup(key); + if (poolKey == null) { + poolKey = key.dup(); + int name_idx = putUtf8(name); + int type_idx = putUtf8(type); + poolKey.at(currentIndex++); + enter(poolKey); + pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag); + pool.writeChar(name_idx); + pool.writeChar(type_idx); + } + return poolKey.index; + } + + @Override + public int size() { + return currentIndex - 1; + } + + @Override + public byte[] entries() { + return pool.bytes(); + } + } + + public static class BasicTypeHelper implements TypeHelper<String, String> { + + @Override + public String elemtype(String s) { + if (!s.startsWith("[")) throw new IllegalStateException("elemtype found: " + s); + return s.substring(1); + } + + @Override + public String arrayOf(String s) { + return "[" + s; + } + + @Override + public String type(String s) { + return "L" + s + ";"; + } + + @Override + public TypeTag tag(String s) { + switch (s.charAt(0)) { + case '[': + case 'L': + return TypeTag.A; + case 'B': + case 'C': + case 'Z': + case 'S': + case 'I': + return TypeTag.I; + case 'F': + return TypeTag.F; + case 'J': + return TypeTag.J; + case 'D': + return TypeTag.D; + case 'V': + return TypeTag.V; + case 'Q': + return TypeTag.Q; + default: + throw new IllegalStateException("Bad type: " + s); + } + } + + @Override + public String nullType() { + return "<null>"; + } + + @Override + public String commonSupertype(String t1, String t2) { + if (t1.equals(t2)) { + return t1; + } else { + try { + Class<?> c1 = from(t1); + Class<?> c2 = from(t2); + if (c1.isAssignableFrom(c2)) { + return t1; + } else if (c2.isAssignableFrom(c1)) { + return t2; + } else { + return "Ljava/lang/Object;"; + } + } catch (Exception e) { + return null; + } + } + } + + public Class<?> from(String desc) throws ReflectiveOperationException { + if (desc.startsWith("[")) { + return Class.forName(desc.replaceAll("/", ".")); + } else { + return Class.forName(symbol(desc).replaceAll("/", ".")); + } + } + + @Override + public Iterator<String> parameterTypes(String s) { + return new Iterator<String>() { + int ch = 1; + + @Override + public boolean hasNext() { + return s.charAt(ch) != ')'; + } + + @Override + public String next() { + char curr = s.charAt(ch); + switch (curr) { + case 'C': + case 'B': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + case 'Z': + ch++; + return String.valueOf(curr); + case '[': + ch++; + return "[" + next(); + case 'L': + case 'Q': + StringBuilder builder = new StringBuilder(); + while (curr != ';') { + builder.append(curr); + curr = s.charAt(++ch); + } + builder.append(';'); + ch++; + return builder.toString(); + default: + throw new AssertionError("cannot parse string: " + s); + } + } + }; + } + + @Override + public String symbolFrom(String s) { + return s; + } + + @Override + public String fromTag(TypeTag tag) { + return tag.name(); + } + + @Override + public String symbol(String type) { + return (type.startsWith("L") || type.startsWith("Q")) ? + type.substring(1, type.length() - 1) : type; + } + + @Override + public String returnType(String s) { + return s.substring(s.indexOf(')') + 1, s.length()); + } + } + + static class MyAttribute { + + int n; + + public MyAttribute(int n) { + this.n = n; + } + + static final String name = "MyAttr"; + + void write(PoolHelper<String, String, byte[]> poolHelper, GrowableByteBuffer buf) { + buf.writeInt(n); + } + } + + public static void main(String[] args) { + byte[] byteArray = new BasicClassBuilder("Foo", 51, 0) + .withSuperclass("java/lang/Object") + .withSuperinterface("java/lang/Runnable") + .withAnnotation(Kind.RUNTIME_VISIBLE, "LMyAnno;", EB -> EB.withPrimitive("i", 10).withEnum("en", "LE;", 10)) + .withAttribute(MyAttribute.name, new MyAttribute(42), MyAttribute::write) + .withField("f", "Ljava/lang/String;") + .withMethod("foo", "(IIJ)V", M -> + M.withFlags(Flag.ACC_STATIC) + .withParameterAnnotation(Kind.RUNTIME_VISIBLE, 0, "LMyAnno2;") + .withCode(TypedCodeBuilder::new, C -> + C.load(0).i2l().load(1).i2l().load(2).invokestatic("Foo", "m", "(JJJ)V", false).return_() + )) + .withMethod("<init>", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.load(0).dup() + .invokespecial("java/lang/Object", "<init>", "()V", false) + .ldc("Hello!") + .putfield(FieldAccessKind.INSTANCE, "Foo", "f", "Ljava/lang/String;") + .return_() + )) + .withMethod("run", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withExceptions("java/io/IOException", "java/lang/ReflectiveOperationException") + .withCode(TypedCodeBuilder::new, C -> + C.load(0) + .invoke(InvocationKind.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false) + .pop() + .return_() + )) + .withMethod("main", "([Ljava/lang/String;)V", M -> + M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) + .withCode(TypedCodeBuilder::new, C -> + C.new_("Foo") + .dup() + .invoke(InvocationKind.INVOKESPECIAL, "Foo", "<init>", "()V", false) + .invoke(InvocationKind.INVOKEVIRTUAL, "Foo", "run", "()V", false) + .label("hey") + .iconst_0() + .iconst_0() + .typed(TypeTag.I) + .if_acmpne("hey") + .const_(10) + .typed(TypeTag.I) + .astore_1() + .typed(TypeTag.I) + .aload_1() + .typed(TypeTag.F) + .anewarray("F") + .typed(TypeTag.F) + .aconst_null() + .typed(TypeTag.F) + .aaload() + .pop() + .const_(1) + .pop() + .withLocal("50", "B") + .const_(50) + .store("50") + .withLocal("150", "S") + .const_(150) + .store("150") + .const_(500000000) + .switch_(S -> + S.withCase(1, one -> + one.const_(42) + .withLocal("foo", "I") + .store("foo"), false) + .withCase(3, three -> + three.nop() + .nop(), false) + .withDefault(D -> + D.nop() + .nop() + .nop())) + .label("endSwitch2") + .const_(50f) + .const_(1f) + .ifcmp(TypeTag.F, CondKind.GT, "bar") + .goto_("hey") + .label("bar") + .aload_0() + .invoke(InvocationKind.INVOKEINTERFACE, "Bar", "name", "()J", true) + .return_(TypeTag.J) + )) + .build(); + try (FileOutputStream fos = new FileOutputStream("Foo.class")) { + fos.write(byteArray); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/ClassBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.function.Consumer; + +public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>> + extends DeclBuilder<S, T, byte[], C> { + + protected TypeHelper<S, T> typeHelper; + protected S thisClass; + protected GrowableByteBuffer interfaces = new GrowableByteBuffer(); + protected GrowableByteBuffer fields = new GrowableByteBuffer(); + protected GrowableByteBuffer methods = new GrowableByteBuffer(); + + int majorVersion; + int minorVersion; + int flags; + int superclass; + int nmethods, nfields, ninterfaces; + + public ClassBuilder(PoolHelper<S, T, byte[]> poolHelper, TypeHelper<S, T> typeHelper) { + super(poolHelper, typeHelper); + this.typeHelper = typeHelper; + } + + public C withMinorVersion(int minorVersion) { + this.minorVersion = minorVersion; + return thisBuilder(); + } + + public C withMajorVersion(int majorVersion) { + this.majorVersion = majorVersion; + return thisBuilder(); + } + + public C withThisClass(S thisClass) { + this.thisClass = thisClass; + return thisBuilder(); + } + + public C withFlags(Flag... flags) { + for (Flag f : flags) { + this.flags |= f.flag; + } + return thisBuilder(); + } + + public C withSuperclass(S s) { + this.superclass = poolHelper.putClass(s); + return thisBuilder(); + } + + public C withSuperinterface(S s) { + this.interfaces.writeChar(poolHelper.putClass(s)); + ninterfaces++; + return thisBuilder(); + } + + public final C withField(CharSequence name, T type) { + return withField(name, type, FB -> { + }); + } + + public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldBuilder) { + FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper); + fieldBuilder.accept(F); + F.build(fields); + nfields++; + return thisBuilder(); + } + + public final C withMethod(CharSequence name, T type) { + return withMethod(name, type, MB -> { + }); + } + + public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodBuilder) { + if (name.toString().contains(".")) { + throw new IllegalArgumentException("Illegal method name " + name); + } + MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper); + methodBuilder.accept(M); + M.build(methods); + nmethods++; + return thisBuilder(); + } + + public byte[] build() { + addAnnotations(); + int thisClassIdx = poolHelper.putClass(thisClass); + byte[] poolBytes = poolHelper.entries(); + GrowableByteBuffer buf = new GrowableByteBuffer(); + buf.writeInt(0xCAFEBABE); + buf.writeChar(minorVersion); + buf.writeChar(majorVersion); + buf.writeChar(poolHelper.size() + 1); + buf.writeBytes(poolBytes); + buf.writeChar(flags); + buf.writeChar(thisClassIdx); + buf.writeChar(superclass); + buf.writeChar(ninterfaces); + if (ninterfaces > 0) { + buf.writeBytes(interfaces); + } + buf.writeChar(nfields); + buf.writeBytes(fields); + buf.writeChar(nmethods); + buf.writeBytes(methods); + buf.writeChar(nattrs); + buf.writeBytes(attributes); + return buf.bytes(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/CodeBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,1312 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; + +public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> { + + protected GrowableByteBuffer code = new GrowableByteBuffer(); + GrowableByteBuffer catchers = new GrowableByteBuffer(); + GrowableByteBuffer stackmaps = new GrowableByteBuffer(); + MethodBuilder<S, T, E> methodBuilder; + int ncatchers; + int stacksize = -1; + int localsize = -1; + int nstackmaps = 0; + + protected enum JumpMode { + NARROW, + WIDE; + } + + CodeBuilder(MethodBuilder<S, T, E> methodBuilder) { + super(methodBuilder.poolHelper, methodBuilder.typeHelper); + this.methodBuilder = methodBuilder; + } + + public C getstatic(S owner, CharSequence name, T type) { + emitOp(Opcode.GETSTATIC, type); + code.writeChar(poolHelper.putFieldRef(owner, name, type)); + return thisBuilder(); + } + + public C putstatic(S owner, CharSequence name, T type) { + emitOp(Opcode.PUTSTATIC, type); + code.writeChar(poolHelper.putFieldRef(owner, name, type)); + return thisBuilder(); + } + + public C getfield(S owner, CharSequence name, T type) { + emitOp(Opcode.GETFIELD, type); + code.writeChar(poolHelper.putFieldRef(owner, name, type)); + return thisBuilder(); + } + + public C putfield(S owner, CharSequence name, T type) { + emitOp(Opcode.PUTFIELD, type); + code.writeChar(poolHelper.putFieldRef(owner, name, type)); + return thisBuilder(); + } + + public C vwithfield(S owner, CharSequence name, T type) { + emitOp(Opcode.VWITHFIELD, type); + code.writeChar(poolHelper.putFieldRef(owner, name, type)); + return thisBuilder(); + } + + public C invokevirtual(S owner, CharSequence name, T type, boolean isInterface) { + emitOp(Opcode.INVOKEVIRTUAL, type); + code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface)); + return thisBuilder(); + } + + public C invokespecial(S owner, CharSequence name, T type, boolean isInterface) { + emitOp(Opcode.INVOKESPECIAL, type); + code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface)); + return thisBuilder(); + } + + public C invokestatic(S owner, CharSequence name, T type, boolean isInterface) { + emitOp(Opcode.INVOKESTATIC, type); + code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface)); + return thisBuilder(); + } + + public C invokeinterface(S owner, CharSequence name, T type) { + emitOp(Opcode.INVOKEINTERFACE, type); + code.writeChar(poolHelper.putMethodRef(owner, name, type, true)); + int nargs = 1; + Iterator<T> it = typeHelper.parameterTypes(type); + while (it.hasNext()) { + nargs += typeHelper.tag(it.next()).width; + } + code.writeByte(nargs); + code.writeByte(0); + return thisBuilder(); + } + + public C invokedynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Object... staticArgs) { + throw new UnsupportedOperationException("Not supported yet"); + } + + public C new_(S clazz) { + emitOp(Opcode.NEW, clazz); + code.writeChar(poolHelper.putClass(clazz)); + return thisBuilder(); + } + + public C vdefault(S target) { + emitOp(Opcode.VDEFAULT, target); + code.writeChar(poolHelper.putClass(target)); + return thisBuilder(); + } + + public C newarray(TypeTag tag) { + emitOp(Opcode.NEWARRAY, tag); + int newarraycode = tag.newarraycode; + if (newarraycode == -1) { + throw new IllegalStateException("Bad tag " + tag); + } + code.writeByte(newarraycode); + return thisBuilder(); + } + + public C anewarray(S array) { + emitOp(Opcode.ANEWARRAY, array); + code.writeChar(poolHelper.putClass(array)); + return thisBuilder(); + } + + public C checkcast(S target) { + emitOp(Opcode.CHECKCAST, target); + code.writeChar(poolHelper.putClass(target)); + return thisBuilder(); + } + + public C instanceof_(S target) { + emitOp(Opcode.INSTANCEOF); + code.writeChar(poolHelper.putClass(target)); + return thisBuilder(); + } + + public C multianewarray(S array, byte dims) { + emitOp(Opcode.MULTIANEWARRAY, new Object[]{array, dims}); + code.writeChar(poolHelper.putClass(array)).writeByte(dims); + return thisBuilder(); + } + + public C vbox(S target) { + emitOp(Opcode.VBOX, target); + code.writeChar(poolHelper.putClass(target)); + return thisBuilder(); + } + + public C vunbox(S target) { + emitOp(Opcode.VUNBOX, target); + code.writeChar(poolHelper.putClass(target)); + return thisBuilder(); + } + + public C ldc(Object o) { + emitOp(Opcode.LDC, o); + code.writeByte(poolHelper.putValue(o)); + return thisBuilder(); + } + + public C ldc_w(Object o) { + emitOp(Opcode.LDC_W, o); + code.writeChar(poolHelper.putValue(o)); + return thisBuilder(); + } + + public C ldc2_w(Object o) { + emitOp(Opcode.LDC2_W, o); + code.writeChar(poolHelper.putValue(o)); + return thisBuilder(); + } + + private C ldc(Object o, boolean fat) { + int idx = poolHelper.putValue(o); + if (fat) { + return ldc2_w(o); + } else if (idx > 63) { + return ldc_w(o); + } else { + return ldc(o); + } + } + + public C ldc(String o) { + return ldc(o, false); + } + + public C ldc(Class<?> o) { + return ldc(o, false); + } + + public C ldc(MethodHandle o) { + return ldc(o, false); + } + + public C ldc(MethodType o) { + return ldc(o, false); + } + + public C ldc(int i) { + return ldc(i, false); + } + + public C ldc(float f) { + return ldc(f, false); + } + + public C ldc(long l) { + return ldc(l, true); + } + + public C ldc(double l) { + return ldc(l, true); + } + + //other non-CP dependent opcodes + public C areturn() { + return emitOp(Opcode.ARETURN); + } + + public C ireturn() { + return emitOp(Opcode.IRETURN); + } + + public C freturn() { + return emitOp(Opcode.FRETURN); + } + + public C lreturn() { + return emitOp(Opcode.LRETURN); + } + + public C dreturn() { + return emitOp(Opcode.DRETURN); + } + + public C return_() { + return emitOp(Opcode.RETURN); + } + + public C vreturn() { + return emitOp(Opcode.VRETURN); + } + + protected C emitWideIfNeeded(Opcode opcode, int n) { + boolean wide = n > Byte.MAX_VALUE; + if (wide) { + wide(); + } + emitOp(opcode, n); + if (wide) { + code.writeChar(n); + } else { + code.writeByte(n); + } + return thisBuilder(); + } + + protected C emitWideIfNeeded(Opcode opcode, int n, int v) { + boolean wide = n > Byte.MAX_VALUE || v > Byte.MAX_VALUE; + if (wide) { + wide(); + } + emitOp(opcode, n); + if (wide) { + code.writeChar(n).writeChar(v); + } else { + code.writeByte(n).writeByte(v); + } + return thisBuilder(); + } + + public TypedBuilder typed(TypeTag typeTag) { + return typed(typeTag, _unused -> new TypedBuilder()); + } + + protected <TB extends TypedBuilder> TB typed(TypeTag typeTag, Function<TypeTag, TB> typedBuilderFunc) { + emitOp(Opcode.TYPED); + code.writeChar(poolHelper.putType(typeHelper.fromTag(typeTag))); + return typedBuilderFunc.apply(typeTag); + } + + public class TypedBuilder { + public C aload_0() { + return CodeBuilder.this.aload_0(); + } + + public C aload_1() { + return CodeBuilder.this.aload_1(); + } + + public C aload_2() { + return CodeBuilder.this.aload_2(); + } + + public C aload_3() { + return CodeBuilder.this.aload_3(); + } + + public C aload(int n) { + return CodeBuilder.this.aload(n); + } + + public C astore_0() { + return CodeBuilder.this.astore_0(); + } + + public C astore_1() { + return CodeBuilder.this.astore_1(); + } + + public C astore_2() { + return CodeBuilder.this.astore_2(); + } + + public C astore_3() { + return CodeBuilder.this.astore_3(); + } + + public C astore(int n) { + return CodeBuilder.this.astore(n); + } + + public C aaload() { + return CodeBuilder.this.aaload(); + } + + public C aastore() { + return CodeBuilder.this.aastore(); + } + + public C areturn() { + return CodeBuilder.this.areturn(); + } + + public C anewarray(S s) { + return CodeBuilder.this.anewarray(s); + } + + public C aconst_null() { + return CodeBuilder.this.aconst_null(); + } + + public C if_acmpeq(short target) { + return CodeBuilder.this.if_acmpeq(target); + } + + public C if_acmpne(short target) { + return CodeBuilder.this.if_acmpeq(target); + } + } + + public C vload(int i) { + return emitWideIfNeeded(Opcode.VLOAD, i); + } + + public C aload(int i) { + return emitWideIfNeeded(Opcode.ALOAD, i); + } + + public C iload(int i) { + return emitWideIfNeeded(Opcode.ILOAD, i); + } + + public C fload(int i) { + return emitWideIfNeeded(Opcode.FLOAD, i); + } + + public C lload(int i) { + return emitWideIfNeeded(Opcode.LLOAD, i); + } + + public C dload(int i) { + return emitWideIfNeeded(Opcode.DLOAD, i); + } + + public C aload_0() { + return emitOp(Opcode.ALOAD_0); + } + + public C iload_0() { + return emitOp(Opcode.ILOAD_0); + } + + public C fload_0() { + return emitOp(Opcode.FLOAD_0); + } + + public C lload_0() { + return emitOp(Opcode.LLOAD_0); + } + + public C dload_0() { + return emitOp(Opcode.DLOAD_0); + } + + public C aload_1() { + return emitOp(Opcode.ALOAD_1); + } + + public C iload_1() { + return emitOp(Opcode.ILOAD_1); + } + + public C fload_1() { + return emitOp(Opcode.FLOAD_1); + } + + public C lload_1() { + return emitOp(Opcode.LLOAD_1); + } + + public C dload_1() { + return emitOp(Opcode.DLOAD_1); + } + + public C aload_2() { + return emitOp(Opcode.ALOAD_2); + } + + public C iload_2() { + return emitOp(Opcode.ILOAD_2); + } + + public C fload_2() { + return emitOp(Opcode.FLOAD_2); + } + + public C lload_2() { + return emitOp(Opcode.LLOAD_2); + } + + public C dload_2() { + return emitOp(Opcode.DLOAD_2); + } + + public C aload_3() { + return emitOp(Opcode.ALOAD_3); + } + + public C iload_3() { + return emitOp(Opcode.ILOAD_3); + } + + public C fload_3() { + return emitOp(Opcode.FLOAD_3); + } + + public C lload_3() { + return emitOp(Opcode.LLOAD_3); + } + + public C dload_3() { + return emitOp(Opcode.DLOAD_3); + } + + public C vstore(int i) { + return emitWideIfNeeded(Opcode.VSTORE, i); + } + + public C astore(int i) { + return emitWideIfNeeded(Opcode.ASTORE, i); + } + + public C istore(int i) { + return emitWideIfNeeded(Opcode.ISTORE, i); + } + + public C fstore(int i) { + return emitWideIfNeeded(Opcode.FSTORE, i); + } + + public C lstore(int i) { + return emitWideIfNeeded(Opcode.LSTORE, i); + } + + public C dstore(int i) { + return emitWideIfNeeded(Opcode.DSTORE, i); + } + + public C astore_0() { + return emitOp(Opcode.ASTORE_0); + } + + public C istore_0() { + return emitOp(Opcode.ISTORE_0); + } + + public C fstore_0() { + return emitOp(Opcode.FSTORE_0); + } + + public C lstore_0() { + return emitOp(Opcode.LSTORE_0); + } + + public C dstore_0() { + return emitOp(Opcode.DSTORE_0); + } + + public C astore_1() { + return emitOp(Opcode.ASTORE_1); + } + + public C istore_1() { + return emitOp(Opcode.ISTORE_1); + } + + public C fstore_1() { + return emitOp(Opcode.FSTORE_1); + } + + public C lstore_1() { + return emitOp(Opcode.LSTORE_1); + } + + public C dstore_1() { + return emitOp(Opcode.DSTORE_1); + } + + public C astore_2() { + return emitOp(Opcode.ASTORE_2); + } + + public C istore_2() { + return emitOp(Opcode.ISTORE_2); + } + + public C fstore_2() { + return emitOp(Opcode.FSTORE_2); + } + + public C lstore_2() { + return emitOp(Opcode.LSTORE_2); + } + + public C dstore_2() { + return emitOp(Opcode.DSTORE_2); + } + + public C astore_3() { + return emitOp(Opcode.ASTORE_3); + } + + public C istore_3() { + return emitOp(Opcode.ISTORE_3); + } + + public C fstore_3() { + return emitOp(Opcode.FSTORE_3); + } + + public C lstore_3() { + return emitOp(Opcode.LSTORE_3); + } + + public C dstore_3() { + return emitOp(Opcode.DSTORE_3); + } + + //... + + public C iaload() { + return emitOp(Opcode.IALOAD); + } + + public C laload() { + return emitOp(Opcode.LALOAD); + } + + public C faload() { + return emitOp(Opcode.FALOAD); + } + + public C daload() { + return emitOp(Opcode.DALOAD); + } + + public C vaload() { + return emitOp(Opcode.VALOAD); + } + + public C aaload() { + return emitOp(Opcode.AALOAD); + } + + public C baload() { + return emitOp(Opcode.BALOAD); + } + + public C caload() { + return emitOp(Opcode.CALOAD); + } + + public C saload() { + return emitOp(Opcode.SALOAD); + } + + public C iastore() { + return emitOp(Opcode.IASTORE); + } + + public C lastore() { + return emitOp(Opcode.LASTORE); + } + + public C fastore() { + return emitOp(Opcode.FASTORE); + } + + public C dastore() { + return emitOp(Opcode.DASTORE); + } + + public C vastore() { + return emitOp(Opcode.VASTORE); + } + + public C aastore() { + return emitOp(Opcode.AASTORE); + } + + public C bastore() { + return emitOp(Opcode.BASTORE); + } + + public C castore() { + return emitOp(Opcode.CASTORE); + } + + public C sastore() { + return emitOp(Opcode.SASTORE); + } + + public C nop() { + return emitOp(Opcode.NOP); + } + + public C aconst_null() { + return emitOp(Opcode.ACONST_NULL); + } + + public C iconst_0() { + return emitOp(Opcode.ICONST_0); + } + + public C iconst_1() { + return emitOp(Opcode.ICONST_1); + } + + public C iconst_2() { + return emitOp(Opcode.ICONST_2); + } + + public C iconst_3() { + return emitOp(Opcode.ICONST_3); + } + + public C iconst_4() { + return emitOp(Opcode.ICONST_4); + } + + public C iconst_5() { + return emitOp(Opcode.ICONST_5); + } + + public C iconst_m1() { + return emitOp(Opcode.ICONST_M1); + } + + public C lconst_0() { + return emitOp(Opcode.LCONST_0); + } + + public C lconst_1() { + return emitOp(Opcode.LCONST_1); + } + + public C fconst_0() { + return emitOp(Opcode.FCONST_0); + } + + public C fconst_1() { + return emitOp(Opcode.FCONST_1); + } + + public C fconst_2() { + return emitOp(Opcode.FCONST_2); + } + + public C dconst_0() { + return emitOp(Opcode.DCONST_0); + } + + public C dconst_1() { + return emitOp(Opcode.DCONST_1); + } + + public C sipush(int s) { + emitOp(Opcode.SIPUSH); + code.writeChar(s); + return thisBuilder(); + } + + public C bipush(int b) { + emitOp(Opcode.BIPUSH); + code.writeByte(b); + return thisBuilder(); + } + + public C pop() { + return emitOp(Opcode.POP); + } + + public C pop2() { + return emitOp(Opcode.POP2); + } + + public C dup() { + return emitOp(Opcode.DUP); + } + + public C dup_x1() { + return emitOp(Opcode.DUP_X1); + } + + public C dup_x2() { + return emitOp(Opcode.DUP_X2); + } + + public C dup2() { + return emitOp(Opcode.DUP2); + } + + public C dup2_x1() { + return emitOp(Opcode.DUP2_X1); + } + + public C dup2_x2() { + return emitOp(Opcode.DUP2_X2); + } + + public C swap() { + return emitOp(Opcode.SWAP); + } + + public C iadd() { + return emitOp(Opcode.IADD); + } + + public C ladd() { + return emitOp(Opcode.LADD); + } + + public C fadd() { + return emitOp(Opcode.FADD); + } + + public C dadd() { + return emitOp(Opcode.DADD); + } + + public C isub() { + return emitOp(Opcode.ISUB); + } + + public C lsub() { + return emitOp(Opcode.LSUB); + } + + public C fsub() { + return emitOp(Opcode.FSUB); + } + + public C dsub() { + return emitOp(Opcode.DSUB); + } + + public C imul() { + return emitOp(Opcode.IMUL); + } + + public C lmul() { + return emitOp(Opcode.LMUL); + } + + public C fmul() { + return emitOp(Opcode.FMUL); + } + + public C dmul() { + return emitOp(Opcode.DMUL); + } + + public C idiv() { + return emitOp(Opcode.IDIV); + } + + public C ldiv() { + return emitOp(Opcode.LDIV); + } + + public C fdiv() { + return emitOp(Opcode.FDIV); + } + + public C ddiv() { + return emitOp(Opcode.DDIV); + } + + public C irem() { + return emitOp(Opcode.IREM); + } + + public C lrem() { + return emitOp(Opcode.LREM); + } + + public C frem() { + return emitOp(Opcode.FREM); + } + + public C drem() { + return emitOp(Opcode.DREM); + } + + public C ineg() { + return emitOp(Opcode.INEG); + } + + public C lneg() { + return emitOp(Opcode.LNEG); + } + + public C fneg() { + return emitOp(Opcode.FNEG); + } + + public C dneg() { + return emitOp(Opcode.DNEG); + } + + public C ishl() { + return emitOp(Opcode.ISHL); + } + + public C lshl() { + return emitOp(Opcode.LSHL); + } + + public C ishr() { + return emitOp(Opcode.ISHR); + } + + public C lshr() { + return emitOp(Opcode.LSHR); + } + + public C iushr() { + return emitOp(Opcode.IUSHR); + } + + public C lushr() { + return emitOp(Opcode.LUSHR); + } + + public C iand() { + return emitOp(Opcode.IAND); + } + + public C land() { + return emitOp(Opcode.LAND); + } + + public C ior() { + return emitOp(Opcode.IOR); + } + + public C lor() { + return emitOp(Opcode.LOR); + } + + public C ixor() { + return emitOp(Opcode.IXOR); + } + + public C lxor() { + return emitOp(Opcode.LXOR); + } + + public C iinc(int index, int val) { + return emitWideIfNeeded(Opcode.IINC, index, val); + } + + public C i2l() { + return emitOp(Opcode.I2L); + } + + public C i2f() { + return emitOp(Opcode.I2F); + } + + public C i2d() { + return emitOp(Opcode.I2D); + } + + public C l2i() { + return emitOp(Opcode.L2I); + } + + public C l2f() { + return emitOp(Opcode.L2F); + } + + public C l2d() { + return emitOp(Opcode.L2D); + } + + public C f2i() { + return emitOp(Opcode.F2I); + } + + public C f2l() { + return emitOp(Opcode.F2L); + } + + public C f2d() { + return emitOp(Opcode.F2D); + } + + public C d2i() { + return emitOp(Opcode.D2I); + } + + public C d2l() { + return emitOp(Opcode.D2L); + } + + public C d2f() { + return emitOp(Opcode.D2F); + } + + public C i2b() { + return emitOp(Opcode.I2B); + } + + public C i2c() { + return emitOp(Opcode.I2C); + } + + public C i2s() { + return emitOp(Opcode.I2S); + } + + public C lcmp() { + return emitOp(Opcode.LCMP); + } + + public C fcmpl() { + return emitOp(Opcode.FCMPL); + } + + public C fcmpg() { + return emitOp(Opcode.FCMPG); + } + + public C dcmpl() { + return emitOp(Opcode.DCMPL); + } + + public C dcmpg() { + return emitOp(Opcode.DCMPG); + } + + public C ifeq(short target) { + return emitNarrowJumpOp(Opcode.IFEQ, target); + } + + public C ifne(short target) { + return emitNarrowJumpOp(Opcode.IFNE, target); + } + + public C iflt(short target) { + return emitNarrowJumpOp(Opcode.IFLT, target); + } + + public C ifge(short target) { + return emitNarrowJumpOp(Opcode.IFGE, target); + } + + public C ifgt(short target) { + return emitNarrowJumpOp(Opcode.IFGT, target); + } + + public C ifle(short target) { + return emitNarrowJumpOp(Opcode.IFLE, target); + } + + public C if_icmpeq(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPEQ, target); + } + + public C if_icmpne(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPNE, target); + } + + public C if_icmplt(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPLT, target); + } + + public C if_icmpge(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPGE, target); + } + + public C if_icmpgt(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPGT, target); + } + + public C if_icmple(short target) { + return emitNarrowJumpOp(Opcode.IF_ICMPLE, target); + } + + public C if_acmpeq(short target) { + return emitNarrowJumpOp(Opcode.IF_ACMPEQ, target); + } + + public C if_acmpne(short target) { + return emitNarrowJumpOp(Opcode.IF_ACMPNE, target); + } + + public C goto_(short target) { + return emitNarrowJumpOp(Opcode.GOTO_, target); + } + + public C jsr(short target) { + return emitNarrowJumpOp(Opcode.JSR, target); + } + + public C ret(int index) { + return emitWideIfNeeded(Opcode.RET, index); + } + + public C tableswitch(int low, int high, int defaultTarget, int... targets) { + if (high - low + 1 != targets.length) throw new IllegalStateException("Bad targets length"); + emitOp(Opcode.TABLESWITCH); + //padding + int start = code.offset; + if ((start % 4) != 0) { + //add padding + for (int i = 0; i < 4 - (start % 4); i++) { + code.writeByte(0); + } + } + code.writeInt(defaultTarget) + .writeInt(low) + .writeInt(high); + for (int target : targets) { + code.writeInt(target); + } + return thisBuilder(); + } + + public C lookupswitch(int defaultTarget, int... npairs) { + if (npairs.length % 2 != 0) throw new IllegalStateException("Bad npairs length"); + emitOp(Opcode.LOOKUPSWITCH); + //padding + int start = code.offset; + for (int i = 0; i < (4 - (start % 4)); i++) { + code.writeByte(0); + } + code.writeInt(defaultTarget) + .writeInt(npairs.length / 2); + for (int i = 0; i < npairs.length; i += 2) { + code.writeInt(npairs[i]); + code.writeInt(npairs[i + 1]); + } + return thisBuilder(); + } + + public C invokedynamic() { + throw new UnsupportedOperationException(); + } //TODO: maybe later :-) + + public C arraylength() { + return emitOp(Opcode.ARRAYLENGTH); + } + + public C athrow() { + return emitOp(Opcode.ATHROW); + } + + public C monitorenter() { + return emitOp(Opcode.MONITORENTER); + } + + public C monitorexit() { + return emitOp(Opcode.MONITOREXIT); + } + + public C wide() { + return emitOp(Opcode.WIDE); + } + + public C if_null(short offset) { + return emitNarrowJumpOp(Opcode.IF_NULL, offset); + } + + public C if_nonnull(short offset) { + return emitNarrowJumpOp(Opcode.IF_NONNULL, offset); + } + + public C goto_w(int target) { + return emitWideJumpOp(Opcode.GOTO_W, target); + } + + public C jsr_w(int target) { + return emitWideJumpOp(Opcode.JSR_W, target); + } + + public C withCatch(S type, int start, int end, int offset) { + catchers.writeChar(start); + catchers.writeChar(end); + catchers.writeChar(offset); + catchers.writeChar(type != null ? poolHelper.putClass(type) : 0); + ncatchers++; + return thisBuilder(); + } + + public C withLocalSize(int localsize) { + this.localsize = localsize; + return thisBuilder(); + } + + public C withStackSize(int stacksize) { + this.stacksize = stacksize; + return thisBuilder(); + } + + protected int localsize() { + return localsize; + } + + void build(GrowableByteBuffer buf) { + buf.writeChar(stacksize); //max stack size + buf.writeChar(localsize()); //max locals + buf.writeInt(code.offset); + buf.writeBytes(code); + buf.writeChar(ncatchers); + buf.writeBytes(catchers); + buf.writeChar(nattrs); //attributes + buf.writeBytes(attributes); + } + + byte[] build() { + GrowableByteBuffer buf = new GrowableByteBuffer(); + build(buf); + return buf.bytes(); + } + + protected C emitNarrowJumpOp(Opcode opcode, short target) { + emitOp(opcode); + emitOffset(code, JumpMode.NARROW, target); + return thisBuilder(); + } + + protected C emitWideJumpOp(Opcode opcode, int target) { + emitOp(opcode); + emitOffset(code, JumpMode.WIDE, target); + return thisBuilder(); + } + + protected C emitOp(Opcode opcode) { + return emitOp(opcode, null); + } + + protected C emitOp(Opcode opcode, Object optPoolValue) { + code.writeByte(opcode.code); + return thisBuilder(); + } + + protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) { + if (jumpMode == JumpMode.NARROW) { + buf.writeChar((short) offset); + } else { + buf.writeInt(offset); + } + } + + int offset() { + return code.offset; + } + + /*** stackmap support ***/ + + /** + * The tags and constants used in compressed stackmap. + */ + static final int SAME_FRAME_SIZE = 64; + static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247; + static final int SAME_FRAME_EXTENDED = 251; + static final int FULL_FRAME = 255; + static final int MAX_LOCAL_LENGTH_DIFF = 4; + + @SuppressWarnings("unchecked") + private void writeStackMapType(T t) { + if (t == null) { + stackmaps.writeByte(0); + } else { + switch (typeHelper.tag(t)) { + case B: + case C: + case S: + case I: + case Z: + stackmaps.writeByte(1); + break; + case F: + stackmaps.writeByte(2); + break; + case D: + stackmaps.writeByte(3); + break; + case J: + stackmaps.writeByte(4); + break; + case Q: + case A: + if (t == typeHelper.nullType()) { + stackmaps.writeByte(5); //null + } else { + //TODO: uninit this, top? + stackmaps.writeByte(7); + stackmaps.writeChar(poolHelper.putClass(typeHelper.symbol(t))); + } + break; + default: + throw new IllegalStateException("Bad type"); + } + } + } + + public void sameFrame(int offsetDelta) { + int frameType = (offsetDelta < SAME_FRAME_SIZE) ? + offsetDelta : SAME_FRAME_EXTENDED; + stackmaps.writeByte(frameType); + if (frameType == SAME_FRAME_EXTENDED) { + stackmaps.writeChar(offsetDelta); + } + } + + public void sameLocals1StackItemFrame(int offsetDelta, T stackItem) { + int frameType = (offsetDelta < SAME_FRAME_SIZE) ? + (SAME_FRAME_SIZE + offsetDelta) : SAME_LOCALS_1_STACK_ITEM_EXTENDED; + stackmaps.writeByte(frameType); + if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + stackmaps.writeChar(offsetDelta); + } + writeStackMapType(stackItem); + } + + public void appendFrame(int offsetDelta, int prevLocalsSize, List<T> locals) { + int frameType = SAME_FRAME_EXTENDED + (locals.size() - prevLocalsSize); + stackmaps.writeByte(frameType); + stackmaps.writeChar(offsetDelta); + for (int i = prevLocalsSize; i < locals.size(); i++) { + writeStackMapType(locals.get(i)); + } + } + + public void chopFrame(int offsetDelta, int droppedVars) { + int frameType = SAME_FRAME_EXTENDED - droppedVars; + stackmaps.writeByte(frameType); + stackmaps.writeChar(offsetDelta); + } + + public void fullFrame(int offsetDelta, List<T> locals, List<T> stackItems) { + stackmaps.writeByte(FULL_FRAME); + stackmaps.writeChar(offsetDelta); + stackmaps.writeChar(locals.size()); + for (T local : locals) { + writeStackMapType(local); + } + + stackmaps.writeChar(stackItems.size()); + for (T stackType : stackItems) { + writeStackMapType(stackType); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/DeclBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.function.Consumer; + +public class DeclBuilder<S, T, E, D extends DeclBuilder<S, T, E, D>> + extends AttributeBuilder<S, T, E, D> { + + protected int flags; + AnnotationsBuilder<S, T, E> runtimeInvisibleAnnotations; + AnnotationsBuilder<S, T, E> runtimeVisibleAnnotations; + + + DeclBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) { + super(poolHelper, typeHelper); + } + + public D withFlags(Flag... flags) { + for (Flag f : flags) { + this.flags |= f.flag; + } + return thisBuilder(); + } + + public D withFlags(int flags) { + withFlags(Flag.parse(flags)); + return thisBuilder(); + } + + public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType) { + getAnnotations(kind).withAnnotation(annoType, null); + return thisBuilder(); + } + + public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) { + getAnnotations(kind).withAnnotation(annoType, annotations); + return thisBuilder(); + } + + private AnnotationsBuilder<S, T, E> getAnnotations(AnnotationsBuilder.Kind kind) { + switch (kind) { + case RUNTIME_INVISIBLE: + if (runtimeInvisibleAnnotations == null) { + runtimeInvisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper); + } + return runtimeInvisibleAnnotations; + case RUNTIME_VISIBLE: + if (runtimeVisibleAnnotations == null) { + runtimeVisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper); + } + return runtimeVisibleAnnotations; + } + throw new IllegalStateException(); + } + + void addAnnotations() { + if (runtimeVisibleAnnotations != null) { + withAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotations.build()); + } + if (runtimeInvisibleAnnotations != null) { + withAttribute("RuntimeInvisibleAnnotations", runtimeVisibleAnnotations.build()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/FieldBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public class FieldBuilder<S, T, E> extends MemberBuilder<S, T, E, FieldBuilder<S, T, E>> { + public FieldBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) { + super(name, type, poolHelper, typeHelper); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/Flag.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.EnumSet; + +public enum Flag { + ACC_PUBLIC(0x0001), + ACC_PROTECTED(0x0004), + ACC_PRIVATE(0x0002), + ACC_INTERFACE(0x0200), + ACC_ENUM(0x4000), + ACC_ANNOTATION(0x2000), + ACC_SUPER(0x0020), + ACC_ABSTRACT(0x0400), + ACC_VOLATILE(0x0040), + ACC_TRANSIENT(0x0080), + ACC_SYNTHETIC(0x1000), + ACC_STATIC(0x0008), + ACC_FINAL(0x0010), + ACC_SYNCHRONIZED(0x0020), + ACC_BRIDGE(0x0040), + ACC_VARARGS(0x0080), + ACC_NATIVE(0x0100), + ACC_STRICT(0x0800); + + public int flag; + + Flag(int flag) { + this.flag = flag; + } + + static Flag[] parse(int flagsMask) { + EnumSet<Flag> flags = EnumSet.noneOf(Flag.class); + for (Flag f : Flag.values()) { + if ((f.flag & flagsMask) != 0) { + flags.add(f); + } + } + return flags.stream().toArray(Flag[]::new); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/GrowableByteBuffer.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.function.Consumer; + +public class GrowableByteBuffer { + + public GrowableByteBuffer() { + } + + byte[] elems = new byte[64]; + int offset = 0; + + public GrowableByteBuffer writeByte(int b) { + return writeBytes(1, b); + } + + public GrowableByteBuffer writeChar(int x) { + return writeBytes(2, x); + } + + public GrowableByteBuffer writeInt(int x) { + return writeBytes(4, x); + } + + public GrowableByteBuffer writeFloat(float x) { + return writeInt(Float.floatToIntBits(x)); + } + + public GrowableByteBuffer writeLong(long x) { + return writeBytes(8, x); + } + + public GrowableByteBuffer writeDouble(double x) { + writeLong(Double.doubleToLongBits(x)); + return this; + } + + public GrowableByteBuffer writeBytes(byte[] barr) { + expandIfNeeded(barr.length); + System.arraycopy(barr, 0, elems, offset, barr.length); + offset += barr.length; + return this; + } + + public GrowableByteBuffer writeBytes(GrowableByteBuffer bb) { + expandIfNeeded(bb.offset); + System.arraycopy(bb.elems, 0, elems, offset, bb.offset); + offset += bb.offset; + return this; + } + + public GrowableByteBuffer withOffset(int offset, Consumer<GrowableByteBuffer> actions) { + int prevOffset = this.offset; + this.offset = offset; + actions.accept(this); + this.offset = prevOffset; + return this; + } + + private GrowableByteBuffer writeBytes(int size, long x) { + expandIfNeeded(size); + for (int i = 0; i < size; i++) { + elems[offset++] = (byte) ((x >> 8 * (size - i - 1)) & 0xFF); + } + return this; + } + + void expandIfNeeded(int increment) { + if (offset + increment > elems.length) { + int newsize = elems.length * 2; + while (offset + increment > newsize) { + newsize *= 2; + } + byte[] newelems = new byte[newsize]; + System.arraycopy(elems, 0, newelems, 0, offset); + elems = newelems; + } + } + + public byte[] bytes() { + byte[] bytes = new byte[offset]; + System.arraycopy(elems, 0, bytes, 0, offset); + return bytes; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/MacroCodeBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,702 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Consumer; + +public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> { + + JumpMode jumpMode = JumpMode.NARROW; + + Map<CharSequence, Integer> labels = new HashMap<>(); + List<PendingJump> pendingJumps = new LinkedList<>(); + + class PendingJump { + CharSequence label; + int pc; + + PendingJump(CharSequence label, int pc) { + this.label = label; + this.pc = pc; + } + + boolean resolve(CharSequence label, int offset) { + if (this.label.equals(label)) { + //patch offset + code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc)); + return true; + } else { + return false; + } + } + } + + public enum InvocationKind { + INVOKESTATIC, + INVOKEVIRTUAL, + INVOKESPECIAL, + INVOKEINTERFACE; + } + + public enum FieldAccessKind { + STATIC, + INSTANCE; + } + + public enum CondKind { + EQ(0), + NE(1), + LT(2), + GE(3), + GT(4), + LE(5); + + int offset; + + CondKind(int offset) { + this.offset = offset; + } + + public CondKind negate() { + switch (this) { + case EQ: + return NE; + case NE: + return EQ; + case LT: + return GE; + case GE: + return LT; + case GT: + return LE; + case LE: + return GT; + default: + throw new IllegalStateException("Unknown cond"); + } + } + } + + static class WideJumpException extends RuntimeException { + static final long serialVersionUID = 42L; + } + + public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) { + super(methodBuilder); + } + + public C load(TypeTag type, int n) { + if (type == TypeTag.Q) { + return vload(n); + } else { + switch (n) { + case 0: + return emitOp(Opcode.ILOAD_0.at(type, 4)); + case 1: + return emitOp(Opcode.ILOAD_1.at(type, 4)); + case 2: + return emitOp(Opcode.ILOAD_2.at(type, 4)); + case 3: + return emitOp(Opcode.ILOAD_3.at(type, 4)); + default: + return emitWideIfNeeded(Opcode.ILOAD.at(type), n); + } + } + } + + public C store(TypeTag type, int n) { + if (type == TypeTag.Q) { + return vstore(n); + } else { + switch (n) { + case 0: + return emitOp(Opcode.ISTORE_0.at(type, 4)); + case 1: + return emitOp(Opcode.ISTORE_1.at(type, 4)); + case 2: + return emitOp(Opcode.ISTORE_2.at(type, 4)); + case 3: + return emitOp(Opcode.ISTORE_3.at(type, 4)); + default: + return emitWideIfNeeded(Opcode.ISTORE.at(type), n); + } + } + } + + public C arrayload(TypeTag type) { + return emitOp(Opcode.IALOAD.at(type)); + } + + public C arraystore(TypeTag type, int n) { + return emitOp(Opcode.IASTORE.at(type)); + } + + public C const_(int i) { + switch (i) { + case -1: + return iconst_m1(); + case 0: + return iconst_0(); + case 1: + return iconst_1(); + case 2: + return iconst_2(); + case 3: + return iconst_3(); + case 4: + return iconst_4(); + case 5: + return iconst_5(); + default: + if (i > 0 && i <= Byte.MAX_VALUE) { + return bipush(i); + } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) { + return sipush(i); + } else { + return ldc(i); + } + } + } + + public C const_(long l) { + if (l == 0) { + return lconst_0(); + } else if (l == 1) { + return lconst_1(); + } else { + return ldc(l); + } + } + + public C const_(float f) { + if (f == 0) { + return fconst_0(); + } else if (f == 1) { + return fconst_1(); + } else if (f == 2) { + return fconst_2(); + } else { + return ldc(f); + } + } + + public C const_(double d) { + if (d == 0) { + return dconst_0(); + } else if (d == 1) { + return dconst_1(); + } else { + return ldc(d); + } + } + + public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) { + switch (fak) { + case INSTANCE: + return getfield(owner, name, type); + case STATIC: + return getstatic(owner, name, type); + default: + throw new IllegalStateException(); + } + } + + public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) { + switch (fak) { + case INSTANCE: + return putfield(owner, name, type); + case STATIC: + return putstatic(owner, name, type); + default: + throw new IllegalStateException(); + } + } + + public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) { + switch (ik) { + case INVOKESTATIC: + return invokestatic(owner, name, type, isInterface); + case INVOKEVIRTUAL: + return invokevirtual(owner, name, type, isInterface); + case INVOKESPECIAL: + return invokespecial(owner, name, type, isInterface); + case INVOKEINTERFACE: + if (!isInterface) throw new AssertionError(); + return invokeinterface(owner, name, type); + default: + throw new IllegalStateException(); + } + } + + public C add(TypeTag type) { + return emitOp(Opcode.IADD.at(type)); + } + + public C sub(TypeTag type) { + return emitOp(Opcode.ISUB.at(type)); + } + + public C mul(TypeTag type) { + return emitOp(Opcode.IMUL.at(type)); + } + + public C div(TypeTag type) { + return emitOp(Opcode.IDIV.at(type)); + } + + public C rem(TypeTag type) { + return emitOp(Opcode.IREM.at(type)); + } + + public C neg(TypeTag type) { + return emitOp(Opcode.INEG.at(type)); + } + + public C shl(TypeTag type) { + return emitOp(Opcode.ISHL.at(type)); + } + + public C shr(TypeTag type) { + return emitOp(Opcode.ISHR.at(type)); + } + + public C ushr(TypeTag type) { + return emitOp(Opcode.ISHR.at(type)); + } + + public C and(TypeTag type) { + return emitOp(Opcode.IAND.at(type)); + } + + public C or(TypeTag type) { + return emitOp(Opcode.IOR.at(type)); + } + + public C xor(TypeTag type) { + return emitOp(Opcode.IXOR.at(type)); + } + + public C return_(TypeTag type) { + switch (type) { + case V: + return return_(); + case Q: + return vreturn(); + default: + return emitOp(Opcode.IRETURN.at(type)); + } + } + + @Override + public LabelledTypedBuilder typed(TypeTag typeTag) { + return super.typed(typeTag, _unused -> new LabelledTypedBuilder()); + } + + public class LabelledTypedBuilder extends TypedBuilder { + public C if_acmpeq(CharSequence target) { + return ifcmp(TypeTag.A, CondKind.EQ, target); + } + + public C if_acmpne(CharSequence target) { + return ifcmp(TypeTag.A, CondKind.NE, target); + } + } + + public C conv(TypeTag from, TypeTag to) { + switch (from) { + case B: + case C: + case S: + switch (to) { + case J: + return i2l(); + case F: + return i2f(); + case D: + return i2d(); + } + break; + case I: + switch (to) { + case J: + return i2l(); + case F: + return i2f(); + case D: + return i2d(); + case B: + return i2b(); + case C: + return i2c(); + case S: + return i2s(); + } + break; + case J: + switch (to) { + case I: + return l2i(); + case F: + return l2f(); + case D: + return l2d(); + } + break; + case F: + switch (to) { + case I: + return f2i(); + case J: + return f2l(); + case D: + return f2d(); + } + break; + case D: + switch (to) { + case I: + return d2i(); + case J: + return d2l(); + case F: + return d2f(); + } + break; + } + //no conversion is necessary - do nothing! + return thisBuilder(); + } + + public C ifcmp(TypeTag type, CondKind cond, CharSequence label) { + switch (type) { + case I: + return emitCondJump(Opcode.IF_ICMPEQ, cond, label); + case A: + return emitCondJump(Opcode.IF_ACMPEQ, cond, label); + case J: + return lcmp().emitCondJump(Opcode.IFEQ, cond, label); + case D: + return dcmpg().emitCondJump(Opcode.IFEQ, cond, label); + case F: + return fcmpg().emitCondJump(Opcode.IFEQ, cond, label); + default: + throw new IllegalArgumentException("Bad cmp type"); + } + } + + public C goto_(CharSequence label) { + emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W); + emitOffset(code, jumpMode, labelOffset(label)); + return thisBuilder(); + } + + protected int labelOffset(CharSequence label) { + int pc = code.offset - 1; + Integer labelPc = labels.get(label); + if (labelPc == null) { + addPendingJump(label, pc); + } + return labelPc == null ? 0 : (labelPc - pc); + } + + public C label(CharSequence s) { + int pc = code.offset; + Object old = labels.put(s, pc); + if (old != null) { + throw new IllegalStateException("label already exists"); + } + resolveJumps(s, pc); + return thisBuilder(); + } + + //FIXME: address this jumpy mess - i.e. offset and state update work against each other! + public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) { + if (jumpMode == JumpMode.NARROW) { + emitOp(opcode.at(ck)); + emitOffset(code, jumpMode, labelOffset(label)); + } else { + emitOp(opcode.at(ck.negate())); + emitOffset(code, JumpMode.NARROW, 8); + goto_w(labelOffset(label)); + } + return thisBuilder(); + } + + void addPendingJump(CharSequence label, int pc) { + pendingJumps.add(new PendingJump(label, pc)); + } + + void resolveJumps(CharSequence label, int pc) { + Iterator<PendingJump> jumpsIt = pendingJumps.iterator(); + while (jumpsIt.hasNext()) { + PendingJump jump = jumpsIt.next(); + if (jump.resolve(label, pc)) { + jumpsIt.remove(); + } + } + } + + @Override + protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) { + if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) { + throw new WideJumpException(); + } + super.emitOffset(buf, jumpMode, offset); + } + + public C jsr(CharSequence label) { + emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W); + emitOffset(code, jumpMode, labelOffset(label)); + return thisBuilder(); + } + + @SuppressWarnings("unchecked") + public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) { + int start = code.offset; + tryBlock.accept((C) this); + int end = code.offset; + CatchBuilder catchBuilder = makeCatchBuilder(start, end); + catchBlocks.accept(catchBuilder); + catchBuilder.build(); + return thisBuilder(); + } + + void clear() { + code.offset = 0; + catchers.offset = 0; + ncatchers = 0; + labels.clear(); + pendingJumps = null; + } + + protected CatchBuilder makeCatchBuilder(int start, int end) { + return new CatchBuilder(start, end); + } + + public class CatchBuilder { + int start, end; + + String endLabel = labelName(); + + Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>(); + public Consumer<? super C> finalizer; + List<Integer> pendingGaps = new ArrayList<>(); + + public CatchBuilder(int start, int end) { + this.start = start; + this.end = end; + } + + public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) { + catchers.put(exc, catcher); + return this; + } + + public CatchBuilder withFinally(Consumer<? super C> finalizer) { + this.finalizer = finalizer; + return this; + } + + @SuppressWarnings("unchecked") + void build() { + if (finalizer != null) { + finalizer.accept((C) MacroCodeBuilder.this); + } + goto_(endLabel); + for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) { + emitCatch(catcher_entry.getKey(), catcher_entry.getValue()); + } + if (finalizer != null) { + emitFinalizer(); + } + resolveJumps(endLabel, code.offset); + } + + @SuppressWarnings("unchecked") + protected void emitCatch(S exc, Consumer<? super C> catcher) { + int offset = code.offset; + MacroCodeBuilder.this.withCatch(exc, start, end, offset); + catcher.accept((C) MacroCodeBuilder.this); + if (finalizer != null) { + int startFinalizer = code.offset; + finalizer.accept((C) MacroCodeBuilder.this); + pendingGaps.add(startFinalizer); + pendingGaps.add(code.offset); + } + goto_(endLabel); + } + + @SuppressWarnings("unchecked") + protected void emitFinalizer() { + int offset = code.offset; + pop(); + for (int i = 0; i < pendingGaps.size(); i += 2) { + MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset); + } + MacroCodeBuilder.this.withCatch(null, start, end, offset); + finalizer.accept((C) MacroCodeBuilder.this); + } + +// @SuppressWarnings("unchecked") +// CatchBuilder withCatch(S exc, Consumer<? super C> catcher) { +// int offset = code.offset; +// MacroCodeBuilder.this.withCatch(exc, start, end, offset); +// catcher.accept((C)MacroCodeBuilder.this); +// return this; +// } +// +// @SuppressWarnings("unchecked") +// CatchBuilder withFinally(Consumer<? super C> catcher) { +// int offset = code.offset; +// MacroCodeBuilder.this.withCatch(null, start, end, offset); +// catcher.accept((C)MacroCodeBuilder.this); +// return this; +// } + } + + @SuppressWarnings("unchecked") + public C switch_(Consumer<? super SwitchBuilder> consumer) { + int start = code.offset; + SwitchBuilder sb = makeSwitchBuilder(); + consumer.accept(sb); + int nlabels = sb.cases.size(); + switch (sb.switchCode()) { + case LOOKUPSWITCH: { + int[] lookupOffsets = new int[nlabels * 2]; + int i = 0; + for (Integer v : sb.cases.keySet()) { + lookupOffsets[i] = v; + i += 2; + } + lookupswitch(0, lookupOffsets); + //backpatch lookup + int curr = code.offset - (8 * nlabels) - 8; + int defaultOffset = code.offset - start; + code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset)); + sb.defaultCase.accept((C) this); + curr += 12; + for (Consumer<? super C> case_ : sb.cases.values()) { + int offset = code.offset; + code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start)); + case_.accept((C) this); + curr += 8; + } + break; + } + case TABLESWITCH: { + int[] tableOffsets = new int[sb.hi - sb.lo + 1]; + tableswitch(sb.lo, sb.hi, 0, tableOffsets); + //backpatch table + int curr = code.offset - (4 * tableOffsets.length) - 12; + int defaultOffset = code.offset - start; + code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset)); + sb.defaultCase.accept((C) this); + curr += 12; + int lastCasePc = -1; + for (int i = sb.lo; i <= sb.hi; i++) { + Consumer<? super C> case_ = sb.cases.get(i); + if (case_ != null) { + lastCasePc = code.offset; + case_.accept((C) this); + } + int offset = lastCasePc - start; + code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset)); + curr += 4; + } + } + } + resolveJumps(sb.endLabel, code.offset); + return thisBuilder(); + } + + private static int labelCount = 0; + + String labelName() { + return "label" + labelCount++; + } + + protected SwitchBuilder makeSwitchBuilder() { + return new SwitchBuilder(); + } + + public class SwitchBuilder { + Map<Integer, Consumer<? super C>> cases = new TreeMap<>(); + int lo = Integer.MAX_VALUE; + int hi = Integer.MIN_VALUE; + String endLabel = labelName(); + + public Consumer<? super C> defaultCase; + + @SuppressWarnings("unchecked") + public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) { + if (value > hi) { + hi = value; + } + if (value < lo) { + lo = value; + } + if (!fallthrough) { + Consumer<? super C> prevCase = case_; + case_ = C -> { + prevCase.accept(C); + C.goto_(endLabel); + }; + } + cases.put(value, case_); + return this; + } + + @SuppressWarnings("unchecked") + public SwitchBuilder withDefault(Consumer<? super C> defaultCase) { + if (this.defaultCase != null) { + throw new IllegalStateException("default already set"); + } + this.defaultCase = defaultCase; + return this; + } + + Opcode switchCode() { + int nlabels = cases.size(); + // Determine whether to issue a tableswitch or a lookupswitch + // instruction. + long table_space_cost = 4 + ((long) hi - lo + 1); // words + long lookup_space_cost = 3 + 2 * (long) nlabels; + return + nlabels > 0 && + table_space_cost <= lookup_space_cost + ? + Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/MemberBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public class MemberBuilder<S, T, E, M extends MemberBuilder<S, T, E, M>> extends DeclBuilder<S, T, E, M> { + + CharSequence name; + T desc; + + MemberBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) { + super(poolHelper, typeHelper); + this.name = name; + this.desc = type; + } + + void build(GrowableByteBuffer buf) { + addAnnotations(); + buf.writeChar(flags); + buf.writeChar(poolHelper.putUtf8(name)); + buf.writeChar(poolHelper.putType(desc)); + buf.writeChar(nattrs); + buf.writeBytes(attributes); + } + + byte[] build() { + GrowableByteBuffer buf = new GrowableByteBuffer(); + addAnnotations(); + build(buf); + return buf.bytes(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/MethodBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import jdk.experimental.bytecode.CodeBuilder.JumpMode; + +import java.util.Iterator; +import java.util.function.Consumer; +import java.util.function.Function; + +public class MethodBuilder<S, T, E> extends MemberBuilder<S, T, E, MethodBuilder<S, T, E>> { + + S thisClass; + ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations; + ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations; + + public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) { + super(name, type, pool, typeHelper); + this.thisClass = thisClass; + } + + public <C extends CodeBuilder<S, T, E, ?>> MethodBuilder<S, T, E> withCode(Function<? super MethodBuilder<S, T, E>, ? extends C> func, + Consumer<? super C> code) { + C codeBuilder = func.apply(this); + int start = attributes.offset; + try { + code.accept(codeBuilder); + } catch (MacroCodeBuilder.WideJumpException ex) { + //wide jumps! Redo the code + ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).jumpMode = JumpMode.WIDE; + ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).clear(); + code.accept(codeBuilder); + } + + attributes.writeChar(poolHelper.putUtf8("Code")); + attributes.writeInt(0); + codeBuilder.build(attributes); + int length = attributes.offset - start; + //avoid using lambda here + int prevOffset = attributes.offset; + try { + attributes.offset = start + 2; + attributes.writeInt(length - 6); + } finally { + attributes.offset = prevOffset; + } + nattrs++; + return this; + } + + public MethodBuilder<S, T, E> withCode(Consumer<? super CodeBuilder<S, T, E, ?>> code) { + return withCode(CodeBuilder::new, code); + } + + @SuppressWarnings({"varargs", "unchecked"}) + public MethodBuilder<S, T, E> withExceptions(S... exceptions) { + attributes.writeChar(poolHelper.putUtf8("Exceptions")); + attributes.writeInt(2 + (2 * exceptions.length)); + attributes.writeChar(exceptions.length); + for (S exception : exceptions) { + attributes.writeChar(poolHelper.putClass(exception)); + } + nattrs++; + return this; + } + + public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) { + getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null); + return this; + } + + public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) { + getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, annotations); + return this; + } + + private ParameterAnnotationsBuilder getParameterAnnotations(AnnotationsBuilder.Kind kind) { + switch (kind) { + case RUNTIME_INVISIBLE: + if (runtimeInvisibleParameterAnnotations == null) { + runtimeInvisibleParameterAnnotations = new ParameterAnnotationsBuilder(); + } + return runtimeInvisibleParameterAnnotations; + case RUNTIME_VISIBLE: + if (runtimeVisibleParameterAnnotations == null) { + runtimeVisibleParameterAnnotations = new ParameterAnnotationsBuilder(); + } + return runtimeVisibleParameterAnnotations; + } + throw new IllegalStateException(); + } + + class ParameterAnnotationsBuilder { + + GrowableByteBuffer parameterAnnos = new GrowableByteBuffer(); + + @SuppressWarnings({"unchecked", "rawtypes"}) + AnnotationsBuilder<S, T, E>[] builders = new AnnotationsBuilder[nparams()]; + + ParameterAnnotationsBuilder() { + for (int i = 0; i < builders.length; i++) { + builders[i] = new AnnotationsBuilder<>(poolHelper, typeHelper); + } + } + + byte[] build() { + parameterAnnos.writeByte(builders.length); + for (AnnotationsBuilder<S, T, E> builder : builders) { + parameterAnnos.writeBytes(builder.build()); + } + return parameterAnnos.bytes(); + } + + int nparams() { + Iterator<T> paramsIt = typeHelper.parameterTypes(desc); + int nparams = 0; + while (paramsIt.hasNext()) { + paramsIt.next(); + nparams++; + } + return nparams; + } + } + + @Override + void addAnnotations() { + super.addAnnotations(); + if (runtimeInvisibleParameterAnnotations != null) { + withAttribute("RuntimeInvisibleParameterAnnotations", runtimeInvisibleParameterAnnotations.build()); + } + if (runtimeVisibleParameterAnnotations != null) { + withAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotations.build()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/Opcode.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import jdk.experimental.bytecode.MacroCodeBuilder.CondKind; + +public enum Opcode { + + NOP(0), + ACONST_NULL(1), + ICONST_M1(2), + ICONST_0(3), + ICONST_1(4), + ICONST_2(5), + ICONST_3(6), + ICONST_4(7), + ICONST_5(8), + LCONST_0(9), + LCONST_1(10), + FCONST_0(11), + FCONST_1(12), + FCONST_2(13), + DCONST_0(14), + DCONST_1(15), + BIPUSH(16), + SIPUSH(17), + LDC(18), + LDC_W(19), + LDC2_W(20), + ILOAD(21), + LLOAD(22), + FLOAD(23), + DLOAD(24), + ALOAD(25), + ILOAD_0(26), + ILOAD_1(27), + ILOAD_2(28), + ILOAD_3(29), + LLOAD_0(30), + LLOAD_1(31), + LLOAD_2(32), + LLOAD_3(33), + FLOAD_0(34), + FLOAD_1(35), + FLOAD_2(36), + FLOAD_3(37), + DLOAD_0(38), + DLOAD_1(39), + DLOAD_2(40), + DLOAD_3(41), + ALOAD_0(42), + ALOAD_1(43), + ALOAD_2(44), + ALOAD_3(45), + IALOAD(46), + LALOAD(47), + FALOAD(48), + DALOAD(49), + AALOAD(50), + BALOAD(51), + CALOAD(52), + SALOAD(53), + ISTORE(54), + LSTORE(55), + FSTORE(56), + DSTORE(57), + ASTORE(58), + ISTORE_0(59), + ISTORE_1(60), + ISTORE_2(61), + ISTORE_3(62), + LSTORE_0(63), + LSTORE_1(64), + LSTORE_2(65), + LSTORE_3(66), + FSTORE_0(67), + FSTORE_1(68), + FSTORE_2(69), + FSTORE_3(70), + DSTORE_0(71), + DSTORE_1(72), + DSTORE_2(73), + DSTORE_3(74), + ASTORE_0(75), + ASTORE_1(76), + ASTORE_2(77), + ASTORE_3(78), + IASTORE(79), + LASTORE(80), + FASTORE(81), + DASTORE(82), + AASTORE(83), + BASTORE(84), + CASTORE(85), + SASTORE(86), + POP(87), + POP2(88), + DUP(89), + DUP_X1(90), + DUP_X2(91), + DUP2(92), + DUP2_X1(93), + DUP2_X2(94), + SWAP(95), + IADD(96), + LADD(97), + FADD(98), + DADD(99), + ISUB(100), + LSUB(101), + FSUB(102), + DSUB(103), + IMUL(104), + LMUL(105), + FMUL(106), + DMUL(107), + IDIV(108), + LDIV(109), + FDIV(110), + DDIV(111), + IREM(112), + LREM(113), + FREM(114), + DREM(115), + INEG(116), + LNEG(117), + FNEG(118), + DNEG(119), + ISHL(120), + LSHL(121), + ISHR(122), + LSHR(123), + IUSHR(124), + LUSHR(125), + IAND(126), + LAND(127), + IOR(128), + LOR(129), + IXOR(130), + LXOR(131), + IINC(132), + I2L(133), + I2F(134), + I2D(135), + L2I(136), + L2F(137), + L2D(138), + F2I(139), + F2L(140), + F2D(141), + D2I(142), + D2L(143), + D2F(144), + I2B(145), + I2C(146), + I2S(147), + LCMP(148), + FCMPL(149), + FCMPG(150), + DCMPL(151), + DCMPG(152), + IFEQ(153), + IFNE(154), + IFLT(155), + IFGE(156), + IFGT(157), + IFLE(158), + IF_ICMPEQ(159), + IF_ICMPNE(160), + IF_ICMPLT(161), + IF_ICMPGE(162), + IF_ICMPGT(163), + IF_ICMPLE(164), + IF_ACMPEQ(165), + IF_ACMPNE(166), + GOTO_(167), + JSR(168), + RET(169), + TABLESWITCH(170), + LOOKUPSWITCH(171), + IRETURN(172), + LRETURN(173), + FRETURN(174), + DRETURN(175), + ARETURN(176), + RETURN(177), + GETSTATIC(178), + PUTSTATIC(179), + GETFIELD(180), + PUTFIELD(181), + INVOKEVIRTUAL(182), + INVOKESPECIAL(183), + INVOKESTATIC(184), + INVOKEINTERFACE(185), + INVOKEDYNAMIC(186), + NEW(187), + NEWARRAY(188), + ANEWARRAY(189), + ARRAYLENGTH(190), + ATHROW(191), + CHECKCAST(192), + INSTANCEOF(193), + MONITORENTER(194), + MONITOREXIT(195), + WIDE(196), + MULTIANEWARRAY(197), + IF_NULL(198), + IF_NONNULL(199), + GOTO_W(200), + JSR_W(201), + VLOAD(203), + VSTORE(204), + VALOAD(205), + VASTORE(206), + VRETURN(207), + VDEFAULT(208), + VWITHFIELD(209), + VBOX(210), + VUNBOX(211), + TYPED(212); + + int code; + + Opcode(int code) { + this.code = code; + } + + protected Opcode at(TypeTag type) { + return at(type, 1); + } + + protected Opcode at(CondKind cond) { + return at(cond.offset, 1); + } + + protected Opcode at(TypeTag type, int multiplier) { + return at(type.offset, multiplier); + } + + private Opcode at(int offset, int multiplier) { + if (offset < 0) throw new AssertionError(); + return Opcode.values()[code + (multiplier * offset)]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/PoolHelper.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public interface PoolHelper<S, T, E> { + int putClass(S symbol); + + int putFieldRef(S owner, CharSequence name, T type); + + int putMethodRef(S owner, CharSequence name, T type, boolean isInterface); + + int putUtf8(CharSequence s); + + int putType(T t); + + int putValue(Object v); + + int putMethodType(T t); + + int putHandle(int refKind, S owner, CharSequence name, T type); + + int putInvokeDynamic(CharSequence invokedName, T invokedType, int bsmKind, S bsmClass, CharSequence bsmName, T bsmType, Object... staticArgs); + + int size(); + + E entries(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/PoolTag.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public enum PoolTag { + CONSTANT_UTF8(1), + CONSTANT_UNICODE(2), + CONSTANT_INTEGER(3), + CONSTANT_FLOAT(4), + CONSTANT_LONG(5), + CONSTANT_DOUBLE(6), + CONSTANT_CLASS(7), + CONSTANT_STRING(8), + CONSTANT_FIELDREF(9), + CONSTANT_METHODREF(10), + CONSTANT_INTERFACEMETHODREF(11), + CONSTANT_NAMEANDTYPE(12), + CONSTANT_METHODHANDLE(15), + CONSTANT_METHODTYPE(16), + CONSTANT_INVOKEDYNAMIC(18); + + public final int tag; + + PoolTag(int tag) { + this.tag = tag; + } + + static PoolTag from(int tag) { + return values()[tag - 1]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/Type.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public interface Type { + TypeTag getTag(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/TypeHelper.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.util.Iterator; + +public interface TypeHelper<S, T> { + T elemtype(T t); + + T arrayOf(T t); + + Iterator<T> parameterTypes(T t); + + T fromTag(TypeTag tag); + + T returnType(T t); + + T type(S s); + + S symbol(T type); + + TypeTag tag(T t); + + S symbolFrom(String s); + + T commonSupertype(T t1, T t2); + + T nullType(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/TypeTag.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +public enum TypeTag implements Type { + B("B", 0, 1, 8), + S("S", 0, 1, 9), + I("I", 0, 1, 10), + F("F", 2, 1, 6), + J("J", 1, 2, 11), + D("D", 3, 2, 7), + A("A", 4, 1, -1), + C("C", 0, 1, 5), + Z("Z", 0, 1, 4), + V("V", -1, -1, -1), + Q("Q", -1, 1, -1); + + String typeStr; + int offset; + int width; + int newarraycode; + + TypeTag(String typeStr, int offset, int width, int newarraycode) { + this.typeStr = typeStr; + this.offset = offset; + this.width = width; + this.newarraycode = newarraycode; + } + + static TypeTag commonSupertype(TypeTag t1, TypeTag t2) { + if (t1.isIntegral() && t2.isIntegral()) { + int p1 = t1.ordinal(); + int p2 = t2.ordinal(); + return (p1 <= p2) ? t2 : t1; + } else { + return null; + } + } + + public int width() { + return width; + } + + boolean isIntegral() { + switch (this) { + case B: + case S: + case I: + return true; + default: + return false; + } + } + + @Override + public TypeTag getTag() { + return this; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/bytecode/TypedCodeBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.bytecode; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> { + + State lastStackMapState; + int lastStackMapPc = -1; + Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>(); + protected State state; + int depth = 0; + int currLocalOffset = 0; + + class StatefulPendingJump extends PendingJump { + + State state; + + StatefulPendingJump(CharSequence label, int pc, State state) { + super(label, pc); + this.state = state; + } + + @Override + boolean resolve(CharSequence label, int pc) { + boolean b = super.resolve(label, pc); + if (b) { + TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state); + } + return b; + } + } + + class LocalVarInfo { + CharSequence name; + int offset; + int depth; + TypeTag type; + + LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) { + this.name = name; + this.offset = offset; + this.depth = depth; + this.type = type; + } + } + + public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) { + super(methodBuilder); + T t = methodBuilder.desc; + state = new State(); + if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) { + T clazz = typeHelper.type(methodBuilder.thisClass); + state.load(clazz, currLocalOffset++); //TODO: uninit?? + } + Iterator<T> paramsIt = typeHelper.parameterTypes(t); + while (paramsIt.hasNext()) { + T p = paramsIt.next(); + state.load(p, currLocalOffset); + currLocalOffset += typeHelper.tag(p).width; + } + lastStackMapState = state.dup(); + stacksize = state.stack.size(); + localsize = state.locals.size(); + } + + @Override + protected C emitOp(Opcode opcode, Object optPoolValue) { + updateState(opcode, optPoolValue); + return super.emitOp(opcode, optPoolValue); + } + + @Override + protected SwitchBuilder makeSwitchBuilder() { + return new TypedSwitchBuilder(); + } + + class TypedSwitchBuilder extends SwitchBuilder { + + @Override + public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) { + super.withCase(value, c -> { + withLocalScope(() -> { + State prevState = state; + state = prevState.dup(); + emitStackMap(c.offset()); + case_.accept(c); + state = prevState; + }); + }, fallthrough); + return this; + } + + @Override + public SwitchBuilder withDefault(Consumer<? super C> defaultCase) { + super.withDefault(c -> { + withLocalScope(() -> { + State prevState = state; + state = prevState.dup(); + emitStackMap(c.offset()); + defaultCase.accept(c); + state = prevState; + }); + }); + return this; + } + } + + @Override + public StatefulTypedBuilder typed(TypeTag tag) { + return super.typed(tag, StatefulTypedBuilder::new); + } + + public class StatefulTypedBuilder extends LabelledTypedBuilder { + + TypeTag tag; + + StatefulTypedBuilder(TypeTag tag) { + this.tag = tag; + } + + @Override + public C astore_0() { + return storeAndUpdate(super::astore_0); + } + + @Override + public C astore_1() { + return storeAndUpdate(super::astore_1); + } + + @Override + public C astore_2() { + return storeAndUpdate(super::astore_2); + } + + @Override + public C astore_3() { + return storeAndUpdate(super::astore_3); + } + + @Override + public C astore(int n) { + return storeAndUpdate(() -> super.astore(n)); + } + + @Override + public C aastore() { + return storeAndUpdate(super::aastore); + } + + @Override + public C areturn() { + state.pop(tag); + state.push(typeHelper.nullType()); + return super.areturn(); + } + + @Override + public C anewarray(S s) { + super.anewarray(s); + state.pop(); + state.push(typeHelper.arrayOf(typeHelper.type(s))); + return thisBuilder(); + } + + @Override + public C aconst_null() { + super.aconst_null(); + state.pop(); + state.push(tag); + return thisBuilder(); + } + + public C if_acmpeq(CharSequence label) { + return jumpAndUpdate(() -> super.if_acmpeq(label)); + } + + public C if_acmpne(CharSequence label) { + return jumpAndUpdate(() -> super.if_acmpne(label)); + } + + private C storeAndUpdate(Supplier<C> op) { + state.pop(tag); + state.push(typeHelper.nullType()); + return op.get(); + } + + private C jumpAndUpdate(Supplier<C> op) { + state.pop(tag); + state.pop(tag); + state.push(typeHelper.nullType()); + state.push(typeHelper.nullType()); + return op.get(); + } + } + + public class State { + public final ArrayList<T> stack; + public final Vector<T> locals; + boolean alive; + + State(ArrayList<T> stack, Vector<T> locals) { + this.stack = stack; + this.locals = locals; + } + + State() { + this(new ArrayList<>(), new Vector<>()); + } + + void push(TypeTag tag) { + switch (tag) { + case A: + case V: + throw new IllegalStateException("Bad type tag"); + default: + push(typeHelper.fromTag(tag)); + } + } + + void push(T t) { + stack.add(t); + if (width(t) == 2) { + stack.add(null); + } + if (stack.size() > stacksize) { + stacksize = stack.size(); + } + } + + T peek() { + return stack.get(stack.size() - 1); + } + + T tosType() { + T tos = peek(); + if (tos == null) { + //double slot + tos = stack.get(stack.size() - 2); + } + return tos; + } + + T popInternal() { + return stack.remove(stack.size() - 1); + } + + @SuppressWarnings("unchecked") + T pop() { + if (stack.size() == 0 || peek() == null) throw new IllegalStateException(); + return popInternal(); + } + + T pop2() { + T o = stack.get(stack.size() - 2); + TypeTag t = typeHelper.tag(o); + if (t.width != 2) throw new IllegalStateException(); + popInternal(); + popInternal(); + return o; + } + + T pop(TypeTag t) { + return (t.width() == 2) ? + pop2() : pop(); + } + + void load(TypeTag tag, int index) { + if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag"); + load(typeHelper.fromTag(tag), index); + } + + void load(T t, int index) { + ensureDefined(index); + locals.set(index, t); + if (width(t) == 2) { + locals.add(null); + } + if (locals.size() > localsize) { + localsize = locals.size(); + } + } + + void ensureDefined(int index) { + if (index >= locals.size()) { + locals.setSize(index + 1); + } + } + + State dup() { + State newState = new State(new ArrayList<>(stack), new Vector<>(locals)); + return newState; + } + + State merge(State that) { + if (!alive) { return that; } + if (that.stack.size() != stack.size()) { + throw new IllegalStateException("Bad stack size at merge point"); + } + for (int i = 0; i < stack.size(); i++) { + T t1 = stack.get(i); + T t2 = that.stack.get(i); + stack.set(i, merge(t1, t2, "Bad stack type at merge point")); + } + int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size(); + for (int i = 0; i < nlocals; i++) { + T t1 = locals.get(i); + T t2 = that.locals.get(i); + locals.set(i, merge(t1, t2, "Bad local type at merge point")); + } + if (locals.size() > nlocals) { + for (int i = nlocals; i < locals.size(); i++) { + locals.remove(i); + } + } + return this; + } + + T merge(T t1, T t2, String msg) { + if (t1 == null && t2 == null) { + return t1; + } + T res; + TypeTag tag1 = typeHelper.tag(t1); + TypeTag tag2 = typeHelper.tag(t2); + if (tag1 != TypeTag.A && tag2 != TypeTag.A && + tag1 != TypeTag.Q && tag2 != TypeTag.Q) { + res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2)); + } else if (t1 == typeHelper.nullType()) { + res = t2; + } else if (t2 == typeHelper.nullType()) { + res = t1; + } else { + res = typeHelper.commonSupertype(t1, t2); + } + if (res == null) { + throw new IllegalStateException(msg); + } + return res; + } + + @Override + public String toString() { + return String.format("[locals = %s, stack = %s]", locals, stack); + } + } + + int width(T o) { + return o == typeHelper.nullType() ? + TypeTag.A.width() : + typeHelper.tag(o).width; + } + + @SuppressWarnings("unchecked") + public void updateState(Opcode op, Object optValue) { + switch (op) { + case VALOAD: + case AALOAD: + state.pop(); + state.push(typeHelper.elemtype(state.pop())); + break; + case GOTO_: + state.alive = false; + break; + case NOP: + case IINC: + case INEG: + case LNEG: + case FNEG: + case DNEG: + break; + case ACONST_NULL: + state.push(typeHelper.nullType()); + break; + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + state.push(TypeTag.I); + break; + case LCONST_0: + case LCONST_1: + state.push(TypeTag.J); + break; + case FCONST_0: + case FCONST_1: + case FCONST_2: + state.push(TypeTag.F); + break; + case DCONST_0: + case DCONST_1: + state.push(TypeTag.D); + break; + case ILOAD_0: + case FLOAD_0: + case ALOAD_0: + case LLOAD_0: + case DLOAD_0: + state.push(state.locals.get(0)); + break; + case ILOAD_1: + case FLOAD_1: + case ALOAD_1: + case LLOAD_1: + case DLOAD_1: + state.push(state.locals.get(1)); + break; + case ILOAD_2: + case FLOAD_2: + case ALOAD_2: + case LLOAD_2: + case DLOAD_2: + state.push(state.locals.get(2)); + break; + case ILOAD_3: + case FLOAD_3: + case ALOAD_3: + case LLOAD_3: + case DLOAD_3: + state.push(state.locals.get(3)); + break; + case ILOAD: + case FLOAD: + case ALOAD: + case LLOAD: + case DLOAD: + case VLOAD: + state.push(state.locals.get((Integer) optValue)); + break; + case IALOAD: + case BALOAD: + case CALOAD: + case SALOAD: + state.pop(); + state.pop(); + state.push(TypeTag.I); + break; + case LALOAD: + state.pop(); + state.pop(); + state.push(TypeTag.J); + break; + case FALOAD: + state.pop(); + state.pop(); + state.push(TypeTag.F); + break; + case DALOAD: + state.pop(); + state.pop(); + state.push(TypeTag.D); + break; + case ISTORE_0: + case FSTORE_0: + case ASTORE_0: + state.load(state.pop(), 0); + break; + case ISTORE_1: + case FSTORE_1: + case ASTORE_1: + state.load(state.pop(), 1); + break; + case ISTORE_2: + case FSTORE_2: + case ASTORE_2: + state.load(state.pop(), 2); + break; + case ISTORE_3: + case FSTORE_3: + case ASTORE_3: + state.load(state.pop(), 3); + break; + case ISTORE: + case FSTORE: + case ASTORE: + case VSTORE: + state.load(state.pop(), (int) optValue); + break; + case LSTORE_0: + case DSTORE_0: + state.load(state.pop2(), 0); + break; + case LSTORE_1: + case DSTORE_1: + state.load(state.pop2(), 1); + break; + case LSTORE_2: + case DSTORE_2: + state.load(state.pop2(), 2); + break; + case LSTORE_3: + case DSTORE_3: + state.load(state.pop2(), 3); + break; + case LSTORE: + case DSTORE: + state.load(state.pop2(), (int) optValue); + break; + case POP: + case LSHR: + case LSHL: + case LUSHR: + state.pop(); + break; + case VRETURN: + case ARETURN: + case IRETURN: + case FRETURN: + state.pop(); + break; + case ATHROW: + state.pop(); + break; + case POP2: + state.pop2(); + break; + case LRETURN: + case DRETURN: + state.pop2(); + break; + case DUP: + state.push(state.peek()); + break; + case RETURN: + break; + case ARRAYLENGTH: + state.pop(); + state.push(TypeTag.I); + break; + case ISUB: + case IADD: + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + state.pop(); + state.pop(); + state.push(TypeTag.I); + break; + case VASTORE: + case AASTORE: + state.pop(); + state.pop(); + state.pop(); + break; + case LAND: + case LOR: + case LXOR: + case LREM: + case LDIV: + case LMUL: + case LSUB: + case LADD: + state.pop2(); + state.pop2(); + state.push(TypeTag.J); + break; + case LCMP: + state.pop2(); + state.pop2(); + state.push(TypeTag.I); + break; + case L2I: + state.pop2(); + state.push(TypeTag.I); + break; + case I2L: + state.pop(); + state.push(TypeTag.J); + break; + case I2F: + state.pop(); + state.push(TypeTag.F); + break; + case I2D: + state.pop(); + state.push(TypeTag.D); + break; + case L2F: + state.pop2(); + state.push(TypeTag.F); + break; + case L2D: + state.pop2(); + state.push(TypeTag.D); + break; + case F2I: + state.pop(); + state.push(TypeTag.I); + break; + case F2L: + state.pop(); + state.push(TypeTag.J); + break; + case F2D: + state.pop(); + state.push(TypeTag.D); + break; + case D2I: + state.pop2(); + state.push(TypeTag.I); + break; + case D2L: + state.pop2(); + state.push(TypeTag.J); + break; + case D2F: + state.pop2(); + state.push(TypeTag.F); + break; + case TABLESWITCH: + case LOOKUPSWITCH: + state.pop(); + break; + case DUP_X1: { + T val1 = state.pop(); + T val2 = state.pop(); + state.push(val1); + state.push(val2); + state.push(val1); + break; + } + case BASTORE: + state.pop(); + state.pop(); + state.pop(); + break; + case I2B: + case I2C: + case I2S: + break; + case FMUL: + case FADD: + case FSUB: + case FDIV: + case FREM: + state.pop(); + state.pop(); + state.push(TypeTag.F); + break; + case CASTORE: + case IASTORE: + case FASTORE: + case SASTORE: + state.pop(); + state.pop(); + state.pop(); + break; + case LASTORE: + case DASTORE: + state.pop2(); + state.pop(); + state.pop(); + break; + case DUP2: + if (state.peek() != null) { + //form 1 + T value1 = state.pop(); + T value2 = state.pop(); + state.push(value2); + state.push(value1); + state.push(value2); + state.push(value1); + } else { + //form 2 + T value = state.pop2(); + state.push(value); + state.push(value); + } + break; + case DUP2_X1: + if (state.peek() != null) { + T value1 = state.pop(); + T value2 = state.pop(); + T value3 = state.pop(); + state.push(value2); + state.push(value1); + state.push(value3); + state.push(value2); + state.push(value1); + } else { + T value1 = state.pop2(); + T value2 = state.pop(); + state.push(value1); + state.push(value2); + state.push(value1); + } + break; + case DUP2_X2: + if (state.peek() != null) { + T value1 = state.pop(); + T value2 = state.pop(); + if (state.peek() != null) { + // form 1 + T value3 = state.pop(); + T value4 = state.pop(); + state.push(value2); + state.push(value1); + state.push(value4); + state.push(value3); + state.push(value2); + state.push(value1); + } else { + // form 3 + T value3 = state.pop2(); + state.push(value2); + state.push(value1); + state.push(value3); + state.push(value2); + state.push(value1); + } + } else { + T value1 = state.pop2(); + if (state.peek() != null) { + // form 2 + T value2 = state.pop(); + T value3 = state.pop(); + state.push(value1); + state.push(value3); + state.push(value2); + state.push(value1); + } else { + // form 4 + T value2 = state.pop2(); + state.push(value1); + state.push(value2); + state.push(value1); + } + } + break; + case DUP_X2: { + T value1 = state.pop(); + if (state.peek() != null) { + // form 1 + T value2 = state.pop(); + T value3 = state.pop(); + state.push(value1); + state.push(value3); + state.push(value2); + state.push(value1); + } else { + // form 2 + T value2 = state.pop2(); + state.push(value1); + state.push(value2); + state.push(value1); + } + } + break; + case FCMPL: + case FCMPG: + state.pop(); + state.pop(); + state.push(TypeTag.I); + break; + case DCMPL: + case DCMPG: + state.pop2(); + state.pop2(); + state.push(TypeTag.I); + break; + case SWAP: { + T value1 = state.pop(); + T value2 = state.pop(); + state.push(value1); + state.push(value2); + break; + } + case DADD: + case DSUB: + case DMUL: + case DDIV: + case DREM: + state.pop2(); + state.pop2(); + state.push(TypeTag.D); + break; + case RET: + break; + case WIDE: + // must be handled by the caller. + return; + case MONITORENTER: + case MONITOREXIT: + state.pop(); + break; + case NEW: + case VDEFAULT: + state.push(typeHelper.type((S) optValue)); + break; + case NEWARRAY: + state.pop(); + state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue))); + break; + case ANEWARRAY: + state.pop(); + state.push(typeHelper.arrayOf(typeHelper.arrayOf(typeHelper.type((S)optValue)))); + break; + case VBOX: + case VUNBOX: + state.pop(); + state.push(typeHelper.type((S) optValue)); + break; + case MULTIANEWARRAY: + for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) { + state.pop(); + } + state.push(typeHelper.type((S) ((Object[]) optValue)[0])); + break; + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + case INVOKESPECIAL: + case INVOKESTATIC: + processInvoke(op, (T) optValue); + break; + case GETSTATIC: + state.push((T) optValue); + break; + case GETFIELD: + state.pop(); + state.push((T) optValue); + break; + case PUTSTATIC: { + TypeTag tag = typeHelper.tag((T) optValue); + if (tag.width == 1) { + state.pop(); + } else { + state.pop2(); + } + break; + } + case PUTFIELD: { + TypeTag tag = typeHelper.tag((T) optValue); + if (tag.width == 1) { + state.pop(); + } else { + state.pop2(); + } + state.pop(); + break; + } + case VWITHFIELD: { + TypeTag tag = typeHelper.tag((T) optValue); + if (tag.width == 1) { + state.pop(); + } else { + state.pop2(); + } + break; + } + case BIPUSH: + case SIPUSH: + state.push(TypeTag.I); + break; + case LDC: + case LDC_W: + case LDC2_W: + state.push(ldcType(optValue)); + break; + case IF_ACMPEQ: + case IF_ICMPEQ: + case IF_ACMPNE: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ICMPLT: + case IF_ICMPNE: + state.pop(); + state.pop(); + break; + case IF_NONNULL: + case IF_NULL: + case IFEQ: + case IFGE: + case IFGT: + case IFLE: + case IFLT: + case IFNE: + state.pop(); + break; + case INSTANCEOF: + state.pop(); + state.push(TypeTag.Z); + break; + case TYPED: + break; + case CHECKCAST: + state.pop(); + state.push(typeHelper.type((S) optValue)); + break; + + default: + throw new UnsupportedOperationException("Unsupported opcode: " + op); + } + } + + void processInvoke(Opcode opcode, T invokedType) { + Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType); + while (paramsIt.hasNext()) { + T t = paramsIt.next(); + TypeTag tag = typeHelper.tag(t); + if (tag.width == 2) { + state.popInternal(); + state.popInternal(); + } else { + state.popInternal(); + } + } + if (opcode != Opcode.INVOKESTATIC) { + state.pop(); //receiver + } + T retType = typeHelper.returnType(invokedType); + TypeTag retTag = typeHelper.tag(retType); + if (retTag != TypeTag.V) + state.push(retType); + } + + T ldcType(Object o) { + if (o instanceof Double) { + return typeHelper.fromTag(TypeTag.D); + } else if (o instanceof Long) { + return typeHelper.fromTag(TypeTag.J); + } else if (o instanceof Float) { + return typeHelper.fromTag(TypeTag.F); + } else if (o instanceof Integer) { + return typeHelper.fromTag(TypeTag.I); + } else if (o instanceof Class<?>) { + return typeHelper.type(typeHelper.symbolFrom("java/lang/Class")); + } else if (o instanceof String) { + return typeHelper.type(typeHelper.symbolFrom("java/lang/String")); + } else if (o instanceof MethodHandle) { + return typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle")); + } else if (o instanceof MethodType) { + return typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType")); + } else { + throw new IllegalStateException(); + } + } + + public C load(int index) { + return load(typeHelper.tag(state.locals.get(index)), index); + } + + public C store(int index) { + return store(typeHelper.tag(state.tosType()), index); + } + + @Override + public C withLocalSize(int localsize) { + throw new IllegalStateException("Local size automatically computed"); + } + + @Override + public C withStackSize(int stacksize) { + throw new IllegalStateException("Stack size automatically computed"); + } + + public C withLocal(CharSequence name, T type) { + int offset = currLocalOffset; + TypeTag tag = typeHelper.tag(type); + lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag)); + state.load(type, offset); + currLocalOffset += tag.width; + return thisBuilder(); + } + + public C load(CharSequence local) { + return load(lvarOffsets.get(local).offset); + } + + public C store(CharSequence local) { + return store(lvarOffsets.get(local).offset); + } + + @Override + public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) { + return super.withTry(c -> { + withLocalScope(() -> { + tryBlock.accept(c); + }); + }, catchBlocks); + } + + @Override + protected CatchBuilder makeCatchBuilder(int start, int end) { + return new TypedCatchBuilder(start, end); + } + + class TypedCatchBuilder extends CatchBuilder { + + State initialState = state.dup(); + + TypedCatchBuilder(int start, int end) { + super(start, end); + } + + @Override + protected void emitCatch(S exc, Consumer<? super C> catcher) { + withLocalScope(() -> { + state.push(typeHelper.type(exc)); + emitStackMap(code.offset); + super.emitCatch(exc, catcher); + state = initialState; + }); + } + + @Override + protected void emitFinalizer() { + withLocalScope(() -> { + state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable"))); + emitStackMap(code.offset); + super.emitFinalizer(); + }); + } + } + + protected void withLocalScope(Runnable runnable) { + int prevDepth = depth; + try { + depth++; + runnable.run(); + } finally { + Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator(); + while (lvarIt.hasNext()) { + LocalVarInfo lvi = lvarIt.next().getValue(); + if (lvi.depth == depth) { + int width = lvi.type.width; + currLocalOffset -= width; + lvarIt.remove(); + } + } + depth = prevDepth; + } + } + + @Override + void addPendingJump(CharSequence label, int pc) { + pendingJumps.add(new StatefulPendingJump(label, pc, state.dup())); + } + + @Override + void resolveJumps(CharSequence label, int pc) { + super.resolveJumps(label, pc); + emitStackMap(pc); + } + + //TODO: optimize stackmap generation by avoiding intermediate classes + protected void emitStackMap(int pc) { + //stack map generation + if (pc > lastStackMapPc) { + writeStackMapFrame(pc); + lastStackMapState = state.dup(); + lastStackMapPc = pc; + nstackmaps++; + } + } + + @Override + void build(GrowableByteBuffer buf) { + if (stacksize == -1) { + throw new IllegalStateException("Bad stack size"); + } + if (localsize == -1) { + throw new IllegalStateException("Bad locals size"); + } + if (nstackmaps > 0) { + GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer(); + stackmapsAttr.writeChar(nstackmaps); + stackmapsAttr.writeBytes(stackmaps); + withAttribute("StackMapTable", stackmapsAttr.bytes()); + } + super.build(buf); + } + + /** + * Compare this frame with the previous frame and produce + * an entry of compressed stack map frame. + */ + void writeStackMapFrame(int pc) { + List<T> locals = state.locals; + List<T> stack = state.stack; + List<T> prev_locals = lastStackMapState.locals; + int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1; + if (stack.size() == 1) { + if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) { + sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1)); + return; + } + } else if (stack.size() == 0) { + int diff_length = prev_locals.size() - locals.size(); + if (diff_length == 0) { + sameFrame(offset_delta); + return; + } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) { + appendFrame(offset_delta, prev_locals.size(), locals); + return; + } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) { + chopFrame(offset_delta, diff_length); + return; + } + } + fullFrame(offset_delta, locals, stack); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/experimental/value/MethodHandleBuilder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.experimental.value; + +import jdk.experimental.bytecode.BasicClassBuilder.BasicPoolHelper; +import jdk.experimental.bytecode.BasicClassBuilder.BasicTypeHelper; +import jdk.experimental.bytecode.ClassBuilder; +import jdk.experimental.bytecode.CodeBuilder; +import jdk.experimental.bytecode.Flag; +import jdk.experimental.bytecode.MethodBuilder; +import jdk.experimental.bytecode.PoolHelper; +import jdk.experimental.bytecode.TypeHelper; +import jdk.experimental.bytecode.TypeTag; +import jdk.experimental.bytecode.TypedCodeBuilder; +import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder.IsolatedMethodPoolHelper; +import jdk.internal.misc.Unsafe; +import sun.security.action.GetPropertyAction; +import valhalla.shady.MinimalValueTypes_1_0; +import valhalla.shady.ValueTypeHolder; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Utility class for building method handles. + */ +public class MethodHandleBuilder { + + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static final boolean ENABLE_POOL_PATCHES; + + static { + Properties props = GetPropertyAction.privilegedGetProperties(); + ENABLE_POOL_PATCHES = Boolean.parseBoolean( + props.getProperty("valhalla.enablePoolPatches")); + } + + public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { + return loadCode(lookup, name, name, type, builder); + } + + public static MethodHandle loadCode(Lookup lookup, String className, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { + String descriptor = type.toMethodDescriptorString(); + return loadCode(lookup, className, methodName, descriptor, MethodHandleCodeBuilder::new, + clazz -> { + try { + return lookup.findStatic(clazz, methodName, MethodType.fromMethodDescriptorString(descriptor, lookup.lookupClass().getClassLoader())); + } catch (ReflectiveOperationException ex) { + throw new IllegalStateException(ex); + } + }, + builder); + } + + protected static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode( + Lookup lookup, String className, String methodName, String type, + Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc, + Function<Class<?>, Z> resFunc, Consumer<? super C> builder) { + + IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup); + isolatedMethodBuilder + .withSuperclass(Object.class) + .withMajorVersion(52) + .withMinorVersion(0) + .withFlags(Flag.ACC_PUBLIC) + .withMethod(methodName, type, M -> + M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC) + .withCode(builderFunc, builder)); + + try { + byte[] barr = isolatedMethodBuilder.build(); + MinimalValueTypes_1_0.maybeDump(className, barr.clone()); + Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, isolatedMethodBuilder.patches()); + UNSAFE.ensureClassInitialized(clazz); + return resFunc.apply(clazz); + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + public static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> { + + private static final Class<?> THIS_CLASS = new Object() { }.getClass(); + + public IsolatedMethodBuilder(String clazz, Lookup lookup) { + super(ENABLE_POOL_PATCHES ? + new IsolatedMethodPatchingPoolHelper(clazz) : + new IsolatedMethodPoolHelper(clazz), + new IsolatedMethodTypeHelper(lookup)); + withThisClass(THIS_CLASS); + } + + public Class<?> thisClass() { + return THIS_CLASS; + } + + Object[] patches() { + return ((IsolatedMethodPoolHelper)poolHelper).patches(); + } + + static class IsolatedMethodTypeHelper implements TypeHelper<Class<?>, String> { + + BasicTypeHelper basicTypeHelper = new BasicTypeHelper(); + Lookup lookup; + + IsolatedMethodTypeHelper(Lookup lookup) { + this.lookup = lookup; + } + + @Override + public String elemtype(String s) { + return basicTypeHelper.elemtype(s); + } + + @Override + public String arrayOf(String s) { + return basicTypeHelper.arrayOf(s); + } + + @Override + public Iterator<String> parameterTypes(String s) { + return basicTypeHelper.parameterTypes(s); + } + + @Override + public String fromTag(TypeTag tag) { + return basicTypeHelper.fromTag(tag); + } + + @Override + public String returnType(String s) { + return basicTypeHelper.returnType(s); + } + + @Override + public String type(Class<?> aClass) { + if (aClass.isArray()) { + return aClass.getName().replaceAll("\\.", "/"); + } else { + return MinimalValueTypes_1_0.isValueType(aClass) ? + "Q" + aClass.getName().replaceAll("\\.", "/") + ";" : + "L" + aClass.getName().replaceAll("\\.", "/") + ";"; + } + } + + @Override + public Class<?> symbol(String desc) { + if (desc.endsWith("$Value;")) { + //value class - avoid Class.forName on the DVT + try { + int numDims = 0; + while (desc.startsWith("[")) { + desc = desc.substring(1); + numDims++; + } + Class<?> box = Class.forName(basicTypeHelper.symbol(desc) + .replaceAll("/", ".") + .replaceAll("\\$Value", ""), true, lookup.lookupClass().getClassLoader()); + ValueTypeHolder<?> vt = MinimalValueTypes_1_0.getValueFor(box); + return numDims == 0 ? + vt.valueClass() : vt.arrayValueClass(numDims); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } else { + try { + if (desc.startsWith("[")) { + return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); + } else { + return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); + } + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + } + + @Override + public TypeTag tag(String s) { + return basicTypeHelper.tag(s); + } + + @Override + public Class<?> symbolFrom(String s) { + return symbol(s); + } + + @Override + public String commonSupertype(String t1, String t2) { + return basicTypeHelper.commonSupertype(t1, t2); + } + + @Override + public String nullType() { + return basicTypeHelper.nullType(); + } + } + + static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, byte[]> { + BasicPoolHelper basicPoolHelper = new BasicPoolHelper(); + String clazz; + + IsolatedMethodPoolHelper(String clazz) { + this.clazz = clazz; + } + + String from(Class<?> c) { + String name; + boolean isValue = MinimalValueTypes_1_0.isValueType(c); + if (c == THIS_CLASS) { + //THIS_CLASS cannot be a DVT (by construction) - never mangle + name = clazz; + } else { + name = isValue ? + MinimalValueTypes_1_0.mangleValueClassName(c.getName()) : + c.getName(); + } + return name.replaceAll("\\.", "/"); + } + + @Override + public int putClass(Class<?> symbol) { + return basicPoolHelper.putClass(from(symbol)); + } + + @Override + public int putFieldRef(Class<?> owner, CharSequence name, String type) { + return basicPoolHelper.putFieldRef(from(owner), name, type); + } + + @Override + public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) { + return basicPoolHelper.putMethodRef(from(owner), name, type, isInterface); + } + + @Override + public int putUtf8(CharSequence s) { + return basicPoolHelper.putUtf8(s); + } + + @Override + public int putType(String s) { + return basicPoolHelper.putType(s); + } + + @Override + public int putValue(Object v) { + return basicPoolHelper.putValue(v); + } + + @Override + public int putMethodType(String s) { + return basicPoolHelper.putMethodType(s); + } + + @Override + public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) { + return basicPoolHelper.putHandle(refKind, from(owner), name, type); + } + + @Override + public int putInvokeDynamic(CharSequence invokedName, String invokedType, int bskKind, Class<?> bsmClass, CharSequence bsmName, String bsmType, Object... staticArgs) { + return basicPoolHelper.putInvokeDynamic(invokedName, invokedType, bskKind, from(bsmClass), bsmName, bsmType, staticArgs); + } + + @Override + public int size() { + return basicPoolHelper.size(); + } + + @Override + public byte[] entries() { + return basicPoolHelper.entries(); + } + + Object[] patches() { + return null; + } + } + + @Override + public byte[] build() { + return super.build(); + } + } + + static class IsolatedMethodPatchingPoolHelper extends IsolatedMethodPoolHelper { + + public IsolatedMethodPatchingPoolHelper(String clazz) { + super(clazz); + } + + Map<Object, CpPatch> cpPatches = new HashMap<>(); + int cph = 0; // for counting constant placeholders + + static class CpPatch { + + final int index; + final String placeholder; + final Object value; + + CpPatch(int index, String placeholder, Object value) { + this.index = index; + this.placeholder = placeholder; + this.value = value; + } + + public String toString() { + return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value; + } + } + + @Override + public int putValue(Object v) { + if (v instanceof String || v instanceof Integer || v instanceof Float || v instanceof Double || v instanceof Long) { + return basicPoolHelper.putValue(v); + } + assert (!v.getClass().isPrimitive()) : v; + return patchPoolEntry(v); // CP patching support + } + + int patchPoolEntry(Object v) { + String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; + if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(v) + ">>"; + if (cpPatches.containsKey(cpPlaceholder)) { + throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); + } + // insert placeholder in CP and remember the patch + int index = basicPoolHelper.putValue(cpPlaceholder); // TODO check if already in the constant pool + cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v)); + return index; + } + + @Override + Object[] patches() { + int size = size(); + Object[] res = new Object[size]; + for (CpPatch p : cpPatches.values()) { + if (p.index >= size) + throw new InternalError("bad cp patch index"); + res[p.index] = p.value; + } + return res; + } + + private static String debugString(Object arg) { + // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString + return arg.toString(); + } + } + + public static class MethodHandleCodeBuilder<T extends MethodHandleCodeBuilder<T>> extends TypedCodeBuilder<Class<?>, String, byte[], T> { + + BasicTypeHelper basicTypeHelper = new BasicTypeHelper(); + + public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) { + super(methodBuilder); + } + + TypeTag getTagType(String s) { + return basicTypeHelper.tag(s); + } + + public T ifcmp(String s, CondKind cond, CharSequence label) { + return super.ifcmp(getTagType(s), cond, label); + } + + public T return_(String s) { + return super.return_(getTagType(s)); + } + } +}
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java Tue Sep 26 16:13:10 2017 +0200 @@ -264,4 +264,9 @@ * given class loader. */ Stream<ModuleLayer> layers(ClassLoader loader); + + /** + * Loads a derived ValueType class + */ + Class<?> loadValueTypeClass(Module module, ClassLoader loader, String name); }
--- a/src/java.base/share/classes/module-info.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/module-info.java Tue Sep 26 16:13:10 2017 +0200 @@ -128,7 +128,6 @@ exports javax.security.auth.x500; exports javax.security.cert; - // additional qualified exports may be inserted at build time // see make/gensrc/GenModuleInfo.gmk @@ -317,6 +316,7 @@ exports sun.util.resources to jdk.localedata; + exports valhalla.shady to jdk.incubator.mvt; // the service types defined by the APIs in this module
--- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Tue Sep 26 16:13:10 2017 +0200 @@ -25,6 +25,8 @@ package sun.invoke.util; +import valhalla.shady.MinimalValueTypes_1_0; + import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; @@ -84,7 +86,7 @@ private static Class<?> parseSig(String str, int[] i, int end, ClassLoader loader) { if (i[0] == end) return null; char c = str.charAt(i[0]++); - if (c == 'L') { + if (c == 'L' || c == 'Q') { int begc = i[0], endc = str.indexOf(';', begc); if (endc < 0) return null; i[0] = endc+1; @@ -146,11 +148,15 @@ private static void unparseSig(Class<?> t, StringBuilder sb) { char c = Wrapper.forBasicType(t).basicTypeChar(); - if (c != 'L') { + if (MinimalValueTypes_1_0.isValueType(t)) { + //patch signatures for DVT + c = 'Q'; + } + if (c != 'L' && c != 'Q') { sb.append(c); } else { boolean lsemi = (!t.isArray()); - if (lsemi) sb.append('L'); + if (lsemi) sb.append(c); sb.append(t.getName().replace('.', '/')); if (lsemi) sb.append(';'); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/valhalla/shady/MinimalValueTypes_1_0.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package valhalla.shady; + +import jdk.experimental.bytecode.BasicClassBuilder; +import jdk.internal.misc.Unsafe; +import jdk.internal.misc.VM; +import sun.security.action.GetPropertyAction; + +import java.lang.annotation.Annotation; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.ProtectionDomain; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.SharedSecrets; +public class MinimalValueTypes_1_0 { + + public static final int V53_1 = 1 << 16 | 53; + public static final int ACC_VALUE = ACC_NATIVE; + public static final String OBJECT_CLASS_DESC = "java/lang/Object"; + public static final String VALUE_CLASS_DESC = "java/lang/__Value"; + + public static final String DERIVE_VALUE_TYPE_DESC = "Ljdk/incubator/mvt/ValueCapableClass;"; + public static final String DERIVE_VT_CLASSNAME_POSTFIX = "$Value"; + public static final int DERIVE_VT_CLASS_ACCESS = ACC_PUBLIC|ACC_SUPER|ACC_FINAL|ACC_VALUE|ACC_SYNTHETIC; + + public static final boolean DUMP_CLASS_FILES; + private static final boolean VALUE_TYPE_ENABLED; + private static final JavaLangAccess JLA; + + static { + // Use same property as in j.l.invoke.MethodHandleStatics + Properties props = GetPropertyAction.privilegedGetProperties(); + DUMP_CLASS_FILES = Boolean.parseBoolean( + props.getProperty("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES")); + + VALUE_TYPE_ENABLED = Boolean.parseBoolean( + props.getProperty("valhalla.enableValueType")); + + JLA = SharedSecrets.getJavaLangAccess(); + } + + private static final ConcurrentHashMap<Class<?>, ValueTypeHolder<?>> BOX_TO_VT + = new ConcurrentHashMap<>(); + + /** + * Returns the {@code ValueTypeHolder} representing the value type + * for the given value capable type. + * + * @throws UnsupportedOperationException if MVT is not enabled + * @throws IllegalArgumentException if the given class is not value capable class + */ + @SuppressWarnings("unchecked") + public static <T> ValueTypeHolder<T> getValueFor(Class<T> vcc) { + if (!MinimalValueTypes_1_0.isValueTypeEnabled()) { + throw new UnsupportedOperationException("MVT is not enabled"); + } + + if (!MinimalValueTypes_1_0.isValueCapable(vcc)) { + throw new IllegalArgumentException("Class " + vcc + " not a value capable class"); + } + + ValueTypeHolder<T> vt = (ValueTypeHolder<T>) BOX_TO_VT.get(vcc); + if (vt != null) { + return vt; + } + + Class<T> valueClass = (Class<T>) MinimalValueTypes_1_0.getValueTypeClass(vcc); + vt = new ValueTypeHolder<T>(vcc, valueClass); + ValueTypeHolder<T> old = (ValueTypeHolder<T>) BOX_TO_VT.putIfAbsent(vcc, vt); + if (old != null) { + vt = old; + } + return vt; + } + + /** + * Returns the {@code ValueTypeHolder} representing the value type + * for the given class is a derived value type; otherwise {@code null}. + */ + @SuppressWarnings("unchecked") + public static <T> ValueTypeHolder<T> findValueType(Class<T> c) { + if (MinimalValueTypes_1_0.isValueType(c)) { + return (ValueTypeHolder<T>) getValueFor(MinimalValueTypes_1_0.getValueCapableClass(c)); + } else { + return null; + } + } + + /** + * Returns true if MVT is enabled. + * + * jdk.incubator.mvt must be resolved in the boot layer. + */ + public static boolean isValueTypeEnabled() { + return VALUE_TYPE_ENABLED; + } + + public static boolean isValueType(Class<?> dvt) { + return (dvt.getModifiers() & ACC_VALUE) != 0; + } + + /** + * Returns true if the given class is a value-capable class, i.e. + * annotated with @ValueCapableClass. + */ + public static boolean isValueCapable(Class<?> vcc) { + if (!isValueTypeEnabled()) { + return false; + } + + return ValueClassHelper.hasValueCapableAnnotation(vcc); + } + + public static Class<?> getValueCapableClass(Class<?> dvt) { + if (!isValueType(dvt)) { + throw new IllegalArgumentException(dvt + " is not a derived value type"); + } + + Class<?> c = Class.forName(dvt.getModule(), getValueCapableClassName(dvt.getName())); + if (c == null || !isValueCapable(c)) { + throw new InternalError(dvt + " not bound to ValueType"); + } + return c; + } + + public static Class<?> getValueTypeClass(Class<?> vcc) { + if (!isValueCapable(vcc)) { + throw new IllegalArgumentException(vcc + " is not a value capable class"); + } + return loadValueTypeClass(vcc, getValueTypeClassName(vcc.getName())); + } + + public static Class<?> loadValueTypeClass(Class<?> vcc, String className) { + if (!isValueCapable(vcc)) { + throw new IllegalArgumentException(vcc.getName() + " is not a value capable class"); + } + return JLA.loadValueTypeClass(vcc.getModule(), vcc.getClassLoader(), className); + } + + /** + * This method is invoked by the VM. + * + * @param fds : name/sig pairs + * @param fmods : field modifiers + */ + public static String createDerivedValueType(String vccInternalClassName, + ClassLoader cl, + ProtectionDomain pd, + String[] fds, + int[] fmods) { + String vtInternalClassName = getValueTypeClassName(vccInternalClassName); + ValueTypeDesc valueTypeDesc = new ValueTypeDesc(vccInternalClassName, fds, fmods); + byte[] valueTypeBytes = createValueType(valueTypeDesc); + Class<?> vtClass = Unsafe.getUnsafe().defineClass(vtInternalClassName, valueTypeBytes, 0, valueTypeBytes.length, cl, pd); + return vtInternalClassName; + } + + public static byte[] createValueType(ValueTypeDesc valueTypeDesc) { + + String valueTypeClassName = getValueTypeClassName(valueTypeDesc); + + BasicClassBuilder builder = new BasicClassBuilder(mangleValueClassName(valueTypeClassName), 53, 1) + .withFlags(DERIVE_VT_CLASS_ACCESS) + .withSuperclass(mangleValueClassName(VALUE_CLASS_DESC)); + + ValueTypeDesc.Field[] fields = valueTypeDesc.getFields(); + for (ValueTypeDesc.Field field : fields) { + builder.withField(field.name, field.type, F -> F.withFlags(field.modifiers)); + } + + byte[] newBytes = builder.build(); + maybeDump(valueTypeClassName, newBytes); + return newBytes; + } + + /** debugging flag for saving generated class files */ + private static final File DUMP_CLASS_FILES_DIR; + + static { + if (DUMP_CLASS_FILES) { + try { + File dumpDir = new File("DUMP_CLASS_FILES"); + if (!dumpDir.exists()) { + dumpDir.mkdirs(); + } + DUMP_CLASS_FILES_DIR = dumpDir; + System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/..."); + } catch (Exception e) { + throw new InternalError(e); + } + } + else { + DUMP_CLASS_FILES_DIR = null; + } + } + + public static void maybeDump(final String className, final byte[] classFile) { + if (!DUMP_CLASS_FILES) { + return; + } + + java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<>() { + public Void run() { + String dumpName = className; + File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName + ".class"); + File root = dumpFile.getParentFile(); + if (!root.exists() && !root.mkdirs()) { + throw new IllegalStateException("Could not create dump file directory: " + root); + } + System.out.println("dump: " + dumpFile); + try (OutputStream os = new FileOutputStream(dumpFile); + BufferedOutputStream bos = new BufferedOutputStream(os)) { + bos.write(classFile); + } catch (IOException ex) { + throw new InternalError(ex); + } + return null; + } + }); + } + + private final native Class<?> getDerivedValueType(Class<?> ofClass); + + public static Class<?> getValueClass() { + return isValueTypeEnabled() ? ValueClassHelper.VALUE_CLASS : null; + } + + public static String getValueTypeClassName(ValueTypeDesc valueTypeDesc) { + return getValueTypeClassName(valueTypeDesc.getName()); + } + + public static String getValueTypeClassName(String vccName) { + return vccName + DERIVE_VT_CLASSNAME_POSTFIX; + } + + public static String getValueCapableClassName(String valName) { + return valName.substring(0, valName.length() - DERIVE_VT_CLASSNAME_POSTFIX.length()); + } + + public static String mangleValueClassName(String name) { + return ";Q" + name + ";"; + } + + /* + * This helper class should only be loaded when MVT is enabled. + * Otherwise, it will load __Value but if MVT is not enabled and + * that would fail verification. + */ + private static class ValueClassHelper { + static final Class<?> VALUE_CLASS = + (Class<?>)(Object)__Value.class; //hack around static type-system checks + + static volatile Class<? extends Annotation> annotationClass; + static volatile Class<?> valueTypeClass; + + static boolean hasValueCapableAnnotation(Class<?> c) { + if (!VM.isModuleSystemInited()) { + return false; + } + Class<? extends Annotation> annClass = annotationClass; + if (annClass == null) { + annotationClass = annClass = loadValueCapableAnnotation(); + } + return annClass != null && + c.getDeclaredAnnotation(annClass) != null; + } + + /* + * Returns jdk.incubator.mvt.ValueCapableClass annotation class + */ + static Class<? extends Annotation> loadValueCapableAnnotation() { + Module module = ModuleLayer.boot().findModule("jdk.incubator.mvt") + .orElse(null); + if (module != null) { + @SuppressWarnings("unchecked") + Class<? extends Annotation> c = (Class<? extends Annotation>) + Class.forName(module, "jdk.incubator.mvt.ValueCapableClass"); + return c; + } else { + return null; + } + } + + /* + * Returns jdk.incubator.mvt.ValueType class + */ + static Class<?> incubatorValueTypeClass() { + Class<?> c = valueTypeClass; + if (c == null) { + Module module = ModuleLayer.boot().findModule("jdk.incubator.mvt") + .orElse(null); + if (module != null) { + valueTypeClass = c = + Class.forName(module, "jdk.incubator.mvt.ValueType"); + } + } + return valueTypeClass; + } + } + + /* + * Returns jdk.incubator.mvt.ValueType class from jdk.incubator.mvt module + */ + static Class<?> getIncubatorValueTypeClass() { + if (!isValueTypeEnabled()) { + throw new UnsupportedOperationException("MVT is not enabled"); + } + return ValueClassHelper.incubatorValueTypeClass(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/valhalla/shady/ValueTypeDesc.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,82 @@ + +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package valhalla.shady; + +public class ValueTypeDesc { + + public static class Field { + public String name; + public String type; + public int modifiers; + + public Field(String name, String type, int modifiers) { + this.name = name; + this.type = type; + this.modifiers = modifiers; + } + + public String toString() { + return "Field " + name + ":" + type + + " mod=0x" + Integer.toHexString(modifiers); + } + } + + String name; + Field[] fields; + + public ValueTypeDesc(String name, String[] fds, int[] fmods) { + this.name = name; + this.fields = new Field[fds.length / 2]; + for (int i = 0; i < fds.length; i += 2) { + int f = i / 2; + this.fields[f] = new Field(fds[i], fds[i + 1], fmods[f]); + } + } + + public ValueTypeDesc(String name, Field[] fields) { + this.name = name; + this.fields = fields; + } + + public String getName() { return name; } + public Field[] getFields() { return fields; } + + public String getConstructorDesc() { + String desc = "("; + for (Field field : getFields()) { + desc += field.type; + } + return desc + ")V"; + } + + public String toString() { + String s = getName() + "\n"; + for (Field f : fields) { + s += "\t" + f.toString() + "\n"; + } + return s; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/valhalla/shady/ValueTypeHolder.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package valhalla.shady; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import jdk.experimental.bytecode.AnnotationsBuilder.Kind; +import jdk.experimental.bytecode.Flag; +import jdk.experimental.bytecode.Opcode; +import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder; +import jdk.experimental.value.MethodHandleBuilder.MethodHandleCodeBuilder; +import jdk.experimental.value.MethodHandleBuilder; +import jdk.experimental.bytecode.MacroCodeBuilder.CondKind; +import jdk.experimental.bytecode.TypeTag; +import sun.invoke.util.BytecodeDescriptor; +import sun.invoke.util.Wrapper; +import valhalla.shady.ValueTypeHolder.ValueHandleKind.ValueHandleKey; + +// Rough place holder just now... +public class ValueTypeHolder<T> { + + enum ValueHandleKind { + BOX("box"), + UNBOX("unbox"), + DEFAULT("defaultValueConstant"), + EQ("substitutabilityTest"), + HASH("substitutabilityHashCode"), + ARRAYLENGTH("arrayLength"), + WITHER("findWither", Lookup.class, String.class, Class.class) { + @Override + ValueHandleKey key(Object fieldName) { + return new ValueHandleKey(this, fieldName); + } + }, + UNREFLECT_WITHERS("unreflectWithers", Lookup.class, boolean.class, Field[].class) { + @Override + ValueHandleKey key(Object fields) { + return new ValueHandleKey(this, fields); + } + }, + NEWARRAY("newArray"), + VALOAD("arrayGetter"), + VASTORE("arraySetter"), + MULTINEWARRAY("newMultiArray", int.class) { + @Override + ValueHandleKey key(Object dims) { + return new ValueHandleKey(this, dims); + } + }, + IDENTITY("identity"), + GETTER("findGetter", Lookup.class, String.class, Class.class) { + @Override + ValueHandleKey key(Object fieldName) { + return new ValueHandleKey(this, fieldName); + } + }; + + final MethodHandle handle; + + ValueHandleKind(String handleName, Class<?>... argtypes) { + try { + this.handle = MethodHandles.lookup().findVirtual(ValueTypeHolder.class, handleName, MethodType.methodType(MethodHandle.class, argtypes)); + } catch (ReflectiveOperationException ex) { + throw new IllegalArgumentException("Cannot initialize value handle key for: " + handleName); + } + } + + String handleName() { + return MethodHandles.lookup().revealDirect(handle).getName(); + } + + MethodType handleType() { + return MethodHandles.lookup().revealDirect(handle).getMethodType(); + } + + ValueHandleKey key() { + return new ValueHandleKey(this, null); + } + + ValueHandleKey key(Object optArg) { + throw new IllegalStateException(); + } + + static class ValueHandleKey { + ValueHandleKind kind; + Object optArg; + + ValueHandleKey(ValueHandleKind kind, Object optArg) { + this.kind = kind; + this.optArg = optArg; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ValueHandleKey) { + ValueHandleKey that = (ValueHandleKey)obj; + return Objects.equals(kind, that.kind) && + Objects.equals(optArg, that.optArg); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(kind) * 31 + Objects.hashCode(optArg); + } + } + } + + private static final Lookup IMPL_LOOKUP; + + static { + try { + Field f = Lookup.class.getDeclaredField("IMPL_LOOKUP"); + f.setAccessible(true); + IMPL_LOOKUP = (Lookup)f.get(null); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + public static <T> Class<T> makeValueTypeClass(Lookup lookup, String name, String[] fieldNames, Class<?>... fieldTypes) throws ReflectiveOperationException { + if (fieldNames.length != fieldTypes.length) { + throw new IllegalArgumentException("Field names length and field types length must match"); + } + if (!(fieldNames.length > 0)) { + throw new IllegalArgumentException("Field length must be greater than zero"); + } + IsolatedMethodBuilder builder = new IsolatedMethodBuilder(name, lookup); + builder.withMajorVersion(53) + .withMinorVersion(0) + .withSuperclass(Object.class) + .withFlags(Flag.ACC_FINAL) + .withAnnotation(Kind.RUNTIME_VISIBLE, MinimalValueTypes_1_0.DERIVE_VALUE_TYPE_DESC); + //add fields + for (int i = 0 ; i < fieldNames.length ; i++) { + builder.withField(fieldNames[i], BytecodeDescriptor.unparse(fieldTypes[i]), F -> F.withFlags(Flag.ACC_FINAL)); + } + //add constructor + String ctype = BytecodeDescriptor.unparseMethod(void.class, fieldTypes); + builder.withMethod("<init>", ctype, M -> M.withCode(MethodHandleCodeBuilder::new, C -> { + C.aload_0().invokespecial(Object.class, "<init>", "()V", false); + int l = 1; + for (int i = 0 ; i < fieldNames.length ; i++) { + String fType = BytecodeDescriptor.unparse(fieldTypes[i]); + C.aload_0().load(l).putfield(builder.thisClass(), fieldNames[i], fType); + l += Wrapper.forBasicType(fieldTypes[i]).stackSlots(); + } + C.return_(); + })); + //add equals and hashCode + builder.withMethod("equals", "(Ljava/lang/Object;)Z", M -> + M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new, + C -> substitutabilityTestBuilder(true, builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C))); + builder.withMethod("hashCode", "()I", M -> + M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new, + C -> substitutabilityHashCodeBuilder(builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C))); + byte[] barr = builder.build(); + MinimalValueTypes_1_0.maybeDump(name, barr); + @SuppressWarnings("unchecked") + Class<T> vtClass = (Class<T>)lookup.defineClass(barr); + return vtClass; + } + + private Lookup boxLookup; + private Lookup valueLookup; + private Map<ValueHandleKind.ValueHandleKey, MethodHandle> handleMap = new ConcurrentHashMap<>(); + + ValueTypeHolder(Class<T> boxClass, Class<T> valueClass) { + this.boxLookup = IMPL_LOOKUP.in(boxClass); + this.valueLookup = IMPL_LOOKUP.in(valueClass); + } + + @SuppressWarnings("unchecked") + public Class<T> boxClass() { + return (Class<T>)boxLookup.lookupClass(); + } + + public Class<?> sourceClass() { + return boxClass(); + } + + public Class<?> valueClass() { + return valueLookup.lookupClass(); + } + + public Class<?> arrayValueClass() { + return arrayValueClass(1); + } + + public Class<?> arrayValueClass(int dims) { + String dimsStr = "[[[[[[[[[[[[[[[["; + if (dims < 1 || dims > 16) { + throw new IllegalArgumentException("cannot create array class for dimension > 16"); + } + String cn = dimsStr.substring(0, dims) + "Q" + valueClass().getName() + ";"; + return MinimalValueTypes_1_0.loadValueTypeClass(boxLookup.lookupClass(), cn); + } + + public String toString() { + return "ValueType boxClass=" + boxClass() + " valueClass=" + valueClass(); + } + + public MethodHandle defaultValueConstant() { + ValueHandleKey key = ValueHandleKind.DEFAULT.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(valueClass()), + C -> C.vdefault(valueClass()).vreturn()); + } + + public MethodHandle substitutabilityTest() { + ValueHandleKey key = ValueHandleKind.EQ.key(); + return getOrLoad(valueLookup, key, + () -> MethodType.methodType(boolean.class, valueClass(), valueClass()), + C -> substitutabilityTestBuilder(false, valueClass(), FieldInfo.stream(valueFields()), C)); + } + + private static <T extends MethodHandleCodeBuilder<T>> void substitutabilityTestBuilder(boolean needsInstanceCheck, Class<?> clazz, Stream<FieldInfo> fInfos, MethodHandleCodeBuilder<T> C) { + if (needsInstanceCheck) { + C.aload_1() + .instanceof_(clazz) + .emitCondJump(Opcode.IFEQ, CondKind.EQ, "fail") + .aload_1() + .checkcast(clazz) + .store(1); + } + fInfos.forEach(fInfo -> { + String fDesc = BytecodeDescriptor.unparse(fInfo.getType()); + if (fInfo.getType().isPrimitive()) { + //field is a primitive type - perform bytecode comparison + C.load(0).getfield(clazz, fInfo.getName(), fDesc); + C.load(1).getfield(clazz, fInfo.getName(), fDesc); + C.ifcmp(fDesc, CondKind.NE, "fail"); + } else if (MinimalValueTypes_1_0.isValueType(fInfo.getType())) { + //field is a value type - call subst handle recursively + C.load(0).getfield(clazz, fInfo.getName(), fDesc).dup().store(2); + valueHandleBuilder(fInfo.getType(), ValueHandleKind.EQ.key(), C) + .load(2) + .load(1).getfield(clazz, fInfo.getName(), fDesc) + .invokevirtual(MethodHandle.class, "invoke", + MethodType.methodType(boolean.class, fInfo.getType(), fInfo.getType()).toMethodDescriptorString(), false) + .const_(0).ifcmp(TypeTag.I, CondKind.EQ, "fail"); + } else { + //otherwise, field is a reference type, fallback to Objects.equals + C.load(0).getfield(clazz, fInfo.getName(), fDesc); + C.load(1).getfield(clazz, fInfo.getName(), fDesc); + C.invokestatic(Objects.class, "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false); + C.const_(0).ifcmp(TypeTag.I, CondKind.EQ, "fail"); + } + }); + C.const_(1); + C.ireturn(); + C.label("fail"); + C.const_(0); + C.ireturn(); + } + + public MethodHandle substitutabilityHashCode() { + ValueHandleKey key = ValueHandleKind.HASH.key(); + return getOrLoad(valueLookup, key, + () -> MethodType.methodType(int.class, valueClass()), + C -> substitutabilityHashCodeBuilder(valueClass(), FieldInfo.stream(valueFields()), C)); + } + + private static <T extends MethodHandleCodeBuilder<T>> void substitutabilityHashCodeBuilder(Class<?> clazz, Stream<FieldInfo> fInfos, MethodHandleCodeBuilder<T> C) { + C.withLocal("res", "I"); + C.const_(1).store("res"); + fInfos.forEach(fInfo -> { + String desc = BytecodeDescriptor.unparse(fInfo.getType()); + if (fInfo.getType().isPrimitive()) { + C.load(0).getfield(clazz, fInfo.getName(), desc); + C.invokestatic(Wrapper.asWrapperType(fInfo.getType()), "hashCode", "(" + desc + ")I", false); + } else if (MinimalValueTypes_1_0.isValueType(fInfo.getType())) { + //field is a value type - call subst handle recursively + C.load(0).getfield(clazz, fInfo.getName(), desc).dup().store(2); + valueHandleBuilder(fInfo.getType(), ValueHandleKind.HASH.key(), C) + .load(2) + .invokevirtual(MethodHandle.class, "invoke", + MethodType.methodType(int.class, fInfo.getType()).toMethodDescriptorString(), false); + } else { + C.load(0).getfield(clazz, fInfo.getName(), desc); + C.invokestatic(Objects.class, "hashCode", "(Ljava/lang/Object;)I", false); + } + C.load("res").const_(31).imul(); + C.iadd().store("res"); + }); + C.load("res").ireturn(); + } + + // ()Q + public MethodHandle findConstructor(Lookup lookup, MethodType type) throws NoSuchMethodException, IllegalAccessException { + return MethodHandles.filterReturnValue(lookup.findConstructor(boxClass(), type), unbox()); + } + + // (F1, ..., Fn)Q, fromDefault == true + // (Q, F1, ..., Fn)Q, fromDefault == false + public MethodHandle unreflectWithers(Lookup lookup, + boolean fromDefault, + Field... fields) throws NoSuchFieldException, IllegalAccessException { + // Allow access if the lookup class is the VCC or DVT and the lookup + // has private access + Class<?> lc = lookup.lookupClass(); + if (!lookup.hasPrivateAccess() || (valueClass() != lc && boxClass() != lc)) { + throw new IllegalAccessException(String.format("Class %s does not have vwithfield access to fields of %s", + lc.getName(), boxClass().getName())); + } + + // Ensure fields are value component fields declared by the VCC + for (Field f : fields) { + if (!isValueField(f) || f.getDeclaringClass() != sourceClass()) { + throw new IllegalArgumentException( + String.format("Field %s is not a value component field declared in value capable class %s", f.getName(), sourceClass().getName())); + } + } + + ValueHandleKey key = ValueHandleKind.UNREFLECT_WITHERS.key(List.of(fromDefault, + FieldInfo.stream(fields).collect(Collectors.toList()))); + return getOrLoad(valueLookup, key, + () -> { + MethodType mt = MethodType.methodType( + valueClass(), + Stream.of(fields).map(Field::getType).toArray(Class[]::new)); + + if (!fromDefault) { + mt = mt.insertParameterTypes(0, valueClass()); + } + return mt; + }, + C -> { + int l = 0; + if (fromDefault) { + C.vdefault(valueClass()); + } else { + C.load(0); + l = 1; + } + + for (Field f : fields) { + String fType = BytecodeDescriptor.unparse(f.getType()); + C.load(l).vwithfield(valueClass(), f.getName(), fType); + l += Wrapper.forBasicType(f.getType()).stackSlots(); + } + C.vreturn(); + }); + } + + // (Q, T)Q + public MethodHandle findWither(Lookup lookup, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { + // Allow access if the lookup class is the VCC or DVT and the lookup + // has private access + Class<?> lc = lookup.lookupClass(); + if (!lookup.hasPrivateAccess() || (valueClass() != lc && boxClass() != lc)) { + throw new IllegalAccessException(String.format("Class %s does not have vwithfield access to field %s.%s", + lc.getName(), boxClass().getName(), name)); + } + + // Check field exists on VCC + lookup.findGetter(boxClass(), name, type); + + ValueHandleKey key = ValueHandleKind.WITHER.key(new FieldInfo(name, type)); + return getOrLoad(valueLookup, key, + () -> MethodType.methodType(valueClass(), valueClass(), type), + C -> C.vload(0).load(1).vwithfield(valueClass(), name, BytecodeDescriptor.unparse(type)).vreturn()); + } + + public MethodHandle unbox() { + ValueHandleKey key = ValueHandleKind.UNBOX.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(valueClass(), boxClass()), + C -> C.load(0).vunbox(valueClass()).vreturn()); + } + + public MethodHandle box() { + ValueHandleKey key = ValueHandleKind.BOX.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(boxClass(), valueClass()), + C -> C.vload(0).vbox(boxClass()).areturn()); + } + + public MethodHandle newArray() { + ValueHandleKey key = ValueHandleKind.NEWARRAY.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(arrayValueClass(), int.class), + C -> C.load(0).anewarray(valueClass()).areturn()); + } + + public MethodHandle arrayGetter() { + ValueHandleKey key = ValueHandleKind.VALOAD.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(valueClass(), arrayValueClass(), int.class), + C -> C.load(0).load(1).vaload().vreturn()); + } + + public MethodHandle arraySetter() { + ValueHandleKey key = ValueHandleKind.VASTORE.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(void.class, arrayValueClass(), int.class, valueClass()), + C -> C.load(0).load(1).load(2).vastore().return_()); + } + + public MethodHandle newMultiArray(int dims) { + Class<?> arrayValueClass = arrayValueClass(dims); + ValueHandleKey key = ValueHandleKind.MULTINEWARRAY.key(dims); + return getOrLoad(boxLookup, key, + () -> { + Class<?>[] params = new Class<?>[dims]; + Arrays.fill(params, int.class); + return MethodType.methodType(arrayValueClass, params); + }, + C -> { + for (int i = 0 ; i < dims ; i++) { + C.load(i); + } + C.multianewarray(arrayValueClass, (byte)dims).areturn(); + }); + } + + public MethodHandle arrayLength() { + ValueHandleKey key = ValueHandleKind.ARRAYLENGTH.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(int.class, arrayValueClass()), + C -> C.load(0).arraylength().ireturn()); + } + + public MethodHandle identity() { + ValueHandleKey key = ValueHandleKind.IDENTITY.key(); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(valueClass(), valueClass()), + C -> C.vload(0).vreturn()); + } + + public MethodHandle findGetter(Lookup lookup, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { + //force access-check + lookup.findGetter(boxClass(), name, type); + + ValueHandleKey key = ValueHandleKind.GETTER.key(new FieldInfo(name, type)); + String fieldType = BytecodeDescriptor.unparse(type); + return getOrLoad(boxLookup, key, + () -> MethodType.methodType(type, valueClass()), + C -> C.vload(0).getfield(valueClass(), name, fieldType).return_(fieldType)); + } + + private static <T extends MethodHandleCodeBuilder<T>> T valueHandleBuilder(Class<?> dvt, ValueHandleKey key, MethodHandleCodeBuilder<T> C) { + MethodType mt = key.kind.handleType(); + if (mt.parameterCount() > 0) { + throw new AssertionError("Non-nilary handle builders not supported yet"); + } + Class<?> vtSupportClass = MinimalValueTypes_1_0.getIncubatorValueTypeClass(); + return C.vbox(MinimalValueTypes_1_0.getValueCapableClass(dvt)) + .invokevirtual(Object.class, "getClass", "()Ljava/lang/Class;", false) + .invokestatic(vtSupportClass, "forClass", + MethodType.methodType(vtSupportClass, Class.class).toMethodDescriptorString(), false) + .invokevirtual(vtSupportClass, key.kind.handleName(), key.kind.handleType().toMethodDescriptorString(), false); + } + + private MethodHandle getOrLoad(Lookup lookup, ValueHandleKey key, Supplier<MethodType> typeSupplier, Consumer<? super MethodHandleCodeBuilder<?>> codeBuilder) { + MethodHandle result = handleMap.get(key); + if (result == null) { + String handleClassName = sourceClass().getName() + "_" + key.kind.handleName(); + result = MethodHandleBuilder.loadCode(lookup, handleClassName, key.kind.handleName(), typeSupplier.get(), codeBuilder); + handleMap.put(key, result); + } + return result; + } + + boolean isValueField(Field f) { + return (f.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) == Modifier.FINAL; + } + + public Field[] valueFields() { + return Stream.of(sourceClass().getDeclaredFields()) + .filter(this::isValueField) + .toArray(Field[]::new); + } + + final static class FieldInfo { + + private final String name; + private final Class<?> type; + + FieldInfo(String name, Class<?> type) { + this.name = name; + this.type = type; + } + + String getName() { return name; } + Class<?> getType() { return type; } + + @Override + public boolean equals(Object o) { + if (o instanceof FieldInfo) { + FieldInfo that = (FieldInfo)o; + return Objects.equals(name, that.name) && + Objects.equals(type, that.type); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(name, type); + } + + private static Stream<FieldInfo> stream(Field[] fields) { + return Stream.of(fields).map(f -> new FieldInfo(f.getName(), f.getType())); + } + + private static Stream<FieldInfo> stream(String[] fieldNames, Class<?>[] fieldTypes) { + return IntStream.range(0, fieldNames.length) + .mapToObj(i -> new FieldInfo(fieldNames[i], fieldTypes[i])); + } + } +}
--- a/src/java.base/share/native/include/classfile_constants.h Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/native/include/classfile_constants.h Tue Sep 26 16:13:10 2017 +0200 @@ -124,6 +124,7 @@ JVM_SIGNATURE_BYTE = 'B', JVM_SIGNATURE_CHAR = 'C', JVM_SIGNATURE_CLASS = 'L', + JVM_SIGNATURE_VALUE_CLASS = 'Q', JVM_SIGNATURE_ENDCLASS = ';', JVM_SIGNATURE_ENUM = 'E', JVM_SIGNATURE_FLOAT = 'F',
--- a/src/java.base/share/native/libjava/System.c Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/native/libjava/System.c Tue Sep 26 16:13:10 2017 +0200 @@ -115,7 +115,7 @@ #endif #define JAVA_MAX_SUPPORTED_VERSION 53 -#define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 +#define JAVA_MAX_SUPPORTED_MINOR_VERSION 1 #ifdef JAVA_SPECIFICATION_VENDOR /* Third party may NOT overwrite this. */ #error "ERROR: No override of JAVA_SPECIFICATION_VENDOR is allowed"
--- a/src/java.base/share/native/libverify/check_code.c Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/native/libverify/check_code.c Tue Sep 26 16:13:10 2017 +0200 @@ -3817,7 +3817,8 @@ array_depth++; continue; /* only time we ever do the loop > 1 */ - case JVM_SIGNATURE_CLASS: { + case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_VALUE_CLASS: { char buffer_space[256]; char *buffer = buffer_space; char *finish = strchr(p, JVM_SIGNATURE_ENDCLASS); @@ -4199,6 +4200,7 @@ args_size += 1; break; case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_VALUE_CLASS: args_size += 1; while (*p != JVM_SIGNATURE_ENDCLASS) p++; break; @@ -4206,7 +4208,7 @@ args_size += 1; while ((*p == JVM_SIGNATURE_ARRAY)) p++; /* If an array of classes, skip over class name, too. */ - if (*p == JVM_SIGNATURE_CLASS) { + if (*p == JVM_SIGNATURE_CLASS || *p == JVM_SIGNATURE_VALUE_CLASS) { while (*p != JVM_SIGNATURE_ENDCLASS) p++; }
--- a/src/java.base/share/native/libverify/check_format.c Tue Sep 26 16:12:35 2017 +0200 +++ b/src/java.base/share/native/libverify/check_format.c Tue Sep 26 16:13:10 2017 +0200 @@ -191,7 +191,8 @@ case JVM_SIGNATURE_DOUBLE: return name + 1; - case JVM_SIGNATURE_CLASS: { + case JVM_SIGNATURE_CLASS: + case JVM_SIGNATURE_VALUE_CLASS: { /* Skip over the classname, if one is there. */ char *p = skip_over_fieldname(name + 1, JNI_TRUE, --length);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/ValueCapableClass.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.mvt; + +import java.lang.annotation.*; + +/** + * A class annotated {@code @ValueCapableClass} is a value-capable class. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface ValueCapableClass { +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/ValueType.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.mvt; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; + +import valhalla.shady.MinimalValueTypes_1_0; +import valhalla.shady.ValueTypeHolder; + +/** + * Value type reflection support. + */ +public class ValueType<T> { + /** + * Returns a {@code ValueType} object representing the specified + * value-capable class. + * + * @param vcc Value-capable class + * @param <T> Value type + * @return a {@code ValueType} object representing the specified + * value-capable class. + * + * @throws IllegalArgumentException if the specified {@code vcc} + * is not a value-capable class. + */ + public static <T> ValueType<T> forClass(Class<T> vcc) { + return new ValueType<>(MinimalValueTypes_1_0.getValueFor(vcc)); + } + + /** + * Returns {@code true} if the specified class is value-capable class. + * + * @param c a Class + * @return true if the specified class is a value-capable class. + */ + public static boolean classHasValueType(Class<?> c) { + if (!MinimalValueTypes_1_0.isValueCapable(c)) { + return false; + } + return MinimalValueTypes_1_0.getValueTypeClass(c) != null; + } + + private final ValueTypeHolder<T> valueTypeHolder; + private ValueType(ValueTypeHolder<T> vt) { + this.valueTypeHolder = vt; + } + + /** + * Returns the value-capable class of this value type. + * + * @return the value-capable class of this value type. + */ + public Class<T> boxClass() { + return valueTypeHolder.boxClass(); + } + + /** + * Returns the derived value type of this value type. + * + * @return the derived value type of this value type. + */ + public Class<?> valueClass() { + return valueTypeHolder.valueClass(); + } + + /** + * Returns an array class of this value type. + * + * @return an array class of this value type. + */ + public Class<?> arrayValueClass() { + return arrayValueClass(1); + } + + /** + * Returns an array class of the specified dimension for this value type. + * + * @return an array class of the specified dimension for this value type. + */ + public Class<?> arrayValueClass(int dims) { + String dimsStr = "[[[[[[[[[[[[[[[["; + if (dims < 1 || dims > 16) { + throw new IllegalArgumentException("cannot create array class for dimension > 16"); + } + String cn = dimsStr.substring(0, dims) + "Q" + valueClass().getName() + ";"; + return MinimalValueTypes_1_0.loadValueTypeClass(boxClass(), cn); + } + + /** + * Returns a string representation of this value type. + * + * @return a string representation of this value type. + */ + public String toString() { + return valueTypeHolder.toString(); + } + + public static <T> ValueType<T> make(Lookup lookup, + String name, + String[] fieldNames, + Class<?>... fieldTypes) + throws ReflectiveOperationException + { + if (fieldNames.length != fieldTypes.length) { + throw new IllegalArgumentException("Field names length and field types length must match"); + } + if (!(fieldNames.length > 0)) { + throw new IllegalArgumentException("Field length must be greater than zero"); + } + Class<T> vtClass = ValueTypeHolder.makeValueTypeClass(lookup, name, fieldNames, fieldTypes); + return forClass(vtClass); + } + + // ()Q + public MethodHandle findConstructor(Lookup lookup, MethodType type) + throws NoSuchMethodException, IllegalAccessException { + return valueTypeHolder.findConstructor(lookup, type); + } + + // (F1, ..., Fn)Q, fromDefault == true + // (Q, F1, ..., Fn)Q, fromDefault == false + public MethodHandle unreflectWithers(Lookup lookup, + boolean fromDefault, + Field... fields) + throws NoSuchFieldException, IllegalAccessException { + return valueTypeHolder.unreflectWithers(lookup, fromDefault, fields); + } + + // (Q, T)Q + public MethodHandle findWither(Lookup lookup, String name, Class<?> type) + throws NoSuchFieldException, IllegalAccessException { + return valueTypeHolder.findWither(lookup, name, type); + } + + public MethodHandle unbox() { + return valueTypeHolder.unbox(); + } + + public MethodHandle box() { + return valueTypeHolder.box(); + } + + public MethodHandle newArray() { + return valueTypeHolder.newArray(); + } + + public MethodHandle arrayGetter() { + return valueTypeHolder.arrayGetter(); + } + + public MethodHandle arraySetter() { + return valueTypeHolder.arraySetter(); + } + + public MethodHandle newMultiArray(int dims) { + return valueTypeHolder.newMultiArray(dims); + } + + public MethodHandle arrayLength() { + return valueTypeHolder.arrayLength(); + } + + public MethodHandle identity() { + return valueTypeHolder.identity(); + } + + public MethodHandle findGetter(Lookup lookup, String name, Class<?> type) + throws NoSuchFieldException, IllegalAccessException { + return valueTypeHolder.findGetter(lookup, name, type); + } + + public MethodHandle substitutabilityTest() { + return valueTypeHolder.substitutabilityTest(); + } + + public MethodHandle substitutabilityHashCode() { + return valueTypeHolder.substitutabilityHashCode(); + } + + public MethodHandle defaultValueConstant() { + return valueTypeHolder.defaultValueConstant(); + } + + public Field[] valueFields() { + return valueTypeHolder.valueFields(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/package-info.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * {@Incubating} + * + * <p> Minimal Value Type Incubating API. + * + * @since 10 + */ +package jdk.incubator.mvt;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.incubator.mvt/share/classes/module-info.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Defines the experimental API for Minimal Value Type. + * + * {@Incubating} + * + * @moduleGraph + * @since 10 + */ +module jdk.incubator.mvt { + exports jdk.incubator.mvt; +}
--- a/test/jdk/TEST.groups Tue Sep 26 16:12:35 2017 +0200 +++ b/test/jdk/TEST.groups Tue Sep 26 16:13:10 2017 +0200 @@ -76,7 +76,12 @@ jdk/internal/jimage \ jdk/internal/math \ jdk/modules \ - vm + vm \ + valhalla/mvt + +# valhalla MVT tests +jdk_valhalla_mvt = \ + valhalla/mvt # All of the java.util package jdk_util = \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/ConstructorTest.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.incubator.mvt.ValueType; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; +import static org.testng.AssertJUnit.assertTrue; + +/* + * @test + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enablePoolPatches=true ConstructorTest + */ + +@Test +public class ConstructorTest { + + static final ValueType<?> VT_Interval = ValueType.forClass(Interval.class); + + static final MethodHandle VT_constructor; + static { + try { + MethodHandle mh = VT_Interval.findConstructor(MethodHandles.lookup(), + MethodType.methodType(void.class, int.class, int.class)); + VT_constructor = MethodHandles.filterReturnValue(mh, VT_Interval.box()); + } catch (Exception e) { + throw new InternalError(e); + } + } + + public void testConstructorWithValidArguments() throws Throwable { + Interval i = (Interval) VT_constructor.invoke(1, 2); + assertEquals(i.l, 1); + assertEquals(i.u, 2); + } + + public void testConstructorWithIllegalArguments() throws Throwable { + try { + Interval i = (Interval) VT_constructor.invoke(3, 1); + fail(); + } catch (Throwable t) { + assertTrue(IllegalArgumentException.class.isInstance(t)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/Interval.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,31 @@ +import jdk.incubator.mvt.ValueCapableClass; + +@ValueCapableClass +public final class Interval { + public final int l; + public final int u; + + public Interval(int l, int u) { + if (l > u) throw new IllegalArgumentException(); + this.l = l; + this.u = u; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Interval that = (Interval) o; + + if (l != that.l) return false; + return u == that.u; + } + + @Override + public int hashCode() { + int result = l; + result = 31 * result + u; + return result; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/MVTAccessCheck.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @run testng/othervm -XX:+EnableMVT -XX:+ValueArrayFlatten MVTAccessCheck + * @run testng/othervm -XX:+EnableMVT -XX:-ValueArrayFlatten MVTAccessCheck + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enableValueLambdaForms=true MVTAccessCheck + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enableValueLambdaForms=true -Dvalhalla.enablePoolPatches=true MVTAccessCheck + */ + +import jdk.incubator.mvt.ValueType; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +@Test +public class MVTAccessCheck { + + static final ValueType<?> VT_Point = ValueType.forClass(Point.class); + static final ValueType<?> VT_PrivatePoint = ValueType.forClass(PrivatePoint.class); + + static final String[] FIELD_NAMES = {"x", "y", "z"}; + static final Class<?>[] FIELD_TYPES = {int.class, short.class, short.class}; + + public void testGetter() throws Throwable { + for (int i = 0; i < FIELD_NAMES.length; i++) { + final int offset = i; + assertThrow(() -> VT_PrivatePoint.findGetter( + MethodHandles.lookup(), FIELD_NAMES[offset], FIELD_TYPES[offset]), IllegalAccessException.class); + } + } + + public void testWither() throws Throwable { + testWither(VT_Point); + testWither(VT_PrivatePoint); + } + + void testWither(ValueType<?> vt) throws Throwable { + for (int i = 0; i < FIELD_NAMES.length; i++) { + final int offset = i; + assertThrow(() -> vt.findWither( + MethodHandles.lookup(), FIELD_NAMES[offset], FIELD_TYPES[offset]), IllegalAccessException.class); + } + } + + public void testSubstitutability() throws Throwable { + PrivatePoint[] pts = {new PrivatePoint(1, (short) 6, (short) 3), new PrivatePoint(1, (short) 2, (short) 3)}; + + MethodHandle substTest = VT_PrivatePoint.substitutabilityTest(); + for (PrivatePoint p1 : pts) { + for (PrivatePoint p2 : pts) { + boolean b1 = (boolean) substTest.invoke(p1, p2); + boolean b2 = p1.equals(p2); + assertEquals(b1, b2); + } + } + + MethodHandle hash = VT_PrivatePoint.substitutabilityHashCode(); + for (PrivatePoint p1 : pts) { + for (PrivatePoint p2 : pts) { + boolean vHashEq = (int) hash.invoke(p1) == (int) hash.invoke(p2); + boolean rHashEq = p1.hashCode() == p2.hashCode(); + assertEquals(vHashEq, rHashEq); + } + } + } + + interface MHAction { + void run() throws Throwable; + } + + static void assertThrow(MHAction action, Class<? extends Throwable> ex) { + try { + action.run(); + Assert.fail(); + } catch (Throwable t) { + assertTrue(ex.isAssignableFrom(t.getClass())); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/MVTReflectionTest.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary basic test for reflection on MVT + * @run testng/othervm -XX:+EnableMVT MVTReflectionTest + */ + +import jdk.incubator.mvt.ValueType; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class MVTReflectionTest { + private static Class<?> VCC = Point.class; + private static ValueType<?> VT = ValueType.forClass(VCC); + private static Class<?> DVT = VT.valueClass(); + + public void testValueCapableClass() throws Exception { + Class<?> pointClass = Class.forName("Point"); + assertEquals(pointClass, VCC); + + // test getSuperClass + assertEquals(pointClass.getSuperclass(), Object.class); + + // test getFields and getDeclaredFields + assertTrue(pointClass.getFields().length == 3); + assertTrue(pointClass.getDeclaredFields().length == 3); + } + + public void testDerivedValueType() throws Exception { + // test Class.forName + try { + Class<?> c = Class.forName(DVT.getName()); + throw new RuntimeException("should fail to load " + c); + } catch (ClassNotFoundException e) {} + + Module m = Point.class.getClassLoader().getUnnamedModule(); + Class<?> c = Class.forName(m, DVT.getName()); + assertEquals(c, null); + + // test getSuperClass + assertEquals(DVT.getSuperclass(), __Value.class); + + // test getFields and getDeclaredFields + try { + DVT.getFields(); + throw new RuntimeException("should fail to getFields on " + DVT); + } catch (UnsupportedOperationException e) {} + + try { + DVT.newInstance(); + throw new RuntimeException("should fail to newInstance on " + DVT); + } catch (UnsupportedOperationException e) {} + } + + public void testValueTypeArray() throws Exception { + Point[] points = new Point[0]; + + Class<?> c1 = Class.forName("[LPoint;"); + Class<?> c2 = VT.arrayValueClass(); + assertEquals(c1, points.getClass()); + + String name = "[Q" + DVT.getName() + ";"; + assertEquals(name, c2.getName()); + + // cannot load an array DVT class + try { + Class.forName(name); + throw new RuntimeException("should fail to load " + name); + } catch (ClassNotFoundException e) {} + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/MVTTest.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules java.base/valhalla.shady + * jdk.incubator.mvt + * @run testng/othervm -XX:+EnableMVT -XX:+ValueArrayFlatten MVTTest + * @run testng/othervm -XX:+EnableMVT -XX:-ValueArrayFlatten MVTTest + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enableValueLambdaForms=true MVTTest + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enableValueLambdaForms=true -Dvalhalla.enablePoolPatches=true MVTTest + */ + +import jdk.incubator.mvt.ValueType; +import org.testng.annotations.Test; +import valhalla.shady.MinimalValueTypes_1_0; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; + +import static java.lang.invoke.MethodType.methodType; +import static org.testng.Assert.assertEquals; + +@Test +public class MVTTest { + static final Class<?> DVT; + + static final ValueType<?> VT = ValueType.forClass(Point.class); + + static final Class<?>[] FIELD_TYPES; + + static final String[] FIELD_NAMES; + + static String TEMPLATE = "Point[x=#x, y=#y, z=#z]"; + + static final Object[] FIELD_VALUES = {42, (short) 43, (short) 44}; + + static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + static final MethodHandle PRINT_POINT; + + static { + DVT = MinimalValueTypes_1_0.getValueTypeClass(Point.class); + + Field[] fs = Point.class.getFields(); + + FIELD_TYPES = new Class<?>[fs.length]; + FIELD_NAMES = new String[fs.length]; + + for (int i = 0; i < fs.length; i++) { + FIELD_TYPES[i] = fs[i].getType(); + FIELD_NAMES[i] = fs[i].getName(); + } + + try { + PRINT_POINT = LOOKUP.findStatic(MVTTest.class, "print", methodType(String.class, Point.class)) + .asType(methodType(String.class, DVT)); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void testDefaultValue() throws Throwable { + for (int i = 0; i < FIELD_NAMES.length; i++) { + MethodHandle getter = MethodHandles.collectArguments( + VT.findGetter(LOOKUP, FIELD_NAMES[i], FIELD_TYPES[i]), + 0, + VT.defaultValueConstant()); + + assertEquals((int) getter.invoke(), 0); + } + } + + public void testWither() throws Throwable { + testWither(Point.lookup()); + testWither(MethodHandles.privateLookupIn(VT.boxClass(), LOOKUP)); + testWither(MethodHandles.privateLookupIn(VT.valueClass(), LOOKUP)); + } + + void testWither(MethodHandles.Lookup l ) throws Throwable { + for (int i = 0; i < FIELD_NAMES.length; i++) { + MethodHandle wither = MethodHandles.collectArguments( + VT.findWither(l, FIELD_NAMES[i], FIELD_TYPES[i]), 0, VT.defaultValueConstant()); + String expected = TEMPLATE.replace("#" + FIELD_NAMES[i], String.valueOf(FIELD_VALUES[i])) + .replaceAll("#[xyz]", "0"); + + assertEquals(printReturn(wither).invoke(FIELD_VALUES[i]), expected); + } + } + + public void testSubstitutability() throws Throwable { + Point[] pts = {new Point(1, (short) 6, (short) 3), new Point(1, (short) 2, (short) 3)}; + + MethodHandle substTest = VT.substitutabilityTest(); + for (Point p1 : pts) { + for (Point p2 : pts) { + assertEquals((boolean) substTest.invoke(p1, p2), p1.equals(p2)); + } + } + + MethodHandle hash = VT.substitutabilityHashCode(); + for (Point p1 : pts) { + for (Point p2 : pts) { + boolean vHashEq = (int) hash.invoke(p1) == (int) hash.invoke(p2); + boolean rHashEq = p1.hashCode() == p2.hashCode(); + assertEquals(vHashEq, rHashEq); + } + } + } + + public void testIdentity() throws Throwable { + String actual = (String) printReturn(MethodHandles.identity(VT.valueClass())) + .invoke(new Point(1, (short) 2, (short) 3)); + assertEquals(actual, "Point[x=1, y=2, z=3]"); + } + + public void testZero() throws Throwable { + String actual = (String) printReturn(MethodHandles.zero(VT.valueClass())) + .invoke(); + assertEquals(actual, "Point[x=0, y=0, z=0]"); + } + + public void testEmpty() throws Throwable { + String actual = (String) printReturn(MethodHandles.empty(methodType(VT.valueClass(), int.class, String.class))) + .invoke(1, ""); + assertEquals(actual, "Point[x=0, y=0, z=0]"); + } + + public void testArray1D() throws Throwable { + //test monodimensional array + Object arr = MethodHandles.arrayConstructor(VT.arrayValueClass()).invoke(10); + for (int i = 0; i < 10; i++) { + Point p = new Point(i, (short) 9, (short) 9); + MethodHandles.arrayElementSetter(VT.arrayValueClass()).invoke(arr, i, p); + } + for (int i = 0; i < 10; i++) { + String actual = (String) printReturn(MethodHandles.arrayElementGetter(VT.arrayValueClass())) + .invoke(arr, i); + String expected = TEMPLATE.replace("#x", String.valueOf(i)) + .replaceAll("#[yz]", "9"); + assertEquals(actual, expected); + } + } + + public void testArray10D() throws Throwable { + //test multidimensional array + Object[] arr2 = (Object[]) MethodHandles.arrayConstructor(VT.arrayValueClass(2)).invoke(10); + for (int i = 0; i < 10; i++) { + Object innerArr = MethodHandles.arrayConstructor(VT.arrayValueClass()).invoke(10); + MethodHandles.arrayElementSetter(VT.arrayValueClass(2)).invoke(arr2, i, innerArr); + for (int j = 0; i < 10; i++) { + Point p = new Point(i, (short) j, (short) 9); + MethodHandles.arrayElementSetter(VT.arrayValueClass()).invoke(innerArr, i, p); + } + } + for (int i = 0; i < 10; i++) { + Object innerArr = MethodHandles.arrayElementGetter(VT.arrayValueClass(2)).invoke(arr2, i); + for (int j = 0; i < 10; i++) { + String actual = (String) printReturn(MethodHandles.arrayElementGetter(VT.arrayValueClass())) + .invoke(innerArr, i); + String expected = TEMPLATE.replace("#x", String.valueOf(i)) + .replace("#y", String.valueOf(j)) + .replace("#z", "9"); + assertEquals(actual, expected); + } + } + } + + public void testMultiArray() throws Throwable { + Object[] arr43 = (Object[]) VT.newMultiArray(2).invoke(4, 3); + for (int i = 0; i < 4; i++) { + Object innerArr = arr43[i]; + for (int j = 0; i < 3; i++) { + Point p = new Point(i, (short) j, (short) 9); + MethodHandles.arrayElementSetter(VT.arrayValueClass()).invoke(innerArr, i, p); + } + } + for (int i = 0; i < 4; i++) { + Object innerArr = MethodHandles.arrayElementGetter(VT.arrayValueClass(2)).invoke(arr43, i); + for (int j = 0; i < 3; i++) { + String actual = (String) printReturn(MethodHandles.arrayElementGetter(VT.arrayValueClass())) + .invoke(innerArr, i); + String expected = TEMPLATE.replace("#x", String.valueOf(i)) + .replace("#y", String.valueOf(j)) + .replace("#z", "9"); + assertEquals(actual, expected); + } + } + } + + public void testLoop() throws Throwable { + Object arr = MethodHandles.arrayConstructor(VT.arrayValueClass()).invoke(10); + for (int i = 0; i < 10; i++) { + Point p = new Point(i, (short) 9, (short) 9); + MethodHandles.arrayElementSetter(VT.arrayValueClass()).invoke(arr, i, p); + } + + /* + iters -> (Point[] )int + + init -> (Point[] )int + + sum -> (int, int, int, int)int + a -> (int, Point, Point, Point)int + b -> (int, Point)int + c -> (int, Point[], int)int + body -> (int, int, Point[])int + */ + + MethodHandle iters = MethodHandles.arrayLength(VT.arrayValueClass()); + + MethodHandle init = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), + 0, + VT.arrayValueClass()); + + MethodHandle sum = LOOKUP.findStatic(MVTTest.class, + "sum", + methodType(int.class, int.class, int.class, short.class, short.class)); + + MethodHandle a = MethodHandles.filterArguments(sum, 1, + VT.findGetter(LOOKUP, FIELD_NAMES[0], FIELD_TYPES[0]), + VT.findGetter(LOOKUP, FIELD_NAMES[1], FIELD_TYPES[1]), + VT.findGetter(LOOKUP, FIELD_NAMES[2], FIELD_TYPES[2])); + + MethodHandle b = MethodHandles.permuteArguments(a, + methodType(int.class, int.class, VT.valueClass()), + 0, 1, 1, 1); + + MethodHandle c = MethodHandles.collectArguments(b, + 1, + MethodHandles.arrayElementGetter(VT.arrayValueClass())); + + MethodHandle body = MethodHandles.permuteArguments(c, + methodType(int.class, int.class, int.class, VT.arrayValueClass()), + 0, 2, 1); + + MethodHandle loop = MethodHandles.countedLoop(iters, init, body); + int actual = (int) loop.invoke(arr); + int expected = 9 * 10 * 2 + 10 * (0 + 9) / 2; + assertEquals(actual, expected); + } + + static int sum(int v, int x, short y, short z) { + return v + x + y + z; + } + + static MethodHandle printReturn(MethodHandle mh) { + return MethodHandles.filterReturnValue(mh, PRINT_POINT); + } + + static String print(Point p) { + return String.format("Point[x=%d, y=%d, z=%d]", p.x, p.y, p.z); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/MethodHandlesTest.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import jdk.incubator.mvt.ValueType; +import jdk.incubator.mvt.ValueCapableClass; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import static java.lang.invoke.MethodType.methodType; +import static org.testng.Assert.*; + +/* + * @test + * @run testng/othervm -XX:+EnableMVT -Dvalhalla.enablePoolPatches=true MethodHandlesTest + */ + +@Test +public class MethodHandlesTest { + @ValueCapableClass + final class ValueCapable {} + + static final ValueType<?> VT = ValueType.forClass(Point.class); + static final Class<?> VCC = Point.class; + static final Class<?> DVT = VT.valueClass(); + + static final MethodHandle ID_DVT_MH = MethodHandles.identity(DVT); // (DVT)DVT + static final MethodHandle ID_VCC_MH = ID_DVT_MH.asType(methodType(VCC, VCC)); // (VCC)VCC + + static final Object ARG = new Point(0, (short)1, (short)2); + + @Test + void testInsertArgumentDVT() throws Throwable { + { + MethodHandle mh = MethodHandles.insertArguments(ID_DVT_MH, 0, ARG); + assertEquals(mh.invokeWithArguments(), ARG); + } + + assertThrows(ClassCastException.class, + () -> MethodHandles.insertArguments(ID_DVT_MH, 0, new ValueCapable())); + + assertThrows(NullPointerException.class, + () -> MethodHandles.insertArguments(ID_DVT_MH, 0, new Object[] { null })); + } + + @Test + void testInsertArgumentVCC() throws Throwable { + assertEquals(MethodHandles.insertArguments(ID_VCC_MH, 0, ARG).invokeWithArguments(), ARG); + + assertThrows(ClassCastException.class, + () -> MethodHandles.insertArguments(ID_VCC_MH, 0, new ValueCapable())); + + { + MethodHandle mh = MethodHandles.insertArguments(ID_VCC_MH, 0, new Object[]{null}); + assertThrows(NullPointerException.class, () -> mh.invokeWithArguments()); + } + } + + @Test + void testConstantDVT() throws Throwable { + assertEquals(MethodHandles.constant(DVT, ARG).type(), methodType(DVT)); + + assertEquals(MethodHandles.constant(DVT, ARG).invokeWithArguments(), ARG); + + assertThrows(ClassCastException.class, + () -> MethodHandles.constant(DVT, new Object())); + + assertThrows(NullPointerException.class, + () -> MethodHandles.constant(DVT, null)); + } + + @Test + void testBindToDVT() { + assertThrows(IllegalArgumentException.class, + () -> ID_DVT_MH.bindTo(ARG)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/valhalla/mvt/Point.java Tue Sep 26 16:13:10 2017 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shore