changeset 3273:0c84a5536918

Enhancement: refactor ClassReader to use Pool, PoolReader
author mcimadamore
date Fri, 11 Dec 2015 14:20:54 +0000
parents 6acf2c8bf289
children b6b09873c7b2
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolReader.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties src/jdk.compiler/share/classes/com/sun/tools/javac/util/ByteBuffer.java test/tools/javac/diags/examples.not-yet.txt
diffstat 8 files changed, 565 insertions(+), 380 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Fri Dec 11 14:20:54 2015 +0000
@@ -2046,7 +2046,7 @@
                         };
                         break;
                     case CAPTURED_OUTER_THIS:
-                        Name name = names.fromString(new String(sym.flatName().toString() + names.dollarThis));
+                        Name name = names.fromString(sym.flatName().toString().replace(".", "$") + names.dollarThis);
                         ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
                             @Override
                             public Symbol baseSymbol() {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Fri Dec 11 14:20:54 2015 +0000
@@ -35,7 +35,9 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.IntFunction;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
 import com.sun.tools.javac.comp.Annotate;
@@ -143,20 +145,15 @@
 
     /** The buffer containing the currently read class file.
      */
-    byte[] buf = new byte[INITIAL_BUFFER_SIZE];
+    ByteBuffer buf;
 
     /** The current input pointer.
      */
     protected int bp;
 
-    /** The objects of the constant pool.
+    /** The pool reader.
      */
-    Object[] poolObj;
-
-    /** For every constant pool entry, an index into buf where the
-     *  defining section of the entry is found.
-     */
-    int[] poolIdx;
+    PoolReader poolReader;
 
     /** The major version number of the class file being read. */
     int majorVersion;
@@ -265,259 +262,23 @@
     /** Read a character.
      */
     char nextChar() {
-        return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
+        char res = buf.getChar(bp);
+        bp += 2;
+        return res;
     }
 
     /** Read a byte.
      */
     int nextByte() {
-        return buf[bp++] & 0xFF;
+        return buf.getByte(bp++) & 0xFF;
     }
 
     /** Read an integer.
      */
     int nextInt() {
-        return
-            ((buf[bp++] & 0xFF) << 24) +
-            ((buf[bp++] & 0xFF) << 16) +
-            ((buf[bp++] & 0xFF) << 8) +
-            (buf[bp++] & 0xFF);
-    }
-
-    /** Extract a character at position bp from buf.
-     */
-    char getChar(int bp) {
-        return
-            (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF));
-    }
-
-    /** Extract an integer at position bp from buf.
-     */
-    int getInt(int bp) {
-        return
-            ((buf[bp] & 0xFF) << 24) +
-            ((buf[bp+1] & 0xFF) << 16) +
-            ((buf[bp+2] & 0xFF) << 8) +
-            (buf[bp+3] & 0xFF);
-    }
-
-
-    /** Extract a long integer at position bp from buf.
-     */
-    long getLong(int bp) {
-        DataInputStream bufin =
-            new DataInputStream(new ByteArrayInputStream(buf, bp, 8));
-        try {
-            return bufin.readLong();
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
-    }
-
-    /** Extract a float at position bp from buf.
-     */
-    float getFloat(int bp) {
-        DataInputStream bufin =
-            new DataInputStream(new ByteArrayInputStream(buf, bp, 4));
-        try {
-            return bufin.readFloat();
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
-    }
-
-    /** Extract a double at position bp from buf.
-     */
-    double getDouble(int bp) {
-        DataInputStream bufin =
-            new DataInputStream(new ByteArrayInputStream(buf, bp, 8));
-        try {
-            return bufin.readDouble();
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
-    }
-
-/************************************************************************
- * Constant Pool Access
- ***********************************************************************/
-
-    /** Index all constant pool entries, writing their start addresses into
-     *  poolIdx.
-     */
-    void indexPool() {
-        poolIdx = new int[nextChar()];
-        poolObj = new Object[poolIdx.length];
-        int i = 1;
-        while (i < poolIdx.length) {
-            poolIdx[i++] = bp;
-            byte tag = buf[bp++];
-            switch (tag) {
-            case CONSTANT_Utf8: case CONSTANT_Unicode: {
-                int len = nextChar();
-                bp = bp + len;
-                break;
-            }
-            case CONSTANT_Class:
-            case CONSTANT_String:
-            case CONSTANT_MethodType:
-                bp = bp + 2;
-                break;
-            case CONSTANT_MethodHandle:
-                bp = bp + 3;
-                break;
-            case CONSTANT_Fieldref:
-            case CONSTANT_Methodref:
-            case CONSTANT_InterfaceMethodref:
-            case CONSTANT_NameandType:
-            case CONSTANT_Integer:
-            case CONSTANT_Float:
-            case CONSTANT_InvokeDynamic:
-                bp = bp + 4;
-                break;
-            case CONSTANT_Long:
-            case CONSTANT_Double:
-                bp = bp + 8;
-                i++;
-                break;
-            default:
-                throw badClassFile("bad.const.pool.tag.at",
-                                   Byte.toString(tag),
-                                   Integer.toString(bp -1));
-            }
-        }
-    }
-
-    /** Read constant pool entry at start address i, use pool as a cache.
-     */
-    Object readPool(int i) {
-        Object result = poolObj[i];
-        if (result != null) return result;
-
-        int index = poolIdx[i];
-        if (index == 0) return null;
-
-        byte tag = buf[index];
-        switch (tag) {
-        case CONSTANT_Utf8:
-            poolObj[i] = names.fromUtf(buf, index + 3, getChar(index + 1));
-            break;
-        case CONSTANT_Unicode:
-            throw badClassFile("unicode.str.not.supported");
-        case CONSTANT_Class:
-            poolObj[i] = readClassOrType(getChar(index + 1));
-            break;
-        case CONSTANT_String:
-            // FIXME: (footprint) do not use toString here
-            poolObj[i] = readName(getChar(index + 1)).toString();
-            break;
-        case CONSTANT_Fieldref: {
-            ClassSymbol owner = readClassSymbol(getChar(index + 1));
-            Tuple2<Name, Type> nt = readNameAndType(getChar(index + 3));
-            poolObj[i] = new VarSymbol(0, nt.elem0, nt.elem1, owner);
-            break;
-        }
-        case CONSTANT_Methodref:
-        case CONSTANT_InterfaceMethodref: {
-            ClassSymbol owner = readClassSymbol(getChar(index + 1));
-            Tuple2<Name, Type> nt = readNameAndType(getChar(index + 3));
-            poolObj[i] = new ReaderMethodSymbol(0, nt.elem0, nt.elem1, owner);
-            break;
-        }
-        case CONSTANT_NameandType:
-            poolObj[i] = new Tuple2<>(
-                readName(getChar(index + 1)),
-                readType(getChar(index + 3)));
-            break;
-        case CONSTANT_Integer:
-            poolObj[i] = getInt(index + 1);
-            break;
-        case CONSTANT_Float:
-            poolObj[i] = new Float(getFloat(index + 1));
-            break;
-        case CONSTANT_Long:
-            poolObj[i] = new Long(getLong(index + 1));
-            break;
-        case CONSTANT_Double:
-            poolObj[i] = new Double(getDouble(index + 1));
-            break;
-        case CONSTANT_MethodHandle:
-            skipBytes(4);
-            break;
-        case CONSTANT_MethodType:
-            skipBytes(3);
-            break;
-        case CONSTANT_InvokeDynamic:
-            skipBytes(5);
-            break;
-        default:
-            throw badClassFile("bad.const.pool.tag", Byte.toString(tag));
-        }
-        return poolObj[i];
-    }
-
-    /** Read signature and convert to type.
-     */
-    Type readType(int i) {
-        int index = poolIdx[i];
-        return sigToType(buf, index + 3, getChar(index + 1));
-    }
-
-    /** If name is an array type or class signature, return the
-     *  corresponding type; otherwise return a ClassSymbol with given name.
-     */
-    Object readClassOrType(int i) {
-        int index =  poolIdx[i];
-        int len = getChar(index + 1);
-        int start = index + 3;
-        Assert.check(buf[start] == '[' || buf[start + len - 1] != ';');
-        // by the above assertion, the following test can be
-        // simplified to (buf[start] == '[')
-        return (buf[start] == '[' || buf[start + len - 1] == ';')
-            ? (Object)sigToType(buf, start, len)
-            : (Object)syms.enterClass(names.fromUtf(internalize(buf, start,
-                                                           len)));
-    }
-
-    /** Read signature and convert to type parameters.
-     */
-    List<Type> readTypeParams(int i) {
-        int index = poolIdx[i];
-        return sigToTypeParams(buf, index + 3, getChar(index + 1));
-    }
-
-    /** Read class entry.
-     */
-    ClassSymbol readClassSymbol(int i) {
-        Object obj = readPool(i);
-        if (obj != null && !(obj instanceof ClassSymbol))
-            throw badClassFile("bad.const.pool.entry",
-                               currentClassFile.toString(),
-                               "CONSTANT_Class_info", i);
-        return (ClassSymbol)obj;
-    }
-
-    /** Read name.
-     */
-    Name readName(int i) {
-        Object obj = readPool(i);
-        if (obj != null && !(obj instanceof Name))
-            throw badClassFile("bad.const.pool.entry",
-                               currentClassFile.toString(),
-                               "CONSTANT_Utf8_info or CONSTANT_String_info", i);
-        return (Name)obj;
-    }
-
-    /** Read name and type.
-     */
-    @SuppressWarnings("unchecked")
-    Tuple2<Name, Type> readNameAndType(int i) {
-        Object obj = readPool(i);
-        if (obj != null && !(obj instanceof Tuple2<?, ?>))
-            throw badClassFile("bad.const.pool.entry",
-                               currentClassFile.toString(),
-                               "CONSTANT_NameAndType_info", i);
-        return (Tuple2<Name, Type>)obj;
+        int res = buf.getInt(bp);
+        bp += 4;
+        return res;
     }
 
 /************************************************************************
@@ -617,7 +378,7 @@
             List<Type> argtypes = sigToTypes(')');
             Type restype = sigToType();
             List<Type> thrown = List.nil();
-            while (signature[sigp] == '^') {
+            while (sigp < siglimit && signature[sigp] == '^') {
                 sigp++;
                 thrown = thrown.prepend(sigToType());
             }
@@ -914,7 +675,7 @@
 
             new AttributeReader(names.ConstantValue, V45_3, MEMBER_ATTRIBUTE) {
                 protected void read(Symbol sym, int attrLen) {
-                    Object v = readPool(nextChar());
+                    Object v = poolReader.getConstant(nextChar());
                     // Ignore ConstantValue attribute if field not final.
                     if ((sym.flags() & FINAL) != 0)
                         ((VarSymbol) sym).setData(v);
@@ -932,9 +693,9 @@
                     int nexceptions = nextChar();
                     List<Type> thrown = List.nil();
                     for (int j = 0; j < nexceptions; j++)
-                        thrown = thrown.prepend(readClassSymbol(nextChar()).type);
+                        thrown = thrown.prepend(poolReader.getClass(nextChar()).type);
                     if (sym.type.getThrownTypes().isEmpty())
-                        sym.type.asMethodType().thrown = thrown.reverse();
+                        sym.type = types.createMethodTypeWithThrown(sym.type, thrown.reverse());
                 }
             },
 
@@ -1003,7 +764,7 @@
             new AttributeReader(names.SourceFile, V45_3, CLASS_ATTRIBUTE) {
                 protected void read(Symbol sym, int attrLen) {
                     ClassSymbol c = (ClassSymbol) sym;
-                    Name n = readName(nextChar());
+                    Name n = poolReader.getName(nextChar());
                     c.sourcefile = new SourceFileObject(n, c.flatname);
                     // If the class is a toplevel class, originating from a Java source file,
                     // but the class name does not match the file name, then it is
@@ -1041,7 +802,7 @@
                         try {
                             ClassType ct1 = (ClassType)c.type;
                             Assert.check(c == currentOwner);
-                            ct1.typarams_field = readTypeParams(nextChar());
+                            ct1.typarams_field = poolReader.getTypeParams(nextChar());
                             ct1.supertype_field = sigToType();
                             ListBuffer<Type> is = new ListBuffer<>();
                             while (sigp != siglimit) is.append(sigToType());
@@ -1051,11 +812,10 @@
                         }
                     } else {
                         List<Type> thrown = sym.type.getThrownTypes();
-                        sym.type = readType(nextChar());
+                        sym.type = poolReader.getType(nextChar());
                         //- System.err.println(" # " + sym.type);
                         if (sym.kind == MTH && sym.type.getThrownTypes().isEmpty())
-                            sym.type.asMethodType().thrown = thrown;
-
+                            sym.type = types.createMethodTypeWithThrown(sym.type, thrown);
                     }
                 }
             },
@@ -1199,7 +959,7 @@
                     Assert.check(sym.kind == MTH && nentries > 0);
                     WhereClause whereClauses = new WhereClause();
                     for (int i = 0 ; i < nentries ; i++) {
-                        Type t = readType(nextChar());
+                        Type t = poolReader.getType(nextChar());
                         WhereClause.Kind wk = WhereClause.Kind.from(nextChar());
                         whereClauses.put(t.tsym, wk);
                     }
@@ -1250,8 +1010,12 @@
         // the scope specified by the attribute
         sym.owner.members().remove(sym);
         ClassSymbol self = (ClassSymbol)sym;
-        ClassSymbol c = readClassSymbol(nextChar());
-        Tuple2<Name, Type> nt = readNameAndType(nextChar());
+        ClassSymbol c = poolReader.getClass(nextChar());
+        int methodIndex = nextChar();
+        Tuple2<Name, Type> nt = null;
+        if (methodIndex > 0) {
+            nt = poolReader.getNameAndType(methodIndex);
+        }
 
         if (c.members_field == null)
             throw badClassFile("bad.enclosing.class", self, c);
@@ -1366,7 +1130,7 @@
     void readAttrs(Symbol sym, AttributeKind kind) {
         char ac = nextChar();
         for (int i = 0; i < ac; i++) {
-            Name attrName = readName(nextChar());
+            Name attrName = poolReader.getName(nextChar());
             int attrLen = nextInt();
             AttributeReader r = attributeReaders.get(attrName);
             if (r != null && r.accepts(kind))
@@ -1445,7 +1209,7 @@
      */
     void attachParameterAnnotations(final Symbol method) {
         final MethodSymbol meth = (MethodSymbol)method;
-        int numParameters = buf[bp++] & 0xFF;
+        int numParameters = buf.getByte(bp++) & 0xFF;
         List<VarSymbol> parameters = meth.params();
         int pnum = 0;
         while (parameters.tail != null) {
@@ -1486,27 +1250,12 @@
         annotate.normal(new AnnotationDefaultCompleter(meth, value));
     }
 
-    Type readTypeOrClassSymbol(int i) {
-        // support preliminary jsr175-format class files
-        if (buf[poolIdx[i]] == CONSTANT_Class)
-            return readClassSymbol(i).type;
-        return readType(i);
-    }
-    Type readEnumType(int i) {
-        // support preliminary jsr175-format class files
-        int index = poolIdx[i];
-        int length = getChar(index + 1);
-        if (buf[index + length + 2] != ';')
-            return syms.enterClass(readName(i)).type;
-        return readType(i);
-    }
-
     CompoundAnnotationProxy readCompoundAnnotation() {
-        Type t = readTypeOrClassSymbol(nextChar());
+        Type t = poolReader.getType(nextChar());
         int numFields = nextChar();
         ListBuffer<Tuple2<Name,Attribute>> pairs = new ListBuffer<>();
         for (int i=0; i<numFields; i++) {
-            Name name = readName(nextChar());
+            Name name = poolReader.getName(nextChar());
             Attribute value = readAttributeValue();
             pairs.append(new Tuple2<>(name, value));
         }
@@ -1719,31 +1468,42 @@
 
     }
 
+    /**
+     * Helper function to read an optional pool entry (with given function); this is used while parsing
+     * InnerClasses and EnclosingMethod attributes, as well as when parsing supertype descriptor,
+     * as per JVMS.
+     */
+    <Z> Z optPoolEntry(int index, IntFunction<Z> poolFunc, Z defaultValue) {
+        return (index == 0) ?
+                defaultValue :
+                poolFunc.apply(index);
+    }
+
     Attribute readAttributeValue() {
-        char c = (char) buf[bp++];
+        char c = (char) buf.getByte(bp++);
         switch (c) {
         case 'B':
-            return new Attribute.Constant(syms.byteType, readPool(nextChar()));
+            return new Attribute.Constant(syms.byteType, poolReader.getConstant(nextChar()));
         case 'C':
-            return new Attribute.Constant(syms.charType, readPool(nextChar()));
+            return new Attribute.Constant(syms.charType, poolReader.getConstant(nextChar()));
         case 'D':
-            return new Attribute.Constant(syms.doubleType, readPool(nextChar()));
+            return new Attribute.Constant(syms.doubleType, poolReader.getConstant(nextChar()));
         case 'F':
-            return new Attribute.Constant(syms.floatType, readPool(nextChar()));
+            return new Attribute.Constant(syms.floatType, poolReader.getConstant(nextChar()));
         case 'I':
-            return new Attribute.Constant(syms.intType, readPool(nextChar()));
+            return new Attribute.Constant(syms.intType, poolReader.getConstant(nextChar()));
         case 'J':
-            return new Attribute.Constant(syms.longType, readPool(nextChar()));
+            return new Attribute.Constant(syms.longType, poolReader.getConstant(nextChar()));
         case 'S':
-            return new Attribute.Constant(syms.shortType, readPool(nextChar()));
+            return new Attribute.Constant(syms.shortType, poolReader.getConstant(nextChar()));
         case 'Z':
-            return new Attribute.Constant(syms.booleanType, readPool(nextChar()));
+            return new Attribute.Constant(syms.booleanType, poolReader.getConstant(nextChar()));
         case 's':
-            return new Attribute.Constant(syms.stringType, readPool(nextChar()).toString());
+            return new Attribute.Constant(syms.stringType, poolReader.getConstant(nextChar()).toString());
         case 'e':
-            return new EnumAttributeProxy(readEnumType(nextChar()), readName(nextChar()));
+            return new EnumAttributeProxy(poolReader.getType(nextChar()), poolReader.getName(nextChar()));
         case 'c':
-            return new Attribute.Class(types, readTypeOrClassSymbol(nextChar()));
+            return new Attribute.Class(types, poolReader.getType(nextChar()));
         case '[': {
             int n = nextChar();
             ListBuffer<Attribute> l = new ListBuffer<>();
@@ -2098,8 +1858,8 @@
      */
     VarSymbol readField() {
         long flags = adjustFieldFlags(nextChar());
-        Name name = readName(nextChar());
-        Type type = readType(nextChar());
+        Name name = poolReader.getName(nextChar());
+        Type type = poolReader.getType(nextChar());
         VarSymbol v = new VarSymbol(flags, name, type, currentOwner);
         readMemberAttrs(v);
         return v;
@@ -2109,8 +1869,8 @@
      */
     MethodSymbol readMethod() {
         long flags = adjustMethodFlags(nextChar());
-        Name name = readName(nextChar());
-        Type type = readType(nextChar());
+        Name name = poolReader.getName(nextChar());
+        Type type = poolReader.getType(nextChar());
         if (currentOwner.isInterface() &&
                 (flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
             if (majorVersion > Version.V52.major ||
@@ -2271,20 +2031,13 @@
         for (Type t: sym.type.getParameterTypes()) {
             int nameIdx = (index < parameterNameIndices.length
                     ? parameterNameIndices[index] : 0);
-            Name name = nameIdx == 0 ? names.empty : readName(nameIdx);
+            Name name = optPoolEntry(nameIdx, poolReader::getName, names.empty);
             paramNames = paramNames.prepend(name);
             index += Code.width(t);
         }
         sym.savedParameterNames = paramNames.reverse();
     }
 
-    /**
-     * skip n bytes
-     */
-    void skipBytes(int n) {
-        bp = bp + n;
-    }
-
     /** Skip a field or method
      */
     void skipMember() {
@@ -2334,7 +2087,7 @@
         if (c.owner.kind == PCK) c.flags_field = flags;
 
         // read own class name and check that it matches
-        ClassSymbol self = readClassSymbol(nextChar());
+        ClassSymbol self = poolReader.getClass(nextChar());
         if (c != self)
             throw badClassFile("class.file.wrong.class",
                                self.flatname);
@@ -2355,13 +2108,12 @@
         bp = startbp;
         int n = nextChar();
         if (ct.supertype_field == null)
-            ct.supertype_field = (n == 0)
-                ? Type.noType
-                : readClassSymbol(n).erasure(types);
+            ct.supertype_field = optPoolEntry(n,
+                    (idx) -> poolReader.getClass(idx).erasure(types), Type.noType);
         n = nextChar();
         List<Type> is = List.nil();
         for (int i = 0; i < n; i++) {
-            Type _inter = readClassSymbol(nextChar()).erasure(types);
+            Type _inter = poolReader.getClass(nextChar()).erasure(types);
             is = is.prepend(_inter);
         }
         if (ct.interfaces_field == null)
@@ -2387,9 +2139,10 @@
         int n = nextChar();
         for (int i = 0; i < n; i++) {
             nextChar(); // skip inner class symbol
-            ClassSymbol outer = readClassSymbol(nextChar());
-            Name name = readName(nextChar());
-            if (name == null) name = names.empty;
+            int outerIdx = nextChar();
+            int nameIdx = nextChar();
+            ClassSymbol outer = optPoolEntry(outerIdx, poolReader::getClass, null);
+            Name name = optPoolEntry(nameIdx, poolReader::getName, names.empty);
             long flags = adjustClassFlags(nextChar());
             if (name.toString().endsWith("$any")) {
                 flags |= ACYCLIC | VIRTUAL;
@@ -2445,7 +2198,8 @@
             printCCF("found.later.version",
                      Integer.toString(minorVersion));
         }
-        indexPool();
+        poolReader = new PoolReader(this, types);
+        bp = poolReader.readPool(buf, bp);
         if (signatureBuffer.length < bp) {
             int ns = Integer.highestOneBit(bp) << 1;
             signatureBuffer = new byte[ns];
@@ -2462,7 +2216,8 @@
         repeatable = null;
         try {
             bp = 0;
-            buf = readInputStream(buf, c.classfile.openInputStream());
+            buf = new ByteBuffer();
+            buf.appendStream(c.classfile.openInputStream());
             readClassBuffer(c);
             if (!missingTypeVariables.isEmpty() && !foundTypeVariables.isEmpty()) {
                 List<Type> missing = missingTypeVariables;
@@ -2496,43 +2251,6 @@
             filling = false;
         }
     }
-    // where
-        private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException {
-            try {
-                buf = ensureCapacity(buf, s.available());
-                int r = s.read(buf);
-                int bp = 0;
-                while (r != -1) {
-                    bp += r;
-                    buf = ensureCapacity(buf, bp);
-                    r = s.read(buf, bp, buf.length - bp);
-                }
-                return buf;
-            } finally {
-                try {
-                    s.close();
-                } catch (IOException e) {
-                    /* Ignore any errors, as this stream may have already
-                     * thrown a related exception which is the one that
-                     * should be reported.
-                     */
-                }
-            }
-        }
-        /*
-         * ensureCapacity will increase the buffer as needed, taking note that
-         * the new buffer will always be greater than the needed and never
-         * exactly equal to the needed size or bp. If equal then the read (above)
-         * will infinitely loop as buf.length - bp == 0.
-         */
-        private static byte[] ensureCapacity(byte[] buf, int needed) {
-            if (buf.length <= needed) {
-                byte[] old = buf;
-                buf = new byte[Integer.highestOneBit(needed) << 1];
-                System.arraycopy(old, 0, buf, 0, old.length);
-            }
-            return buf;
-        }
 
     /** We can only read a single class file at a time; this
      *  flag keeps track of when we are currently reading a class
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java	Fri Dec 11 14:20:54 2015 +0000
@@ -998,10 +998,10 @@
     /** The type of a class pool entry. */
     private Type classForPool(int idx) {
         Pool.Entry e = pool.get(idx);
-        switch (e.tag) {
-            case CONSTANT_Class: return (Type)e.data.get();
-            default:
-                throw new AssertionError("Invalid type of constant pool entry: " + e.data.get());
+        if (e.tag == CONSTANT_Class) {
+            return (Type)e.data.get();
+        } else {
+            throw new AssertionError("Invalid type of constant pool entry: " + e.data.get());
         }
     }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java	Fri Dec 11 14:20:54 2015 +0000
@@ -89,12 +89,10 @@
     }
 
     /**
-     * Pool size.
+     * Pool size (note, as per JVM spec, this is the number of entries + 1).
      */
     int size() {
-        return 1 + entries.keySet().stream()
-                .mapToInt(Entry::size)
-                .sum();
+        return currIndex;
     }
 
     @Override
@@ -119,13 +117,6 @@
         /** Compiler data associated with this entry (optional). */
         Optional<Object> data;
 
-        /**
-         * The entry size.
-         */
-        int size() {
-            return 1;
-        }
-
         Entry(int tag) {
             this(tag, null);
         }
@@ -134,6 +125,19 @@
             this.tag = tag;
             this.data = Optional.ofNullable(data);
         }
+
+        /**
+         * The entry size.
+         */
+        final int size() {
+            switch (tag) {
+                case ClassFile.CONSTANT_Long:
+                case ClassFile.CONSTANT_Double:
+                    return 2;
+                default:
+                    return 1;
+            }
+        }
     }
 
     /**
@@ -260,17 +264,6 @@
                 return false;
             }
         }
-
-        @Override
-        int size() {
-            switch (tag) {
-                case ClassFile.CONSTANT_Long:
-                case ClassFile.CONSTANT_Double:
-                    return 2;
-                default:
-                    return 1;
-            }
-        }
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolReader.java	Fri Dec 11 14:20:54 2015 +0000
@@ -0,0 +1,384 @@
+/*
+ * 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.VarSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.jvm.Pool.Entry;
+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.Optional;
+import java.util.function.Function;
+
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Double;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Fieldref;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Float;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Integer;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_InterfaceMethodref;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_InvokeDynamic;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Long;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodHandle;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Methodref;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodType;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_NameandType;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String;
+import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Utf8;
+import static com.sun.tools.javac.jvm.ClassFile.internalize;
+
+/**
+ * Pool interface towards {@code ClassReader}. Exposes methods to decode and read javac entities
+ * from 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 PoolReader {
+
+    /** The constant pool. */
+    Pool pool;
+
+    ClassReader reader;
+    Types types;
+
+    PoolReader(ClassReader reader, Types types) {
+        this.pool = new Pool();
+        this.reader = reader;
+        this.types = types;
+    }
+
+    /**
+     * Root of special entries used by PoolReader. It's crucial to minimize the number of operations
+     * performed when parsing each pool entry. To achieve that, we create larval entries which
+     * are initially unresolved - and then we resolve them on a by-need basis. Since it's also crucial
+     * to avoid excessive intermediate object creation at this stage, we also skip creation of
+     * regular entries, and we go straight to the underlying javac objects upon resolution.
+     */
+    static abstract class Unresolved<E> extends Entry {
+
+        /** Offset of this entry in the reader byte stream. */
+        int offset;
+        
+        Unresolved(int tag, int offset) {
+            super(tag);
+            this.offset = offset;
+        }
+
+        /**
+         * This method defines the resolution logic associated with this entry.
+         */
+        abstract E doResolve();
+
+        /**
+         * Resolve this entry and return the resolved value.
+         */
+        @SuppressWarnings("unchecked")
+        final E resolve() {
+            if (!data.isPresent()) {
+                data = Optional.of(doResolve());
+            }
+            return (E)data.get();
+        }
+    }
+
+    /**
+     * Unresolved entry pointing to an Utf8 constant.
+     */
+    class UnresolvedName extends Unresolved<Name> {
+
+        /** length of the utf8 constant. */
+        int len;
+
+        UnresolvedName(int start, int len) {
+            super(CONSTANT_Utf8, start);
+            this.offset = start;
+            this.len = len;
+        }
+
+        @Override
+        Name doResolve() {
+            return types.names.fromUtf(reader.buf.elems, offset + 2, len);
+        }
+    }
+
+    /**
+     * Unresolved entry pointing to a pool constant backed by a name (either CONSTANT_Class_info,
+     * or CONSTANT_String_info).
+     */
+    class UnresolvedNameWrapper<E> extends Unresolved<E> {
+
+        /** Mapping function to be used to turn the name into a suitable resolved value. */
+        Function<Name, E> nameMapper;
+
+        UnresolvedNameWrapper(int tag, int start, Function<Name, E> nameMapper) {
+            super(tag, start);
+            this.nameMapper = nameMapper;
+        }
+
+        @Override
+        E doResolve() {
+            return nameMapper.apply(getName(reader.buf.getChar(offset)));
+        }
+    }
+
+    /**
+     * Unresolved entry pointing to a pool constant representing a member (either CONSTANT_FieldRef_info,
+     * or CONSTANT_MethodRef_info, or CONSTANT_InterfaceMethodRef_info).
+     */
+    class UnresolvedMember extends Unresolved<Symbol> {
+
+        UnresolvedMember(int tag, int start) {
+            super(tag, start);
+        }
+
+        @Override
+        Symbol doResolve() {
+            Symbol owner = PoolReader.this.getClass(reader.buf.getChar(offset));
+            Tuple2<Name, Type> nt = getNameAndType(reader.buf.getChar(offset + 2));
+            return (tag == CONSTANT_Fieldref) ?
+                    new VarSymbol(0, nt.elem0, nt.elem1, owner) :
+                    reader.new ReaderMethodSymbol(0, nt.elem0, nt.elem1, owner);
+        }
+    }
+
+    /**
+     * Unresolved entry pointing to a CONSTANT_NameAndType pool constant.
+     */
+    class UnresolvedNameAndType extends Unresolved<Tuple2<Name, Type>> {
+
+        UnresolvedNameAndType(int start) {
+            super(CONSTANT_NameandType, start);
+        }
+
+        @Override
+        Tuple2<Name, Type> doResolve() {
+            Name name = getName(reader.buf.getChar(offset));
+            Name type = getName(reader.buf.getChar(offset + 2));
+            return new Tuple2<>(name, resolveSig(type));
+        }
+    }
+
+    /**
+     * Unresolved entry pointing to a pool entry representing a constant value.
+     */
+    class UnresolvedConstant extends Unresolved<Object> {
+
+        UnresolvedConstant(int tag, int start) {
+            super(tag, start);
+        }
+
+        @Override
+        Object doResolve() {
+            switch (tag) {
+                case CONSTANT_Integer:
+                    return reader.buf.getInt(offset);
+                case CONSTANT_Float:
+                    return reader.buf.getFloat(offset);
+                case CONSTANT_Long:
+                    return reader.buf.getLong(offset);
+                case CONSTANT_Double:
+                    return reader.buf.getDouble(offset);
+                default:
+                    throw new IllegalStateException("Illegal constant entry: " + tag);
+            }
+        }
+    }
+
+    /**
+     * Skipped entry (not all the pool entries need to be parsed by ClassReader).
+     */
+    class Skip extends Unresolved<Void> {
+        public Skip(int tag) {
+            super(tag, -1);
+        }
+
+        @Override
+        Void doResolve() {
+            throw new IllegalStateException("Attempt to resolve skipped entry: " + tag);
+        }
+    }
+
+    /**
+     * Get a class symbol from the pool at given index.
+     */
+    ClassSymbol getClass(int index) {
+        return resolve(index, CONSTANT_Class);
+    }
+
+    /**
+     * Get a type from the pool at given index.
+     */
+    Type getType(int index) {
+        return resolveSig(getName(index));
+    }
+
+    /**
+     * Get a name from the pool at given index.
+     */
+    Name getName(int index) {
+        return resolve(index, CONSTANT_Utf8);
+    }
+
+    /**
+     * Get a name and type pair from the pool at given index.
+     */
+    Tuple2<Name, Type> getNameAndType(int index) {
+        return resolve(index, CONSTANT_NameandType);
+    }
+
+    /**
+     * Get a class symbol from the pool at given index.
+     */
+    Object getConstant(int index) {
+        return resolve(index, -1);
+    }
+
+    /**
+     * Get a type parameter list from the pool at given index.
+     */
+    List<Type> getTypeParams(int index) {
+        Name sig = getName(index);
+        return reader.sigToTypeParams(sig.getByteArray(), sig.getByteOffset(), sig.getByteLength());
+    }
+
+    /**
+     * Parse all constant pool entries, and turn them into unresolved entries.
+     */
+    int readPool(ByteBuffer poolbuf, int offset) {
+        int poolSize = poolbuf.getChar(offset);
+        offset += 2;
+        while (pool.size() < poolSize) {
+            byte tag = poolbuf.getByte(offset++);
+            int start = offset;
+            switch (tag) {
+                case CONSTANT_Utf8: {
+                    int len = poolbuf.getChar(offset);
+                    offset += 2 + len;
+                    pool.put(new UnresolvedName(start, len));
+                    break;
+                }
+                case CONSTANT_Class:
+                    offset += 2;
+                    pool.put(new UnresolvedNameWrapper<>(tag, start, this::resolveClassOrType));
+                    break;
+                case CONSTANT_String:
+                    offset += 2;
+                    pool.put(new UnresolvedNameWrapper<>(tag, start, Name::toString));
+                    break;
+                case CONSTANT_MethodType:
+                    offset += 2;
+                    pool.put(new Skip(tag));
+                    break;
+                case CONSTANT_MethodHandle:
+                    offset += 3;
+                    pool.put(new Skip(tag));
+                    break;
+                case CONSTANT_Fieldref:
+                case CONSTANT_Methodref:
+                case CONSTANT_InterfaceMethodref:
+                    offset += 4;
+                    pool.put(new UnresolvedMember(tag, start));
+                    break;
+                case CONSTANT_NameandType:
+                    offset += 4;
+                    pool.put(new UnresolvedNameAndType(start));
+                    break;
+                case CONSTANT_Integer:
+                case CONSTANT_Float:
+                    offset += 4;
+                    pool.put(new UnresolvedConstant(tag, start));
+                    break;
+                case CONSTANT_Long:
+                case CONSTANT_Double:
+                    offset += 8;
+                    pool.put(new UnresolvedConstant(tag, start));
+                    break;
+                case CONSTANT_InvokeDynamic:
+                    offset += 4;
+                    pool.put(new Skip(tag));
+                    break;                
+                default:
+                    //TODO: deal with Valhalla type entries
+                    throw reader.badClassFile("bad.const.pool.tag.at",
+                            Byte.toString(tag),
+                            Integer.toString(offset - 1));
+            }
+        }
+        return offset;
+    }
+
+    /**
+     * Resolve a pool entry and return its resolved value.
+     */
+    @SuppressWarnings("unchecked")
+    private <E> E resolve(int index, int expectedTag) {
+        if (index <= 0 || index >= pool.size()) {
+            //pool index is outside valid range.
+            throw reader.badClassFile("bad.const.pool.index", reader.currentClassFile.toString(),
+                    index, pool.size());
+        }
+        Unresolved<E> e = (Unresolved<E>)pool.get(index);
+        if (expectedTag != -1 && e.tag != expectedTag) {
+            //resolved entry doesn't match expected tag.
+            throw reader.badClassFile("bad.const.pool.tag.at",
+                        Integer.toString(e.tag),
+                        Integer.toString(index));
+        }
+        return e.resolve();
+    }
+
+    /** If name is an array type or class signature, return the
+     *  corresponding type; otherwise return a ClassSymbol with given name.
+     */
+    private Object resolveClassOrType(Name name) {
+        int start = name.getByteOffset();
+        int len = name.getByteLength();
+        byte[] buf = name.getByteArray();
+        Assert.check(buf[start] == '[' || buf[start + len - 1] != ';');
+        // by the above assertion, the following test can be
+        // simplified to (buf[start] == '[')
+        return (buf[start] == '[' || buf[start + len - 1] == ';')
+            ? reader.sigToType(buf, start, len)
+            : types.syms.enterClass(types.names.fromUtf(internalize(buf, start,
+                                                           len)));
+    }
+
+    /**
+     * Resolve a signature into a type.
+     */
+    private Type resolveSig(Name name) {
+        return reader.sigToType(name.getByteArray(), name.getByteOffset(), name.getByteLength());
+    }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Dec 11 14:20:54 2015 +0000
@@ -1849,6 +1849,11 @@
     bad constant pool entry in {0}\n\
     expected {1} at index {2}
 
+# 0: file name, 1: number (constant pool index), 2: number (constant pool size)
+compiler.misc.bad.const.pool.index=\
+    bad constant pool index in {0}\n\
+    index {1} is not within pool size {2}.
+
 # 0: file name, 1: message segment
 compiler.misc.bad.class.file.header=\
     bad class file: {0}\n\
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/ByteBuffer.java	Tue Dec 08 18:41:55 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/ByteBuffer.java	Fri Dec 11 14:20:54 2015 +0000
@@ -141,6 +141,90 @@
         }
     }
 
+    /** Append the content of a given input stream.
+     */
+    public void appendStream(InputStream is) throws IOException {
+        try {
+            int start = length;
+            elems = ArrayUtils.ensureCapacity(elems, length + is.available());
+            int r = is.read(elems, start, is.available());
+            int bp = start;
+            while (r != -1) {
+                bp += r;
+                elems = ArrayUtils.ensureCapacity(elems, bp);
+                r = is.read(elems, bp, elems.length - bp);
+            }
+        } finally {
+            try {
+                is.close();
+            } catch (IOException e) {
+                /* Ignore any errors, as this stream may have already
+                 * thrown a related exception which is the one that
+                 * should be reported.
+                 */
+            }
+        }
+    }
+
+
+    /** Extract an integer at position bp from elems.
+     */
+    public int getInt(int bp) {
+        return
+            ((elems[bp] & 0xFF) << 24) +
+            ((elems[bp+1] & 0xFF) << 16) +
+            ((elems[bp+2] & 0xFF) << 8) +
+            (elems[bp+3] & 0xFF);
+    }
+
+
+    /** Extract a long integer at position bp from elems.
+     */
+    public long getLong(int bp) {
+        DataInputStream elemsin =
+            new DataInputStream(new ByteArrayInputStream(elems, bp, 8));
+        try {
+            return elemsin.readLong();
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    /** Extract a float at position bp from elems.
+     */
+    public float getFloat(int bp) {
+        DataInputStream elemsin =
+            new DataInputStream(new ByteArrayInputStream(elems, bp, 4));
+        try {
+            return elemsin.readFloat();
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    /** Extract a double at position bp from elems.
+     */
+    public double getDouble(int bp) {
+        DataInputStream elemsin =
+            new DataInputStream(new ByteArrayInputStream(elems, bp, 8));
+        try {
+            return elemsin.readDouble();
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    /** Extract a character at position bp from elems.
+     */
+    public char getChar(int bp) {
+        return
+            (char)(((elems[bp] & 0xFF) << 8) + (elems[bp+1] & 0xFF));
+    }
+
+    public byte getByte(int bp) {
+        return elems[bp];
+    }
+
     /** Append a name.
      */
     public void appendName(Name name) {
--- a/test/tools/javac/diags/examples.not-yet.txt	Tue Dec 08 18:41:55 2015 +0000
+++ b/test/tools/javac/diags/examples.not-yet.txt	Fri Dec 11 14:20:54 2015 +0000
@@ -115,4 +115,5 @@
 compiler.err.cant.inherit.from.anon                     # error for subclass of anonymous class
 compiler.misc.bad.class.file                            # class file is malformed
 compiler.misc.bad.const.pool.entry                      # constant pool entry has wrong type
+compiler.misc.bad.const.pool.index                      # constant pool entry has wrong index
 compiler.misc.incompatible.upper.eq.bounds