src/java.base/share/classes/com/sun/java/util/jar/pack/ClassReader.java
changeset 47216 71c04702a3d5
parent 28059 e576535359cc
child 57956 e0b8b019d2f5
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.java.util.jar.pack;
       
    27 
       
    28 import com.sun.java.util.jar.pack.ConstantPool.ClassEntry;
       
    29 import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
       
    30 import com.sun.java.util.jar.pack.ConstantPool.Entry;
       
    31 import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
       
    32 import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
       
    33 import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
       
    34 import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
       
    35 import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
       
    36 import com.sun.java.util.jar.pack.Package.Class;
       
    37 import com.sun.java.util.jar.pack.Package.InnerClass;
       
    38 import java.io.DataInputStream;
       
    39 import java.io.FilterInputStream;
       
    40 import java.io.IOException;
       
    41 import java.io.InputStream;
       
    42 import java.util.ArrayList;
       
    43 import java.util.Arrays;
       
    44 import java.util.Map;
       
    45 import static com.sun.java.util.jar.pack.Constants.*;
       
    46 
       
    47 /**
       
    48  * Reader for a class file that is being incorporated into a package.
       
    49  * @author John Rose
       
    50  */
       
    51 class ClassReader {
       
    52     int verbose;
       
    53 
       
    54     Package pkg;
       
    55     Class cls;
       
    56     long inPos;
       
    57     long constantPoolLimit = -1;
       
    58     DataInputStream in;
       
    59     Map<Attribute.Layout, Attribute> attrDefs;
       
    60     Map<Attribute.Layout, String> attrCommands;
       
    61     String unknownAttrCommand = "error";;
       
    62 
       
    63     ClassReader(Class cls, InputStream in) throws IOException {
       
    64         this.pkg = cls.getPackage();
       
    65         this.cls = cls;
       
    66         this.verbose = pkg.verbose;
       
    67         this.in = new DataInputStream(new FilterInputStream(in) {
       
    68             public int read(byte b[], int off, int len) throws IOException {
       
    69                 int nr = super.read(b, off, len);
       
    70                 if (nr >= 0)  inPos += nr;
       
    71                 return nr;
       
    72             }
       
    73             public int read() throws IOException {
       
    74                 int ch = super.read();
       
    75                 if (ch >= 0)  inPos += 1;
       
    76                 return ch;
       
    77             }
       
    78             public long skip(long n) throws IOException {
       
    79                 long ns = super.skip(n);
       
    80                 if (ns >= 0)  inPos += ns;
       
    81                 return ns;
       
    82             }
       
    83         });
       
    84     }
       
    85 
       
    86     public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) {
       
    87         this.attrDefs = attrDefs;
       
    88     }
       
    89 
       
    90     public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) {
       
    91         this.attrCommands = attrCommands;
       
    92     }
       
    93 
       
    94     private void skip(int n, String what) throws IOException {
       
    95         Utils.log.warning("skipping "+n+" bytes of "+what);
       
    96         long skipped = 0;
       
    97         while (skipped < n) {
       
    98             long j = in.skip(n - skipped);
       
    99             assert(j > 0);
       
   100             skipped += j;
       
   101         }
       
   102         assert(skipped == n);
       
   103     }
       
   104 
       
   105     private int readUnsignedShort() throws IOException {
       
   106         return in.readUnsignedShort();
       
   107     }
       
   108 
       
   109     private int readInt() throws IOException {
       
   110         return in.readInt();
       
   111     }
       
   112 
       
   113     /** Read a 2-byte int, and return the <em>global</em> CP entry for it. */
       
   114     private Entry readRef() throws IOException {
       
   115         int i = in.readUnsignedShort();
       
   116         return i == 0 ? null : cls.cpMap[i];
       
   117     }
       
   118 
       
   119     private Entry readRef(byte tag) throws IOException {
       
   120         Entry e = readRef();
       
   121         assert(!(e instanceof UnresolvedEntry));
       
   122         checkTag(e, tag);
       
   123         return e;
       
   124     }
       
   125 
       
   126     /** Throw a ClassFormatException if the entry does not match the expected tag type. */
       
   127     private Entry checkTag(Entry e, byte tag) throws ClassFormatException {
       
   128         if (e == null || !e.tagMatches(tag)) {
       
   129             String where = (inPos == constantPoolLimit
       
   130                                 ? " in constant pool"
       
   131                                 : " at pos: " + inPos);
       
   132             String got = (e == null
       
   133                             ? "null CP index"
       
   134                             : "type=" + ConstantPool.tagName(e.tag));
       
   135             throw new ClassFormatException("Bad constant, expected type=" +
       
   136                     ConstantPool.tagName(tag) +
       
   137                     " got "+ got + ", in File: " + cls.file.nameString + where);
       
   138         }
       
   139         return e;
       
   140     }
       
   141     private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException {
       
   142         return nullOK && e == null ? null : checkTag(e, tag);
       
   143     }
       
   144 
       
   145     private Entry readRefOrNull(byte tag) throws IOException {
       
   146         Entry e = readRef();
       
   147         checkTag(e, tag, true);
       
   148         return e;
       
   149     }
       
   150 
       
   151     private Utf8Entry readUtf8Ref() throws IOException {
       
   152         return (Utf8Entry) readRef(CONSTANT_Utf8);
       
   153     }
       
   154 
       
   155     private ClassEntry readClassRef() throws IOException {
       
   156         return (ClassEntry) readRef(CONSTANT_Class);
       
   157     }
       
   158 
       
   159     private ClassEntry readClassRefOrNull() throws IOException {
       
   160         return (ClassEntry) readRefOrNull(CONSTANT_Class);
       
   161     }
       
   162 
       
   163     private SignatureEntry readSignatureRef() throws IOException {
       
   164         // The class file stores a Utf8, but we want a Signature.
       
   165         Entry e = readRef(CONSTANT_Signature);
       
   166         return (e != null && e.getTag() == CONSTANT_Utf8)
       
   167                 ? ConstantPool.getSignatureEntry(e.stringValue())
       
   168                 : (SignatureEntry) e;
       
   169     }
       
   170 
       
   171     void read() throws IOException {
       
   172         boolean ok = false;
       
   173         try {
       
   174             readMagicNumbers();
       
   175             readConstantPool();
       
   176             readHeader();
       
   177             readMembers(false);  // fields
       
   178             readMembers(true);   // methods
       
   179             readAttributes(ATTR_CONTEXT_CLASS, cls);
       
   180             fixUnresolvedEntries();
       
   181             cls.finishReading();
       
   182             assert(0 >= in.read(new byte[1]));
       
   183             ok = true;
       
   184         } finally {
       
   185             if (!ok) {
       
   186                 if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file);
       
   187             }
       
   188         }
       
   189     }
       
   190 
       
   191     void readMagicNumbers() throws IOException {
       
   192         cls.magic = in.readInt();
       
   193         if (cls.magic != JAVA_MAGIC)
       
   194             throw new Attribute.FormatException
       
   195                 ("Bad magic number in class file "
       
   196                  +Integer.toHexString(cls.magic),
       
   197                  ATTR_CONTEXT_CLASS, "magic-number", "pass");
       
   198         int minver = (short) readUnsignedShort();
       
   199         int majver = (short) readUnsignedShort();
       
   200         cls.version = Package.Version.of(majver, minver);
       
   201 
       
   202         //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver);
       
   203         String bad = checkVersion(cls.version);
       
   204         if (bad != null) {
       
   205             throw new Attribute.FormatException
       
   206                 ("classfile version too "+bad+": "
       
   207                  +cls.version+" in "+cls.file,
       
   208                  ATTR_CONTEXT_CLASS, "version", "pass");
       
   209         }
       
   210     }
       
   211 
       
   212     private String checkVersion(Package.Version ver) {
       
   213         int majver = ver.major;
       
   214         int minver = ver.minor;
       
   215         if (majver < pkg.minClassVersion.major ||
       
   216             (majver == pkg.minClassVersion.major &&
       
   217              minver < pkg.minClassVersion.minor)) {
       
   218             return "small";
       
   219         }
       
   220         if (majver > pkg.maxClassVersion.major ||
       
   221             (majver == pkg.maxClassVersion.major &&
       
   222              minver > pkg.maxClassVersion.minor)) {
       
   223             return "large";
       
   224         }
       
   225         return null;  // OK
       
   226     }
       
   227 
       
   228     void readConstantPool() throws IOException {
       
   229         int length = in.readUnsignedShort();
       
   230         //System.err.println("reading CP, length="+length);
       
   231 
       
   232         int[] fixups = new int[length*4];
       
   233         int fptr = 0;
       
   234 
       
   235         Entry[] cpMap = new Entry[length];
       
   236         cpMap[0] = null;
       
   237         for (int i = 1; i < length; i++) {
       
   238             //System.err.println("reading CP elt, i="+i);
       
   239             int tag = in.readByte();
       
   240             switch (tag) {
       
   241                 case CONSTANT_Utf8:
       
   242                     cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF());
       
   243                     break;
       
   244                 case CONSTANT_Integer:
       
   245                     {
       
   246                         cpMap[i] = ConstantPool.getLiteralEntry(in.readInt());
       
   247                     }
       
   248                     break;
       
   249                 case CONSTANT_Float:
       
   250                     {
       
   251                         cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat());
       
   252                     }
       
   253                     break;
       
   254                 case CONSTANT_Long:
       
   255                     {
       
   256                         cpMap[i] = ConstantPool.getLiteralEntry(in.readLong());
       
   257                         cpMap[++i] = null;
       
   258                     }
       
   259                     break;
       
   260                 case CONSTANT_Double:
       
   261                     {
       
   262                         cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble());
       
   263                         cpMap[++i] = null;
       
   264                     }
       
   265                     break;
       
   266 
       
   267                 // just read the refs; do not attempt to resolve while reading
       
   268                 case CONSTANT_Class:
       
   269                 case CONSTANT_String:
       
   270                 case CONSTANT_MethodType:
       
   271                     fixups[fptr++] = i;
       
   272                     fixups[fptr++] = tag;
       
   273                     fixups[fptr++] = in.readUnsignedShort();
       
   274                     fixups[fptr++] = -1;  // empty ref2
       
   275                     break;
       
   276                 case CONSTANT_Fieldref:
       
   277                 case CONSTANT_Methodref:
       
   278                 case CONSTANT_InterfaceMethodref:
       
   279                 case CONSTANT_NameandType:
       
   280                     fixups[fptr++] = i;
       
   281                     fixups[fptr++] = tag;
       
   282                     fixups[fptr++] = in.readUnsignedShort();
       
   283                     fixups[fptr++] = in.readUnsignedShort();
       
   284                     break;
       
   285                 case CONSTANT_InvokeDynamic:
       
   286                     fixups[fptr++] = i;
       
   287                     fixups[fptr++] = tag;
       
   288                     fixups[fptr++] = -1 ^ in.readUnsignedShort();  // not a ref
       
   289                     fixups[fptr++] = in.readUnsignedShort();
       
   290                     break;
       
   291                 case CONSTANT_MethodHandle:
       
   292                     fixups[fptr++] = i;
       
   293                     fixups[fptr++] = tag;
       
   294                     fixups[fptr++] = -1 ^ in.readUnsignedByte();
       
   295                     fixups[fptr++] = in.readUnsignedShort();
       
   296                     break;
       
   297                 default:
       
   298                     throw new ClassFormatException("Bad constant pool tag " +
       
   299                             tag + " in File: " + cls.file.nameString +
       
   300                             " at pos: " + inPos);
       
   301             }
       
   302         }
       
   303         constantPoolLimit = inPos;
       
   304 
       
   305         // Fix up refs, which might be out of order.
       
   306         while (fptr > 0) {
       
   307             if (verbose > 3)
       
   308                 Utils.log.fine("CP fixups ["+fptr/4+"]");
       
   309             int flimit = fptr;
       
   310             fptr = 0;
       
   311             for (int fi = 0; fi < flimit; ) {
       
   312                 int cpi = fixups[fi++];
       
   313                 int tag = fixups[fi++];
       
   314                 int ref = fixups[fi++];
       
   315                 int ref2 = fixups[fi++];
       
   316                 if (verbose > 3)
       
   317                     Utils.log.fine("  cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}");
       
   318                 if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) {
       
   319                     // Defer.
       
   320                     fixups[fptr++] = cpi;
       
   321                     fixups[fptr++] = tag;
       
   322                     fixups[fptr++] = ref;
       
   323                     fixups[fptr++] = ref2;
       
   324                     continue;
       
   325                 }
       
   326                 switch (tag) {
       
   327                 case CONSTANT_Class:
       
   328                     cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue());
       
   329                     break;
       
   330                 case CONSTANT_String:
       
   331                     cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue());
       
   332                     break;
       
   333                 case CONSTANT_Fieldref:
       
   334                 case CONSTANT_Methodref:
       
   335                 case CONSTANT_InterfaceMethodref:
       
   336                     ClassEntry      mclass = (ClassEntry)      checkTag(cpMap[ref],  CONSTANT_Class);
       
   337                     DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
       
   338                     cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr);
       
   339                     break;
       
   340                 case CONSTANT_NameandType:
       
   341                     Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref],  CONSTANT_Utf8);
       
   342                     Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature);
       
   343                     cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype);
       
   344                     break;
       
   345                 case CONSTANT_MethodType:
       
   346                     cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature));
       
   347                     break;
       
   348                 case CONSTANT_MethodHandle:
       
   349                     byte refKind = (byte)(-1 ^ ref);
       
   350                     MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember);
       
   351                     cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef);
       
   352                     break;
       
   353                 case CONSTANT_InvokeDynamic:
       
   354                     DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
       
   355                     cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr);
       
   356                     // Note that ref must be resolved later, using the BootstrapMethods attribute.
       
   357                     break;
       
   358                 default:
       
   359                     assert(false);
       
   360                 }
       
   361             }
       
   362             assert(fptr < flimit);  // Must make progress.
       
   363         }
       
   364 
       
   365         cls.cpMap = cpMap;
       
   366     }
       
   367 
       
   368     private /*non-static*/
       
   369     class UnresolvedEntry extends Entry {
       
   370         final Object[] refsOrIndexes;
       
   371         UnresolvedEntry(byte tag, Object... refsOrIndexes) {
       
   372             super(tag);
       
   373             this.refsOrIndexes = refsOrIndexes;
       
   374             ClassReader.this.haveUnresolvedEntry = true;
       
   375         }
       
   376         Entry resolve() {
       
   377             Class cls = ClassReader.this.cls;
       
   378             Entry res;
       
   379             switch (tag) {
       
   380             case CONSTANT_InvokeDynamic:
       
   381                 BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]);
       
   382                 DescriptorEntry         idescr = (DescriptorEntry) refsOrIndexes[1];
       
   383                 res = ConstantPool.getInvokeDynamicEntry(iboots, idescr);
       
   384                 break;
       
   385             default:
       
   386                 throw new AssertionError();
       
   387             }
       
   388             return res;
       
   389         }
       
   390         private void unresolved() { throw new RuntimeException("unresolved entry has no string"); }
       
   391         public int compareTo(Object x) { unresolved(); return 0; }
       
   392         public boolean equals(Object x) { unresolved(); return false; }
       
   393         protected int computeValueHash() { unresolved(); return 0; }
       
   394         public String stringValue() { unresolved(); return toString(); }
       
   395         public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; }
       
   396     }
       
   397 
       
   398     boolean haveUnresolvedEntry;
       
   399     private void fixUnresolvedEntries() {
       
   400         if (!haveUnresolvedEntry)  return;
       
   401         Entry[] cpMap = cls.getCPMap();
       
   402         for (int i = 0; i < cpMap.length; i++) {
       
   403             Entry e = cpMap[i];
       
   404             if (e instanceof UnresolvedEntry) {
       
   405                 cpMap[i] = e = ((UnresolvedEntry)e).resolve();
       
   406                 assert(!(e instanceof UnresolvedEntry));
       
   407             }
       
   408         }
       
   409         haveUnresolvedEntry = false;
       
   410     }
       
   411 
       
   412     void readHeader() throws IOException {
       
   413         cls.flags = readUnsignedShort();
       
   414         cls.thisClass = readClassRef();
       
   415         cls.superClass = readClassRefOrNull();
       
   416         int ni = readUnsignedShort();
       
   417         cls.interfaces = new ClassEntry[ni];
       
   418         for (int i = 0; i < ni; i++) {
       
   419             cls.interfaces[i] = readClassRef();
       
   420         }
       
   421     }
       
   422 
       
   423     void readMembers(boolean doMethods) throws IOException {
       
   424         int nm = readUnsignedShort();
       
   425         for (int i = 0; i < nm; i++) {
       
   426             readMember(doMethods);
       
   427         }
       
   428     }
       
   429 
       
   430     void readMember(boolean doMethod) throws IOException {
       
   431         int    mflags = readUnsignedShort();
       
   432         Utf8Entry       mname = readUtf8Ref();
       
   433         SignatureEntry  mtype = readSignatureRef();
       
   434         DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype);
       
   435         Class.Member m;
       
   436         if (!doMethod)
       
   437             m = cls.new Field(mflags, descr);
       
   438         else
       
   439             m = cls.new Method(mflags, descr);
       
   440         readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
       
   441                        m);
       
   442     }
       
   443     void readAttributes(int ctype, Attribute.Holder h) throws IOException {
       
   444         int na = readUnsignedShort();
       
   445         if (na == 0)  return;  // nothing to do here
       
   446         if (verbose > 3)
       
   447             Utils.log.fine("readAttributes "+h+" ["+na+"]");
       
   448         for (int i = 0; i < na; i++) {
       
   449             String name = readUtf8Ref().stringValue();
       
   450             int length = readInt();
       
   451             // See if there is a special command that applies.
       
   452             if (attrCommands != null) {
       
   453                 Attribute.Layout lkey = Attribute.keyForLookup(ctype, name);
       
   454                 String cmd = attrCommands.get(lkey);
       
   455                 if (cmd != null) {
       
   456                     switch (cmd) {
       
   457                         case "pass":
       
   458                             String message1 = "passing attribute bitwise in " + h;
       
   459                             throw new Attribute.FormatException(message1, ctype, name, cmd);
       
   460                         case "error":
       
   461                             String message2 = "attribute not allowed in " + h;
       
   462                             throw new Attribute.FormatException(message2, ctype, name, cmd);
       
   463                         case "strip":
       
   464                             skip(length, name + " attribute in " + h);
       
   465                             continue;
       
   466                     }
       
   467                 }
       
   468             }
       
   469             // Find canonical instance of the requested attribute.
       
   470             Attribute a = Attribute.lookup(Package.attrDefs, ctype, name);
       
   471             if (verbose > 4 && a != null)
       
   472                 Utils.log.fine("pkg_attribute_lookup "+name+" = "+a);
       
   473             if (a == null) {
       
   474                 a = Attribute.lookup(this.attrDefs, ctype, name);
       
   475                 if (verbose > 4 && a != null)
       
   476                     Utils.log.fine("this "+name+" = "+a);
       
   477             }
       
   478             if (a == null) {
       
   479                 a = Attribute.lookup(null, ctype, name);
       
   480                 if (verbose > 4 && a != null)
       
   481                     Utils.log.fine("null_attribute_lookup "+name+" = "+a);
       
   482             }
       
   483             if (a == null && length == 0) {
       
   484                 // Any zero-length attr is "known"...
       
   485                 // We can assume an empty attr. has an empty layout.
       
   486                 // Handles markers like Enum, Bridge, Synthetic, Deprecated.
       
   487                 a = Attribute.find(ctype, name, "");
       
   488             }
       
   489             boolean isStackMap = (ctype == ATTR_CONTEXT_CODE
       
   490                                   && (name.equals("StackMap") ||
       
   491                                       name.equals("StackMapX")));
       
   492             if (isStackMap) {
       
   493                 // Known attribute but with a corner case format, "pass" it.
       
   494                 Code code = (Code) h;
       
   495                 final int TOO_BIG = 0x10000;
       
   496                 if (code.max_stack   >= TOO_BIG ||
       
   497                     code.max_locals  >= TOO_BIG ||
       
   498                     code.getLength() >= TOO_BIG ||
       
   499                     name.endsWith("X")) {
       
   500                     // No, we don't really know what to do with this one.
       
   501                     // Do not compress the rare and strange "u4" and "X" cases.
       
   502                     a = null;
       
   503                 }
       
   504             }
       
   505             if (a == null) {
       
   506                 if (isStackMap) {
       
   507                     // Known attribute but w/o a format; pass it.
       
   508                     String message = "unsupported StackMap variant in "+h;
       
   509                     throw new Attribute.FormatException(message, ctype, name,
       
   510                                                         "pass");
       
   511                 } else if ("strip".equals(unknownAttrCommand)) {
       
   512                     // Skip the unknown attribute.
       
   513                     skip(length, "unknown "+name+" attribute in "+h);
       
   514                     continue;
       
   515                 } else {
       
   516                     String message = " is unknown attribute in class " + h;
       
   517                     throw new Attribute.FormatException(message, ctype, name,
       
   518                                                         unknownAttrCommand);
       
   519                 }
       
   520             }
       
   521             long pos0 = inPos;  // in case we want to check it
       
   522             if (a.layout() == Package.attrCodeEmpty) {
       
   523                 // These are hardwired.
       
   524                 Class.Method m = (Class.Method) h;
       
   525                 m.code = new Code(m);
       
   526                 try {
       
   527                     readCode(m.code);
       
   528                 } catch (Instruction.FormatException iie) {
       
   529                     String message = iie.getMessage() + " in " + h;
       
   530                     throw new ClassReader.ClassFormatException(message, iie);
       
   531                 }
       
   532                 assert(length == inPos - pos0);
       
   533                 // Keep empty attribute a...
       
   534             } else if (a.layout() == Package.attrBootstrapMethodsEmpty) {
       
   535                 assert(h == cls);
       
   536                 readBootstrapMethods(cls);
       
   537                 assert(length == inPos - pos0);
       
   538                 // Delete the attribute; it is logically part of the constant pool.
       
   539                 continue;
       
   540             } else if (a.layout() == Package.attrInnerClassesEmpty) {
       
   541                 // These are hardwired also.
       
   542                 assert(h == cls);
       
   543                 readInnerClasses(cls);
       
   544                 assert(length == inPos - pos0);
       
   545                 // Keep empty attribute a...
       
   546             } else if (length > 0) {
       
   547                 byte[] bytes = new byte[length];
       
   548                 in.readFully(bytes);
       
   549                 a = a.addContent(bytes);
       
   550             }
       
   551             if (a.size() == 0 && !a.layout().isEmpty()) {
       
   552                 throw new ClassFormatException(name +
       
   553                         ": attribute length cannot be zero, in " + h);
       
   554             }
       
   555             h.addAttribute(a);
       
   556             if (verbose > 2)
       
   557                 Utils.log.fine("read "+a);
       
   558         }
       
   559     }
       
   560 
       
   561     void readCode(Code code) throws IOException {
       
   562         code.max_stack = readUnsignedShort();
       
   563         code.max_locals = readUnsignedShort();
       
   564         code.bytes = new byte[readInt()];
       
   565         in.readFully(code.bytes);
       
   566         Entry[] cpMap = cls.getCPMap();
       
   567         Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version);
       
   568         int nh = readUnsignedShort();
       
   569         code.setHandlerCount(nh);
       
   570         for (int i = 0; i < nh; i++) {
       
   571             code.handler_start[i] = readUnsignedShort();
       
   572             code.handler_end[i]   = readUnsignedShort();
       
   573             code.handler_catch[i] = readUnsignedShort();
       
   574             code.handler_class[i] = readClassRefOrNull();
       
   575         }
       
   576         readAttributes(ATTR_CONTEXT_CODE, code);
       
   577     }
       
   578 
       
   579     void readBootstrapMethods(Class cls) throws IOException {
       
   580         BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()];
       
   581         for (int i = 0; i < bsms.length; i++) {
       
   582             MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle);
       
   583             Entry[] argRefs = new Entry[readUnsignedShort()];
       
   584             for (int j = 0; j < argRefs.length; j++) {
       
   585                 argRefs[j] = readRef(CONSTANT_LoadableValue);
       
   586             }
       
   587             bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs);
       
   588         }
       
   589         cls.setBootstrapMethods(Arrays.asList(bsms));
       
   590     }
       
   591 
       
   592     void readInnerClasses(Class cls) throws IOException {
       
   593         int nc = readUnsignedShort();
       
   594         ArrayList<InnerClass> ics = new ArrayList<>(nc);
       
   595         for (int i = 0; i < nc; i++) {
       
   596             InnerClass ic =
       
   597                 new InnerClass(readClassRef(),
       
   598                                readClassRefOrNull(),
       
   599                                (Utf8Entry)readRefOrNull(CONSTANT_Utf8),
       
   600                                readUnsignedShort());
       
   601             ics.add(ic);
       
   602         }
       
   603         cls.innerClasses = ics;  // set directly; do not use setInnerClasses.
       
   604         // (Later, ics may be transferred to the pkg.)
       
   605     }
       
   606 
       
   607     static class ClassFormatException extends IOException {
       
   608         private static final long serialVersionUID = -3564121733989501833L;
       
   609 
       
   610         public ClassFormatException(String message) {
       
   611             super(message);
       
   612         }
       
   613 
       
   614         public ClassFormatException(String message, Throwable cause) {
       
   615             super(message, cause);
       
   616         }
       
   617     }
       
   618 }