src/java.base/share/classes/com/sun/java/util/jar/pack/Attribute.java
changeset 47216 71c04702a3d5
parent 32649 2ee9017c7597
child 53018 8bf9268df0e2
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2003, 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.Entry;
       
    29 import com.sun.java.util.jar.pack.ConstantPool.Index;
       
    30 import java.io.ByteArrayOutputStream;
       
    31 import java.io.IOException;
       
    32 import java.util.ArrayList;
       
    33 import java.util.Arrays;
       
    34 import java.util.Collection;
       
    35 import java.util.Collections;
       
    36 import java.util.HashMap;
       
    37 import java.util.List;
       
    38 import java.util.Map;
       
    39 import static com.sun.java.util.jar.pack.Constants.*;
       
    40 
       
    41 /**
       
    42  * Represents an attribute in a class-file.
       
    43  * Takes care to remember where constant pool indexes occur.
       
    44  * Implements the "little language" of Pack200 for describing
       
    45  * attribute layouts.
       
    46  * @author John Rose
       
    47  */
       
    48 class Attribute implements Comparable<Attribute> {
       
    49     // Attribute instance fields.
       
    50 
       
    51     Layout def;     // the name and format of this attr
       
    52     byte[] bytes;   // the actual bytes
       
    53     Object fixups;  // reference relocations, if any are required
       
    54 
       
    55     public String name() { return def.name(); }
       
    56     public Layout layout() { return def; }
       
    57     public byte[] bytes() { return bytes; }
       
    58     public int size() { return bytes.length; }
       
    59     public Entry getNameRef() { return def.getNameRef(); }
       
    60 
       
    61     private Attribute(Attribute old) {
       
    62         this.def = old.def;
       
    63         this.bytes = old.bytes;
       
    64         this.fixups = old.fixups;
       
    65     }
       
    66 
       
    67     public Attribute(Layout def, byte[] bytes, Object fixups) {
       
    68         this.def = def;
       
    69         this.bytes = bytes;
       
    70         this.fixups = fixups;
       
    71         Fixups.setBytes(fixups, bytes);
       
    72     }
       
    73     public Attribute(Layout def, byte[] bytes) {
       
    74         this(def, bytes, null);
       
    75     }
       
    76 
       
    77     public Attribute addContent(byte[] bytes, Object fixups) {
       
    78         assert(isCanonical());
       
    79         if (bytes.length == 0 && fixups == null)
       
    80             return this;
       
    81         Attribute res = new Attribute(this);
       
    82         res.bytes = bytes;
       
    83         res.fixups = fixups;
       
    84         Fixups.setBytes(fixups, bytes);
       
    85         return res;
       
    86     }
       
    87     public Attribute addContent(byte[] bytes) {
       
    88         return addContent(bytes, null);
       
    89     }
       
    90 
       
    91     public void finishRefs(Index ix) {
       
    92         if (fixups != null) {
       
    93             Fixups.finishRefs(fixups, bytes, ix);
       
    94             fixups = null;
       
    95         }
       
    96     }
       
    97 
       
    98     public boolean isCanonical() {
       
    99         return this == def.canon;
       
   100     }
       
   101 
       
   102     @Override
       
   103     public int compareTo(Attribute that) {
       
   104         return this.def.compareTo(that.def);
       
   105     }
       
   106 
       
   107     private static final Map<List<Attribute>, List<Attribute>> canonLists = new HashMap<>();
       
   108     private static final Map<Layout, Attribute> attributes = new HashMap<>();
       
   109     private static final Map<Layout, Attribute> standardDefs = new HashMap<>();
       
   110 
       
   111     // Canonicalized lists of trivial attrs (Deprecated, etc.)
       
   112     // are used by trimToSize, in order to reduce footprint
       
   113     // of some common cases.  (Note that Code attributes are
       
   114     // always zero size.)
       
   115     public static List<Attribute> getCanonList(List<Attribute> al) {
       
   116         synchronized (canonLists) {
       
   117             List<Attribute> cl = canonLists.get(al);
       
   118             if (cl == null) {
       
   119                 cl = new ArrayList<>(al.size());
       
   120                 cl.addAll(al);
       
   121                 cl = Collections.unmodifiableList(cl);
       
   122                 canonLists.put(al, cl);
       
   123             }
       
   124             return cl;
       
   125         }
       
   126     }
       
   127 
       
   128     // Find the canonical empty attribute with the given ctype, name, layout.
       
   129     public static Attribute find(int ctype, String name, String layout) {
       
   130         Layout key = Layout.makeKey(ctype, name, layout);
       
   131         synchronized (attributes) {
       
   132             Attribute a = attributes.get(key);
       
   133             if (a == null) {
       
   134                 a = new Layout(ctype, name, layout).canonicalInstance();
       
   135                 attributes.put(key, a);
       
   136             }
       
   137             return a;
       
   138         }
       
   139     }
       
   140 
       
   141     public static Layout keyForLookup(int ctype, String name) {
       
   142         return Layout.makeKey(ctype, name);
       
   143     }
       
   144 
       
   145     // Find canonical empty attribute with given ctype and name,
       
   146     // and with the standard layout.
       
   147     public static Attribute lookup(Map<Layout, Attribute> defs, int ctype,
       
   148             String name) {
       
   149         if (defs == null) {
       
   150             defs = standardDefs;
       
   151         }
       
   152         return defs.get(Layout.makeKey(ctype, name));
       
   153     }
       
   154 
       
   155     public static Attribute define(Map<Layout, Attribute> defs, int ctype,
       
   156             String name, String layout) {
       
   157         Attribute a = find(ctype, name, layout);
       
   158         defs.put(Layout.makeKey(ctype, name), a);
       
   159         return a;
       
   160     }
       
   161 
       
   162     static {
       
   163         Map<Layout, Attribute> sd = standardDefs;
       
   164         define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH");
       
   165         define(sd, ATTR_CONTEXT_CLASS, "Synthetic", "");
       
   166         define(sd, ATTR_CONTEXT_CLASS, "Deprecated", "");
       
   167         define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH");
       
   168         define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH");
       
   169         define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]");
       
   170         define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]");
       
   171 
       
   172         define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH");
       
   173         define(sd, ATTR_CONTEXT_FIELD, "Synthetic", "");
       
   174         define(sd, ATTR_CONTEXT_FIELD, "Deprecated", "");
       
   175         define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH");
       
   176 
       
   177         define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH");
       
   178         define(sd, ATTR_CONTEXT_METHOD, "Synthetic", "");
       
   179         define(sd, ATTR_CONTEXT_METHOD, "Deprecated", "");
       
   180         define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]");
       
   181         define(sd, ATTR_CONTEXT_METHOD, "MethodParameters", "NB[RUNHFH]");
       
   182         //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]");
       
   183 
       
   184         define(sd, ATTR_CONTEXT_CODE, "StackMapTable",
       
   185                ("[NH[(1)]]" +
       
   186                 "[TB" +
       
   187                 "(64-127)[(2)]" +
       
   188                 "(247)[(1)(2)]" +
       
   189                 "(248-251)[(1)]" +
       
   190                 "(252)[(1)(2)]" +
       
   191                 "(253)[(1)(2)(2)]" +
       
   192                 "(254)[(1)(2)(2)(2)]" +
       
   193                 "(255)[(1)NH[(2)]NH[(2)]]" +
       
   194                 "()[]" +
       
   195                 "]" +
       
   196                 "[H]" +
       
   197                 "[TB(7)[RCH](8)[PH]()[]]"));
       
   198 
       
   199         define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]");
       
   200         define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]");
       
   201         define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable", "NH[PHOHRUHRSHH]");
       
   202         //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]");
       
   203         //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]");
       
   204 
       
   205         // Note:  Code and InnerClasses are special-cased elsewhere.
       
   206         // Their layout specs. are given here for completeness.
       
   207         // The Code spec is incomplete, in that it does not distinguish
       
   208         // bytecode bytes or locate CP references.
       
   209         // The BootstrapMethods attribute is also special-cased
       
   210         // elsewhere as an appendix to the local constant pool.
       
   211     }
       
   212 
       
   213     // Metadata.
       
   214     //
       
   215     // We define metadata using similar layouts
       
   216     // for all five kinds of metadata attributes and 2 type metadata attributes
       
   217     //
       
   218     // Regular annotations are a counted list of [RSHNH[RUH(1)]][...]
       
   219     //   pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...]
       
   220     //
       
   221     // Parameter annotations are a counted list of regular annotations.
       
   222     //   pack.method.attribute.RuntimeVisibleParameterAnnotations=[NB[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...]
       
   223     //
       
   224     // RuntimeInvisible annotations are defined similarly...
       
   225     // Non-method annotations are defined similarly...
       
   226     //
       
   227     // Annotation are a simple tagged value [TB...]
       
   228     //   pack.attribute.method.AnnotationDefault=[TB...]
       
   229 
       
   230     static {
       
   231         String mdLayouts[] = {
       
   232             Attribute.normalizeLayoutString
       
   233             (""
       
   234              +"\n  # parameter_annotations :="
       
   235              +"\n  [ NB[(1)] ]     # forward call to annotations"
       
   236              ),
       
   237             Attribute.normalizeLayoutString
       
   238             (""
       
   239              +"\n  # annotations :="
       
   240              +"\n  [ NH[(1)] ]     # forward call to annotation"
       
   241              +"\n  "
       
   242             ),
       
   243             Attribute.normalizeLayoutString
       
   244              (""
       
   245              +"\n  # annotation :="
       
   246              +"\n  [RSH"
       
   247              +"\n    NH[RUH (1)]   # forward call to value"
       
   248              +"\n    ]"
       
   249              ),
       
   250             Attribute.normalizeLayoutString
       
   251             (""
       
   252              +"\n  # value :="
       
   253              +"\n  [TB # Callable 2 encodes one tagged value."
       
   254              +"\n    (\\B,\\C,\\I,\\S,\\Z)[KIH]"
       
   255              +"\n    (\\D)[KDH]"
       
   256              +"\n    (\\F)[KFH]"
       
   257              +"\n    (\\J)[KJH]"
       
   258              +"\n    (\\c)[RSH]"
       
   259              +"\n    (\\e)[RSH RUH]"
       
   260              +"\n    (\\s)[RUH]"
       
   261              +"\n    (\\[)[NH[(0)]] # backward self-call to value"
       
   262              +"\n    (\\@)[RSH NH[RUH (0)]] # backward self-call to value"
       
   263              +"\n    ()[] ]"
       
   264              )
       
   265         };
       
   266         /*
       
   267          * RuntimeVisibleTypeAnnotation and RuntimeInvisibleTypeAnnotatation are
       
   268          * similar to RuntimeVisibleAnnotation and RuntimeInvisibleAnnotation,
       
   269          * a type-annotation union  and a type-path structure precedes the
       
   270          * annotation structure
       
   271          */
       
   272         String typeLayouts[] = {
       
   273             Attribute.normalizeLayoutString
       
   274             (""
       
   275              +"\n # type-annotations :="
       
   276              +"\n  [ NH[(1)(2)(3)] ]     # forward call to type-annotations"
       
   277             ),
       
   278             Attribute.normalizeLayoutString
       
   279             ( ""
       
   280              +"\n  # type-annotation :="
       
   281              +"\n  [TB"
       
   282              +"\n    (0-1) [B] # {CLASS, METHOD}_TYPE_PARAMETER"
       
   283              +"\n    (16) [FH] # CLASS_EXTENDS"
       
   284              +"\n    (17-18) [BB] # {CLASS, METHOD}_TYPE_PARAMETER_BOUND"
       
   285              +"\n    (19-21) [] # FIELD, METHOD_RETURN, METHOD_RECEIVER"
       
   286              +"\n    (22) [B] # METHOD_FORMAL_PARAMETER"
       
   287              +"\n    (23) [H] # THROWS"
       
   288              +"\n    (64-65) [NH[PHOHH]] # LOCAL_VARIABLE, RESOURCE_VARIABLE"
       
   289              +"\n    (66) [H] # EXCEPTION_PARAMETER"
       
   290              +"\n    (67-70) [PH] # INSTANCEOF, NEW, {CONSTRUCTOR, METHOD}_REFERENCE_RECEIVER"
       
   291              +"\n    (71-75) [PHB] # CAST, {CONSTRUCTOR,METHOD}_INVOCATION_TYPE_ARGUMENT, {CONSTRUCTOR, METHOD}_REFERENCE_TYPE_ARGUMENT"
       
   292              +"\n    ()[] ]"
       
   293             ),
       
   294             Attribute.normalizeLayoutString
       
   295             (""
       
   296              +"\n # type-path"
       
   297              +"\n [ NB[BB] ]"
       
   298             )
       
   299         };
       
   300         Map<Layout, Attribute> sd = standardDefs;
       
   301         String defaultLayout     = mdLayouts[3];
       
   302         String annotationsLayout = mdLayouts[1] + mdLayouts[2] + mdLayouts[3];
       
   303         String paramsLayout      = mdLayouts[0] + annotationsLayout;
       
   304         String typesLayout       = typeLayouts[0] + typeLayouts[1] +
       
   305                                    typeLayouts[2] + mdLayouts[2] + mdLayouts[3];
       
   306 
       
   307         for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) {
       
   308             if (ctype != ATTR_CONTEXT_CODE) {
       
   309                 define(sd, ctype,
       
   310                        "RuntimeVisibleAnnotations",   annotationsLayout);
       
   311                 define(sd, ctype,
       
   312                        "RuntimeInvisibleAnnotations",  annotationsLayout);
       
   313 
       
   314                 if (ctype == ATTR_CONTEXT_METHOD) {
       
   315                     define(sd, ctype,
       
   316                            "RuntimeVisibleParameterAnnotations",   paramsLayout);
       
   317                     define(sd, ctype,
       
   318                            "RuntimeInvisibleParameterAnnotations", paramsLayout);
       
   319                     define(sd, ctype,
       
   320                            "AnnotationDefault", defaultLayout);
       
   321                 }
       
   322             }
       
   323             define(sd, ctype,
       
   324                    "RuntimeVisibleTypeAnnotations", typesLayout);
       
   325             define(sd, ctype,
       
   326                    "RuntimeInvisibleTypeAnnotations", typesLayout);
       
   327         }
       
   328     }
       
   329 
       
   330     public static String contextName(int ctype) {
       
   331         switch (ctype) {
       
   332         case ATTR_CONTEXT_CLASS: return "class";
       
   333         case ATTR_CONTEXT_FIELD: return "field";
       
   334         case ATTR_CONTEXT_METHOD: return "method";
       
   335         case ATTR_CONTEXT_CODE: return "code";
       
   336         }
       
   337         return null;
       
   338     }
       
   339 
       
   340     /** Base class for any attributed object (Class, Field, Method, Code).
       
   341      *  Flags are included because they are used to help transmit the
       
   342      *  presence of attributes.  That is, flags are a mix of modifier
       
   343      *  bits and attribute indicators.
       
   344      */
       
   345     public abstract static
       
   346     class Holder {
       
   347 
       
   348         // We need this abstract method to interpret embedded CP refs.
       
   349         protected abstract Entry[] getCPMap();
       
   350 
       
   351         protected int flags;             // defined here for convenience
       
   352         protected List<Attribute> attributes;
       
   353 
       
   354         public int attributeSize() {
       
   355             return (attributes == null) ? 0 : attributes.size();
       
   356         }
       
   357 
       
   358         public void trimToSize() {
       
   359             if (attributes == null) {
       
   360                 return;
       
   361             }
       
   362             if (attributes.isEmpty()) {
       
   363                 attributes = null;
       
   364                 return;
       
   365             }
       
   366             if (attributes instanceof ArrayList) {
       
   367                 ArrayList<Attribute> al = (ArrayList<Attribute>)attributes;
       
   368                 al.trimToSize();
       
   369                 boolean allCanon = true;
       
   370                 for (Attribute a : al) {
       
   371                     if (!a.isCanonical()) {
       
   372                         allCanon = false;
       
   373                     }
       
   374                     if (a.fixups != null) {
       
   375                         assert(!a.isCanonical());
       
   376                         a.fixups = Fixups.trimToSize(a.fixups);
       
   377                     }
       
   378                 }
       
   379                 if (allCanon) {
       
   380                     // Replace private writable attribute list
       
   381                     // with only trivial entries by public unique
       
   382                     // immutable attribute list with the same entries.
       
   383                     attributes = getCanonList(al);
       
   384                 }
       
   385             }
       
   386         }
       
   387 
       
   388         public void addAttribute(Attribute a) {
       
   389             if (attributes == null)
       
   390                 attributes = new ArrayList<>(3);
       
   391             else if (!(attributes instanceof ArrayList))
       
   392                 attributes = new ArrayList<>(attributes);  // unfreeze it
       
   393             attributes.add(a);
       
   394         }
       
   395 
       
   396         public Attribute removeAttribute(Attribute a) {
       
   397             if (attributes == null)       return null;
       
   398             if (!attributes.contains(a))  return null;
       
   399             if (!(attributes instanceof ArrayList))
       
   400                 attributes = new ArrayList<>(attributes);  // unfreeze it
       
   401             attributes.remove(a);
       
   402             return a;
       
   403         }
       
   404 
       
   405         public Attribute getAttribute(int n) {
       
   406             return attributes.get(n);
       
   407         }
       
   408 
       
   409         protected void visitRefs(int mode, Collection<Entry> refs) {
       
   410             if (attributes == null)  return;
       
   411             for (Attribute a : attributes) {
       
   412                 a.visitRefs(this, mode, refs);
       
   413             }
       
   414         }
       
   415 
       
   416         static final List<Attribute> noAttributes = Arrays.asList(new Attribute[0]);
       
   417 
       
   418         public List<Attribute> getAttributes() {
       
   419             if (attributes == null)
       
   420                 return noAttributes;
       
   421             return attributes;
       
   422         }
       
   423 
       
   424         public void setAttributes(List<Attribute> attrList) {
       
   425             if (attrList.isEmpty())
       
   426                 attributes = null;
       
   427             else
       
   428                 attributes = attrList;
       
   429         }
       
   430 
       
   431         public Attribute getAttribute(String attrName) {
       
   432             if (attributes == null)  return null;
       
   433             for (Attribute a : attributes) {
       
   434                 if (a.name().equals(attrName))
       
   435                     return a;
       
   436             }
       
   437             return null;
       
   438         }
       
   439 
       
   440         public Attribute getAttribute(Layout attrDef) {
       
   441             if (attributes == null)  return null;
       
   442             for (Attribute a : attributes) {
       
   443                 if (a.layout() == attrDef)
       
   444                     return a;
       
   445             }
       
   446             return null;
       
   447         }
       
   448 
       
   449         public Attribute removeAttribute(String attrName) {
       
   450             return removeAttribute(getAttribute(attrName));
       
   451         }
       
   452 
       
   453         public Attribute removeAttribute(Layout attrDef) {
       
   454             return removeAttribute(getAttribute(attrDef));
       
   455         }
       
   456 
       
   457         public void strip(String attrName) {
       
   458             removeAttribute(getAttribute(attrName));
       
   459         }
       
   460     }
       
   461 
       
   462     // Lightweight interface to hide details of band structure.
       
   463     // Also used for testing.
       
   464     public abstract static
       
   465     class ValueStream {
       
   466         public int getInt(int bandIndex) { throw undef(); }
       
   467         public void putInt(int bandIndex, int value) { throw undef(); }
       
   468         public Entry getRef(int bandIndex) { throw undef(); }
       
   469         public void putRef(int bandIndex, Entry ref) { throw undef(); }
       
   470         // Note:  decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref
       
   471         public int decodeBCI(int bciCode) { throw undef(); }
       
   472         public int encodeBCI(int bci) { throw undef(); }
       
   473         public void noteBackCall(int whichCallable) { /* ignore by default */ }
       
   474         private RuntimeException undef() {
       
   475             return new UnsupportedOperationException("ValueStream method");
       
   476         }
       
   477     }
       
   478 
       
   479     // Element kinds:
       
   480     static final byte EK_INT  = 1;     // B H I SH etc.
       
   481     static final byte EK_BCI  = 2;     // PH POH etc.
       
   482     static final byte EK_BCO  = 3;     // OH etc.
       
   483     static final byte EK_FLAG = 4;     // FH etc.
       
   484     static final byte EK_REPL = 5;     // NH[...] etc.
       
   485     static final byte EK_REF  = 6;     // RUH, RUNH, KQH, etc.
       
   486     static final byte EK_UN   = 7;     // TB(...)[...] etc.
       
   487     static final byte EK_CASE = 8;     // (...)[...] etc.
       
   488     static final byte EK_CALL = 9;     // (0), (1), etc.
       
   489     static final byte EK_CBLE = 10;    // [...][...] etc.
       
   490     static final byte EF_SIGN  = 1<<0;   // INT is signed
       
   491     static final byte EF_DELTA = 1<<1;   // BCI/BCI value is diff'ed w/ previous
       
   492     static final byte EF_NULL  = 1<<2;   // null REF is expected/allowed
       
   493     static final byte EF_BACK  = 1<<3;   // call, callable, case is backward
       
   494     static final int NO_BAND_INDEX = -1;
       
   495 
       
   496     /** A "class" of attributes, characterized by a context-type, name
       
   497      *  and format.  The formats are specified in a "little language".
       
   498      */
       
   499     public static
       
   500     class Layout implements Comparable<Layout> {
       
   501         int ctype;       // attribute context type, e.g., ATTR_CONTEXT_CODE
       
   502         String name;     // name of attribute
       
   503         boolean hasRefs; // this kind of attr contains CP refs?
       
   504         String layout;   // layout specification
       
   505         int bandCount;   // total number of elems
       
   506         Element[] elems; // tokenization of layout
       
   507         Attribute canon; // canonical instance of this layout
       
   508 
       
   509         public int ctype() { return ctype; }
       
   510         public String name() { return name; }
       
   511         public String layout() { return layout; }
       
   512         public Attribute canonicalInstance() { return canon; }
       
   513 
       
   514         public Entry getNameRef() {
       
   515             return ConstantPool.getUtf8Entry(name());
       
   516         }
       
   517 
       
   518         public boolean isEmpty() {
       
   519             return layout.isEmpty();
       
   520         }
       
   521 
       
   522         public Layout(int ctype, String name, String layout) {
       
   523             this.ctype = ctype;
       
   524             this.name = name.intern();
       
   525             this.layout = layout.intern();
       
   526             assert(ctype < ATTR_CONTEXT_LIMIT);
       
   527             boolean hasCallables = layout.startsWith("[");
       
   528             try {
       
   529                 if (!hasCallables) {
       
   530                     this.elems = tokenizeLayout(this, -1, layout);
       
   531                 } else {
       
   532                     String[] bodies = splitBodies(layout);
       
   533                     // Make the callables now, so they can be linked immediately.
       
   534                     Element[] lelems = new Element[bodies.length];
       
   535                     this.elems = lelems;
       
   536                     for (int i = 0; i < lelems.length; i++) {
       
   537                         Element ce = this.new Element();
       
   538                         ce.kind = EK_CBLE;
       
   539                         ce.removeBand();
       
   540                         ce.bandIndex = NO_BAND_INDEX;
       
   541                         ce.layout = bodies[i];
       
   542                         lelems[i] = ce;
       
   543                     }
       
   544                     // Next fill them in.
       
   545                     for (int i = 0; i < lelems.length; i++) {
       
   546                         Element ce = lelems[i];
       
   547                         ce.body = tokenizeLayout(this, i, bodies[i]);
       
   548                     }
       
   549                     //System.out.println(Arrays.asList(elems));
       
   550                 }
       
   551             } catch (StringIndexOutOfBoundsException ee) {
       
   552                 // simplest way to catch syntax errors...
       
   553                 throw new RuntimeException("Bad attribute layout: "+layout, ee);
       
   554             }
       
   555             // Some uses do not make a fresh one for each occurrence.
       
   556             // For example, if layout == "", we only need one attr to share.
       
   557             canon = new Attribute(this, noBytes);
       
   558         }
       
   559         private Layout() {}
       
   560         static Layout makeKey(int ctype, String name, String layout) {
       
   561             Layout def = new Layout();
       
   562             def.ctype = ctype;
       
   563             def.name = name.intern();
       
   564             def.layout = layout.intern();
       
   565             assert(ctype < ATTR_CONTEXT_LIMIT);
       
   566             return def;
       
   567         }
       
   568         static Layout makeKey(int ctype, String name) {
       
   569             return makeKey(ctype, name, "");
       
   570         }
       
   571 
       
   572         public Attribute addContent(byte[] bytes, Object fixups) {
       
   573             return canon.addContent(bytes, fixups);
       
   574         }
       
   575         public Attribute addContent(byte[] bytes) {
       
   576             return canon.addContent(bytes, null);
       
   577         }
       
   578 
       
   579         @Override
       
   580         public boolean equals(Object x) {
       
   581             return ( x != null) && ( x.getClass() == Layout.class ) &&
       
   582                     equals((Layout)x);
       
   583         }
       
   584         public boolean equals(Layout that) {
       
   585             return this.name.equals(that.name)
       
   586                 && this.layout.equals(that.layout)
       
   587                 && this.ctype == that.ctype;
       
   588         }
       
   589         @Override
       
   590         public int hashCode() {
       
   591             return (((17 + name.hashCode())
       
   592                     * 37 + layout.hashCode())
       
   593                     * 37 + ctype);
       
   594         }
       
   595         @Override
       
   596         public int compareTo(Layout that) {
       
   597             int r;
       
   598             r = this.name.compareTo(that.name);
       
   599             if (r != 0)  return r;
       
   600             r = this.layout.compareTo(that.layout);
       
   601             if (r != 0)  return r;
       
   602             return this.ctype - that.ctype;
       
   603         }
       
   604         @Override
       
   605         public String toString() {
       
   606             String str = contextName(ctype)+"."+name+"["+layout+"]";
       
   607             // If -ea, print out more informative strings!
       
   608             assert((str = stringForDebug()) != null);
       
   609             return str;
       
   610         }
       
   611         private String stringForDebug() {
       
   612             return contextName(ctype)+"."+name+Arrays.asList(elems);
       
   613         }
       
   614 
       
   615         public
       
   616         class Element {
       
   617             String layout;   // spelling in the little language
       
   618             byte flags;      // EF_SIGN, etc.
       
   619             byte kind;       // EK_UINT, etc.
       
   620             byte len;        // scalar length of element
       
   621             byte refKind;    // CONSTANT_String, etc.
       
   622             int bandIndex;   // which band does this element govern?
       
   623             int value;       // extra parameter
       
   624             Element[] body;  // extra data (for replications, unions, calls)
       
   625 
       
   626             boolean flagTest(byte mask) { return (flags & mask) != 0; }
       
   627 
       
   628             Element() {
       
   629                 bandIndex = bandCount++;
       
   630             }
       
   631 
       
   632             void removeBand() {
       
   633                 --bandCount;
       
   634                 assert(bandIndex == bandCount);
       
   635                 bandIndex = NO_BAND_INDEX;
       
   636             }
       
   637 
       
   638             public boolean hasBand() {
       
   639                 return bandIndex >= 0;
       
   640             }
       
   641             public String toString() {
       
   642                 String str = layout;
       
   643                 // If -ea, print out more informative strings!
       
   644                 assert((str = stringForDebug()) != null);
       
   645                 return str;
       
   646             }
       
   647             private String stringForDebug() {
       
   648                 Element[] lbody = this.body;
       
   649                 switch (kind) {
       
   650                 case EK_CALL:
       
   651                     lbody = null;
       
   652                     break;
       
   653                 case EK_CASE:
       
   654                     if (flagTest(EF_BACK))
       
   655                         lbody = null;
       
   656                     break;
       
   657                 }
       
   658                 return layout
       
   659                     + (!hasBand()?"":"#"+bandIndex)
       
   660                     + "<"+ (flags==0?"":""+flags)+kind+len
       
   661                     + (refKind==0?"":""+refKind) + ">"
       
   662                     + (value==0?"":"("+value+")")
       
   663                     + (lbody==null?"": ""+Arrays.asList(lbody));
       
   664             }
       
   665         }
       
   666 
       
   667         public boolean hasCallables() {
       
   668             return (elems.length > 0 && elems[0].kind == EK_CBLE);
       
   669         }
       
   670         private static final Element[] noElems = {};
       
   671         public Element[] getCallables() {
       
   672             if (hasCallables()) {
       
   673                 Element[] nelems = Arrays.copyOf(elems, elems.length);
       
   674                 return nelems;
       
   675             } else
       
   676                 return noElems;  // no callables at all
       
   677         }
       
   678         public Element[] getEntryPoint() {
       
   679             if (hasCallables())
       
   680                 return elems[0].body;  // body of first callable
       
   681             else {
       
   682                 Element[] nelems = Arrays.copyOf(elems, elems.length);
       
   683                 return nelems;  // no callables; whole body
       
   684             }
       
   685         }
       
   686 
       
   687         /** Return a sequence of tokens from the given attribute bytes.
       
   688          *  Sequence elements will be 1-1 correspondent with my layout tokens.
       
   689          */
       
   690         public void parse(Holder holder,
       
   691                           byte[] bytes, int pos, int len, ValueStream out) {
       
   692             int end = parseUsing(getEntryPoint(),
       
   693                                  holder, bytes, pos, len, out);
       
   694             if (end != pos + len)
       
   695                 throw new InternalError("layout parsed "+(end-pos)+" out of "+len+" bytes");
       
   696         }
       
   697         /** Given a sequence of tokens, return the attribute bytes.
       
   698          *  Sequence elements must be 1-1 correspondent with my layout tokens.
       
   699          *  The returned object is a cookie for Fixups.finishRefs, which
       
   700          *  must be used to harden any references into integer indexes.
       
   701          */
       
   702         public Object unparse(ValueStream in, ByteArrayOutputStream out) {
       
   703             Object[] fixups = { null };
       
   704             unparseUsing(getEntryPoint(), fixups, in, out);
       
   705             return fixups[0]; // return ref-bearing cookie, if any
       
   706         }
       
   707 
       
   708         public String layoutForClassVersion(Package.Version vers) {
       
   709             if (vers.lessThan(JAVA6_MAX_CLASS_VERSION)) {
       
   710                 // Disallow layout syntax in the oldest protocol version.
       
   711                 return expandCaseDashNotation(layout);
       
   712             }
       
   713             return layout;
       
   714         }
       
   715     }
       
   716 
       
   717     public static
       
   718     class FormatException extends IOException {
       
   719         private static final long serialVersionUID = -2542243830788066513L;
       
   720 
       
   721         private int ctype;
       
   722         private String name;
       
   723         String layout;
       
   724         public FormatException(String message,
       
   725                                int ctype, String name, String layout) {
       
   726             super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" +
       
   727                   (message == null? "" : (": " + message)));
       
   728             this.ctype = ctype;
       
   729             this.name = name;
       
   730             this.layout = layout;
       
   731         }
       
   732         public FormatException(String message,
       
   733                                int ctype, String name) {
       
   734             this(message, ctype, name, null);
       
   735         }
       
   736     }
       
   737 
       
   738     void visitRefs(Holder holder, int mode, final Collection<Entry> refs) {
       
   739         if (mode == VRM_CLASSIC) {
       
   740             refs.add(getNameRef());
       
   741         }
       
   742         // else the name is owned by the layout, and is processed elsewhere
       
   743         if (bytes.length == 0)  return;  // quick exit
       
   744         if (!def.hasRefs)       return;  // quick exit
       
   745         if (fixups != null) {
       
   746             Fixups.visitRefs(fixups, refs);
       
   747             return;
       
   748         }
       
   749         // References (to a local cpMap) are embedded in the bytes.
       
   750         def.parse(holder, bytes, 0, bytes.length,
       
   751             new ValueStream() {
       
   752                 @Override
       
   753                 public void putInt(int bandIndex, int value) {
       
   754                 }
       
   755                 @Override
       
   756                 public void putRef(int bandIndex, Entry ref) {
       
   757                     refs.add(ref);
       
   758                 }
       
   759                 @Override
       
   760                 public int encodeBCI(int bci) {
       
   761                     return bci;
       
   762                 }
       
   763             });
       
   764     }
       
   765 
       
   766     public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) {
       
   767         def.parse(holder, bytes, pos, len, out);
       
   768     }
       
   769     public Object unparse(ValueStream in, ByteArrayOutputStream out) {
       
   770         return def.unparse(in, out);
       
   771     }
       
   772 
       
   773     @Override
       
   774     public String toString() {
       
   775         return def
       
   776             +"{"+(bytes == null ? -1 : size())+"}"
       
   777             +(fixups == null? "": fixups.toString());
       
   778     }
       
   779 
       
   780     /** Remove any informal "pretty printing" from the layout string.
       
   781      *  Removes blanks and control chars.
       
   782      *  Removes '#' comments (to end of line).
       
   783      *  Replaces '\c' by the decimal code of the character c.
       
   784      *  Replaces '0xNNN' by the decimal code of the hex number NNN.
       
   785      */
       
   786     public static
       
   787     String normalizeLayoutString(String layout) {
       
   788         StringBuilder buf = new StringBuilder();
       
   789         for (int i = 0, len = layout.length(); i < len; ) {
       
   790             char ch = layout.charAt(i++);
       
   791             if (ch <= ' ') {
       
   792                 // Skip whitespace and control chars
       
   793                 continue;
       
   794             } else if (ch == '#') {
       
   795                 // Skip to end of line.
       
   796                 int end1 = layout.indexOf('\n', i);
       
   797                 int end2 = layout.indexOf('\r', i);
       
   798                 if (end1 < 0)  end1 = len;
       
   799                 if (end2 < 0)  end2 = len;
       
   800                 i = Math.min(end1, end2);
       
   801             } else if (ch == '\\') {
       
   802                 // Map a character reference to its decimal code.
       
   803                 buf.append((int) layout.charAt(i++));
       
   804             } else if (ch == '0' && layout.startsWith("0x", i-1)) {
       
   805                 // Map a hex numeral to its decimal code.
       
   806                 int start = i-1;
       
   807                 int end = start+2;
       
   808                 while (end < len) {
       
   809                     int dig = layout.charAt(end);
       
   810                     if ((dig >= '0' && dig <= '9') ||
       
   811                         (dig >= 'a' && dig <= 'f'))
       
   812                         ++end;
       
   813                     else
       
   814                         break;
       
   815                 }
       
   816                 if (end > start) {
       
   817                     String num = layout.substring(start, end);
       
   818                     buf.append(Integer.decode(num));
       
   819                     i = end;
       
   820                 } else {
       
   821                     buf.append(ch);
       
   822                 }
       
   823             } else {
       
   824                 buf.append(ch);
       
   825             }
       
   826         }
       
   827         String result = buf.toString();
       
   828         if (false && !result.equals(layout)) {
       
   829             Utils.log.info("Normalizing layout string");
       
   830             Utils.log.info("    From: "+layout);
       
   831             Utils.log.info("    To:   "+result);
       
   832         }
       
   833         return result;
       
   834     }
       
   835 
       
   836     /// Subroutines for parsing and unparsing:
       
   837 
       
   838     /** Parse the attribute layout language.
       
   839 <pre>
       
   840   attribute_layout:
       
   841         ( layout_element )* | ( callable )+
       
   842   layout_element:
       
   843         ( integral | replication | union | call | reference )
       
   844 
       
   845   callable:
       
   846         '[' body ']'
       
   847   body:
       
   848         ( layout_element )+
       
   849 
       
   850   integral:
       
   851         ( unsigned_int | signed_int | bc_index | bc_offset | flag )
       
   852   unsigned_int:
       
   853         uint_type
       
   854   signed_int:
       
   855         'S' uint_type
       
   856   any_int:
       
   857         ( unsigned_int | signed_int )
       
   858   bc_index:
       
   859         ( 'P' uint_type | 'PO' uint_type )
       
   860   bc_offset:
       
   861         'O' any_int
       
   862   flag:
       
   863         'F' uint_type
       
   864   uint_type:
       
   865         ( 'B' | 'H' | 'I' | 'V' )
       
   866 
       
   867   replication:
       
   868         'N' uint_type '[' body ']'
       
   869 
       
   870   union:
       
   871         'T' any_int (union_case)* '(' ')' '[' (body)? ']'
       
   872   union_case:
       
   873         '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
       
   874   union_case_tag:
       
   875         ( numeral | numeral '-' numeral )
       
   876   call:
       
   877         '(' numeral ')'
       
   878 
       
   879   reference:
       
   880         reference_type ( 'N' )? uint_type
       
   881   reference_type:
       
   882         ( constant_ref | schema_ref | utf8_ref | untyped_ref )
       
   883   constant_ref:
       
   884         ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' )
       
   885   schema_ref:
       
   886         ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' )
       
   887   utf8_ref:
       
   888         'RU'
       
   889   untyped_ref:
       
   890         'RQ'
       
   891 
       
   892   numeral:
       
   893         '(' ('-')? (digit)+ ')'
       
   894   digit:
       
   895         ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
       
   896  </pre>
       
   897     */
       
   898     static //private
       
   899     Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) {
       
   900         List<Layout.Element> col = new ArrayList<>(layout.length());
       
   901         tokenizeLayout(self, curCble, layout, col);
       
   902         Layout.Element[] res = new Layout.Element[col.size()];
       
   903         col.toArray(res);
       
   904         return res;
       
   905     }
       
   906     static //private
       
   907     void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) {
       
   908         boolean prevBCI = false;
       
   909         for (int len = layout.length(), i = 0; i < len; ) {
       
   910             int start = i;
       
   911             int body;
       
   912             Layout.Element e = self.new Element();
       
   913             byte kind;
       
   914             //System.out.println("at "+i+": ..."+layout.substring(i));
       
   915             // strip a prefix
       
   916             switch (layout.charAt(i++)) {
       
   917             /// layout_element: integral
       
   918             case 'B': case 'H': case 'I': case 'V': // unsigned_int
       
   919                 kind = EK_INT;
       
   920                 --i; // reparse
       
   921                 i = tokenizeUInt(e, layout, i);
       
   922                 break;
       
   923             case 'S': // signed_int
       
   924                 kind = EK_INT;
       
   925                 --i; // reparse
       
   926                 i = tokenizeSInt(e, layout, i);
       
   927                 break;
       
   928             case 'P': // bc_index
       
   929                 kind = EK_BCI;
       
   930                 if (layout.charAt(i++) == 'O') {
       
   931                     // bc_index: 'PO' tokenizeUInt
       
   932                     e.flags |= EF_DELTA;
       
   933                     // must follow P or PO:
       
   934                     if (!prevBCI)
       
   935                         { i = -i; continue; } // fail
       
   936                     i++; // move forward
       
   937                 }
       
   938                 --i; // reparse
       
   939                 i = tokenizeUInt(e, layout, i);
       
   940                 break;
       
   941             case 'O': // bc_offset
       
   942                 kind = EK_BCO;
       
   943                 e.flags |= EF_DELTA;
       
   944                 // must follow P or PO:
       
   945                 if (!prevBCI)
       
   946                     { i = -i; continue; } // fail
       
   947                 i = tokenizeSInt(e, layout, i);
       
   948                 break;
       
   949             case 'F': // flag
       
   950                 kind = EK_FLAG;
       
   951                 i = tokenizeUInt(e, layout, i);
       
   952                 break;
       
   953             case 'N': // replication: 'N' uint '[' elem ... ']'
       
   954                 kind = EK_REPL;
       
   955                 i = tokenizeUInt(e, layout, i);
       
   956                 if (layout.charAt(i++) != '[')
       
   957                     { i = -i; continue; } // fail
       
   958                 i = skipBody(layout, body = i);
       
   959                 e.body = tokenizeLayout(self, curCble,
       
   960                                         layout.substring(body, i++));
       
   961                 break;
       
   962             case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
       
   963                 kind = EK_UN;
       
   964                 i = tokenizeSInt(e, layout, i);
       
   965                 List<Layout.Element> cases = new ArrayList<>();
       
   966                 for (;;) {
       
   967                     // Keep parsing cases until we hit the default case.
       
   968                     if (layout.charAt(i++) != '(')
       
   969                         { i = -i; break; } // fail
       
   970                     int beg = i;
       
   971                     i = layout.indexOf(')', i);
       
   972                     String cstr = layout.substring(beg, i++);
       
   973                     int cstrlen = cstr.length();
       
   974                     if (layout.charAt(i++) != '[')
       
   975                         { i = -i; break; } // fail
       
   976                     // Check for duplication.
       
   977                     if (layout.charAt(i) == ']')
       
   978                         body = i;  // missing body, which is legal here
       
   979                     else
       
   980                         i = skipBody(layout, body = i);
       
   981                     Layout.Element[] cbody
       
   982                         = tokenizeLayout(self, curCble,
       
   983                                          layout.substring(body, i++));
       
   984                     if (cstrlen == 0) {
       
   985                         Layout.Element ce = self.new Element();
       
   986                         ce.body = cbody;
       
   987                         ce.kind = EK_CASE;
       
   988                         ce.removeBand();
       
   989                         cases.add(ce);
       
   990                         break;  // done with the whole union
       
   991                     } else {
       
   992                         // Parse a case string.
       
   993                         boolean firstCaseNum = true;
       
   994                         for (int cp = 0, endp;; cp = endp+1) {
       
   995                             // Look for multiple case tags:
       
   996                             endp = cstr.indexOf(',', cp);
       
   997                             if (endp < 0)  endp = cstrlen;
       
   998                             String cstr1 = cstr.substring(cp, endp);
       
   999                             if (cstr1.length() == 0)
       
  1000                                 cstr1 = "empty";  // will fail parse
       
  1001                             int value0, value1;
       
  1002                             // Check for a case range (new in 1.6).
       
  1003                             int dash = findCaseDash(cstr1, 0);
       
  1004                             if (dash >= 0) {
       
  1005                                 value0 = parseIntBefore(cstr1, dash);
       
  1006                                 value1 = parseIntAfter(cstr1, dash);
       
  1007                                 if (value0 >= value1)
       
  1008                                     { i = -i; break; } // fail
       
  1009                             } else {
       
  1010                                 value0 = value1 = Integer.parseInt(cstr1);
       
  1011                             }
       
  1012                             // Add a case for each value in value0..value1
       
  1013                             for (;; value0++) {
       
  1014                                 Layout.Element ce = self.new Element();
       
  1015                                 ce.body = cbody;  // all cases share one body
       
  1016                                 ce.kind = EK_CASE;
       
  1017                                 ce.removeBand();
       
  1018                                 if (!firstCaseNum)
       
  1019                                     // "backward case" repeats a body
       
  1020                                     ce.flags |= EF_BACK;
       
  1021                                 firstCaseNum = false;
       
  1022                                 ce.value = value0;
       
  1023                                 cases.add(ce);
       
  1024                                 if (value0 == value1)  break;
       
  1025                             }
       
  1026                             if (endp == cstrlen) {
       
  1027                                 break;  // done with this case
       
  1028                             }
       
  1029                         }
       
  1030                     }
       
  1031                 }
       
  1032                 e.body = new Layout.Element[cases.size()];
       
  1033                 cases.toArray(e.body);
       
  1034                 e.kind = kind;
       
  1035                 for (int j = 0; j < e.body.length-1; j++) {
       
  1036                     Layout.Element ce = e.body[j];
       
  1037                     if (matchCase(e, ce.value) != ce) {
       
  1038                         // Duplicate tag.
       
  1039                         { i = -i; break; } // fail
       
  1040                     }
       
  1041                 }
       
  1042                 break;
       
  1043             case '(': // call: '(' '-'? digit+ ')'
       
  1044                 kind = EK_CALL;
       
  1045                 e.removeBand();
       
  1046                 i = layout.indexOf(')', i);
       
  1047                 String cstr = layout.substring(start+1, i++);
       
  1048                 int offset = Integer.parseInt(cstr);
       
  1049                 int target = curCble + offset;
       
  1050                 if (!(offset+"").equals(cstr) ||
       
  1051                     self.elems == null ||
       
  1052                     target < 0 ||
       
  1053                     target >= self.elems.length)
       
  1054                     { i = -i; continue; } // fail
       
  1055                 Layout.Element ce = self.elems[target];
       
  1056                 assert(ce.kind == EK_CBLE);
       
  1057                 e.value = target;
       
  1058                 e.body = new Layout.Element[]{ ce };
       
  1059                 // Is it a (recursive) backward call?
       
  1060                 if (offset <= 0) {
       
  1061                     // Yes.  Mark both caller and callee backward.
       
  1062                     e.flags  |= EF_BACK;
       
  1063                     ce.flags |= EF_BACK;
       
  1064                 }
       
  1065                 break;
       
  1066             case 'K':  // reference_type: constant_ref
       
  1067                 kind = EK_REF;
       
  1068                 switch (layout.charAt(i++)) {
       
  1069                 case 'I': e.refKind = CONSTANT_Integer; break;
       
  1070                 case 'J': e.refKind = CONSTANT_Long; break;
       
  1071                 case 'F': e.refKind = CONSTANT_Float; break;
       
  1072                 case 'D': e.refKind = CONSTANT_Double; break;
       
  1073                 case 'S': e.refKind = CONSTANT_String; break;
       
  1074                 case 'Q': e.refKind = CONSTANT_FieldSpecific; break;
       
  1075 
       
  1076                 // new in 1.7:
       
  1077                 case 'M': e.refKind = CONSTANT_MethodHandle; break;
       
  1078                 case 'T': e.refKind = CONSTANT_MethodType; break;
       
  1079                 case 'L': e.refKind = CONSTANT_LoadableValue; break;
       
  1080                 default: { i = -i; continue; } // fail
       
  1081                 }
       
  1082                 break;
       
  1083             case 'R': // schema_ref
       
  1084                 kind = EK_REF;
       
  1085                 switch (layout.charAt(i++)) {
       
  1086                 case 'C': e.refKind = CONSTANT_Class; break;
       
  1087                 case 'S': e.refKind = CONSTANT_Signature; break;
       
  1088                 case 'D': e.refKind = CONSTANT_NameandType; break;
       
  1089                 case 'F': e.refKind = CONSTANT_Fieldref; break;
       
  1090                 case 'M': e.refKind = CONSTANT_Methodref; break;
       
  1091                 case 'I': e.refKind = CONSTANT_InterfaceMethodref; break;
       
  1092 
       
  1093                 case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref
       
  1094                 case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref
       
  1095 
       
  1096                 // new in 1.7:
       
  1097                 case 'Y': e.refKind = CONSTANT_InvokeDynamic; break;
       
  1098                 case 'B': e.refKind = CONSTANT_BootstrapMethod; break;
       
  1099                 case 'N': e.refKind = CONSTANT_AnyMember; break;
       
  1100 
       
  1101                 default: { i = -i; continue; } // fail
       
  1102                 }
       
  1103                 break;
       
  1104             default: { i = -i; continue; } // fail
       
  1105             }
       
  1106 
       
  1107             // further parsing of refs
       
  1108             if (kind == EK_REF) {
       
  1109                 // reference: reference_type -><- ( 'N' )? tokenizeUInt
       
  1110                 if (layout.charAt(i++) == 'N') {
       
  1111                     e.flags |= EF_NULL;
       
  1112                     i++; // move forward
       
  1113                 }
       
  1114                 --i; // reparse
       
  1115                 i = tokenizeUInt(e, layout, i);
       
  1116                 self.hasRefs = true;
       
  1117             }
       
  1118 
       
  1119             prevBCI = (kind == EK_BCI);
       
  1120 
       
  1121             // store the new element
       
  1122             e.kind = kind;
       
  1123             e.layout = layout.substring(start, i);
       
  1124             col.add(e);
       
  1125         }
       
  1126     }
       
  1127     static //private
       
  1128     String[] splitBodies(String layout) {
       
  1129         List<String> bodies = new ArrayList<>();
       
  1130         // Parse several independent layout bodies:  "[foo][bar]...[baz]"
       
  1131         for (int i = 0; i < layout.length(); i++) {
       
  1132             if (layout.charAt(i++) != '[')
       
  1133                 layout.charAt(-i);  // throw error
       
  1134             int body;
       
  1135             i = skipBody(layout, body = i);
       
  1136             bodies.add(layout.substring(body, i));
       
  1137         }
       
  1138         String[] res = new String[bodies.size()];
       
  1139         bodies.toArray(res);
       
  1140         return res;
       
  1141     }
       
  1142     private static
       
  1143     int skipBody(String layout, int i) {
       
  1144         assert(layout.charAt(i-1) == '[');
       
  1145         if (layout.charAt(i) == ']')
       
  1146             // No empty bodies, please.
       
  1147             return -i;
       
  1148         // skip balanced [...[...]...]
       
  1149         for (int depth = 1; depth > 0; ) {
       
  1150             switch (layout.charAt(i++)) {
       
  1151             case '[': depth++; break;
       
  1152             case ']': depth--; break;
       
  1153             }
       
  1154         }
       
  1155         --i;  // get before bracket
       
  1156         assert(layout.charAt(i) == ']');
       
  1157         return i;  // return closing bracket
       
  1158     }
       
  1159     private static
       
  1160     int tokenizeUInt(Layout.Element e, String layout, int i) {
       
  1161         switch (layout.charAt(i++)) {
       
  1162         case 'V': e.len = 0; break;
       
  1163         case 'B': e.len = 1; break;
       
  1164         case 'H': e.len = 2; break;
       
  1165         case 'I': e.len = 4; break;
       
  1166         default: return -i;
       
  1167         }
       
  1168         return i;
       
  1169     }
       
  1170     private static
       
  1171     int tokenizeSInt(Layout.Element e, String layout, int i) {
       
  1172         if (layout.charAt(i) == 'S') {
       
  1173             e.flags |= EF_SIGN;
       
  1174             ++i;
       
  1175         }
       
  1176         return tokenizeUInt(e, layout, i);
       
  1177     }
       
  1178 
       
  1179     private static
       
  1180     boolean isDigit(char c) {
       
  1181         return c >= '0' && c <= '9';
       
  1182     }
       
  1183 
       
  1184     /** Find an occurrence of hyphen '-' between two numerals. */
       
  1185     static //private
       
  1186     int findCaseDash(String layout, int fromIndex) {
       
  1187         if (fromIndex <= 0)  fromIndex = 1;  // minimum dash pos
       
  1188         int lastDash = layout.length() - 2;  // maximum dash pos
       
  1189         for (;;) {
       
  1190             int dash = layout.indexOf('-', fromIndex);
       
  1191             if (dash < 0 || dash > lastDash)  return -1;
       
  1192             if (isDigit(layout.charAt(dash-1))) {
       
  1193                 char afterDash = layout.charAt(dash+1);
       
  1194                 if (afterDash == '-' && dash+2 < layout.length())
       
  1195                     afterDash = layout.charAt(dash+2);
       
  1196                 if (isDigit(afterDash)) {
       
  1197                     // matched /[0-9]--?[0-9]/; return position of dash
       
  1198                     return dash;
       
  1199                 }
       
  1200             }
       
  1201             fromIndex = dash+1;
       
  1202         }
       
  1203     }
       
  1204     static
       
  1205     int parseIntBefore(String layout, int dash) {
       
  1206         int end = dash;
       
  1207         int beg = end;
       
  1208         while (beg > 0 && isDigit(layout.charAt(beg-1))) {
       
  1209             --beg;
       
  1210         }
       
  1211         if (beg == end)  return Integer.parseInt("empty");
       
  1212         // skip backward over a sign
       
  1213         if (beg >= 1 && layout.charAt(beg-1) == '-')  --beg;
       
  1214         assert(beg == 0 || !isDigit(layout.charAt(beg-1)));
       
  1215         return Integer.parseInt(layout.substring(beg, end));
       
  1216     }
       
  1217     static
       
  1218     int parseIntAfter(String layout, int dash) {
       
  1219         int beg = dash+1;
       
  1220         int end = beg;
       
  1221         int limit = layout.length();
       
  1222         if (end < limit && layout.charAt(end) == '-')  ++end;
       
  1223         while (end < limit && isDigit(layout.charAt(end))) {
       
  1224             ++end;
       
  1225         }
       
  1226         if (beg == end)  return Integer.parseInt("empty");
       
  1227         return Integer.parseInt(layout.substring(beg, end));
       
  1228     }
       
  1229     /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */
       
  1230     static
       
  1231     String expandCaseDashNotation(String layout) {
       
  1232         int dash = findCaseDash(layout, 0);
       
  1233         if (dash < 0)  return layout;  // no dashes (the common case)
       
  1234         StringBuilder result = new StringBuilder(layout.length() * 3);
       
  1235         int sofar = 0;  // how far have we processed the layout?
       
  1236         for (;;) {
       
  1237             // for each dash, collect everything up to the dash
       
  1238             result.append(layout, sofar, dash);
       
  1239             sofar = dash+1;  // skip the dash
       
  1240             // then collect intermediate values
       
  1241             int value0 = parseIntBefore(layout, dash);
       
  1242             int value1 = parseIntAfter(layout, dash);
       
  1243             assert(value0 < value1);
       
  1244             result.append(",");  // close off value0 numeral
       
  1245             for (int i = value0+1; i < value1; i++) {
       
  1246                 result.append(i);
       
  1247                 result.append(",");  // close off i numeral
       
  1248             }
       
  1249             dash = findCaseDash(layout, sofar);
       
  1250             if (dash < 0)  break;
       
  1251         }
       
  1252         result.append(layout, sofar, layout.length());  // collect the rest
       
  1253         return result.toString();
       
  1254     }
       
  1255     static {
       
  1256         assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5"));
       
  1257         assert(expandCaseDashNotation("-2--1").equals("-2,-1"));
       
  1258         assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1"));
       
  1259         assert(expandCaseDashNotation("-1-0").equals("-1,0"));
       
  1260     }
       
  1261 
       
  1262     // Parse attribute bytes, putting values into bands.  Returns new pos.
       
  1263     // Used when reading a class file (local refs resolved with local cpMap).
       
  1264     // Also used for ad hoc scanning.
       
  1265     static
       
  1266     int parseUsing(Layout.Element[] elems, Holder holder,
       
  1267                    byte[] bytes, int pos, int len, ValueStream out) {
       
  1268         int prevBCI = 0;
       
  1269         int prevRBCI = 0;
       
  1270         int end = pos + len;
       
  1271         int[] buf = { 0 };  // for calls to parseInt, holds 2nd result
       
  1272         for (int i = 0; i < elems.length; i++) {
       
  1273             Layout.Element e = elems[i];
       
  1274             int bandIndex = e.bandIndex;
       
  1275             int value;
       
  1276             int BCI, RBCI;
       
  1277             switch (e.kind) {
       
  1278             case EK_INT:
       
  1279                 pos = parseInt(e, bytes, pos, buf);
       
  1280                 value = buf[0];
       
  1281                 out.putInt(bandIndex, value);
       
  1282                 break;
       
  1283             case EK_BCI:  // PH, POH
       
  1284                 pos = parseInt(e, bytes, pos, buf);
       
  1285                 BCI = buf[0];
       
  1286                 RBCI = out.encodeBCI(BCI);
       
  1287                 if (!e.flagTest(EF_DELTA)) {
       
  1288                     // PH:  transmit R(bci), store bci
       
  1289                     value = RBCI;
       
  1290                 } else {
       
  1291                     // POH:  transmit D(R(bci)), store bci
       
  1292                     value = RBCI - prevRBCI;
       
  1293                 }
       
  1294                 prevBCI = BCI;
       
  1295                 prevRBCI = RBCI;
       
  1296                 out.putInt(bandIndex, value);
       
  1297                 break;
       
  1298             case EK_BCO:  // OH
       
  1299                 assert(e.flagTest(EF_DELTA));
       
  1300                 // OH:  transmit D(R(bci)), store D(bci)
       
  1301                 pos = parseInt(e, bytes, pos, buf);
       
  1302                 BCI = prevBCI + buf[0];
       
  1303                 RBCI = out.encodeBCI(BCI);
       
  1304                 value = RBCI - prevRBCI;
       
  1305                 prevBCI = BCI;
       
  1306                 prevRBCI = RBCI;
       
  1307                 out.putInt(bandIndex, value);
       
  1308                 break;
       
  1309             case EK_FLAG:
       
  1310                 pos = parseInt(e, bytes, pos, buf);
       
  1311                 value = buf[0];
       
  1312                 out.putInt(bandIndex, value);
       
  1313                 break;
       
  1314             case EK_REPL:
       
  1315                 pos = parseInt(e, bytes, pos, buf);
       
  1316                 value = buf[0];
       
  1317                 out.putInt(bandIndex, value);
       
  1318                 for (int j = 0; j < value; j++) {
       
  1319                     pos = parseUsing(e.body, holder, bytes, pos, end-pos, out);
       
  1320                 }
       
  1321                 break;  // already transmitted the scalar value
       
  1322             case EK_UN:
       
  1323                 pos = parseInt(e, bytes, pos, buf);
       
  1324                 value = buf[0];
       
  1325                 out.putInt(bandIndex, value);
       
  1326                 Layout.Element ce = matchCase(e, value);
       
  1327                 pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out);
       
  1328 
       
  1329                 break;  // already transmitted the scalar value
       
  1330             case EK_CALL:
       
  1331                 // Adjust band offset if it is a backward call.
       
  1332                 assert(e.body.length == 1);
       
  1333                 assert(e.body[0].kind == EK_CBLE);
       
  1334                 if (e.flagTest(EF_BACK))
       
  1335                     out.noteBackCall(e.value);
       
  1336                 pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out);
       
  1337                 break;  // no additional scalar value to transmit
       
  1338             case EK_REF:
       
  1339                 pos = parseInt(e, bytes, pos, buf);
       
  1340                 int localRef = buf[0];
       
  1341                 Entry globalRef;
       
  1342                 if (localRef == 0) {
       
  1343                     globalRef = null;  // N.B. global null reference is -1
       
  1344                 } else {
       
  1345                     Entry[] cpMap = holder.getCPMap();
       
  1346                     globalRef = (localRef >= 0 && localRef < cpMap.length
       
  1347                                     ? cpMap[localRef]
       
  1348                                     : null);
       
  1349                     byte tag = e.refKind;
       
  1350                     if (globalRef != null && tag == CONSTANT_Signature
       
  1351                         && globalRef.getTag() == CONSTANT_Utf8) {
       
  1352                         // Cf. ClassReader.readSignatureRef.
       
  1353                         String typeName = globalRef.stringValue();
       
  1354                         globalRef = ConstantPool.getSignatureEntry(typeName);
       
  1355                     }
       
  1356                     String got = (globalRef == null
       
  1357                         ? "invalid CP index"
       
  1358                         : "type=" + ConstantPool.tagName(globalRef.tag));
       
  1359                     if (globalRef == null || !globalRef.tagMatches(tag)) {
       
  1360                         throw new IllegalArgumentException(
       
  1361                                 "Bad constant, expected type=" +
       
  1362                                 ConstantPool.tagName(tag) + " got " + got);
       
  1363                     }
       
  1364                 }
       
  1365                 out.putRef(bandIndex, globalRef);
       
  1366                 break;
       
  1367             default: assert(false);
       
  1368             }
       
  1369         }
       
  1370         return pos;
       
  1371     }
       
  1372 
       
  1373     static
       
  1374     Layout.Element matchCase(Layout.Element e, int value) {
       
  1375         assert(e.kind == EK_UN);
       
  1376         int lastj = e.body.length-1;
       
  1377         for (int j = 0; j < lastj; j++) {
       
  1378             Layout.Element ce = e.body[j];
       
  1379             assert(ce.kind == EK_CASE);
       
  1380             if (value == ce.value)
       
  1381                 return ce;
       
  1382         }
       
  1383         return e.body[lastj];
       
  1384     }
       
  1385 
       
  1386     private static
       
  1387     int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) {
       
  1388         int value = 0;
       
  1389         int loBits = e.len * 8;
       
  1390         // Read in big-endian order:
       
  1391         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
       
  1392             value += (bytes[pos++] & 0xFF) << bitPos;
       
  1393         }
       
  1394         if (loBits < 32 && e.flagTest(EF_SIGN)) {
       
  1395             // sign-extend subword value
       
  1396             int hiBits = 32 - loBits;
       
  1397             value = (value << hiBits) >> hiBits;
       
  1398         }
       
  1399         buf[0] = value;
       
  1400         return pos;
       
  1401     }
       
  1402 
       
  1403     // Format attribute bytes, drawing values from bands.
       
  1404     // Used when emptying attribute bands into a package model.
       
  1405     // (At that point CP refs. are not yet assigned indexes.)
       
  1406     static
       
  1407     void unparseUsing(Layout.Element[] elems, Object[] fixups,
       
  1408                       ValueStream in, ByteArrayOutputStream out) {
       
  1409         int prevBCI = 0;
       
  1410         int prevRBCI = 0;
       
  1411         for (int i = 0; i < elems.length; i++) {
       
  1412             Layout.Element e = elems[i];
       
  1413             int bandIndex = e.bandIndex;
       
  1414             int value;
       
  1415             int BCI, RBCI;  // "RBCI" is R(BCI), BCI's coded representation
       
  1416             switch (e.kind) {
       
  1417             case EK_INT:
       
  1418                 value = in.getInt(bandIndex);
       
  1419                 unparseInt(e, value, out);
       
  1420                 break;
       
  1421             case EK_BCI:  // PH, POH
       
  1422                 value = in.getInt(bandIndex);
       
  1423                 if (!e.flagTest(EF_DELTA)) {
       
  1424                     // PH:  transmit R(bci), store bci
       
  1425                     RBCI = value;
       
  1426                 } else {
       
  1427                     // POH:  transmit D(R(bci)), store bci
       
  1428                     RBCI = prevRBCI + value;
       
  1429                 }
       
  1430                 assert(prevBCI == in.decodeBCI(prevRBCI));
       
  1431                 BCI = in.decodeBCI(RBCI);
       
  1432                 unparseInt(e, BCI, out);
       
  1433                 prevBCI = BCI;
       
  1434                 prevRBCI = RBCI;
       
  1435                 break;
       
  1436             case EK_BCO:  // OH
       
  1437                 value = in.getInt(bandIndex);
       
  1438                 assert(e.flagTest(EF_DELTA));
       
  1439                 // OH:  transmit D(R(bci)), store D(bci)
       
  1440                 assert(prevBCI == in.decodeBCI(prevRBCI));
       
  1441                 RBCI = prevRBCI + value;
       
  1442                 BCI = in.decodeBCI(RBCI);
       
  1443                 unparseInt(e, BCI - prevBCI, out);
       
  1444                 prevBCI = BCI;
       
  1445                 prevRBCI = RBCI;
       
  1446                 break;
       
  1447             case EK_FLAG:
       
  1448                 value = in.getInt(bandIndex);
       
  1449                 unparseInt(e, value, out);
       
  1450                 break;
       
  1451             case EK_REPL:
       
  1452                 value = in.getInt(bandIndex);
       
  1453                 unparseInt(e, value, out);
       
  1454                 for (int j = 0; j < value; j++) {
       
  1455                     unparseUsing(e.body, fixups, in, out);
       
  1456                 }
       
  1457                 break;
       
  1458             case EK_UN:
       
  1459                 value = in.getInt(bandIndex);
       
  1460                 unparseInt(e, value, out);
       
  1461                 Layout.Element ce = matchCase(e, value);
       
  1462                 unparseUsing(ce.body, fixups, in, out);
       
  1463                 break;
       
  1464             case EK_CALL:
       
  1465                 assert(e.body.length == 1);
       
  1466                 assert(e.body[0].kind == EK_CBLE);
       
  1467                 unparseUsing(e.body[0].body, fixups, in, out);
       
  1468                 break;
       
  1469             case EK_REF:
       
  1470                 Entry globalRef = in.getRef(bandIndex);
       
  1471                 int localRef;
       
  1472                 if (globalRef != null) {
       
  1473                     // It's a one-element array, really an lvalue.
       
  1474                     fixups[0] = Fixups.addRefWithLoc(fixups[0], out.size(), globalRef);
       
  1475                     localRef = 0; // placeholder for fixups
       
  1476                 } else {
       
  1477                     localRef = 0; // fixed null value
       
  1478                 }
       
  1479                 unparseInt(e, localRef, out);
       
  1480                 break;
       
  1481             default: assert(false); continue;
       
  1482             }
       
  1483         }
       
  1484     }
       
  1485 
       
  1486     private static
       
  1487     void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) {
       
  1488         int loBits = e.len * 8;
       
  1489         if (loBits == 0) {
       
  1490             // It is not stored at all ('V' layout).
       
  1491             return;
       
  1492         }
       
  1493         if (loBits < 32) {
       
  1494             int hiBits = 32 - loBits;
       
  1495             int codedValue;
       
  1496             if (e.flagTest(EF_SIGN))
       
  1497                 codedValue = (value << hiBits) >> hiBits;
       
  1498             else
       
  1499                 codedValue = (value << hiBits) >>> hiBits;
       
  1500             if (codedValue != value)
       
  1501                 throw new InternalError("cannot code in "+e.len+" bytes: "+value);
       
  1502         }
       
  1503         // Write in big-endian order:
       
  1504         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
       
  1505             out.write((byte)(value >>> bitPos));
       
  1506         }
       
  1507     }
       
  1508 
       
  1509 /*
       
  1510     /// Testing.
       
  1511     public static void main(String av[]) {
       
  1512         int maxVal = 12;
       
  1513         int iters = 0;
       
  1514         boolean verbose;
       
  1515         int ap = 0;
       
  1516         while (ap < av.length) {
       
  1517             if (!av[ap].startsWith("-"))  break;
       
  1518             if (av[ap].startsWith("-m"))
       
  1519                 maxVal = Integer.parseInt(av[ap].substring(2));
       
  1520             else if (av[ap].startsWith("-i"))
       
  1521                 iters = Integer.parseInt(av[ap].substring(2));
       
  1522             else
       
  1523                 throw new RuntimeException("Bad option: "+av[ap]);
       
  1524             ap++;
       
  1525         }
       
  1526         verbose = (iters == 0);
       
  1527         if (iters <= 0)  iters = 1;
       
  1528         if (ap == av.length) {
       
  1529             av = new String[] {
       
  1530                 "HH",         // ClassFile.version
       
  1531                 "RUH",        // SourceFile
       
  1532                 "RCHRDNH",    // EnclosingMethod
       
  1533                 "KQH",        // ConstantValue
       
  1534                 "NH[RCH]",    // Exceptions
       
  1535                 "NH[PHH]",    // LineNumberTable
       
  1536                 "NH[PHOHRUHRSHH]",      // LocalVariableTable
       
  1537                 "NH[PHPOHIIH]",         // CharacterRangeTable
       
  1538                 "NH[PHHII]",            // CoverageTable
       
  1539                 "NH[RCHRCNHRUNHFH]",    // InnerClasses
       
  1540                 "NH[RMHNH[KLH]]",       // BootstrapMethods
       
  1541                 "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
       
  1542                 "=AnnotationDefault",
       
  1543                 // Like metadata, but with a compact tag set:
       
  1544                 "[NH[(1)]]"
       
  1545                 +"[NH[(1)]]"
       
  1546                 +"[RSHNH[RUH(1)]]"
       
  1547                 +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]",
       
  1548                 ""
       
  1549             };
       
  1550             ap = 0;
       
  1551         }
       
  1552         Utils.currentInstance.set(new PackerImpl());
       
  1553         final int[][] counts = new int[2][3];  // int bci ref
       
  1554         final Entry[] cpMap = new Entry[maxVal+1];
       
  1555         for (int i = 0; i < cpMap.length; i++) {
       
  1556             if (i == 0)  continue;  // 0 => null
       
  1557             cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i));
       
  1558         }
       
  1559         Package.Class cls = new Package().new Class("");
       
  1560         cls.cpMap = cpMap;
       
  1561         class TestValueStream extends ValueStream {
       
  1562             java.util.Random rand = new java.util.Random(0);
       
  1563             ArrayList history = new ArrayList();
       
  1564             int ckidx = 0;
       
  1565             int maxVal;
       
  1566             boolean verbose;
       
  1567             void reset() { history.clear(); ckidx = 0; }
       
  1568             public int getInt(int bandIndex) {
       
  1569                 counts[0][0]++;
       
  1570                 int value = rand.nextInt(maxVal+1);
       
  1571                 history.add(new Integer(bandIndex));
       
  1572                 history.add(new Integer(value));
       
  1573                 return value;
       
  1574             }
       
  1575             public void putInt(int bandIndex, int token) {
       
  1576                 counts[1][0]++;
       
  1577                 if (verbose)
       
  1578                     System.out.print(" "+bandIndex+":"+token);
       
  1579                 // Make sure this put parallels a previous get:
       
  1580                 int check0 = ((Integer)history.get(ckidx+0)).intValue();
       
  1581                 int check1 = ((Integer)history.get(ckidx+1)).intValue();
       
  1582                 if (check0 != bandIndex || check1 != token) {
       
  1583                     if (!verbose)
       
  1584                         System.out.println(history.subList(0, ckidx));
       
  1585                     System.out.println(" *** Should be "+check0+":"+check1);
       
  1586                     throw new RuntimeException("Failed test!");
       
  1587                 }
       
  1588                 ckidx += 2;
       
  1589             }
       
  1590             public Entry getRef(int bandIndex) {
       
  1591                 counts[0][2]++;
       
  1592                 int value = getInt(bandIndex);
       
  1593                 if (value < 0 || value > maxVal) {
       
  1594                     System.out.println(" *** Unexpected ref code "+value);
       
  1595                     return ConstantPool.getLiteralEntry(new Integer(value));
       
  1596                 }
       
  1597                 return cpMap[value];
       
  1598             }
       
  1599             public void putRef(int bandIndex, Entry ref) {
       
  1600                 counts[1][2]++;
       
  1601                 if (ref == null) {
       
  1602                     putInt(bandIndex, 0);
       
  1603                     return;
       
  1604                 }
       
  1605                 Number refValue = null;
       
  1606                 if (ref instanceof ConstantPool.NumberEntry)
       
  1607                     refValue = ((ConstantPool.NumberEntry)ref).numberValue();
       
  1608                 int value;
       
  1609                 if (!(refValue instanceof Integer)) {
       
  1610                     System.out.println(" *** Unexpected ref "+ref);
       
  1611                     value = -1;
       
  1612                 } else {
       
  1613                     value = ((Integer)refValue).intValue();
       
  1614                 }
       
  1615                 putInt(bandIndex, value);
       
  1616             }
       
  1617             public int encodeBCI(int bci) {
       
  1618                 counts[1][1]++;
       
  1619                 // move LSB to MSB of low byte
       
  1620                 int code = (bci >> 8) << 8;  // keep high bits
       
  1621                 code += (bci & 0xFE) >> 1;
       
  1622                 code += (bci & 0x01) << 7;
       
  1623                 return code ^ (8<<8);  // mark it clearly as coded
       
  1624             }
       
  1625             public int decodeBCI(int bciCode) {
       
  1626                 counts[0][1]++;
       
  1627                 bciCode ^= (8<<8);  // remove extra mark
       
  1628                 int bci = (bciCode >> 8) << 8;  // keep high bits
       
  1629                 bci += (bciCode & 0x7F) << 1;
       
  1630                 bci += (bciCode & 0x80) >> 7;
       
  1631                 return bci;
       
  1632             }
       
  1633         }
       
  1634         TestValueStream tts = new TestValueStream();
       
  1635         tts.maxVal = maxVal;
       
  1636         tts.verbose = verbose;
       
  1637         ByteArrayOutputStream buf = new ByteArrayOutputStream();
       
  1638         for (int i = 0; i < (1 << 30); i = (i + 1) * 5) {
       
  1639             int ei = tts.encodeBCI(i);
       
  1640             int di = tts.decodeBCI(ei);
       
  1641             if (di != i)  System.out.println("i="+Integer.toHexString(i)+
       
  1642                                              " ei="+Integer.toHexString(ei)+
       
  1643                                              " di="+Integer.toHexString(di));
       
  1644         }
       
  1645         while (iters-- > 0) {
       
  1646             for (int i = ap; i < av.length; i++) {
       
  1647                 String layout = av[i];
       
  1648                 if (layout.startsWith("=")) {
       
  1649                     String name = layout.substring(1);
       
  1650                     for (Attribute a : standardDefs.values()) {
       
  1651                         if (a.name().equals(name)) {
       
  1652                             layout = a.layout().layout();
       
  1653                             break;
       
  1654                         }
       
  1655                     }
       
  1656                     if (layout.startsWith("=")) {
       
  1657                         System.out.println("Could not find "+name+" in "+standardDefs.values());
       
  1658                     }
       
  1659                 }
       
  1660                 Layout self = new Layout(0, "Foo", layout);
       
  1661                 if (verbose) {
       
  1662                     System.out.print("/"+layout+"/ => ");
       
  1663                     System.out.println(Arrays.asList(self.elems));
       
  1664                 }
       
  1665                 buf.reset();
       
  1666                 tts.reset();
       
  1667                 Object fixups = self.unparse(tts, buf);
       
  1668                 byte[] bytes = buf.toByteArray();
       
  1669                 // Attach the references to the byte array.
       
  1670                 Fixups.setBytes(fixups, bytes);
       
  1671                 // Patch the references to their frozen values.
       
  1672                 Fixups.finishRefs(fixups, bytes, new Index("test", cpMap));
       
  1673                 if (verbose) {
       
  1674                     System.out.print("  bytes: {");
       
  1675                     for (int j = 0; j < bytes.length; j++) {
       
  1676                         System.out.print(" "+bytes[j]);
       
  1677                     }
       
  1678                     System.out.println("}");
       
  1679                 }
       
  1680                 if (verbose) {
       
  1681                     System.out.print("  parse: {");
       
  1682                 }
       
  1683                 self.parse(cls, bytes, 0, bytes.length, tts);
       
  1684                 if (verbose) {
       
  1685                     System.out.println("}");
       
  1686                 }
       
  1687             }
       
  1688         }
       
  1689         for (int j = 0; j <= 1; j++) {
       
  1690             System.out.print("values "+(j==0?"read":"written")+": {");
       
  1691             for (int k = 0; k < counts[j].length; k++) {
       
  1692                 System.out.print(" "+counts[j][k]);
       
  1693             }
       
  1694             System.out.println(" }");
       
  1695         }
       
  1696     }
       
  1697 //*/
       
  1698 }