changeset 14080:c7c97756db18

MVT 1.0 prototype
author mcimadamore
date Thu, 01 Dec 2016 15:06:38 +0100
parents 1915d0702dbf
children 6d7136544168
files src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/MethodHandles.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/experimental/value/ValueType.java src/java.base/share/classes/jvm/internal/value/DeriveValueType.java src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java src/java.base/share/classes/valhalla/model3/classfile/ClassReader.java src/java.base/share/classes/valhalla/model3/classfile/Code_attribute.java src/java.base/share/classes/valhalla/model3/classfile/Method.java src/java.base/share/classes/valhalla/shady/MinimalValueTypes_1_0.java src/java.base/share/classes/valhalla/shady/ValueTypeDesc.java test/valhalla/mvt/MVTTest.java test/valhalla/mvt/Point.java
diffstat 33 files changed, 6665 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Dec 01 15:06:38 2016 +0100
@@ -25,6 +25,7 @@
 
 package java.lang.invoke;
 
+import java.lang.reflect.Array;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Arrays;
@@ -33,6 +34,7 @@
 import java.util.List;
 import java.util.function.Function;
 
+import jdk.experimental.value.ValueType;
 import jdk.internal.vm.annotation.Stable;
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
@@ -40,6 +42,8 @@
 import sun.invoke.util.Wrapper;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
+import valhalla.shady.MinimalValueTypes_1_0;
+
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
@@ -368,6 +372,8 @@
         assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict));  // caller responsibility
         if (dst == void.class)
             return dst;
+        ValueType<?> srcVT = getValueFor(src);
+        ValueType<?> dstVT = getValueFor(dst);
         MethodHandle fn;
         if (src.isPrimitive()) {
             if (src == void.class) {
@@ -379,8 +385,8 @@
                 // Examples: int->Integer, boolean->Object, float->Number
                 Wrapper wsrc = Wrapper.forPrimitiveType(src);
                 fn = ValueConversions.boxExact(wsrc);
-                assert(fn.type().parameterType(0) == wsrc.primitiveType());
-                assert(fn.type().returnType() == wsrc.wrapperType());
+                assert (fn.type().parameterType(0) == wsrc.primitiveType());
+                assert (fn.type().returnType() == wsrc.wrapperType());
                 if (!VerifyType.isNullConversion(wsrc.wrapperType(), dst, strict)) {
                     // Corner case, such as int->Long, which will probably fail.
                     MethodType mt = MethodType.methodType(dst, src);
@@ -390,6 +396,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()) {
@@ -403,6 +416,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
@@ -414,6 +434,19 @@
         return fn;
     }
 
+    @SuppressWarnings("unchecked")
+    static <Z> ValueType<Z> getValueFor(Class<Z> c) {
+        try {
+            if (MinimalValueTypes_1_0.isValueType(c)) {
+                return (ValueType<Z>)ValueType.forClass(MinimalValueTypes_1_0.getValueCapableClass(c));
+            } else {
+                return null;
+            }
+        } catch (Throwable ex) {
+            return null;
+        }
+    }
+
     static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
         MethodType type = target.type();
         int last = type.parameterCount() - 1;
@@ -1871,7 +1904,8 @@
             MH_iterateNext           = 11,
             MH_tryFinallyExec        = 12,
             MH_tryFinallyVoidExec    = 13,
-            MH_LIMIT                 = 14;
+            MH_Array_newInstance     = 14,
+            MH_LIMIT                 = 15;
 
     static MethodHandle getConstantHandle(int idx) {
         MethodHandle handle = HANDLES[idx];
@@ -1941,6 +1975,9 @@
                 case MH_tryFinallyVoidExec:
                     return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor",
                             MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class));
+                case MH_Array_newInstance:
+                    return IMPL_LOOKUP.findStatic(Array.class, "newInstance",
+                            MethodType.methodType(Object.class, Class.class, int.class));
             }
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Dec 01 15:06:38 2016 +0100
@@ -26,8 +26,11 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.*;
 
+import jdk.experimental.value.ValueType;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;
 import sun.invoke.util.Wrapper;
@@ -35,6 +38,8 @@
 import sun.reflect.Reflection;
 import sun.reflect.misc.ReflectUtil;
 import sun.security.util.SecurityConstants;
+import valhalla.shady.MinimalValueTypes_1_0;
+
 import java.lang.invoke.LambdaForm.BasicType;
 
 import static java.lang.invoke.MethodHandleStatics.*;
@@ -933,6 +938,16 @@
          */
         public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             String name = "<init>";
+            if (MinimalValueTypes_1_0.isValueType(refc)) {
+                try {
+                    //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);
+                } catch (ClassNotFoundException ex) {
+                    throw new NoSuchElementException(ex.getMessage());
+                }
+            }
             MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
             return getDirectConstructor(refc, ctor);
         }
@@ -1894,6 +1909,31 @@
 
         static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>();
     }
