changeset 3271:e7cdaa508aea

forgot to hg add PoolWriter
author mcimadamore
date Tue, 08 Dec 2015 16:29:38 +0000
parents 2b24dfe17c16
children 6acf2c8bf289
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java
diffstat 2 files changed, 637 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Dec 08 14:42:24 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Dec 08 16:29:38 2015 +0000
@@ -950,7 +950,7 @@
                 // Map the receiver type to the actually type, not just "array"
                 rcvrType = TreeInfo.unerasedTypeOrType(tree.getQualifierExpression());
             } else {
-                 rcvrType = types.decorateDescriptor(TreeInfo.unerasedTypeOrType(tree.getQualifierExpression()),
+                rcvrType = types.decorateDescriptor(TreeInfo.unerasedTypeOrType(tree.getQualifierExpression()),
                          tree.sym.enclClass().type);
             }
             if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java	Tue Dec 08 16:29:38 2015 +0000
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2015, 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 com.sun.tools.javac.jvm;
+
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
+import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol.BootstrapArgument;
+import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ArrayType;
+import com.sun.tools.javac.code.Type.CapturedType;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.code.Type.DelegatedType;
+import com.sun.tools.javac.code.Type.TypeVar;
+import com.sun.tools.javac.code.Type.WildcardType;
+import com.sun.tools.javac.code.TypeMetadata;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.code.Types.DefaultSymbolVisitor;
+import com.sun.tools.javac.code.Types.DefaultTypeVisitor;
+import com.sun.tools.javac.jvm.ClassWriter.PoolOverflow;
+import com.sun.tools.javac.jvm.ClassWriter.StringOverflow;
+import com.sun.tools.javac.jvm.Gen.Descriptor;
+import com.sun.tools.javac.jvm.Gen.PoolMode;
+import com.sun.tools.javac.jvm.Pool.CompoundType;
+import com.sun.tools.javac.jvm.Pool.Constant;
+import com.sun.tools.javac.jvm.Pool.InvokeDynamic;
+import com.sun.tools.javac.jvm.Pool.MemberRef;
+import com.sun.tools.javac.jvm.Pool.MethodHandle;
+import com.sun.tools.javac.jvm.Pool.MethodType;
+import com.sun.tools.javac.jvm.Pool.NameAndType;
+import com.sun.tools.javac.jvm.Pool.ClassRef;
+import com.sun.tools.javac.jvm.Pool.Entry;
+import com.sun.tools.javac.jvm.Pool.ParameterizedType;
+import com.sun.tools.javac.jvm.Pool.StringRef;
+import com.sun.tools.javac.jvm.Pool.TypeVariable;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.ByteBuffer;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Tuple.Tuple2;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.sun.tools.javac.code.Kinds.Kind.TYP;
+import static com.sun.tools.javac.code.TypeTag.ANY_BOUND;
+import static com.sun.tools.javac.code.TypeTag.CLASS;
+import static com.sun.tools.javac.code.TypeTag.NONE;
+
+/**
+ * Pool interface towards {@code ClassWriter}. Exposes methods to encode and write javac entities
+ * into the constant pool.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class PoolWriter {
+
+    /** The constant pool. */
+    Pool pool;
+
+    Types types;
+
+    /** Set of enabled pool modes. */
+    EnumSet<PoolMode> poolModes;
+
+    /** The inner classes to be written, as an ordered set (enclosing first). */
+    LinkedHashSet<ClassSymbol> innerClasses = new LinkedHashSet<>();
+
+    /** The list of entries in the BootstrapMethods attribute. */
+    Map<BootstrapMethodsKey, Tuple2<MethodHandle, Integer>> bootstrapMethods = new LinkedHashMap<>();
+
+    /** Did this class encoded any of the Valhalla-specific CP-forms? */
+    boolean useNewCPForms = false;
+
+    public PoolWriter(Types types, EnumSet<PoolMode> poolModes) {
+        this.pool = new Pool();
+        this.types = types;
+        this.poolModes = poolModes;
+    }
+
+    /**
+     * Puts a symbol into the pool and returns the index of the resulting entry.
+     * Note: if a matching entry is already in the pool, no new entry is persisted.
+     */
+    int putSymbol(Symbol o) {
+        return makeSymbol(o).index;
+    }
+
+    /**
+     * Puts a type into the pool and returns the index of the resulting entry.
+     * Note: if a matching entry is already in the pool, no new entry is persisted.
+     */
+    int putType(Type t) {
+        return makeType(t, this::typeToSig).index;
+    }
+
+    /**
+     * Puts a constant into the pool and returns the index of the resulting entry.
+     * Note: if a matching entry is already in the pool, no new entry is persisted.
+     */
+    int putConstant(Object o) {
+        return makeConstant(o).index;
+    }
+
+    /**
+     * Puts a name and type pair into the pool and returns the index of the resulting entry.
+     * Note: if a matching entry is already in the pool, no new entry is persisted.
+     */
+    @SuppressWarnings("unchecked")
+    int putNameAndType(Symbol s) {
+        return makeNameAndType(s.name, s.type).index;
+    }
+
+    /**
+     * Puts a class type into the pool and returns the index of the resulting entry.
+     * Note: if a matching entry is already in the pool, no new entry is persisted.
+     */
+    int putClass(Type t) {
+        return makeClass(t).index;
+    }
+
+    /**
+     * Create a new class reference entry from given type.
+     */
+    private ClassRef makeClass(Type ct) {
+        if (ct.isCompound()) {
+            //intersection types are not supported
+            ct = types.directSupertypes(ct).head;
+        }
+        if (ct.tsym.owner.kind == TYP) {
+            makeSymbol(ct.tsym.owner);
+        }
+        if (ct.hasTag(CLASS)) {
+            enterInner((ClassSymbol)ct.tsym);
+        }
+        Entry clazz = makeType(ct, this::typeToClassName);
+        ClassRef cref = new ClassRef(clazz, ct);
+        return (ClassRef)pool.put(cref);
+    }
+
+    /**
+     * Enter an inner class into the `innerClasses' set.
+     */
+    void enterInner(ClassSymbol c) {
+        if (c.type.isCompound()) {
+            throw new AssertionError("Unexpected intersection type: " + c.type);
+        }
+        c.complete();
+        if (!c.type.hasTag(CLASS)) return; // arrays
+        if (c.owner.enclClass() != null && !innerClasses.contains(c)) {
+            enterInner(c.owner.enclClass());
+            innerClasses.add(c);
+            putSymbol(c);
+            if (!c.name.isEmpty()) {
+                putConstant(c.name);
+            }
+        }
+    }
+
+    /**
+     * Create a new constant entry from given constant value.
+     * Supported values are int, float, long, double, Name and String.
+     */
+    @SuppressWarnings("unchecked")
+    private Entry makeConstant(Object o) {
+        if (o instanceof String) {
+            Constant<Name> name = (Constant<Name>)makeConstant(types.names.fromString((String)o));
+            Entry e = new StringRef(name, (String)o);
+            return pool.put(e);
+        } else {
+            //assume it's some other constant
+            Entry e = new Constant<>(constantTag(o), o, o);
+            return pool.put(e);
+        }
+    }
+    //where
+        private int constantTag(Object o) {
+            if (o instanceof Name) {
+                return ClassFile.CONSTANT_Utf8;
+            } else if (o instanceof Integer) {
+                return ClassFile.CONSTANT_Integer;
+            } else if (o instanceof Long) {
+                return ClassFile.CONSTANT_Long;
+            } else if (o instanceof Float) {
+                return ClassFile.CONSTANT_Float;
+            } else if (o instanceof Double) {
+                return ClassFile.CONSTANT_Double;
+            } else if (o instanceof String) {
+                return ClassFile.CONSTANT_String;
+            } else {
+                Assert.error("Invalid constant: " + o + " " + o.getClass().getSimpleName());
+                return -1;
+            }
+        }
+
+    /**
+     * Create a new name and type entry from given name and type pair.
+     */
+    @SuppressWarnings("unchecked")
+    private NameAndType makeNameAndType(Name name, Type type) {
+        Constant<Name> nameEntry = (Constant<Name>)makeConstant(name);
+        NameAndType nt = new NameAndType(nameEntry, makeType(type, this::typeToSig));
+        return (NameAndType)pool.put(nt);
+    }
+
+    /**
+     * Create a new entry form a given symbol.
+     */
+    private Entry makeSymbol(Symbol s) {
+        return s.accept(symbolFactory, null);
+    }
+
+    /**
+     * Factory used to create entries from symbols.
+     */
+    private Symbol.Visitor<Entry, Void> symbolFactory = new DefaultSymbolVisitor<Entry, Void>() {
+        @Override
+        public Entry visitMethodSymbol(MethodSymbol s, Void _unused) {
+            if (s.isHandle()) {
+                return visitHandle((MethodHandleSymbol)s);
+            } else if (s.isDynamic()) {
+                return visitDynamicMethod((DynamicMethodSymbol)s);
+            } else {
+                return visitMember(s);
+            }
+        }
+
+        @Override
+        public Entry visitVarSymbol(VarSymbol s, Void _unused) {
+            return visitMember(s);
+        }
+
+        @Override
+        public Entry visitClassSymbol(ClassSymbol csym, Void _unused) {
+            return makeClass(csym.erasure(types));
+        }
+        
+        @Override
+        public Entry visitSymbol(Symbol s, Void _unused) {
+            Assert.check(s instanceof Descriptor);
+            return visitDescriptor((Descriptor)s);
+        }
+        
+        private Entry visitMember(Symbol sym) {
+            ClassRef site = (ClassRef)visit(sym.enclClass(), null);
+            NameAndType nt = makeNameAndType(sym.name, sym.type);
+            Entry e = new MemberRef(memberTag(sym.enclClass().type, sym), site, nt, sym);
+            return pool.put(e);
+        }
+        
+        private Entry visitDescriptor(Descriptor d) {
+            ClassRef cref = makeClass(d.site);
+            NameAndType nt = makeNameAndType(d.name, d.externalType(types));
+            Entry e = new MemberRef(memberTag(d.site, d), cref, nt, d);
+            return pool.put(e);
+        }
+        
+        private Entry visitDynamicMethod(DynamicMethodSymbol dynSym) {
+            MethodHandle handle = (MethodHandle)makeSymbol(((MethodSymbol)dynSym.bsm).asHandle()); //bsm
+
+            Entry[] staticArgs = Stream.of(dynSym.staticArgs)
+                    .map(this::visitStaticIndyArg)
+                    .toArray(Entry[]::new);
+
+            BootstrapMethodsKey key = new BootstrapMethodsKey(staticArgs);
+
+            // Figure out the index for existing BSM; create a new BSM if no key
+            Tuple2<MethodHandle, Integer> val = bootstrapMethods.get(key);
+            if (val == null) {
+                int index = bootstrapMethods.size();
+                val = new Tuple2<>(handle, index);
+                bootstrapMethods.put(key, val);
+            }
+
+            int bsmIndex = val.elem1;
+            NameAndType nt = makeNameAndType(dynSym.name, dynSym.type);
+            Entry e = new InvokeDynamic(bsmIndex, nt);
+            return pool.put(e);
+        }
+
+        private Entry visitStaticIndyArg(BootstrapArgument<?> staticArg) {
+            switch (staticArg.kind) {
+                case DOUBLE:
+                case FLOAT:
+                case LONG:
+                case INT:
+                case STRING:
+                    return makeConstant(staticArg.data);
+                case METHOD_HANDLE:
+                case CLASS:
+                    return makeSymbol(staticArg.asSymbol().get());
+                case METHOD_TYPE:
+                    return makeType(staticArg.asType().get(), PoolWriter.this::typeToSig);
+                default:
+                    throw new AssertionError("Cannot get here!");
+            }
+        }
+        
+        private Entry visitHandle(MethodHandleSymbol handle) {
+            MemberRef ref = (MemberRef)makeSymbol(handle.baseSymbol());
+            Entry e = new MethodHandle(handle.referenceKind(), ref, handle);
+            return pool.put(e);
+        }
+
+        int memberTag(Type site, Symbol sym) {
+            switch (sym.kind) {
+                case VAR:
+                    return ClassFile.CONSTANT_Fieldref;
+                case MTH:
+                    return site.tsym.isInterface() ?
+                            ClassFile.CONSTANT_InterfaceMethodref : ClassFile.CONSTANT_Methodref;
+                default:
+                    Assert.error("Invalid symbol: " + sym);
+                    return -1;
+            }
+        }
+    };
+
+    /**
+     * Helper function: retrieves descriptor associated with given type.
+     */
+    private Name typeToSig(Type t) {
+        return types.typeDesc(t, this::enterInner);
+    }
+
+    /**
+     * Helper function: retrieves class name associated with given type.
+     */
+    private Name typeToClassName(Type t) {
+        Name name = typeToSig(t);
+        return name.toString().startsWith("L") || name.toString().startsWith("Q") ?
+                    types.names.fromString(name.toString().substring(1, name.length() - 1)) :
+                    name;
+    }
+
+    /**
+     * Create a new entry entry from given type.
+     */
+    private Entry makeType(Type t, Function<Type, Name> typeNameFunction) {
+        TypeFactory tf = !poolModes.contains(PoolMode.SIGNATURES) ?
+                legacyTypeFactory : genericTypeFactory;
+        return t.accept(tf, typeNameFunction);
+    }
+
+    /**
+     * Legacy factory used to create entries from types. All types are mapped into Utf8 entries.
+     */
+    class TypeFactory extends DefaultTypeVisitor<Entry, Function<Type, Name>> {
+        @Override
+        public Entry visitType(Type t, Function<Type, Name> typeNameFunc) {
+            //compatibility mode - always return nominal type signatures
+            Constant<Name> type = new Constant<>(ClassFile.CONSTANT_Utf8, typeNameFunc.apply(t), t);
+            return pool.put(type);
+        }
+
+        @Override
+        public Entry visitMethodType(Type.MethodType t, Function<Type, Name> typeNameFunction) {
+            if (t.isConstant()) {
+                Entry methodType = visitType(t.baseType(), PoolWriter.this::typeToSig);
+                Entry e = new MethodType(methodType, t.asMethodType());
+                return pool.put(e);
+            } else {
+                return visitType(t, typeNameFunction);
+            }
+        }
+    }
+
+    /** Legacy type factory. */
+    TypeFactory legacyTypeFactory = new TypeFactory();
+
+    /** Generic type factory. Some entries are mapped into new structural forms. */
+    TypeFactory genericTypeFactory = new TypeFactory() {
+
+        final String arrayTemplate = "[[[[[[[[[[[[[[[[#";
+
+        @Override
+        public Entry visitType(Type t, Function<Type, Name> typeNameFunc) {
+            if (t.hasTag(ANY_BOUND)) {
+                return visit(types.syms.objectType, typeNameFunc);
+            } else {
+                return super.visitType(t, typeNameFunc);
+            }
+        }
+
+        @Override
+        public Entry visitCapturedType(CapturedType t, Function<Type, Name> typeNameFunc) {
+            return visit(t.wildcard, typeNameFunc);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Entry visitTypeVar(TypeVar tv, Function<Type, Name> typeNameFunc) {
+            useNewCPForms = true;
+            Constant<Name> name = (Constant<Name>)makeConstant(tv.tsym.name);
+            Entry owner = makeSymbol(tv.tsym.owner);
+            Entry[] bounds = types.getBounds(tv).stream()
+                    .map(b -> visit(b, typeNameFunc))
+                    .toArray(Entry[]::new);
+            Entry e = new TypeVariable(name, owner, bounds, tv);
+            return pool.put(e);
+        }
+
+        @Override
+        public Entry visitWildcardType(WildcardType t, Function<Type, Name> typeNameFunc) {
+            return visit(t.type, typeNameFunc);
+        }
+
+        @Override
+        public Entry visitClassType(ClassType t, Function<Type, Name> typeNameFunc) {
+            if (t.allparams().isEmpty()) {
+                return super.visitClassType(t, typeNameFunc);
+            } else {
+                useNewCPForms = true;
+                ClassRef clazz = (ClassRef)makeSymbol(t.tsym);
+                Entry[] typeargs = t.getTypeArguments().stream()
+                        .map(ta -> visit(ta, PoolWriter.this::typeToSig))
+                        .toArray(Entry[]::new);
+                Entry e = new ParameterizedType(clazz, typeargs, t);
+                return pool.put(e);
+            }
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Entry visitArrayType(ArrayType t, Function<Type, Name> typeNameFunc) {
+            Type elemRec = elemtypeRecursive(t);
+            if (elemRec.hasTag(CLASS) && elemRec.allparams().isEmpty()) {
+                return super.visitArrayType(t, typeNameFunc);
+            } else {
+                useNewCPForms = true;
+                int dim = types.dimensions(t);
+                Assert.check(dim <= 16);
+                Constant<Name> template = (Constant<Name>)makeConstant(types.names.fromString(
+                        arrayTemplate.substring(arrayTemplate.length() - 1 - dim, arrayTemplate.length())));
+                Entry[] holes = new Entry[] { visit(elemRec, typeNameFunc) };
+                Entry e = new CompoundType(template, holes, t);
+                return pool.put(e);
+            }
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Entry visitMethodType(Type.MethodType t, Function<Type, Name> typeNameFunc) {
+            if (t.isConstant()) {
+                return super.visitMethodType(t, typeNameFunc);
+            } else {
+                useNewCPForms = true;
+                List<Type> holes = methodHoleTypes(t);
+                Function<Type, String> holeMapper = h -> h.hasTag(NONE) ? "#" : typeToSig(h).toString();
+                String args = holes.tail.stream()
+                        .map(holeMapper)
+                        .collect(Collectors.joining("","(",")"));
+                String ret = holeMapper.apply(holes.head);
+                Entry e = new CompoundType((Constant<Name>)makeConstant(types.names.fromString(args + ret)),
+                        holes.stream()
+                                .filter(h -> h.hasTag(NONE))
+                                .map(h -> makeType(h.baseType(), PoolWriter.this::typeToSig))
+                                .toArray(Entry[]::new), t);
+                return pool.put(e);
+            }
+        }
+
+        private List<Type> methodHoleTypes(Type.MethodType type) {
+            return type.getParameterTypes().prepend(type.getReturnType()).stream()
+                    .map(t -> (t.isPrimitiveOrVoid() || (t.hasTag(TypeTag.CLASS) && t.allparams().isEmpty())) ?
+                            t : new DelegatedType(TypeTag.NONE, t) {
+                                @Override
+                                public Type cloneWithMetadata(TypeMetadata metadata) {
+                                    return null;
+                                }
+                                @Override
+                                public Type baseType() {
+                                    return t;
+                                }
+                    })
+                    .collect(List.collector());
+        }
+
+        private Type elemtypeRecursive(Type type) {
+            Type arr = type;
+            while (types.elemtype(arr) != null) {
+                arr = types.elemtype(arr);
+            }
+            return arr;
+        }
+    };
+
+    /**
+     * Write pool contents into given byte buffer.
+     */
+    @SuppressWarnings("unchecked")
+    void writePool(ByteBuffer poolbuf) throws PoolOverflow, StringOverflow {
+        int size = pool.size();
+        if (size > Pool.MAX_ENTRIES) {
+            throw new PoolOverflow();
+        }
+        poolbuf.appendChar(size);
+        for (Entry e : pool) {
+            poolbuf.appendByte(e.tag);
+            Assert.check(e.index > 0);
+            switch (e.tag) {
+                case ClassFile.CONSTANT_InterfaceMethodref:
+                case ClassFile.CONSTANT_Methodref:
+                case ClassFile.CONSTANT_Fieldref:
+                    MemberRef ref = (MemberRef)e;
+                    poolbuf.appendChar(ref.owner.index);
+                    poolbuf.appendChar(ref.nt.index);
+                    break;
+                case ClassFile.CONSTANT_Integer:
+                    poolbuf.appendInt(((Constant<Integer>)e).value);
+                    break;
+                case ClassFile.CONSTANT_Long:
+                    poolbuf.appendLong(((Constant<Long>)e).value);
+                    break;
+                case ClassFile.CONSTANT_Float:
+                    poolbuf.appendFloat(((Constant<Float>)e).value);
+                    break;
+                case ClassFile.CONSTANT_Double:
+                    poolbuf.appendDouble(((Constant<Double>)e).value);
+                    break;
+                case ClassFile.CONSTANT_Utf8:
+                    Name value = ((Constant<Name>)e).value;
+                    byte[] bs = value.toUtf();
+                    poolbuf.appendChar(bs.length);
+                    poolbuf.appendBytes(bs, 0, bs.length);
+                    if (bs.length > Pool.MAX_STRING_LENGTH) {
+                        throw new StringOverflow(new String(bs));
+                    }
+                    break;
+                case ClassFile.CONSTANT_String:
+                    poolbuf.appendChar(((StringRef)e).strValue.index);
+                    break;
+                case ClassFile.CONSTANT_Class:
+                    poolbuf.appendChar(((ClassRef)e).clazz.index);
+                    break;
+                case ClassFile.CONSTANT_NameandType:
+                    poolbuf.appendChar(((NameAndType)e).name.index);
+                    poolbuf.appendChar(((NameAndType)e).type.index);
+                    break;
+                case ClassFile.CONSTANT_MethodHandle:
+                    poolbuf.appendByte(((MethodHandle)e).refKind);
+                    poolbuf.appendChar(((MethodHandle)e).ref.index);
+                    break;
+                case ClassFile.CONSTANT_MethodType:
+                    poolbuf.appendChar(((MethodType)e).methodType.index);
+                    break;
+                case ClassFile.CONSTANT_InvokeDynamic:
+                    poolbuf.appendChar(((InvokeDynamic)e).bsmIndex);
+                    poolbuf.appendChar(((InvokeDynamic)e).nt.index);
+                    break;
+                case ClassFile.CONSTANT_TypeVar:
+                    poolbuf.appendChar(((TypeVariable)e).name.index);
+                    poolbuf.appendChar(((TypeVariable)e).owner.index);
+                    poolbuf.appendByte(((TypeVariable)e).bounds.length);
+                    Stream.of(((TypeVariable)e).bounds).mapToInt(b -> b.index).forEach(poolbuf::appendChar);
+                    break;
+                case ClassFile.CONSTANT_ParameterizedType:
+                    poolbuf.appendChar(((ParameterizedType)e).clazz.index);
+                    poolbuf.appendByte(((ParameterizedType)e).typeargs.length);
+                    Stream.of(((ParameterizedType)e).typeargs).mapToInt(ta -> ta.index).forEach(poolbuf::appendChar);
+                    break;
+                case ClassFile.CONSTANT_CompoundType:
+                    poolbuf.appendChar(((CompoundType)e).template.index);
+                    poolbuf.appendByte(((CompoundType)e).holes.length);
+                    Stream.of(((CompoundType)e).holes).mapToInt(h -> h.index).forEach(poolbuf::appendChar);
+                    break;
+                default:
+                    Assert.error("Unsupported entry: " + e.tag);
+            }
+        }
+    }
+
+    /**
+     * Class used as a key into the BootstrapMethods attribute.
+     */
+    static class BootstrapMethodsKey {
+
+        /** Static args entries. */
+        Entry[] staticArgs;
+
+        BootstrapMethodsKey(Entry[] staticArgs) {
+            this.staticArgs = staticArgs;
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(staticArgs);
+        }
+
+        public boolean equals(Object o) {
+            if (o instanceof BootstrapMethodsKey) {
+                BootstrapMethodsKey that = (BootstrapMethodsKey)o;
+                return Objects.deepEquals(that.staticArgs, staticArgs);
+            } else {
+                return false;
+            }
+        }
+    }
+}