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         @java.io.Serial
 720         private static final long serialVersionUID = -2542243830788066513L;
 721 
 722         private int ctype;
 723         private String name;
 724         String layout;
 725         public FormatException(String message,
 726                                int ctype, String name, String layout) {
 727             super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" +
 728                   (message == null? "" : (": " + message)));
 729             this.ctype = ctype;
 730             this.name = name;
 731             this.layout = layout;
 732         }
 733         public FormatException(String message,
 734                                int ctype, String name) {
 735             this(message, ctype, name, null);
 736         }
 737     }
 738 
 739     void visitRefs(Holder holder, int mode, final Collection<Entry> refs) {
 740         if (mode == VRM_CLASSIC) {
 741             refs.add(getNameRef());
 742         }
 743         // else the name is owned by the layout, and is processed elsewhere
 744         if (bytes.length == 0)  return;  // quick exit
 745         if (!def.hasRefs)       return;  // quick exit
 746         if (fixups != null) {
 747             Fixups.visitRefs(fixups, refs);
 748             return;
 749         }
 750         // References (to a local cpMap) are embedded in the bytes.
 751         def.parse(holder, bytes, 0, bytes.length,
 752             new ValueStream() {
 753                 @Override
 754                 public void putInt(int bandIndex, int value) {
 755                 }
 756                 @Override
 757                 public void putRef(int bandIndex, Entry ref) {
 758                     refs.add(ref);
 759                 }
 760                 @Override
 761                 public int encodeBCI(int bci) {
 762                     return bci;
 763                 }
 764             });
 765     }
 766 
 767     public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) {
 768         def.parse(holder, bytes, pos, len, out);
 769     }
 770     public Object unparse(ValueStream in, ByteArrayOutputStream out) {
 771         return def.unparse(in, out);
 772     }
 773 
 774     @Override
 775     public String toString() {
 776         return def
 777             +"{"+(bytes == null ? -1 : size())+"}"
 778             +(fixups == null? "": fixups.toString());
 779     }
 780 
 781     /** Remove any informal "pretty printing" from the layout string.
 782      *  Removes blanks and control chars.
 783      *  Removes '#' comments (to end of line).
 784      *  Replaces '\c' by the decimal code of the character c.
 785      *  Replaces '0xNNN' by the decimal code of the hex number NNN.
 786      */
 787     public static
 788     String normalizeLayoutString(String layout) {
 789         StringBuilder buf = new StringBuilder();
 790         for (int i = 0, len = layout.length(); i < len; ) {
 791             char ch = layout.charAt(i++);
 792             if (ch <= ' ') {
 793                 // Skip whitespace and control chars
 794                 continue;
 795             } else if (ch == '#') {
 796                 // Skip to end of line.
 797                 int end1 = layout.indexOf('\n', i);
 798                 int end2 = layout.indexOf('\r', i);
 799                 if (end1 < 0)  end1 = len;
 800                 if (end2 < 0)  end2 = len;
 801                 i = Math.min(end1, end2);
 802             } else if (ch == '\\') {
 803                 // Map a character reference to its decimal code.
 804                 buf.append((int) layout.charAt(i++));
 805             } else if (ch == '0' && layout.startsWith("0x", i-1)) {
 806                 // Map a hex numeral to its decimal code.
 807                 int start = i-1;
 808                 int end = start+2;
 809                 while (end < len) {
 810                     int dig = layout.charAt(end);
 811                     if ((dig >= '0' && dig <= '9') ||
 812                         (dig >= 'a' && dig <= 'f'))
 813                         ++end;
 814                     else
 815                         break;
 816                 }
 817                 if (end > start) {
 818                     String num = layout.substring(start, end);
 819                     buf.append(Integer.decode(num));
 820                     i = end;
 821                 } else {
 822                     buf.append(ch);
 823                 }
 824             } else {
 825                 buf.append(ch);
 826             }
 827         }
 828         String result = buf.toString();
 829         if (false && !result.equals(layout)) {
 830             Utils.log.info("Normalizing layout string");
 831             Utils.log.info("    From: "+layout);
 832             Utils.log.info("    To:   "+result);
 833         }
 834         return result;
 835     }
 836 
 837     /// Subroutines for parsing and unparsing:
 838 
 839     /** Parse the attribute layout language.
 840 <pre>
 841   attribute_layout:
 842         ( layout_element )* | ( callable )+
 843   layout_element:
 844         ( integral | replication | union | call | reference )
 845 
 846   callable:
 847         '[' body ']'
 848   body:
 849         ( layout_element )+
 850 
 851   integral:
 852         ( unsigned_int | signed_int | bc_index | bc_offset | flag )
 853   unsigned_int:
 854         uint_type
 855   signed_int:
 856         'S' uint_type
 857   any_int:
 858         ( unsigned_int | signed_int )
 859   bc_index:
 860         ( 'P' uint_type | 'PO' uint_type )
 861   bc_offset:
 862         'O' any_int
 863   flag:
 864         'F' uint_type
 865   uint_type:
 866         ( 'B' | 'H' | 'I' | 'V' )
 867 
 868   replication:
 869         'N' uint_type '[' body ']'
 870 
 871   union:
 872         'T' any_int (union_case)* '(' ')' '[' (body)? ']'
 873   union_case:
 874         '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
 875   union_case_tag:
 876         ( numeral | numeral '-' numeral )
 877   call:
 878         '(' numeral ')'
 879 
 880   reference:
 881         reference_type ( 'N' )? uint_type
 882   reference_type:
 883         ( constant_ref | schema_ref | utf8_ref | untyped_ref )
 884   constant_ref:
 885         ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' )
 886   schema_ref:
 887         ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' )
 888   utf8_ref:
 889         'RU'
 890   untyped_ref:
 891         'RQ'
 892 
 893   numeral:
 894         '(' ('-')? (digit)+ ')'
 895   digit:
 896         ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
 897  </pre>
 898     */
 899     static //private
 900     Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) {
 901         List<Layout.Element> col = new ArrayList<>(layout.length());
 902         tokenizeLayout(self, curCble, layout, col);
 903         Layout.Element[] res = new Layout.Element[col.size()];
 904         col.toArray(res);
 905         return res;
 906     }
 907     static //private
 908     void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) {
 909         boolean prevBCI = false;
 910         for (int len = layout.length(), i = 0; i < len; ) {
 911             int start = i;
 912             int body;
 913             Layout.Element e = self.new Element();
 914             byte kind;
 915             //System.out.println("at "+i+": ..."+layout.substring(i));
 916             // strip a prefix
 917             switch (layout.charAt(i++)) {
 918             /// layout_element: integral
 919             case 'B': case 'H': case 'I': case 'V': // unsigned_int
 920                 kind = EK_INT;
 921                 --i; // reparse
 922                 i = tokenizeUInt(e, layout, i);
 923                 break;
 924             case 'S': // signed_int
 925                 kind = EK_INT;
 926                 --i; // reparse
 927                 i = tokenizeSInt(e, layout, i);
 928                 break;
 929             case 'P': // bc_index
 930                 kind = EK_BCI;
 931                 if (layout.charAt(i++) == 'O') {
 932                     // bc_index: 'PO' tokenizeUInt
 933                     e.flags |= EF_DELTA;
 934                     // must follow P or PO:
 935                     if (!prevBCI)
 936                         { i = -i; continue; } // fail
 937                     i++; // move forward
 938                 }
 939                 --i; // reparse
 940                 i = tokenizeUInt(e, layout, i);
 941                 break;
 942             case 'O': // bc_offset
 943                 kind = EK_BCO;
 944                 e.flags |= EF_DELTA;
 945                 // must follow P or PO:
 946                 if (!prevBCI)
 947                     { i = -i; continue; } // fail
 948                 i = tokenizeSInt(e, layout, i);
 949                 break;
 950             case 'F': // flag
 951                 kind = EK_FLAG;
 952                 i = tokenizeUInt(e, layout, i);
 953                 break;
 954             case 'N': // replication: 'N' uint '[' elem ... ']'
 955                 kind = EK_REPL;
 956                 i = tokenizeUInt(e, layout, i);
 957                 if (layout.charAt(i++) != '[')
 958                     { i = -i; continue; } // fail
 959                 i = skipBody(layout, body = i);
 960                 e.body = tokenizeLayout(self, curCble,
 961                                         layout.substring(body, i++));
 962                 break;
 963             case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
 964                 kind = EK_UN;
 965                 i = tokenizeSInt(e, layout, i);
 966                 List<Layout.Element> cases = new ArrayList<>();
 967                 for (;;) {
 968                     // Keep parsing cases until we hit the default case.
 969                     if (layout.charAt(i++) != '(')
 970                         { i = -i; break; } // fail
 971                     int beg = i;
 972                     i = layout.indexOf(')', i);
 973                     String cstr = layout.substring(beg, i++);
 974                     int cstrlen = cstr.length();
 975                     if (layout.charAt(i++) != '[')
 976                         { i = -i; break; } // fail
 977                     // Check for duplication.
 978                     if (layout.charAt(i) == ']')
 979                         body = i;  // missing body, which is legal here
 980                     else
 981                         i = skipBody(layout, body = i);
 982                     Layout.Element[] cbody
 983                         = tokenizeLayout(self, curCble,
 984                                          layout.substring(body, i++));
 985                     if (cstrlen == 0) {
 986                         Layout.Element ce = self.new Element();
 987                         ce.body = cbody;
 988                         ce.kind = EK_CASE;
 989                         ce.removeBand();
 990                         cases.add(ce);
 991                         break;  // done with the whole union
 992                     } else {
 993                         // Parse a case string.
 994                         boolean firstCaseNum = true;
 995                         for (int cp = 0, endp;; cp = endp+1) {
 996                             // Look for multiple case tags:
 997                             endp = cstr.indexOf(',', cp);
 998                             if (endp < 0)  endp = cstrlen;
 999                             String cstr1 = cstr.substring(cp, endp);
1000                             if (cstr1.isEmpty())
1001                                 cstr1 = "empty";  // will fail parse
1002                             int value0, value1;
1003                             // Check for a case range (new in 1.6).
1004                             int dash = findCaseDash(cstr1, 0);
1005                             if (dash >= 0) {
1006                                 value0 = parseIntBefore(cstr1, dash);
1007                                 value1 = parseIntAfter(cstr1, dash);
1008                                 if (value0 >= value1)
1009                                     { i = -i; break; } // fail
1010                             } else {
1011                                 value0 = value1 = Integer.parseInt(cstr1);
1012                             }
1013                             // Add a case for each value in value0..value1
1014                             for (;; value0++) {
1015                                 Layout.Element ce = self.new Element();
1016                                 ce.body = cbody;  // all cases share one body
1017                                 ce.kind = EK_CASE;
1018                                 ce.removeBand();
1019                                 if (!firstCaseNum)
1020                                     // "backward case" repeats a body
1021                                     ce.flags |= EF_BACK;
1022                                 firstCaseNum = false;
1023                                 ce.value = value0;
1024                                 cases.add(ce);
1025                                 if (value0 == value1)  break;
1026                             }
1027                             if (endp == cstrlen) {
1028                                 break;  // done with this case
1029                             }
1030                         }
1031                     }
1032                 }
1033                 e.body = new Layout.Element[cases.size()];
1034                 cases.toArray(e.body);
1035                 e.kind = kind;
1036                 for (int j = 0; j < e.body.length-1; j++) {
1037                     Layout.Element ce = e.body[j];
1038                     if (matchCase(e, ce.value) != ce) {
1039                         // Duplicate tag.
1040                         { i = -i; break; } // fail
1041                     }
1042                 }
1043                 break;
1044             case '(': // call: '(' '-'? digit+ ')'
1045                 kind = EK_CALL;
1046                 e.removeBand();
1047                 i = layout.indexOf(')', i);
1048                 String cstr = layout.substring(start+1, i++);
1049                 int offset = Integer.parseInt(cstr);
1050                 int target = curCble + offset;
1051                 if (!(offset+"").equals(cstr) ||
1052                     self.elems == null ||
1053                     target < 0 ||
1054                     target >= self.elems.length)
1055                     { i = -i; continue; } // fail
1056                 Layout.Element ce = self.elems[target];
1057                 assert(ce.kind == EK_CBLE);
1058                 e.value = target;
1059                 e.body = new Layout.Element[]{ ce };
1060                 // Is it a (recursive) backward call?
1061                 if (offset <= 0) {
1062                     // Yes.  Mark both caller and callee backward.
1063                     e.flags  |= EF_BACK;
1064                     ce.flags |= EF_BACK;
1065                 }
1066                 break;
1067             case 'K':  // reference_type: constant_ref
1068                 kind = EK_REF;
1069                 switch (layout.charAt(i++)) {
1070                 case 'I': e.refKind = CONSTANT_Integer; break;
1071                 case 'J': e.refKind = CONSTANT_Long; break;
1072                 case 'F': e.refKind = CONSTANT_Float; break;
1073                 case 'D': e.refKind = CONSTANT_Double; break;
1074                 case 'S': e.refKind = CONSTANT_String; break;
1075                 case 'Q': e.refKind = CONSTANT_FieldSpecific; break;
1076 
1077                 // new in 1.7:
1078                 case 'M': e.refKind = CONSTANT_MethodHandle; break;
1079                 case 'T': e.refKind = CONSTANT_MethodType; break;
1080                 case 'L': e.refKind = CONSTANT_LoadableValue; break;
1081                 default: { i = -i; continue; } // fail
1082                 }
1083                 break;
1084             case 'R': // schema_ref
1085                 kind = EK_REF;
1086                 switch (layout.charAt(i++)) {
1087                 case 'C': e.refKind = CONSTANT_Class; break;
1088                 case 'S': e.refKind = CONSTANT_Signature; break;
1089                 case 'D': e.refKind = CONSTANT_NameandType; break;
1090                 case 'F': e.refKind = CONSTANT_Fieldref; break;
1091                 case 'M': e.refKind = CONSTANT_Methodref; break;
1092                 case 'I': e.refKind = CONSTANT_InterfaceMethodref; break;
1093 
1094                 case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref
1095                 case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref
1096 
1097                 // new in 1.7:
1098                 case 'Y': e.refKind = CONSTANT_InvokeDynamic; break;
1099                 case 'B': e.refKind = CONSTANT_BootstrapMethod; break;
1100                 case 'N': e.refKind = CONSTANT_AnyMember; break;
1101 
1102                 default: { i = -i; continue; } // fail
1103                 }
1104                 break;
1105             default: { i = -i; continue; } // fail
1106             }
1107 
1108             // further parsing of refs
1109             if (kind == EK_REF) {
1110                 // reference: reference_type -><- ( 'N' )? tokenizeUInt
1111                 if (layout.charAt(i++) == 'N') {
1112                     e.flags |= EF_NULL;
1113                     i++; // move forward
1114                 }
1115                 --i; // reparse
1116                 i = tokenizeUInt(e, layout, i);
1117                 self.hasRefs = true;
1118             }
1119 
1120             prevBCI = (kind == EK_BCI);
1121 
1122             // store the new element
1123             e.kind = kind;
1124             e.layout = layout.substring(start, i);
1125             col.add(e);
1126         }
1127     }
1128     static //private
1129     String[] splitBodies(String layout) {
1130         List<String> bodies = new ArrayList<>();
1131         // Parse several independent layout bodies:  "[foo][bar]...[baz]"
1132         for (int i = 0; i < layout.length(); i++) {
1133             if (layout.charAt(i++) != '[')
1134                 layout.charAt(-i);  // throw error
1135             int body;
1136             i = skipBody(layout, body = i);
1137             bodies.add(layout.substring(body, i));
1138         }
1139         String[] res = new String[bodies.size()];
1140         bodies.toArray(res);
1141         return res;
1142     }
1143     private static
1144     int skipBody(String layout, int i) {
1145         assert(layout.charAt(i-1) == '[');
1146         if (layout.charAt(i) == ']')
1147             // No empty bodies, please.
1148             return -i;
1149         // skip balanced [...[...]...]
1150         for (int depth = 1; depth > 0; ) {
1151             switch (layout.charAt(i++)) {
1152             case '[': depth++; break;
1153             case ']': depth--; break;
1154             }
1155         }
1156         --i;  // get before bracket
1157         assert(layout.charAt(i) == ']');
1158         return i;  // return closing bracket
1159     }
1160     private static
1161     int tokenizeUInt(Layout.Element e, String layout, int i) {
1162         switch (layout.charAt(i++)) {
1163         case 'V': e.len = 0; break;
1164         case 'B': e.len = 1; break;
1165         case 'H': e.len = 2; break;
1166         case 'I': e.len = 4; break;
1167         default: return -i;
1168         }
1169         return i;
1170     }
1171     private static
1172     int tokenizeSInt(Layout.Element e, String layout, int i) {
1173         if (layout.charAt(i) == 'S') {
1174             e.flags |= EF_SIGN;
1175             ++i;
1176         }
1177         return tokenizeUInt(e, layout, i);
1178     }
1179 
1180     private static
1181     boolean isDigit(char c) {
1182         return c >= '0' && c <= '9';
1183     }
1184 
1185     /** Find an occurrence of hyphen '-' between two numerals. */
1186     static //private
1187     int findCaseDash(String layout, int fromIndex) {
1188         if (fromIndex <= 0)  fromIndex = 1;  // minimum dash pos
1189         int lastDash = layout.length() - 2;  // maximum dash pos
1190         for (;;) {
1191             int dash = layout.indexOf('-', fromIndex);
1192             if (dash < 0 || dash > lastDash)  return -1;
1193             if (isDigit(layout.charAt(dash-1))) {
1194                 char afterDash = layout.charAt(dash+1);
1195                 if (afterDash == '-' && dash+2 < layout.length())
1196                     afterDash = layout.charAt(dash+2);
1197                 if (isDigit(afterDash)) {
1198                     // matched /[0-9]--?[0-9]/; return position of dash
1199                     return dash;
1200                 }
1201             }
1202             fromIndex = dash+1;
1203         }
1204     }
1205     static
1206     int parseIntBefore(String layout, int dash) {
1207         int end = dash;
1208         int beg = end;
1209         while (beg > 0 && isDigit(layout.charAt(beg-1))) {
1210             --beg;
1211         }
1212         if (beg == end)  return Integer.parseInt("empty");
1213         // skip backward over a sign
1214         if (beg >= 1 && layout.charAt(beg-1) == '-')  --beg;
1215         assert(beg == 0 || !isDigit(layout.charAt(beg-1)));
1216         return Integer.parseInt(layout.substring(beg, end));
1217     }
1218     static
1219     int parseIntAfter(String layout, int dash) {
1220         int beg = dash+1;
1221         int end = beg;
1222         int limit = layout.length();
1223         if (end < limit && layout.charAt(end) == '-')  ++end;
1224         while (end < limit && isDigit(layout.charAt(end))) {
1225             ++end;
1226         }
1227         if (beg == end)  return Integer.parseInt("empty");
1228         return Integer.parseInt(layout.substring(beg, end));
1229     }
1230     /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */
1231     static
1232     String expandCaseDashNotation(String layout) {
1233         int dash = findCaseDash(layout, 0);
1234         if (dash < 0)  return layout;  // no dashes (the common case)
1235         StringBuilder result = new StringBuilder(layout.length() * 3);
1236         int sofar = 0;  // how far have we processed the layout?
1237         for (;;) {
1238             // for each dash, collect everything up to the dash
1239             result.append(layout, sofar, dash);
1240             sofar = dash+1;  // skip the dash
1241             // then collect intermediate values
1242             int value0 = parseIntBefore(layout, dash);
1243             int value1 = parseIntAfter(layout, dash);
1244             assert(value0 < value1);
1245             result.append(",");  // close off value0 numeral
1246             for (int i = value0+1; i < value1; i++) {
1247                 result.append(i);
1248                 result.append(",");  // close off i numeral
1249             }
1250             dash = findCaseDash(layout, sofar);
1251             if (dash < 0)  break;
1252         }
1253         result.append(layout, sofar, layout.length());  // collect the rest
1254         return result.toString();
1255     }
1256     static {
1257         assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5"));
1258         assert(expandCaseDashNotation("-2--1").equals("-2,-1"));
1259         assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1"));
1260         assert(expandCaseDashNotation("-1-0").equals("-1,0"));
1261     }
1262 
1263     // Parse attribute bytes, putting values into bands.  Returns new pos.
1264     // Used when reading a class file (local refs resolved with local cpMap).
1265     // Also used for ad hoc scanning.
1266     static
1267     int parseUsing(Layout.Element[] elems, Holder holder,
1268                    byte[] bytes, int pos, int len, ValueStream out) {
1269         int prevBCI = 0;
1270         int prevRBCI = 0;
1271         int end = pos + len;
1272         int[] buf = { 0 };  // for calls to parseInt, holds 2nd result
1273         for (int i = 0; i < elems.length; i++) {
1274             Layout.Element e = elems[i];
1275             int bandIndex = e.bandIndex;
1276             int value;
1277             int BCI, RBCI;
1278             switch (e.kind) {
1279             case EK_INT:
1280                 pos = parseInt(e, bytes, pos, buf);
1281                 value = buf[0];
1282                 out.putInt(bandIndex, value);
1283                 break;
1284             case EK_BCI:  // PH, POH
1285                 pos = parseInt(e, bytes, pos, buf);
1286                 BCI = buf[0];
1287                 RBCI = out.encodeBCI(BCI);
1288                 if (!e.flagTest(EF_DELTA)) {
1289                     // PH:  transmit R(bci), store bci
1290                     value = RBCI;
1291                 } else {
1292                     // POH:  transmit D(R(bci)), store bci
1293                     value = RBCI - prevRBCI;
1294                 }
1295                 prevBCI = BCI;
1296                 prevRBCI = RBCI;
1297                 out.putInt(bandIndex, value);
1298                 break;
1299             case EK_BCO:  // OH
1300                 assert(e.flagTest(EF_DELTA));
1301                 // OH:  transmit D(R(bci)), store D(bci)
1302                 pos = parseInt(e, bytes, pos, buf);
1303                 BCI = prevBCI + buf[0];
1304                 RBCI = out.encodeBCI(BCI);
1305                 value = RBCI - prevRBCI;
1306                 prevBCI = BCI;
1307                 prevRBCI = RBCI;
1308                 out.putInt(bandIndex, value);
1309                 break;
1310             case EK_FLAG:
1311                 pos = parseInt(e, bytes, pos, buf);
1312                 value = buf[0];
1313                 out.putInt(bandIndex, value);
1314                 break;
1315             case EK_REPL:
1316                 pos = parseInt(e, bytes, pos, buf);
1317                 value = buf[0];
1318                 out.putInt(bandIndex, value);
1319                 for (int j = 0; j < value; j++) {
1320                     pos = parseUsing(e.body, holder, bytes, pos, end-pos, out);
1321                 }
1322                 break;  // already transmitted the scalar value
1323             case EK_UN:
1324                 pos = parseInt(e, bytes, pos, buf);
1325                 value = buf[0];
1326                 out.putInt(bandIndex, value);
1327                 Layout.Element ce = matchCase(e, value);
1328                 pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out);
1329 
1330                 break;  // already transmitted the scalar value
1331             case EK_CALL:
1332                 // Adjust band offset if it is a backward call.
1333                 assert(e.body.length == 1);
1334                 assert(e.body[0].kind == EK_CBLE);
1335                 if (e.flagTest(EF_BACK))
1336                     out.noteBackCall(e.value);
1337                 pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out);
1338                 break;  // no additional scalar value to transmit
1339             case EK_REF:
1340                 pos = parseInt(e, bytes, pos, buf);
1341                 int localRef = buf[0];
1342                 Entry globalRef;
1343                 if (localRef == 0) {
1344                     globalRef = null;  // N.B. global null reference is -1
1345                 } else {
1346                     Entry[] cpMap = holder.getCPMap();
1347                     globalRef = (localRef >= 0 && localRef < cpMap.length
1348                                     ? cpMap[localRef]
1349                                     : null);
1350                     byte tag = e.refKind;
1351                     if (globalRef != null && tag == CONSTANT_Signature
1352                         && globalRef.getTag() == CONSTANT_Utf8) {
1353                         // Cf. ClassReader.readSignatureRef.
1354                         String typeName = globalRef.stringValue();
1355                         globalRef = ConstantPool.getSignatureEntry(typeName);
1356                     }
1357                     String got = (globalRef == null
1358                         ? "invalid CP index"
1359                         : "type=" + ConstantPool.tagName(globalRef.tag));
1360                     if (globalRef == null || !globalRef.tagMatches(tag)) {
1361                         throw new IllegalArgumentException(
1362                                 "Bad constant, expected type=" +
1363                                 ConstantPool.tagName(tag) + " got " + got);
1364                     }
1365                 }
1366                 out.putRef(bandIndex, globalRef);
1367                 break;
1368             default: assert(false);
1369             }
1370         }
1371         return pos;
1372     }
1373 
1374     static
1375     Layout.Element matchCase(Layout.Element e, int value) {
1376         assert(e.kind == EK_UN);
1377         int lastj = e.body.length-1;
1378         for (int j = 0; j < lastj; j++) {
1379             Layout.Element ce = e.body[j];
1380             assert(ce.kind == EK_CASE);
1381             if (value == ce.value)
1382                 return ce;
1383         }
1384         return e.body[lastj];
1385     }
1386 
1387     private static
1388     int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) {
1389         int value = 0;
1390         int loBits = e.len * 8;
1391         // Read in big-endian order:
1392         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
1393             value += (bytes[pos++] & 0xFF) << bitPos;
1394         }
1395         if (loBits < 32 && e.flagTest(EF_SIGN)) {
1396             // sign-extend subword value
1397             int hiBits = 32 - loBits;
1398             value = (value << hiBits) >> hiBits;
1399         }
1400         buf[0] = value;
1401         return pos;
1402     }
1403 
1404     // Format attribute bytes, drawing values from bands.
1405     // Used when emptying attribute bands into a package model.
1406     // (At that point CP refs. are not yet assigned indexes.)
1407     static
1408     void unparseUsing(Layout.Element[] elems, Object[] fixups,
1409                       ValueStream in, ByteArrayOutputStream out) {
1410         int prevBCI = 0;
1411         int prevRBCI = 0;
1412         for (int i = 0; i < elems.length; i++) {
1413             Layout.Element e = elems[i];
1414             int bandIndex = e.bandIndex;
1415             int value;
1416             int BCI, RBCI;  // "RBCI" is R(BCI), BCI's coded representation
1417             switch (e.kind) {
1418             case EK_INT:
1419                 value = in.getInt(bandIndex);
1420                 unparseInt(e, value, out);
1421                 break;
1422             case EK_BCI:  // PH, POH
1423                 value = in.getInt(bandIndex);
1424                 if (!e.flagTest(EF_DELTA)) {
1425                     // PH:  transmit R(bci), store bci
1426                     RBCI = value;
1427                 } else {
1428                     // POH:  transmit D(R(bci)), store bci
1429                     RBCI = prevRBCI + value;
1430                 }
1431                 assert(prevBCI == in.decodeBCI(prevRBCI));
1432                 BCI = in.decodeBCI(RBCI);
1433                 unparseInt(e, BCI, out);
1434                 prevBCI = BCI;
1435                 prevRBCI = RBCI;
1436                 break;
1437             case EK_BCO:  // OH
1438                 value = in.getInt(bandIndex);
1439                 assert(e.flagTest(EF_DELTA));
1440                 // OH:  transmit D(R(bci)), store D(bci)
1441                 assert(prevBCI == in.decodeBCI(prevRBCI));
1442                 RBCI = prevRBCI + value;
1443                 BCI = in.decodeBCI(RBCI);
1444                 unparseInt(e, BCI - prevBCI, out);
1445                 prevBCI = BCI;
1446                 prevRBCI = RBCI;
1447                 break;
1448             case EK_FLAG:
1449                 value = in.getInt(bandIndex);
1450                 unparseInt(e, value, out);
1451                 break;
1452             case EK_REPL:
1453                 value = in.getInt(bandIndex);
1454                 unparseInt(e, value, out);
1455                 for (int j = 0; j < value; j++) {
1456                     unparseUsing(e.body, fixups, in, out);
1457                 }
1458                 break;
1459             case EK_UN:
1460                 value = in.getInt(bandIndex);
1461                 unparseInt(e, value, out);
1462                 Layout.Element ce = matchCase(e, value);
1463                 unparseUsing(ce.body, fixups, in, out);
1464                 break;
1465             case EK_CALL:
1466                 assert(e.body.length == 1);
1467                 assert(e.body[0].kind == EK_CBLE);
1468                 unparseUsing(e.body[0].body, fixups, in, out);
1469                 break;
1470             case EK_REF:
1471                 Entry globalRef = in.getRef(bandIndex);
1472                 int localRef;
1473                 if (globalRef != null) {
1474                     // It's a one-element array, really an lvalue.
1475                     fixups[0] = Fixups.addRefWithLoc(fixups[0], out.size(), globalRef);
1476                     localRef = 0; // placeholder for fixups
1477                 } else {
1478                     localRef = 0; // fixed null value
1479                 }
1480                 unparseInt(e, localRef, out);
1481                 break;
1482             default: assert(false); continue;
1483             }
1484         }
1485     }
1486 
1487     private static
1488     void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) {
1489         int loBits = e.len * 8;
1490         if (loBits == 0) {
1491             // It is not stored at all ('V' layout).
1492             return;
1493         }
1494         if (loBits < 32) {
1495             int hiBits = 32 - loBits;
1496             int codedValue;
1497             if (e.flagTest(EF_SIGN))
1498                 codedValue = (value << hiBits) >> hiBits;
1499             else
1500                 codedValue = (value << hiBits) >>> hiBits;
1501             if (codedValue != value)
1502                 throw new InternalError("cannot code in "+e.len+" bytes: "+value);
1503         }
1504         // Write in big-endian order:
1505         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
1506             out.write((byte)(value >>> bitPos));
1507         }
1508     }
1509 
1510 /*
1511     /// Testing.
1512     public static void main(String av[]) {
1513         int maxVal = 12;
1514         int iters = 0;
1515         boolean verbose;
1516         int ap = 0;
1517         while (ap < av.length) {
1518             if (!av[ap].startsWith("-"))  break;
1519             if (av[ap].startsWith("-m"))
1520                 maxVal = Integer.parseInt(av[ap].substring(2));
1521             else if (av[ap].startsWith("-i"))
1522                 iters = Integer.parseInt(av[ap].substring(2));
1523             else
1524                 throw new RuntimeException("Bad option: "+av[ap]);
1525             ap++;
1526         }
1527         verbose = (iters == 0);
1528         if (iters <= 0)  iters = 1;
1529         if (ap == av.length) {
1530             av = new String[] {
1531                 "HH",         // ClassFile.version
1532                 "RUH",        // SourceFile
1533                 "RCHRDNH",    // EnclosingMethod
1534                 "KQH",        // ConstantValue
1535                 "NH[RCH]",    // Exceptions
1536                 "NH[PHH]",    // LineNumberTable
1537                 "NH[PHOHRUHRSHH]",      // LocalVariableTable
1538                 "NH[PHPOHIIH]",         // CharacterRangeTable
1539                 "NH[PHHII]",            // CoverageTable
1540                 "NH[RCHRCNHRUNHFH]",    // InnerClasses
1541                 "NH[RMHNH[KLH]]",       // BootstrapMethods
1542                 "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
1543                 "=AnnotationDefault",
1544                 // Like metadata, but with a compact tag set:
1545                 "[NH[(1)]]"
1546                 +"[NH[(1)]]"
1547                 +"[RSHNH[RUH(1)]]"
1548                 +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]",
1549                 ""
1550             };
1551             ap = 0;
1552         }
1553         Utils.currentInstance.set(new PackerImpl());
1554         final int[][] counts = new int[2][3];  // int bci ref
1555         final Entry[] cpMap = new Entry[maxVal+1];
1556         for (int i = 0; i < cpMap.length; i++) {
1557             if (i == 0)  continue;  // 0 => null
1558             cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i));
1559         }
1560         Package.Class cls = new Package().new Class("");
1561         cls.cpMap = cpMap;
1562         class TestValueStream extends ValueStream {
1563             java.util.Random rand = new java.util.Random(0);
1564             ArrayList history = new ArrayList();
1565             int ckidx = 0;
1566             int maxVal;
1567             boolean verbose;
1568             void reset() { history.clear(); ckidx = 0; }
1569             public int getInt(int bandIndex) {
1570                 counts[0][0]++;
1571                 int value = rand.nextInt(maxVal+1);
1572                 history.add(new Integer(bandIndex));
1573                 history.add(new Integer(value));
1574                 return value;
1575             }
1576             public void putInt(int bandIndex, int token) {
1577                 counts[1][0]++;
1578                 if (verbose)
1579                     System.out.print(" "+bandIndex+":"+token);
1580                 // Make sure this put parallels a previous get:
1581                 int check0 = ((Integer)history.get(ckidx+0)).intValue();
1582                 int check1 = ((Integer)history.get(ckidx+1)).intValue();
1583                 if (check0 != bandIndex || check1 != token) {
1584                     if (!verbose)
1585                         System.out.println(history.subList(0, ckidx));
1586                     System.out.println(" *** Should be "+check0+":"+check1);
1587                     throw new RuntimeException("Failed test!");
1588                 }
1589                 ckidx += 2;
1590             }
1591             public Entry getRef(int bandIndex) {
1592                 counts[0][2]++;
1593                 int value = getInt(bandIndex);
1594                 if (value < 0 || value > maxVal) {
1595                     System.out.println(" *** Unexpected ref code "+value);
1596                     return ConstantPool.getLiteralEntry(new Integer(value));
1597                 }
1598                 return cpMap[value];
1599             }
1600             public void putRef(int bandIndex, Entry ref) {
1601                 counts[1][2]++;
1602                 if (ref == null) {
1603                     putInt(bandIndex, 0);
1604                     return;
1605                 }
1606                 Number refValue = null;
1607                 if (ref instanceof ConstantPool.NumberEntry)
1608                     refValue = ((ConstantPool.NumberEntry)ref).numberValue();
1609                 int value;
1610                 if (!(refValue instanceof Integer)) {
1611                     System.out.println(" *** Unexpected ref "+ref);
1612                     value = -1;
1613                 } else {
1614                     value = ((Integer)refValue).intValue();
1615                 }
1616                 putInt(bandIndex, value);
1617             }
1618             public int encodeBCI(int bci) {
1619                 counts[1][1]++;
1620                 // move LSB to MSB of low byte
1621                 int code = (bci >> 8) << 8;  // keep high bits
1622                 code += (bci & 0xFE) >> 1;
1623                 code += (bci & 0x01) << 7;
1624                 return code ^ (8<<8);  // mark it clearly as coded
1625             }
1626             public int decodeBCI(int bciCode) {
1627                 counts[0][1]++;
1628                 bciCode ^= (8<<8);  // remove extra mark
1629                 int bci = (bciCode >> 8) << 8;  // keep high bits
1630                 bci += (bciCode & 0x7F) << 1;
1631                 bci += (bciCode & 0x80) >> 7;
1632                 return bci;
1633             }
1634         }
1635         TestValueStream tts = new TestValueStream();
1636         tts.maxVal = maxVal;
1637         tts.verbose = verbose;
1638         ByteArrayOutputStream buf = new ByteArrayOutputStream();
1639         for (int i = 0; i < (1 << 30); i = (i + 1) * 5) {
1640             int ei = tts.encodeBCI(i);
1641             int di = tts.decodeBCI(ei);
1642             if (di != i)  System.out.println("i="+Integer.toHexString(i)+
1643                                              " ei="+Integer.toHexString(ei)+
1644                                              " di="+Integer.toHexString(di));
1645         }
1646         while (iters-- > 0) {
1647             for (int i = ap; i < av.length; i++) {
1648                 String layout = av[i];
1649                 if (layout.startsWith("=")) {
1650                     String name = layout.substring(1);
1651                     for (Attribute a : standardDefs.values()) {
1652                         if (a.name().equals(name)) {
1653                             layout = a.layout().layout();
1654                             break;
1655                         }
1656                     }
1657                     if (layout.startsWith("=")) {
1658                         System.out.println("Could not find "+name+" in "+standardDefs.values());
1659                     }
1660                 }
1661                 Layout self = new Layout(0, "Foo", layout);
1662                 if (verbose) {
1663                     System.out.print("/"+layout+"/ => ");
1664                     System.out.println(Arrays.asList(self.elems));
1665                 }
1666                 buf.reset();
1667                 tts.reset();
1668                 Object fixups = self.unparse(tts, buf);
1669                 byte[] bytes = buf.toByteArray();
1670                 // Attach the references to the byte array.
1671                 Fixups.setBytes(fixups, bytes);
1672                 // Patch the references to their frozen values.
1673                 Fixups.finishRefs(fixups, bytes, new Index("test", cpMap));
1674                 if (verbose) {
1675                     System.out.print("  bytes: {");
1676                     for (int j = 0; j < bytes.length; j++) {
1677                         System.out.print(" "+bytes[j]);
1678                     }
1679                     System.out.println("}");
1680                 }
1681                 if (verbose) {
1682                     System.out.print("  parse: {");
1683                 }
1684                 self.parse(cls, bytes, 0, bytes.length, tts);
1685                 if (verbose) {
1686                     System.out.println("}");
1687                 }
1688             }
1689         }
1690         for (int j = 0; j <= 1; j++) {
1691             System.out.print("values "+(j==0?"read":"written")+": {");
1692             for (int k = 0; k < counts[j].length; k++) {
1693                 System.out.print(" "+counts[j][k]);
1694             }
1695             System.out.println(" }");
1696         }
1697     }
1698 //*/
1699 }