+    /**
+     * Produces a method handle constructing arrays of a desired type.
+     * The return type of the method handle will be the array type.
+     * The type of its sole argument will be {@code int}, which specifies the size of the array.
+     * @param arrayClass an array type
+     * @return a method handle which can create arrays of the given type
+     * @throws NullPointerException if the argument is {@code null}
+     * @throws IllegalArgumentException if {@code arrayClass} is not an array type
+     * @see java.lang.reflect.Array#newInstance(Class, int)
+     * @since 9
+     */
+
+    public static MethodHandle arrayConstructor(Class<?> arrayClass) throws IllegalArgumentException {
+        if (!arrayClass.isArray()) {
+            throw newIllegalArgumentException("not an array class: " + arrayClass.getName());
+        }
+        ValueType<?> 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));
+        }
+    }
 
     /**
      * Produces a method handle giving read access to elements of an array.
@@ -1907,7 +1947,10 @@
      */
     public static
     MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
-        return MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
+        ValueType<?> compValue = valueComponent(arrayClass);
+        return (compValue != null) ?
+                compValue.arrayGetter() :
+                MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
     }
 
     /**
@@ -1922,7 +1965,24 @@
      */
     public static
     MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
-        return MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
+        ValueType<?> compValue = valueComponent(arrayClass);
+        return (compValue != null) ?
+                compValue.arraySetter() :
+                MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <Z> ValueType<Z> valueComponent(Class<Z> clazz) {
+        Class<?> comp = clazz.getComponentType();
+        if (MinimalValueTypes_1_0.isValueType(comp)) {
+            try {
+                return (ValueType<Z>)ValueType.forClass(MinimalValueTypes_1_0.getValueCapableClass(comp));
+            } catch (ClassNotFoundException ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return null;
+        }
     }
 
     /// method handle invocation (reflective style)
@@ -2396,6 +2456,60 @@
         return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY);
     }
 
