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.isEmpty())
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 }