changeset 47252:e99c25c2f4a6 mvt

import changes from mvt/jdk
author dsimms
date Tue, 26 Sep 2017 16:13:10 +0200
parents da9ee38a81e8
children 4bded5db79fd
files src/java.base/share/classes/java/lang/Class.java src/java.base/share/classes/java/lang/System.java src/java.base/share/classes/java/lang/__Value.java src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java src/java.base/share/classes/java/lang/invoke/LambdaForm.java src/java.base/share/classes/java/lang/invoke/LambdaFormBuilder.java src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/java/lang/invoke/MethodType.java src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java src/java.base/share/classes/jdk/experimental/bytecode/AbstractBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/AnnotationsBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/AttributeBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/BasicClassBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/ClassBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/CodeBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/DeclBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/FieldBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/Flag.java src/java.base/share/classes/jdk/experimental/bytecode/GrowableByteBuffer.java src/java.base/share/classes/jdk/experimental/bytecode/MacroCodeBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/MemberBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/MethodBuilder.java src/java.base/share/classes/jdk/experimental/bytecode/Opcode.java src/java.base/share/classes/jdk/experimental/bytecode/PoolHelper.java src/java.base/share/classes/jdk/experimental/bytecode/PoolTag.java src/java.base/share/classes/jdk/experimental/bytecode/Type.java src/java.base/share/classes/jdk/experimental/bytecode/TypeHelper.java src/java.base/share/classes/jdk/experimental/bytecode/TypeTag.java src/java.base/share/classes/jdk/experimental/bytecode/TypedCodeBuilder.java src/java.base/share/classes/jdk/experimental/value/MethodHandleBuilder.java src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java src/java.base/share/classes/module-info.java src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java src/java.base/share/classes/valhalla/shady/MinimalValueTypes_1_0.java src/java.base/share/classes/valhalla/shady/ValueTypeDesc.java src/java.base/share/classes/valhalla/shady/ValueTypeHolder.java src/java.base/share/native/include/classfile_constants.h src/java.base/share/native/libjava/System.c src/java.base/share/native/libverify/check_code.c src/java.base/share/native/libverify/check_format.c src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/ValueCapableClass.java src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/ValueType.java src/jdk.incubator.mvt/share/classes/jdk/incubator/mvt/package-info.java src/jdk.incubator.mvt/share/classes/module-info.java test/jdk/TEST.groups test/jdk/valhalla/mvt/ConstructorTest.java test/jdk/valhalla/mvt/Interval.java test/jdk/valhalla/mvt/MVTAccessCheck.java test/jdk/valhalla/mvt/MVTReflectionTest.java test/jdk/valhalla/mvt/MVTTest.java test/jdk/valhalla/mvt/MethodHandlesTest.java test/jdk/valhalla/mvt/Point.java test/jdk/valhalla/mvt/PrivatePoint.java test/jdk/valhalla/mvt/TEST.properties test/jdk/valhalla/mvt/TestValueInValue.java test/jdk/valhalla/mvt/UnreflectWithersTest.java test/jdk/valhalla/mvt/WithFieldTest.java test/jdk/valhalla/mvt/modules/Driver.java test/jdk/valhalla/mvt/modules/m/module-info.java test/jdk/valhalla/mvt/modules/m/p/Main.java test/jdk/valhalla/mvt/modules/m/p/internal/Point.java test/jdk/valhalla/mvt/point/Point.java test/jdk/valhalla/mvt/point/TestPoint.java test/jdk/valhalla/mvt/vector/Long2.java test/jdk/valhalla/mvt/vector/Utils.java test/jdk/valhalla/mvt/vector/VectorLibrary.java test/jdk/valhalla/mvt/vector/VectorTest.java test/jdk/valhalla/mvt/vector/VectorUtils.java
diffstat 73 files changed, 10522 insertions(+), 44 deletions(-) [+]
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