+    /**
+     * Produces a constant method handle of the requested return type which
+     * returns the default value for that type every time it is invoked.
+     * The resulting constant method handle will have no side effects.
+     * <p>The returned method handle is equivalent to {@code empty(methodType(type))}.
+     * It is also equivalent to {@code explicitCastArguments(constant(Object.class, null), methodType(type))},
+     * since {@code explicitCastArguments} converts {@code null} to default values.
+     * @param type the expected return type of the desired method handle
+     * @return a constant method handle that takes no arguments
+     *         and returns the default value of the given type (or void, if the type is void)
+     * @throws NullPointerException if the argument is null
+     * @see MethodHandles#constant
+     * @see MethodHandles#empty
+     * @see MethodHandles#explicitCastArguments
+     * @since 9
+     */
+
+    public static MethodHandle zero(Class<?> type) {
+        Objects.requireNonNull(type);
+        if (type.isPrimitive()) {
+            return zero(Wrapper.forPrimitiveType(type), type);
+        } else if (MinimalValueTypes_1_0.isValueType(type)) {
+            try {
+                return ValueType.forClass(MinimalValueTypes_1_0.getValueCapableClass(type)).defaultValueConstant();
+            } catch (ClassNotFoundException ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return zero(Wrapper.OBJECT, type);
+        }
+    }
+
+    /**
+     * Produces a method handle of the requested type which ignores any arguments, does nothing,
+     * and returns a suitable default depending on the return type.
+     * That is, it returns a zero primitive value, a {@code null}, or {@code void}.
+     * <p>The returned method handle is equivalent to
+     * {@code dropArguments(zero(type.returnType()), 0, type.parameterList())}.
+     * <p>
+     * @apiNote Given a predicate and target, a useful "if-then" construct can be produced as
+     * {@code guardWithTest(pred, target, empty(target.type())}.
+     * @param type the type of the desired method handle
+     * @return a constant method handle of the given type, which returns a default value of the given return type
+     * @throws NullPointerException if the argument is null
+     * @see MethodHandles#zero
+     * @see MethodHandles#constant
+     * @since 9
+     */
+
+    public static  MethodHandle empty(MethodType type) {
+        Objects.requireNonNull(type);
+        return dropArguments(zero(type.returnType()), 0, type.parameterList());
+    }
+
     private static MethodHandle zero(Wrapper btw, Class<?> rtype) {
         int pos = btw.ordinal();
         MethodHandle zero = ZERO_MHS[pos];
@@ -4181,5 +4295,4 @@
                     cleanup.type(), target.type());
         }
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/experimental/bytecode/AbstractBuilder.java	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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(String name, byte[] bytes) {
+        attributes.writeChar(poolHelper.putUtf8(name));
+        attributes.writeInt(bytes.length);
+        attributes.writeBytes(bytes);
+        nattrs++;
+        return thisBuilder();
+    }
+
+    public <Z> D withAttribute(String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,789 @@
+/*
+ * 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();
+
+        static class PoolKey {
+            PoolTag tag;
+            Object o1;
+            Object o2;
+            Object o3;
+            int size = -1;
+            int hash;
+            int index = -1;
+            PoolKey next;
+
+            void setUtf8(String 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(String 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, String 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, String name, String type) {
+            return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type);
+        }
+
+        @Override
+        public int putMethodRef(String owner, String name, String type, boolean isInterface) {
+            return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF,
+                    owner, name, type);
+        }
+
+        int putMemberRef(PoolTag poolTag, String owner, String 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(String invokedName, String invokedType, String bsmClass, String bsmName, String bsmType, Object... staticArgs) {
+            return 0; //TODO
+        }
+
+        @Override
+        public int putType(String s) {
+            return putUtf8(s);
+        }
+
+        public int putUtf8(String 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 String 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 String 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(String 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();
+            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")
+                                                        .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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,134 @@
+/*
+ * 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(String name, T type) {
+        return withField(name, type, FB -> {
+        });
+    }
+
+    public C withField(String 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(String name, T type) {
+        return withMethod(name, type, MB -> {
+        });
+    }
+
+    public C withMethod(String name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodBuilder) {
+        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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,1239 @@
+/*
+ * 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;
+
+public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> {
+
+    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;
+
+    enum JumpMode {
+        NARROW,
+        WIDE;
+    }
+
+    CodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder.poolHelper, methodBuilder.typeHelper);
+        this.methodBuilder = methodBuilder;
+    }
+
+    public C getstatic(S owner, String name, T type) {
+        emitOp(Opcode.GETSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putstatic(S owner, String name, T type) {
+        emitOp(Opcode.PUTSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C getfield(S owner, String name, T type) {
+        emitOp(Opcode.GETFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C vgetfield(S owner, String name, T type) {
+        emitOp(Opcode.VGETFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putfield(S owner, String name, T type) {
+        emitOp(Opcode.PUTFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C invokevirtual(S owner, String name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKEVIRTUAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokespecial(S owner, String name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESPECIAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokestatic(S owner, String name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESTATIC, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokeinterface(S owner, String 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(String invokedName, T invokedType, S bsmClass, String 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 vnew_(S clazz, String name, T desc) {
+        emitOp(Opcode.VNEW, clazz);
+        code.writeChar(poolHelper.putMethodRef(clazz, name, desc, false));
+        return thisBuilder();
+    }
+
+    public C vnewarray(S array) {
+        emitOp(Opcode.VNEWARRAY, array);
+        code.writeChar(poolHelper.putClass(array));
+        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);
+        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 multivnewarray(S array, byte dims) {
+        emitOp(Opcode.MULTIVNEWARRAY, 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 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;
+        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 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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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(String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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;
+        }
+    }
+
+    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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,687 @@
+/*
+ * 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<String, Integer> labels = new HashMap<>();
+    List<PendingJump> pendingJumps = new LinkedList<>();
+
+    class PendingJump {
+        String label;
+        int pc;
+
+        PendingJump(String label, int pc) {
+            this.label = label;
+            this.pc = pc;
+        }
+
+        boolean resolve(String 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 iaload(TypeTag type) {
+        return emitOp(Opcode.IALOAD.at(type));
+    }
+
+    public C iastore(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, String 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, String 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, String 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));
+        }
+    }
+
+    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, String 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_(String label) {
+        emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
+        emitOffset(code, jumpMode, labelOffset(label));
+        return thisBuilder();
+    }
+
+    protected int labelOffset(String 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(String 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, String 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(String label, int pc) {
+        pendingJumps.add(new PendingJump(label, pc));
+    }
+
+    void resolveJumps(String 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(String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -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> {
+
+    String name;
+    T desc;
+
+    MemberBuilder(String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -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, String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,268 @@
+/*
+ * 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),
+    VNEW(207),
+    VNEWARRAY(208),
+    MULTIVNEWARRAY(209),
+    VRETURN(210),
+    VGETFIELD(211),
+    VBOX(216),
+    VUNBOX(217);
+
+    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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,46 @@
+/*
+ * 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, String name, T type);
+
+    int putMethodRef(S owner, String name, T type, boolean isInterface);
+
+    int putUtf8(String s);
+
+    int putType(T t);
+
+    int putValue(Object v);
+
+    int putInvokeDynamic(String invokedName, T invokedType, S bsmClass, String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,1064 @@
+/*
+ * 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;
+
+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<String, LocalVarInfo> lvarOffsets = new HashMap<>();
+    State state;
+    int depth = 0;
+    int currLocalOffset = 0;
+
+    class StatefulPendingJump extends PendingJump {
+
+        State state;
+
+        StatefulPendingJump(String label, int pc, State state) {
+            super(label, pc);
+            this.state = state;
+        }
+
+        @Override
+        boolean resolve(String label, int pc) {
+            boolean b = super.resolve(label, pc);
+            if (b) {
+                TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
+            }
+            return b;
+        }
+    }
+
+    class LocalVarInfo {
+        String name;
+        int offset;
+        int depth;
+        TypeTag type;
+
+        LocalVarInfo(String 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 C goto_(String label) {
+//        if (!labels.containsKey(label)) {
+//            throw new IllegalStateException("Illegal uncoditional forward jump");
+//        }
+//        return super.goto_(label);
+//    }
+//
+//    @Override
+//    public C goto_(short target) {
+//        if (target > 0) {
+//            throw new IllegalStateException("Illegal uncoditional forward jump");
+//        }
+//        return super.goto_(target);
+//    }
+//
+//    @Override
+//    public C goto_w(int target) {
+//        if (target > 0) {
+//            throw new IllegalStateException("Illegal uncoditional forward jump");
+//        }
+//        return super.goto_w(target);
+//    }
+//
+//    @Override
+//    public C jsr(String label) {
+//        if (!labels.containsKey(label)) {
+//            throw new IllegalStateException("Illegal uncoditional forward jump");
+//        }
+//        return super.jsr(label);
+//    }
+//
+//    @Override
+//    public C jsr(short target) {
+//        if (target > 0) {
+//            throw new IllegalStateException("Illegal uncoditional forward jump");
+//        }
+//        return super.jsr(target);
+//    }
+
+    class State {
+        ArrayList<T> stack;
+        Vector<T> locals;
+
+        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 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;
+        }
+
+        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 (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 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_:
+                break;
+            case NOP:
+            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 VNEW:
+            case NEW:
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case NEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
+                break;
+            case VNEWARRAY:
+            case ANEWARRAY:
+            case VBOX:
+            case VUNBOX:
+                state.pop();
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case MULTIVNEWARRAY:
+            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 VGETFIELD:
+            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();
+                }
+                pop();
+                break;
+            }
+            case PUTFIELD: {
+                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 CHECKCAST:
+                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.locals.get(index)), 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(String 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(String local) {
+        return load(lvarOffsets.get(local).offset);
+    }
+
+    public C store(String 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<String, 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(String label, int pc) {
+        pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
+    }
+
+    @Override
+    void resolveJumps(String 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	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,227 @@
+/*
+ * 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.value;
+
+import jdk.experimental.bytecode.BasicClassBuilder.BasicPoolHelper;
+import jdk.experimental.bytecode.BasicClassBuilder.BasicTypeHelper;
+import jdk.experimental.bytecode.ClassBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.PoolHelper;
+import jdk.experimental.bytecode.TypeHelper;
+import jdk.experimental.bytecode.TypeTag;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import jdk.internal.misc.Unsafe;
+import sun.security.action.GetPropertyAction;
+import valhalla.shady.MinimalValueTypes_1_0;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.ProxyClassesDumper;
+import java.security.AccessController;
+import java.util.Iterator;
+import java.util.PropertyPermission;
+import java.util.function.Consumer;
+
+/**
+ * Utility class for building method handles.
+ */
+public class MethodHandleBuilder {
+
+    static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super TypedCodeBuilder<Class<?>, String, byte[], ?>> builder) {
+            return loadCode(lookup, name, type.toMethodDescriptorString(), builder);
+    }
+
+    public static MethodHandle loadCode(Lookup lookup, String name, String type, Consumer<? super TypedCodeBuilder<Class<?>, String, byte[], ?>> builder) {
+        IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder();
+        isolatedMethodBuilder
+                .withSuperclass(Object.class)
+                .withMajorVersion(52)
+                .withMinorVersion(0)
+                .withFlags(Flag.ACC_PUBLIC)
+                .withMethod(name, type, M ->
+                    M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
+                     .withCode(TypedCodeBuilder::new, builder));
+
+        try {
+            byte[] barr = isolatedMethodBuilder.build();
+            ProxyClassesDumper dumper = dumper();
+            if (dumper != null) {
+                dumper.dumpClass(name, barr.clone());
+            }
+            Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, null);
+            return lookup.findStatic(clazz, name, MethodType.fromMethodDescriptorString(type, lookup.lookupClass().getClassLoader()));
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> {
+
+        IsolatedMethodBuilder() {
+            super(new IsolatedMethodPoolHelper(), new IsolatedMethodTypeHelper());
+            withThisClass(new Object() { }.getClass());
+        }
+
+        static class IsolatedMethodTypeHelper implements TypeHelper<Class<?>, String> {
+
+            BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
+
+            @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 type) {
+                try {
+                    return basicTypeHelper.from(type);
+                } 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 from(Class<?> c) {
+                return c.getName().replaceAll("\\.", "/");
+            }
+
+            @Override
+            public int putClass(Class<?> symbol) {
+                return basicPoolHelper.putClass(from(symbol));
+            }
+
+            @Override
+            public int putFieldRef(Class<?> owner, String name, String type) {
+                return basicPoolHelper.putFieldRef(from(owner), name, type);
+            }
+
+            @Override
+            public int putMethodRef(Class<?> owner, String name, String type, boolean isInterface) {
+                return basicPoolHelper.putMethodRef(from(owner), name, type, isInterface);
+            }
+
+            @Override
+            public int putUtf8(String 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 putInvokeDynamic(String invokedName, String invokedType, Class<?> bsmClass, String bsmName, String bsmType, Object... staticArgs) {
+                return basicPoolHelper.putInvokeDynamic(invokedName, invokedType, from(bsmClass), bsmName, bsmType, staticArgs);
+            }
+
+            @Override
+            public int size() {
+                return basicPoolHelper.size();
+            }
+
+            @Override
+            public byte[] entries() {
+                return basicPoolHelper.entries();
+            }
+        }
+
+        @Override
+        public byte[] build() {
+            return super.build();
+        }
+    }
+
+    private static ProxyClassesDumper dumper() {
+        final String key = "valhalla.dumpIsolatedMethodClasses";
+        String path = AccessController.doPrivileged(
+                new GetPropertyAction(key), null,
+                new PropertyPermission(key, "read"));
+        return (null == path) ? null : ProxyClassesDumper.getInstance(path);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/experimental/value/ValueType.java	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,391 @@
+/*
+ * 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.value;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
+
+import jdk.experimental.value.ValueType.ValueHandleKind.ValueHandleKey;
+import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
+import jdk.experimental.bytecode.TypeTag;
+import jdk.internal.misc.Unsafe;
+import sun.invoke.util.BytecodeDescriptor;
+import sun.invoke.util.Wrapper;
+import valhalla.shady.MinimalValueTypes_1_0;
+
+// Rough place holder just now...
+public class ValueType<T> {
+
+    static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    enum ValueHandleKind {
+        BOX,
+        UNBOX,
+        DEFAULT,
+        EQ,
+        HASH,
+        WITHER() {
+            @Override
+            ValueHandleKey key(Object fieldName) {
+               return new ValueHandleKey(this, fieldName);
+            }
+        },
+        MAKE,
+        NEWARRAY,
+        VALOAD,
+        VASTORE,
+        MULTINEWARRAY() {
+            @Override
+            ValueHandleKey key(Object dims) {
+               return new ValueHandleKey(this, dims);
+            }
+        };
+
+        ValueHandleKey key() {
+            return new ValueHandleKey(this, null);
+        }
+
+        ValueHandleKey key(Object optArg) {
+            throw new IllegalStateException();
+        }
+
+        static class ValueHandleKey {
+            ValueHandleKind kind;
+            Optional<Object> optArg;
+
+            ValueHandleKey(ValueHandleKind kind, Object optArg) {
+                this.kind = kind;
+                this.optArg = Optional.ofNullable(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);
+        }
+    }
+
+    private static final ConcurrentHashMap<Class<?>, ValueType<?>> BOX_TO_VT = new ConcurrentHashMap<>();
+
+    public static boolean classHasValueType(Class<?> x) {
+        return MinimalValueTypes_1_0.classHasValueType(x);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> ValueType<T> forClass(Class<T> x) {
+        ValueType<T> vt = (ValueType<T>) BOX_TO_VT.get(x);
+        if (vt != null) {
+            return vt;
+        }
+
+        try {
+            Class<T> valueClass = (Class<T>) MinimalValueTypes_1_0.getValueTypeClass(x);
+            vt = new ValueType<T>(x, valueClass);
+            ValueType<T> old = (ValueType<T>) BOX_TO_VT.putIfAbsent(x, vt);
+            if (old != null) {
+                vt = old;
+            }
+            return vt;
+        }
+        catch (ClassNotFoundException cne) {
+            throw new IllegalArgumentException("Class " + x + " not bound to ValueType", cne);
+        }
+    }
+
+    private Class<T> boxClass;
+    private Class<?> valueClass;
+    private Lookup lookup;
+    private Map<ValueHandleKind.ValueHandleKey, MethodHandle> handleMap = new ConcurrentHashMap<>();
+
+    private ValueType(Class<T> boxClass, Class<T> valueClass) {
+        this.boxClass = boxClass;
+        this.valueClass = valueClass;
+        this.lookup = IMPL_LOOKUP.in(boxClass);
+    }
+
+    public Class<T> boxClass() {
+        return boxClass;
+    }
+
+    public Class<?> sourceClass() {
+        return boxClass();
+    }
+
+    public Class<?> valueClass() {
+        return valueClass;
+    }
+
+    public Class<?> arrayValueClass() {
+        return arrayValueClass(1);
+    }
+
+    public Class<?> arrayValueClass(int dims) {
+        try {
+            String dimsStr = "[[[[[[[[[[[[[[[[";
+            if (dims < 1 || dims > 16) {
+                throw new IllegalArgumentException("cannot create array class for dimension > 16");
+            }
+            return Class.forName(dimsStr.substring(0, dims) + "Q" + valueClass().getName() + ";", false, lookup.lookupClass().getClassLoader());
+        } catch (ClassNotFoundException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    public String toString() {
+        return "ValueType boxClass=" + boxClass() + " valueClass=" + valueClass();
+    }
+
+    public MethodHandle defaultValueConstant() {
+        ValueHandleKey key = ValueHandleKind.DEFAULT.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "default" + sourceClass().getName(), MethodType.methodType(valueClass()),
+                    C -> {
+                        C.new_(boxClass()).vunbox(valueClass()).vreturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle substitutabilityTest() {
+        ValueHandleKey key = ValueHandleKind.EQ.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "subTest" + sourceClass().getName(), MethodType.methodType(boolean.class, valueClass(), valueClass()),
+                    C -> {
+                        for (Field f : valueFields()) {
+                            C.vload(0).vgetfield(valueClass(), f.getName(), BytecodeDescriptor.unparse(f.getType()));
+                            C.vload(1).vgetfield(valueClass(), f.getName(), BytecodeDescriptor.unparse(f.getType()));
+                            if (f.getType().isPrimitive()) {
+                                C.ifcmp(TypeTag.I, CondKind.NE, "fail");
+                            } else {
+                                C.invokestatic(Objects.class, "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false);
+                                C.const_(0).ifcmp(TypeTag.I, CondKind.NE, "fail");
+                            }
+                        }
+                        C.const_(1);
+                        C.ireturn();
+                        C.label("fail");
+                        C.const_(0);
+                        C.ireturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle substitutabilityHashCode() {
+        ValueHandleKey key = ValueHandleKind.HASH.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "subHash" + sourceClass().getName(), MethodType.methodType(int.class, valueClass()),
+                    C -> {
+                        C.withLocal("res", "I");
+                        C.const_(1).store("res");
+                        for (Field f : valueFields()) {
+                            String desc = BytecodeDescriptor.unparse(f.getType());
+                            C.vload(0).vgetfield(valueClass(), f.getName(), desc);
+                            C.load("res").const_(31).imul();
+                            if (f.getType().isPrimitive()) {
+                                C.invokestatic(Wrapper.asWrapperType(f.getType()), "hashCode", "(" + desc + ")I", false);
+                            } else {
+                                C.invokestatic(Objects.class, "hashCode", "(Ljava/lang/Object)I", false);
+                            }
+                            C.iadd().store("res");
+                        }
+                        C.load("res").ireturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    //Todo: when 'vwithfield' is ready, this handle could be greatly simplified
+    public MethodHandle findWither(String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+        ValueHandleKey key = ValueHandleKind.WITHER.key(name);
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            Field field = boxClass().getDeclaredField(name);
+            if (field == null || !field.getType().equals(type) ||
+                    (field.getModifiers() & Modifier.STATIC) != 0) {
+                throw new NoSuchFieldException(name);
+            }
+            Class<?> erasedType = type.isPrimitive() ?
+                    type : Object.class;
+            Method unsafeMethod = Stream.of(UNSAFE.getClass().getDeclaredMethods())
+                    .filter(m -> m.getName().startsWith("put") &&
+                            Arrays.asList(m.getParameterTypes()).equals(Arrays.asList(Object.class, long.class, erasedType)))
+                    .findFirst().get();
+            long fieldOffset = UNSAFE.objectFieldOffset(field);
+            result = MethodHandleBuilder.loadCode(lookup, "wither" + sourceClass().getName() + ":" + name, MethodType.methodType(valueClass(), UNSAFE.getClass(), valueClass(), type),
+                    C -> {
+                        C.withLocal("boxedVal", BytecodeDescriptor.unparse(boxClass()))
+                         .load(1)
+                         .vbox(boxClass())
+                         .store("boxedVal")
+                         .load(0)
+                         .load("boxedVal")
+                         .const_(fieldOffset)
+                         .load(2);
+                         MethodType unsafeMT = MethodType.methodType(unsafeMethod.getReturnType(), unsafeMethod.getParameterTypes());
+                         C.invokevirtual(UNSAFE.getClass(), unsafeMethod.getName(), BytecodeDescriptor.unparse(unsafeMT), false)
+                          .load("boxedVal")
+                          .vunbox(valueClass())
+                          .vreturn();
+                    }).bindTo(UNSAFE);
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle unbox() {
+        ValueHandleKey key = ValueHandleKind.UNBOX.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "unbox" + sourceClass().getName(), MethodType.methodType(valueClass(), boxClass()),
+                    C -> {
+                        C.load(0).vunbox(valueClass()).vreturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle box() {
+        ValueHandleKey key = ValueHandleKind.BOX.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "box" + sourceClass().getName(), MethodType.methodType(boxClass(), valueClass()),
+                    C -> {
+                        C.vload(0).vbox(boxClass()).areturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle newArray() {
+        Class<?> arrayValueClass = arrayValueClass();
+        ValueHandleKey key = ValueHandleKind.NEWARRAY.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "newArray" + sourceClass().getName(), MethodType.methodType(arrayValueClass, int.class),
+                    C -> {
+                        C.load(0).vnewarray(arrayValueClass).areturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle arrayGetter() {
+        Class<?> arrayValueClass = arrayValueClass();
+        ValueHandleKey key = ValueHandleKind.VALOAD.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "arrayGet" + sourceClass().getName(), MethodType.methodType(valueClass(), arrayValueClass, int.class),
+                    C -> {
+                        C.load(0).load(1).vaload().vreturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle arraySetter() {
+        Class<?> arrayValueClass = arrayValueClass();
+        ValueHandleKey key = ValueHandleKind.VASTORE.key();
+        MethodHandle result = handleMap.get(key);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "arraySet" + sourceClass().getName(), MethodType.methodType(void.class, arrayValueClass, int.class, valueClass()),
+                    C -> {
+                        C.load(0).load(1).load(2).vastore().return_();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    public MethodHandle newMultiArray(int dims) {
+        Class<?> arrayValueClass = arrayValueClass(dims);
+        ValueHandleKey key = ValueHandleKind.MULTINEWARRAY.key(dims);
+        MethodHandle result = handleMap.get(key);
+        Class<?>[] params = new Class<?>[dims];
+        Arrays.fill(params, int.class);
+        if (result == null) {
+            result = MethodHandleBuilder.loadCode(lookup, "newMultiArray" + sourceClass().getName(), MethodType.methodType(arrayValueClass, params),
+                    C -> {
+                        for (int i = 0 ; i < dims ; i++) {
+                            C.load(i);
+                        }
+                        C.multivnewarray(arrayValueClass, (byte)dims).areturn();
+                    });
+            handleMap.put(key, result);
+        }
+        return result;
+    }
+
+    private Field[] valueFields() {
+        int valFieldMask = Modifier.PUBLIC | Modifier.FINAL;
+        return Stream.of(sourceClass().getDeclaredFields())
+                .filter(f -> (f.getModifiers() & (valFieldMask | Modifier.STATIC)) == valFieldMask)
+                .toArray(Field[]::new);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jvm/internal/value/DeriveValueType.java	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,33 @@
+/*
+ * 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 jvm.internal.value;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface DeriveValueType {
+}
--- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java	Thu Dec 01 15:06:38 2016 +0100
@@ -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(';');
         }
--- a/src/java.base/share/classes/valhalla/model3/classfile/ClassReader.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/valhalla/model3/classfile/ClassReader.java	Thu Dec 01 15:06:38 2016 +0100
@@ -44,7 +44,7 @@
  */
 public class ClassReader {
     ClassReader(valhalla.model3.classfile.ClassFile classFile, InputStream in, Attribute.Factory attributeFactory) throws IOException {
-        this.classFile = Objects.requireNonNull(classFile);
+        this.classFile = classFile;
         this.attributeFactory = Objects.requireNonNull(attributeFactory);
         this.in = new DataInputStream(new BufferedInputStream(in));
     }
--- a/src/java.base/share/classes/valhalla/model3/classfile/Code_attribute.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/valhalla/model3/classfile/Code_attribute.java	Thu Dec 01 15:06:38 2016 +0100
@@ -30,6 +30,7 @@
 import valhalla.model3.classfile.Attributes;
 import valhalla.model3.classfile.Instruction;
 
+import java.util.stream.Stream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
@@ -59,6 +60,19 @@
         public final int index;
     }
 
+    public Code_attribute(ConstantPool cp, int name_index, int max_stack, int max_locals, byte[] code, Exception_data[] exception_table, Attribute[] attrs) {
+        super(name_index, 2 + 2 + 4 + code.length
+                + 2 + exception_table.length * 8
+                + 2 + Stream.of(attrs).mapToInt(Attribute::byteLength).sum());
+        this.max_stack = max_stack;
+        this.max_locals = max_locals;
+        this.code_length = code.length;
+        this.code = code;
+        this.exception_table_length = exception_table.length;
+        this.exception_table = exception_table;
+        this.attributes = attrs == null ? null : new Attributes(cp, attrs);
+    }
+
     // Temporary hack for now -- add copy constructor
     public Code_attribute(ConstantPool cp, valhalla.model3.classfile.Code_attribute other, Attribute[] attrs) {
         super(other.attribute_name_index, other.attribute_length);
--- a/src/java.base/share/classes/valhalla/model3/classfile/Method.java	Thu Nov 24 11:25:43 2016 +0100
+++ b/src/java.base/share/classes/valhalla/model3/classfile/Method.java	Thu Dec 01 15:06:38 2016 +0100
@@ -27,12 +27,15 @@
 
 import valhalla.model3.classfile.*;
 import valhalla.model3.classfile.AccessFlags;
+import valhalla.model3.classfile.Attribute.Factory;
 import valhalla.model3.classfile.Attributes;
 import valhalla.model3.classfile.ClassReader;
 import valhalla.model3.classfile.ConstantPool;
 import valhalla.model3.classfile.ConstantPoolException;
 import valhalla.model3.classfile.Descriptor;
 
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/valhalla/shady/MinimalValueTypes_1_0.java	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,142 @@
+/*
+ * 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.ProxyClassesDumper;
+import java.security.AccessController;
+import java.security.ProtectionDomain;
+import java.util.PropertyPermission;
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+import sun.misc.Unsafe;
+import sun.security.action.GetPropertyAction;
+
+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 = "Ljvm/internal/value/DeriveValueType;";
+    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;
+
+    private static final ProxyClassesDumper dumper;
+
+    static {
+        final String key = "valhalla.dumpProxyClasses";
+        String path = AccessController.doPrivileged(
+                new GetPropertyAction(key), null,
+                new PropertyPermission(key , "read"));
+        dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
+    }
+
+    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 boolean isValueType(Class<?> x) {
+        return (x.getModifiers() & ACC_VALUE) != 0;
+    }
+
+    public static Class<?> getValueCapableClass(Class<?> x) throws ClassNotFoundException {
+        if (!isValueType(x)) {
+            throw new IllegalArgumentException("Expected ValueType");
+        }
+        return Class.forName(getValueCapableClassName(x.getName()), true, x.getClassLoader());
+    }
+
+    public static Class<?> getValueTypeClass(Class<?> x)  throws ClassNotFoundException {
+        if (isValueType(x)) {
+            throw new IllegalArgumentException("Expected Value Capable Class");
+        }
+        return Class.forName(getValueTypeClassName(x.getName()), true, x.getClassLoader());
+    }
+
+    public static String getValueTypeClassName(Class<?> x) {
+        String vtName = x.getName();
+        if (!isValueType(x)) {
+            vtName += DERIVE_VT_CLASSNAME_POSTFIX;
+        }
+        return vtName;
+    }
+
+    public static boolean classHasValueType(Class<?> x) {
+        if (x.getDeclaredAnnotation(jvm.internal.value.DeriveValueType.class) == null) {
+            return false;
+        }
+        try {
+            Class.forName(getValueTypeClassName(x), true, x.getClassLoader());
+            return true;
+        }
+        catch (ClassNotFoundException cnfe) {
+            return false;
+        }
+    }
+
+    // fds   : name/sig pairs
+    // 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, dumper);
+        Class<?> vtClass = Unsafe.getUnsafe().defineClass(vtInternalClassName, valueTypeBytes, 0, valueTypeBytes.length, cl, pd);
+        return vtInternalClassName;
+    }
+
+    public static byte[] createValueType(ValueTypeDesc valueTypeDesc, ProxyClassesDumper dumper) {
+
+        String valueTypeClassName = getValueTypeClassName(valueTypeDesc);
+
+        BasicClassBuilder builder = new BasicClassBuilder(valueTypeClassName, 53, 1)
+                .withFlags(DERIVE_VT_CLASS_ACCESS)
+                .withSuperclass(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();
+        if (dumper != null) {
+            dumper.dumpClass(valueTypeClassName, newBytes);
+        }
+        return newBytes;
+    }
+
+    private final native Class<?> getDerivedValueType(Class<?> ofClass);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/valhalla/shady/ValueTypeDesc.java	Thu Dec 01 15:06:38 2016 +0100
@@ -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/test/valhalla/mvt/MVTTest.java	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @run main/othervm -Xverify:none MVTTest
+ */
+
+import jdk.experimental.value.ValueType;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class MVTTest {
+
+    public static void main(String[] args) throws Throwable {
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+    	Class<?> VCC = Point.class;
+    	ValueType<?> VT = ValueType.forClass(VCC);
+    	Class<?> DVT = VT.valueClass();
+		String[] fieldNames = { "x", "y", "z" };
+		Class<?>[] fieldTypes = { int.class, short.class, short.class };
+		Object[] fieldValues = { 42, (short)43, (short)44 };
+
+		MethodHandle printPoint = lookup.findStatic(MVTTest.class, "print", MethodType.methodType(String.class, VCC))
+				                        .asType(MethodType.methodType(String.class, DVT));
+
+        String template = "Point[x=#x, y=#y, z=#z]";
+		for (int i = 0 ; i < fieldNames.length ; i++) {
+			MethodHandle wither = MethodHandles.collectArguments(VT.findWither(fieldNames[i], fieldTypes[i]), 0, VT.defaultValueConstant());
+            String expected = template.replace("#" + fieldNames[i], String.valueOf(fieldValues[i]))
+                                      .replaceAll("#[xyz]", "0");
+
+			assertTrue(MethodHandles.filterReturnValue(wither, printPoint).invoke(fieldValues[i]).equals(expected));
+		}
+
+		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) {
+			   assertTrue((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();
+               assertTrue(vHashEq == rHashEq);
+		   }
+		}
+
+        MethodHandle vConstr = lookup.findConstructor(VT.valueClass(), MethodType.methodType(void.class, int.class, short.class, short.class));
+        assertTrue(vConstr != null);
+
+		assertTrue(MethodHandles.filterReturnValue(MethodHandles.identity(VT.valueClass()), printPoint)
+                                .invoke(new Point(1, (short)2, (short)3)).equals("Point[x=1, y=2, z=3]")); //identity
+
+        assertTrue(MethodHandles.filterReturnValue(MethodHandles.zero(VT.valueClass()), printPoint)
+                                .invoke().equals("Point[x=0, y=0, z=0]")); //zero
+
+        assertTrue(MethodHandles.filterReturnValue(MethodHandles.empty(MethodType.methodType(VT.valueClass(), int.class, String.class)), printPoint)
+                                .invoke(1, "").equals("Point[x=0, y=0, z=0]")); //empty
+
+        //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 expected = template.replace("#x", String.valueOf(i))
+                                      .replaceAll("#[yz]", "9");
+			assertTrue(MethodHandles.filterReturnValue(MethodHandles.arrayElementGetter(VT.arrayValueClass()), printPoint)
+                                    .invoke(arr, i).equals(expected));
+		}
+
+		//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 expected = template.replace("#x", String.valueOf(i))
+                                          .replace("#y", String.valueOf(j))
+                                          .replace("#z", "9");
+                assertTrue(MethodHandles.filterReturnValue(MethodHandles.arrayElementGetter(VT.arrayValueClass()), printPoint)
+                                    .invoke(innerArr, i).equals(expected));
+            }
+		}
+
+        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(arr2, i);
+            for (int j = 0 ; i < 3 ; i++) {
+                String expected = template.replace("#x", String.valueOf(i))
+                                          .replace("#y", String.valueOf(j))
+                                          .replace("#z", "9");
+                assertTrue(MethodHandles.filterReturnValue(MethodHandles.arrayElementGetter(VT.arrayValueClass()), printPoint)
+                                    .invoke(innerArr, i).equals(expected));
+            }
+		}
+
+		System.err.println("Checks executed: " + assertCount);
+    }
+
+    static int assertCount = 0;
+
+    static void assertTrue(boolean cond) {
+		assertCount++;
+		if (!cond) {
+			throw new AssertionError();
+		}
+	}
+
+	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/valhalla/mvt/Point.java	Thu Dec 01 15:06:38 2016 +0100
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+import java.util.Objects;
+
+@jvm.internal.value.DeriveValueType
+final class Point {
+    public final int x;
+    public final short y;
+    public final short z;
+
+    Point(int x, short y, short z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof Point) {
+            Point that = (Point)o;
+            return that.x == x &&
+                    that.y == y &&
+                    that.z == z;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(x, y, z);
+    }
+}