|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | package com.sun.java.util.jar.pack; | 
|  |  | 
|  | import com.sun.java.util.jar.pack.ConstantPool.Entry; | 
|  | import com.sun.java.util.jar.pack.ConstantPool.Index; | 
|  | import java.io.ByteArrayOutputStream; | 
|  | import java.io.IOException; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Arrays; | 
|  | import java.util.Collection; | 
|  | import java.util.Collections; | 
|  | import java.util.HashMap; | 
|  | import java.util.List; | 
|  | import java.util.Map; | 
|  | import static com.sun.java.util.jar.pack.Constants.*; | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  | class Attribute implements Comparable<Attribute> { | 
|  |     // Attribute instance fields. | 
|  |  | 
|  |     Layout def;      | 
|  |     byte[] bytes;    | 
|  |     Object fixups;   | 
|  |  | 
|  |     public String name() { return def.name(); } | 
|  |     public Layout layout() { return def; } | 
|  |     public byte[] bytes() { return bytes; } | 
|  |     public int size() { return bytes.length; } | 
|  |     public Entry getNameRef() { return def.getNameRef(); } | 
|  |  | 
|  |     private Attribute(Attribute old) { | 
|  |         this.def = old.def; | 
|  |         this.bytes = old.bytes; | 
|  |         this.fixups = old.fixups; | 
|  |     } | 
|  |  | 
|  |     public Attribute(Layout def, byte[] bytes, Object fixups) { | 
|  |         this.def = def; | 
|  |         this.bytes = bytes; | 
|  |         this.fixups = fixups; | 
|  |         Fixups.setBytes(fixups, bytes); | 
|  |     } | 
|  |     public Attribute(Layout def, byte[] bytes) { | 
|  |         this(def, bytes, null); | 
|  |     } | 
|  |  | 
|  |     public Attribute addContent(byte[] bytes, Object fixups) { | 
|  |         assert(isCanonical()); | 
|  |         if (bytes.length == 0 && fixups == null) | 
|  |             return this; | 
|  |         Attribute res = new Attribute(this); | 
|  |         res.bytes = bytes; | 
|  |         res.fixups = fixups; | 
|  |         Fixups.setBytes(fixups, bytes); | 
|  |         return res; | 
|  |     } | 
|  |     public Attribute addContent(byte[] bytes) { | 
|  |         return addContent(bytes, null); | 
|  |     } | 
|  |  | 
|  |     public void finishRefs(Index ix) { | 
|  |         if (fixups != null) { | 
|  |             Fixups.finishRefs(fixups, bytes, ix); | 
|  |             fixups = null; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     public boolean isCanonical() { | 
|  |         return this == def.canon; | 
|  |     } | 
|  |  | 
|  |     @Override | 
|  |     public int compareTo(Attribute that) { | 
|  |         return this.def.compareTo(that.def); | 
|  |     } | 
|  |  | 
|  |     private static final Map<List<Attribute>, List<Attribute>> canonLists = new HashMap<>(); | 
|  |     private static final Map<Layout, Attribute> attributes = new HashMap<>(); | 
|  |     private static final Map<Layout, Attribute> standardDefs = new HashMap<>(); | 
|  |  | 
|  |     // Canonicalized lists of trivial attrs (Deprecated, etc.) | 
|  |     // are used by trimToSize, in order to reduce footprint | 
|  |     // of some common cases.  (Note that Code attributes are | 
|  |      | 
|  |     public static List<Attribute> getCanonList(List<Attribute> al) { | 
|  |         synchronized (canonLists) { | 
|  |             List<Attribute> cl = canonLists.get(al); | 
|  |             if (cl == null) { | 
|  |                 cl = new ArrayList<>(al.size()); | 
|  |                 cl.addAll(al); | 
|  |                 cl = Collections.unmodifiableList(cl); | 
|  |                 canonLists.put(al, cl); | 
|  |             } | 
|  |             return cl; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |     public static Attribute find(int ctype, String name, String layout) { | 
|  |         Layout key = Layout.makeKey(ctype, name, layout); | 
|  |         synchronized (attributes) { | 
|  |             Attribute a = attributes.get(key); | 
|  |             if (a == null) { | 
|  |                 a = new Layout(ctype, name, layout).canonicalInstance(); | 
|  |                 attributes.put(key, a); | 
|  |             } | 
|  |             return a; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     public static Layout keyForLookup(int ctype, String name) { | 
|  |         return Layout.makeKey(ctype, name); | 
|  |     } | 
|  |  | 
|  |     // Find canonical empty attribute with given ctype and name, | 
|  |      | 
|  |     public static Attribute lookup(Map<Layout, Attribute> defs, int ctype, | 
|  |             String name) { | 
|  |         if (defs == null) { | 
|  |             defs = standardDefs; | 
|  |         } | 
|  |         return defs.get(Layout.makeKey(ctype, name)); | 
|  |     } | 
|  |  | 
|  |     public static Attribute define(Map<Layout, Attribute> defs, int ctype, | 
|  |             String name, String layout) { | 
|  |         Attribute a = find(ctype, name, layout); | 
|  |         defs.put(Layout.makeKey(ctype, name), a); | 
|  |         return a; | 
|  |     } | 
|  |  | 
|  |     static { | 
|  |         Map<Layout, Attribute> sd = standardDefs; | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH"); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "Synthetic", ""); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "Deprecated", ""); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); | 
|  |         define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]"); | 
|  |  | 
|  |         define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH"); | 
|  |         define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); | 
|  |         define(sd, ATTR_CONTEXT_FIELD, "Deprecated", ""); | 
|  |         define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH"); | 
|  |  | 
|  |         define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH"); | 
|  |         define(sd, ATTR_CONTEXT_METHOD, "Synthetic", ""); | 
|  |         define(sd, ATTR_CONTEXT_METHOD, "Deprecated", ""); | 
|  |         define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]"); | 
|  |         define(sd, ATTR_CONTEXT_METHOD, "MethodParameters", "NB[RUNHFH]"); | 
|  |         //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]"); | 
|  |  | 
|  |         define(sd, ATTR_CONTEXT_CODE, "StackMapTable", | 
|  |                ("[NH[(1)]]" + | 
|  |                 "[TB" + | 
|  |                 "(64-127)[(2)]" + | 
|  |                 "(247)[(1)(2)]" + | 
|  |                 "(248-251)[(1)]" + | 
|  |                 "(252)[(1)(2)]" + | 
|  |                 "(253)[(1)(2)(2)]" + | 
|  |                 "(254)[(1)(2)(2)(2)]" + | 
|  |                 "(255)[(1)NH[(2)]NH[(2)]]" + | 
|  |                 "()[]" + | 
|  |                 "]" + | 
|  |                 "[H]" + | 
|  |                 "[TB(7)[RCH](8)[PH]()[]]")); | 
|  |  | 
|  |         define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]"); | 
|  |         define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]"); | 
|  |         define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable", "NH[PHOHRUHRSHH]"); | 
|  |         //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]"); | 
|  |         //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]"); | 
|  |  | 
|  |         // Note:  Code and InnerClasses are special-cased elsewhere. | 
|  |         // Their layout specs. are given here for completeness. | 
|  |         // The Code spec is incomplete, in that it does not distinguish | 
|  |         // bytecode bytes or locate CP references. | 
|  |         // The BootstrapMethods attribute is also special-cased | 
|  |         // elsewhere as an appendix to the local constant pool. | 
|  |     } | 
|  |  | 
|  |     // Metadata. | 
|  |     // | 
|  |     // We define metadata using similar layouts | 
|  |     // for all five kinds of metadata attributes and 2 type metadata attributes | 
|  |     // | 
|  |     // Regular annotations are a counted list of [RSHNH[RUH(1)]][...] | 
|  |     //   pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...] | 
|  |     // | 
|  |     // Parameter annotations are a counted list of regular annotations. | 
|  |     //   pack.method.attribute.RuntimeVisibleParameterAnnotations=[NB[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...] | 
|  |     // | 
|  |     // RuntimeInvisible annotations are defined similarly... | 
|  |     // Non-method annotations are defined similarly... | 
|  |     // | 
|  |     // Annotation are a simple tagged value [TB...] | 
|  |     //   pack.attribute.method.AnnotationDefault=[TB...] | 
|  |  | 
|  |     static { | 
|  |         String mdLayouts[] = { | 
|  |             Attribute.normalizeLayoutString | 
|  |             ("" | 
|  |              +"\n  # parameter_annotations :=" | 
|  |              +"\n  [ NB[(1)] ]     # forward call to annotations" | 
|  |              ), | 
|  |             Attribute.normalizeLayoutString | 
|  |             ("" | 
|  |              +"\n  # annotations :=" | 
|  |              +"\n  [ NH[(1)] ]     # forward call to annotation" | 
|  |              +"\n  " | 
|  |             ), | 
|  |             Attribute.normalizeLayoutString | 
|  |              ("" | 
|  |              +"\n  # annotation :=" | 
|  |              +"\n  [RSH" | 
|  |              +"\n    NH[RUH (1)]   # forward call to value" | 
|  |              +"\n    ]" | 
|  |              ), | 
|  |             Attribute.normalizeLayoutString | 
|  |             ("" | 
|  |              +"\n  # value :=" | 
|  |              +"\n  [TB # Callable 2 encodes one tagged value." | 
|  |              +"\n    (\\B,\\C,\\I,\\S,\\Z)[KIH]" | 
|  |              +"\n    (\\D)[KDH]" | 
|  |              +"\n    (\\F)[KFH]" | 
|  |              +"\n    (\\J)[KJH]" | 
|  |              +"\n    (\\c)[RSH]" | 
|  |              +"\n    (\\e)[RSH RUH]" | 
|  |              +"\n    (\\s)[RUH]" | 
|  |              +"\n    (\\[)[NH[(0)]] # backward self-call to value" | 
|  |              +"\n    (\\@)[RSH NH[RUH (0)]] # backward self-call to value" | 
|  |              +"\n    ()[] ]" | 
|  |              ) | 
|  |         }; | 
|  |          | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         String typeLayouts[] = { | 
|  |             Attribute.normalizeLayoutString | 
|  |             ("" | 
|  |              +"\n # type-annotations :=" | 
|  |              +"\n  [ NH[(1)(2)(3)] ]     # forward call to type-annotations" | 
|  |             ), | 
|  |             Attribute.normalizeLayoutString | 
|  |             ( "" | 
|  |              +"\n  # type-annotation :=" | 
|  |              +"\n  [TB" | 
|  |              +"\n    (0-1) [B] # {CLASS, METHOD}_TYPE_PARAMETER" | 
|  |              +"\n    (16) [FH] # CLASS_EXTENDS" | 
|  |              +"\n    (17-18) [BB] # {CLASS, METHOD}_TYPE_PARAMETER_BOUND" | 
|  |              +"\n    (19-21) [] # FIELD, METHOD_RETURN, METHOD_RECEIVER" | 
|  |              +"\n    (22) [B] # METHOD_FORMAL_PARAMETER" | 
|  |              +"\n    (23) [H] # THROWS" | 
|  |              +"\n    (64-65) [NH[PHOHH]] # LOCAL_VARIABLE, RESOURCE_VARIABLE" | 
|  |              +"\n    (66) [H] # EXCEPTION_PARAMETER" | 
|  |              +"\n    (67-70) [PH] # INSTANCEOF, NEW, {CONSTRUCTOR, METHOD}_REFERENCE_RECEIVER" | 
|  |              +"\n    (71-75) [PHB] # CAST, {CONSTRUCTOR,METHOD}_INVOCATION_TYPE_ARGUMENT, {CONSTRUCTOR, METHOD}_REFERENCE_TYPE_ARGUMENT" | 
|  |              +"\n    ()[] ]" | 
|  |             ), | 
|  |             Attribute.normalizeLayoutString | 
|  |             ("" | 
|  |              +"\n # type-path" | 
|  |              +"\n [ NB[BB] ]" | 
|  |             ) | 
|  |         }; | 
|  |         Map<Layout, Attribute> sd = standardDefs; | 
|  |         String defaultLayout     = mdLayouts[3]; | 
|  |         String annotationsLayout = mdLayouts[1] + mdLayouts[2] + mdLayouts[3]; | 
|  |         String paramsLayout      = mdLayouts[0] + annotationsLayout; | 
|  |         String typesLayout       = typeLayouts[0] + typeLayouts[1] + | 
|  |                                    typeLayouts[2] + mdLayouts[2] + mdLayouts[3]; | 
|  |  | 
|  |         for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { | 
|  |             if (ctype != ATTR_CONTEXT_CODE) { | 
|  |                 define(sd, ctype, | 
|  |                        "RuntimeVisibleAnnotations",   annotationsLayout); | 
|  |                 define(sd, ctype, | 
|  |                        "RuntimeInvisibleAnnotations",  annotationsLayout); | 
|  |  | 
|  |                 if (ctype == ATTR_CONTEXT_METHOD) { | 
|  |                     define(sd, ctype, | 
|  |                            "RuntimeVisibleParameterAnnotations",   paramsLayout); | 
|  |                     define(sd, ctype, | 
|  |                            "RuntimeInvisibleParameterAnnotations", paramsLayout); | 
|  |                     define(sd, ctype, | 
|  |                            "AnnotationDefault", defaultLayout); | 
|  |                 } | 
|  |             } | 
|  |             define(sd, ctype, | 
|  |                    "RuntimeVisibleTypeAnnotations", typesLayout); | 
|  |             define(sd, ctype, | 
|  |                    "RuntimeInvisibleTypeAnnotations", typesLayout); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     public static String contextName(int ctype) { | 
|  |         switch (ctype) { | 
|  |         case ATTR_CONTEXT_CLASS: return "class"; | 
|  |         case ATTR_CONTEXT_FIELD: return "field"; | 
|  |         case ATTR_CONTEXT_METHOD: return "method"; | 
|  |         case ATTR_CONTEXT_CODE: return "code"; | 
|  |         } | 
|  |         return null; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public abstract static | 
|  |     class Holder { | 
|  |  | 
|  |          | 
|  |         protected abstract Entry[] getCPMap(); | 
|  |  | 
|  |         protected int flags;              | 
|  |         protected List<Attribute> attributes; | 
|  |  | 
|  |         public int attributeSize() { | 
|  |             return (attributes == null) ? 0 : attributes.size(); | 
|  |         } | 
|  |  | 
|  |         public void trimToSize() { | 
|  |             if (attributes == null) { | 
|  |                 return; | 
|  |             } | 
|  |             if (attributes.isEmpty()) { | 
|  |                 attributes = null; | 
|  |                 return; | 
|  |             } | 
|  |             if (attributes instanceof ArrayList) { | 
|  |                 ArrayList<Attribute> al = (ArrayList<Attribute>)attributes; | 
|  |                 al.trimToSize(); | 
|  |                 boolean allCanon = true; | 
|  |                 for (Attribute a : al) { | 
|  |                     if (!a.isCanonical()) { | 
|  |                         allCanon = false; | 
|  |                     } | 
|  |                     if (a.fixups != null) { | 
|  |                         assert(!a.isCanonical()); | 
|  |                         a.fixups = Fixups.trimToSize(a.fixups); | 
|  |                     } | 
|  |                 } | 
|  |                 if (allCanon) { | 
|  |                     // Replace private writable attribute list | 
|  |                     // with only trivial entries by public unique | 
|  |                      | 
|  |                     attributes = getCanonList(al); | 
|  |                 } | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         public void addAttribute(Attribute a) { | 
|  |             if (attributes == null) | 
|  |                 attributes = new ArrayList<>(3); | 
|  |             else if (!(attributes instanceof ArrayList)) | 
|  |                 attributes = new ArrayList<>(attributes);   | 
|  |             attributes.add(a); | 
|  |         } | 
|  |  | 
|  |         public Attribute removeAttribute(Attribute a) { | 
|  |             if (attributes == null)       return null; | 
|  |             if (!attributes.contains(a))  return null; | 
|  |             if (!(attributes instanceof ArrayList)) | 
|  |                 attributes = new ArrayList<>(attributes);   | 
|  |             attributes.remove(a); | 
|  |             return a; | 
|  |         } | 
|  |  | 
|  |         public Attribute getAttribute(int n) { | 
|  |             return attributes.get(n); | 
|  |         } | 
|  |  | 
|  |         protected void visitRefs(int mode, Collection<Entry> refs) { | 
|  |             if (attributes == null)  return; | 
|  |             for (Attribute a : attributes) { | 
|  |                 a.visitRefs(this, mode, refs); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         static final List<Attribute> noAttributes = Arrays.asList(new Attribute[0]); | 
|  |  | 
|  |         public List<Attribute> getAttributes() { | 
|  |             if (attributes == null) | 
|  |                 return noAttributes; | 
|  |             return attributes; | 
|  |         } | 
|  |  | 
|  |         public void setAttributes(List<Attribute> attrList) { | 
|  |             if (attrList.isEmpty()) | 
|  |                 attributes = null; | 
|  |             else | 
|  |                 attributes = attrList; | 
|  |         } | 
|  |  | 
|  |         public Attribute getAttribute(String attrName) { | 
|  |             if (attributes == null)  return null; | 
|  |             for (Attribute a : attributes) { | 
|  |                 if (a.name().equals(attrName)) | 
|  |                     return a; | 
|  |             } | 
|  |             return null; | 
|  |         } | 
|  |  | 
|  |         public Attribute getAttribute(Layout attrDef) { | 
|  |             if (attributes == null)  return null; | 
|  |             for (Attribute a : attributes) { | 
|  |                 if (a.layout() == attrDef) | 
|  |                     return a; | 
|  |             } | 
|  |             return null; | 
|  |         } | 
|  |  | 
|  |         public Attribute removeAttribute(String attrName) { | 
|  |             return removeAttribute(getAttribute(attrName)); | 
|  |         } | 
|  |  | 
|  |         public Attribute removeAttribute(Layout attrDef) { | 
|  |             return removeAttribute(getAttribute(attrDef)); | 
|  |         } | 
|  |  | 
|  |         public void strip(String attrName) { | 
|  |             removeAttribute(getAttribute(attrName)); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     // Lightweight interface to hide details of band structure. | 
|  |      | 
|  |     public abstract static | 
|  |     class ValueStream { | 
|  |         public int getInt(int bandIndex) { throw undef(); } | 
|  |         public void putInt(int bandIndex, int value) { throw undef(); } | 
|  |         public Entry getRef(int bandIndex) { throw undef(); } | 
|  |         public void putRef(int bandIndex, Entry ref) { throw undef(); } | 
|  |          | 
|  |         public int decodeBCI(int bciCode) { throw undef(); } | 
|  |         public int encodeBCI(int bci) { throw undef(); } | 
|  |         public void noteBackCall(int whichCallable) { /* ignore by default */ } | 
|  |         private RuntimeException undef() { | 
|  |             return new UnsupportedOperationException("ValueStream method"); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     // Element kinds: | 
|  |     static final byte EK_INT  = 1;      | 
|  |     static final byte EK_BCI  = 2;      | 
|  |     static final byte EK_BCO  = 3;      | 
|  |     static final byte EK_FLAG = 4;      | 
|  |     static final byte EK_REPL = 5;      | 
|  |     static final byte EK_REF  = 6;      | 
|  |     static final byte EK_UN   = 7;      | 
|  |     static final byte EK_CASE = 8;      | 
|  |     static final byte EK_CALL = 9;      | 
|  |     static final byte EK_CBLE = 10;     | 
|  |     static final byte EF_SIGN  = 1<<0;    | 
|  |     static final byte EF_DELTA = 1<<1;    | 
|  |     static final byte EF_NULL  = 1<<2;    | 
|  |     static final byte EF_BACK  = 1<<3;    | 
|  |     static final int NO_BAND_INDEX = -1; | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     public static | 
|  |     class Layout implements Comparable<Layout> { | 
|  |         int ctype;        | 
|  |         String name;      | 
|  |         boolean hasRefs;  | 
|  |         String layout;    | 
|  |         int bandCount;    | 
|  |         Element[] elems;  | 
|  |         Attribute canon;  | 
|  |  | 
|  |         public int ctype() { return ctype; } | 
|  |         public String name() { return name; } | 
|  |         public String layout() { return layout; } | 
|  |         public Attribute canonicalInstance() { return canon; } | 
|  |  | 
|  |         public Entry getNameRef() { | 
|  |             return ConstantPool.getUtf8Entry(name()); | 
|  |         } | 
|  |  | 
|  |         public boolean isEmpty() { | 
|  |             return layout.isEmpty(); | 
|  |         } | 
|  |  | 
|  |         public Layout(int ctype, String name, String layout) { | 
|  |             this.ctype = ctype; | 
|  |             this.name = name.intern(); | 
|  |             this.layout = layout.intern(); | 
|  |             assert(ctype < ATTR_CONTEXT_LIMIT); | 
|  |             boolean hasCallables = layout.startsWith("["); | 
|  |             try { | 
|  |                 if (!hasCallables) { | 
|  |                     this.elems = tokenizeLayout(this, -1, layout); | 
|  |                 } else { | 
|  |                     String[] bodies = splitBodies(layout); | 
|  |                      | 
|  |                     Element[] lelems = new Element[bodies.length]; | 
|  |                     this.elems = lelems; | 
|  |                     for (int i = 0; i < lelems.length; i++) { | 
|  |                         Element ce = this.new Element(); | 
|  |                         ce.kind = EK_CBLE; | 
|  |                         ce.removeBand(); | 
|  |                         ce.bandIndex = NO_BAND_INDEX; | 
|  |                         ce.layout = bodies[i]; | 
|  |                         lelems[i] = ce; | 
|  |                     } | 
|  |                      | 
|  |                     for (int i = 0; i < lelems.length; i++) { | 
|  |                         Element ce = lelems[i]; | 
|  |                         ce.body = tokenizeLayout(this, i, bodies[i]); | 
|  |                     } | 
|  |                     //System.out.println(Arrays.asList(elems)); | 
|  |                 } | 
|  |             } catch (StringIndexOutOfBoundsException ee) { | 
|  |                  | 
|  |                 throw new RuntimeException("Bad attribute layout: "+layout, ee); | 
|  |             } | 
|  |             // Some uses do not make a fresh one for each occurrence. | 
|  |              | 
|  |             canon = new Attribute(this, noBytes); | 
|  |         } | 
|  |         private Layout() {} | 
|  |         static Layout makeKey(int ctype, String name, String layout) { | 
|  |             Layout def = new Layout(); | 
|  |             def.ctype = ctype; | 
|  |             def.name = name.intern(); | 
|  |             def.layout = layout.intern(); | 
|  |             assert(ctype < ATTR_CONTEXT_LIMIT); | 
|  |             return def; | 
|  |         } | 
|  |         static Layout makeKey(int ctype, String name) { | 
|  |             return makeKey(ctype, name, ""); | 
|  |         } | 
|  |  | 
|  |         public Attribute addContent(byte[] bytes, Object fixups) { | 
|  |             return canon.addContent(bytes, fixups); | 
|  |         } | 
|  |         public Attribute addContent(byte[] bytes) { | 
|  |             return canon.addContent(bytes, null); | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public boolean equals(Object x) { | 
|  |             return ( x != null) && ( x.getClass() == Layout.class ) && | 
|  |                     equals((Layout)x); | 
|  |         } | 
|  |         public boolean equals(Layout that) { | 
|  |             return this.name.equals(that.name) | 
|  |                 && this.layout.equals(that.layout) | 
|  |                 && this.ctype == that.ctype; | 
|  |         } | 
|  |         @Override | 
|  |         public int hashCode() { | 
|  |             return (((17 + name.hashCode()) | 
|  |                     * 37 + layout.hashCode()) | 
|  |                     * 37 + ctype); | 
|  |         } | 
|  |         @Override | 
|  |         public int compareTo(Layout that) { | 
|  |             int r; | 
|  |             r = this.name.compareTo(that.name); | 
|  |             if (r != 0)  return r; | 
|  |             r = this.layout.compareTo(that.layout); | 
|  |             if (r != 0)  return r; | 
|  |             return this.ctype - that.ctype; | 
|  |         } | 
|  |         @Override | 
|  |         public String toString() { | 
|  |             String str = contextName(ctype)+"."+name+"["+layout+"]"; | 
|  |              | 
|  |             assert((str = stringForDebug()) != null); | 
|  |             return str; | 
|  |         } | 
|  |         private String stringForDebug() { | 
|  |             return contextName(ctype)+"."+name+Arrays.asList(elems); | 
|  |         } | 
|  |  | 
|  |         public | 
|  |         class Element { | 
|  |             String layout;    | 
|  |             byte flags;       | 
|  |             byte kind;        | 
|  |             byte len;         | 
|  |             byte refKind;     | 
|  |             int bandIndex;    | 
|  |             int value;        | 
|  |             Element[] body;   | 
|  |  | 
|  |             boolean flagTest(byte mask) { return (flags & mask) != 0; } | 
|  |  | 
|  |             Element() { | 
|  |                 bandIndex = bandCount++; | 
|  |             } | 
|  |  | 
|  |             void removeBand() { | 
|  |                 --bandCount; | 
|  |                 assert(bandIndex == bandCount); | 
|  |                 bandIndex = NO_BAND_INDEX; | 
|  |             } | 
|  |  | 
|  |             public boolean hasBand() { | 
|  |                 return bandIndex >= 0; | 
|  |             } | 
|  |             public String toString() { | 
|  |                 String str = layout; | 
|  |                  | 
|  |                 assert((str = stringForDebug()) != null); | 
|  |                 return str; | 
|  |             } | 
|  |             private String stringForDebug() { | 
|  |                 Element[] lbody = this.body; | 
|  |                 switch (kind) { | 
|  |                 case EK_CALL: | 
|  |                     lbody = null; | 
|  |                     break; | 
|  |                 case EK_CASE: | 
|  |                     if (flagTest(EF_BACK)) | 
|  |                         lbody = null; | 
|  |                     break; | 
|  |                 } | 
|  |                 return layout | 
|  |                     + (!hasBand()?"":"#"+bandIndex) | 
|  |                     + "<"+ (flags==0?"":""+flags)+kind+len | 
|  |                     + (refKind==0?"":""+refKind) + ">" | 
|  |                     + (value==0?"":"("+value+")") | 
|  |                     + (lbody==null?"": ""+Arrays.asList(lbody)); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         public boolean hasCallables() { | 
|  |             return (elems.length > 0 && elems[0].kind == EK_CBLE); | 
|  |         } | 
|  |         private static final Element[] noElems = {}; | 
|  |         public Element[] getCallables() { | 
|  |             if (hasCallables()) { | 
|  |                 Element[] nelems = Arrays.copyOf(elems, elems.length); | 
|  |                 return nelems; | 
|  |             } else | 
|  |                 return noElems;   | 
|  |         } | 
|  |         public Element[] getEntryPoint() { | 
|  |             if (hasCallables()) | 
|  |                 return elems[0].body;   | 
|  |             else { | 
|  |                 Element[] nelems = Arrays.copyOf(elems, elems.length); | 
|  |                 return nelems;   | 
|  |             } | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |          */ | 
|  |         public void parse(Holder holder, | 
|  |                           byte[] bytes, int pos, int len, ValueStream out) { | 
|  |             int end = parseUsing(getEntryPoint(), | 
|  |                                  holder, bytes, pos, len, out); | 
|  |             if (end != pos + len) | 
|  |                 throw new InternalError("layout parsed "+(end-pos)+" out of "+len+" bytes"); | 
|  |         } | 
|  |          | 
|  |  | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         public Object unparse(ValueStream in, ByteArrayOutputStream out) { | 
|  |             Object[] fixups = { null }; | 
|  |             unparseUsing(getEntryPoint(), fixups, in, out); | 
|  |             return fixups[0];  | 
|  |         } | 
|  |  | 
|  |         public String layoutForClassVersion(Package.Version vers) { | 
|  |             if (vers.lessThan(JAVA6_MAX_CLASS_VERSION)) { | 
|  |                  | 
|  |                 return expandCaseDashNotation(layout); | 
|  |             } | 
|  |             return layout; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     public static | 
|  |     class FormatException extends IOException { | 
|  |         private static final long serialVersionUID = -2542243830788066513L; | 
|  |  | 
|  |         private int ctype; | 
|  |         private String name; | 
|  |         String layout; | 
|  |         public FormatException(String message, | 
|  |                                int ctype, String name, String layout) { | 
|  |             super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" + | 
|  |                   (message == null? "" : (": " + message))); | 
|  |             this.ctype = ctype; | 
|  |             this.name = name; | 
|  |             this.layout = layout; | 
|  |         } | 
|  |         public FormatException(String message, | 
|  |                                int ctype, String name) { | 
|  |             this(message, ctype, name, null); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     void visitRefs(Holder holder, int mode, final Collection<Entry> refs) { | 
|  |         if (mode == VRM_CLASSIC) { | 
|  |             refs.add(getNameRef()); | 
|  |         } | 
|  |         // else the name is owned by the layout, and is processed elsewhere | 
|  |         if (bytes.length == 0)  return;   | 
|  |         if (!def.hasRefs)       return;   | 
|  |         if (fixups != null) { | 
|  |             Fixups.visitRefs(fixups, refs); | 
|  |             return; | 
|  |         } | 
|  |          | 
|  |         def.parse(holder, bytes, 0, bytes.length, | 
|  |             new ValueStream() { | 
|  |                 @Override | 
|  |                 public void putInt(int bandIndex, int value) { | 
|  |                 } | 
|  |                 @Override | 
|  |                 public void putRef(int bandIndex, Entry ref) { | 
|  |                     refs.add(ref); | 
|  |                 } | 
|  |                 @Override | 
|  |                 public int encodeBCI(int bci) { | 
|  |                     return bci; | 
|  |                 } | 
|  |             }); | 
|  |     } | 
|  |  | 
|  |     public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) { | 
|  |         def.parse(holder, bytes, pos, len, out); | 
|  |     } | 
|  |     public Object unparse(ValueStream in, ByteArrayOutputStream out) { | 
|  |         return def.unparse(in, out); | 
|  |     } | 
|  |  | 
|  |     @Override | 
|  |     public String toString() { | 
|  |         return def | 
|  |             +"{"+(bytes == null ? -1 : size())+"}" | 
|  |             +(fixups == null? "": fixups.toString()); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     public static | 
|  |     String normalizeLayoutString(String layout) { | 
|  |         StringBuilder buf = new StringBuilder(); | 
|  |         for (int i = 0, len = layout.length(); i < len; ) { | 
|  |             char ch = layout.charAt(i++); | 
|  |             if (ch <= ' ') { | 
|  |                  | 
|  |                 continue; | 
|  |             } else if (ch == '#') { | 
|  |                  | 
|  |                 int end1 = layout.indexOf('\n', i); | 
|  |                 int end2 = layout.indexOf('\r', i); | 
|  |                 if (end1 < 0)  end1 = len; | 
|  |                 if (end2 < 0)  end2 = len; | 
|  |                 i = Math.min(end1, end2); | 
|  |             } else if (ch == '\\') { | 
|  |                  | 
|  |                 buf.append((int) layout.charAt(i++)); | 
|  |             } else if (ch == '0' && layout.startsWith("0x", i-1)) { | 
|  |                  | 
|  |                 int start = i-1; | 
|  |                 int end = start+2; | 
|  |                 while (end < len) { | 
|  |                     int dig = layout.charAt(end); | 
|  |                     if ((dig >= '0' && dig <= '9') || | 
|  |                         (dig >= 'a' && dig <= 'f')) | 
|  |                         ++end; | 
|  |                     else | 
|  |                         break; | 
|  |                 } | 
|  |                 if (end > start) { | 
|  |                     String num = layout.substring(start, end); | 
|  |                     buf.append(Integer.decode(num)); | 
|  |                     i = end; | 
|  |                 } else { | 
|  |                     buf.append(ch); | 
|  |                 } | 
|  |             } else { | 
|  |                 buf.append(ch); | 
|  |             } | 
|  |         } | 
|  |         String result = buf.toString(); | 
|  |         if (false && !result.equals(layout)) { | 
|  |             Utils.log.info("Normalizing layout string"); | 
|  |             Utils.log.info("    From: "+layout); | 
|  |             Utils.log.info("    To:   "+result); | 
|  |         } | 
|  |         return result; | 
|  |     } | 
|  |  | 
|  |     /// Subroutines for parsing and unparsing: | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |     */ | 
|  |     static  | 
|  |     Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) { | 
|  |         List<Layout.Element> col = new ArrayList<>(layout.length()); | 
|  |         tokenizeLayout(self, curCble, layout, col); | 
|  |         Layout.Element[] res = new Layout.Element[col.size()]; | 
|  |         col.toArray(res); | 
|  |         return res; | 
|  |     } | 
|  |     static  | 
|  |     void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) { | 
|  |         boolean prevBCI = false; | 
|  |         for (int len = layout.length(), i = 0; i < len; ) { | 
|  |             int start = i; | 
|  |             int body; | 
|  |             Layout.Element e = self.new Element(); | 
|  |             byte kind; | 
|  |             //System.out.println("at "+i+": ..."+layout.substring(i)); | 
|  |              | 
|  |             switch (layout.charAt(i++)) { | 
|  |              | 
|  |             case 'B': case 'H': case 'I': case 'V':  | 
|  |                 kind = EK_INT; | 
|  |                 --i;  | 
|  |                 i = tokenizeUInt(e, layout, i); | 
|  |                 break; | 
|  |             case 'S':  | 
|  |                 kind = EK_INT; | 
|  |                 --i;  | 
|  |                 i = tokenizeSInt(e, layout, i); | 
|  |                 break; | 
|  |             case 'P':  | 
|  |                 kind = EK_BCI; | 
|  |                 if (layout.charAt(i++) == 'O') { | 
|  |                      | 
|  |                     e.flags |= EF_DELTA; | 
|  |                      | 
|  |                     if (!prevBCI) | 
|  |                         { i = -i; continue; }  | 
|  |                     i++;  | 
|  |                 } | 
|  |                 --i;  | 
|  |                 i = tokenizeUInt(e, layout, i); | 
|  |                 break; | 
|  |             case 'O':  | 
|  |                 kind = EK_BCO; | 
|  |                 e.flags |= EF_DELTA; | 
|  |                  | 
|  |                 if (!prevBCI) | 
|  |                     { i = -i; continue; }  | 
|  |                 i = tokenizeSInt(e, layout, i); | 
|  |                 break; | 
|  |             case 'F':  | 
|  |                 kind = EK_FLAG; | 
|  |                 i = tokenizeUInt(e, layout, i); | 
|  |                 break; | 
|  |             case 'N':  | 
|  |                 kind = EK_REPL; | 
|  |                 i = tokenizeUInt(e, layout, i); | 
|  |                 if (layout.charAt(i++) != '[') | 
|  |                     { i = -i; continue; }  | 
|  |                 i = skipBody(layout, body = i); | 
|  |                 e.body = tokenizeLayout(self, curCble, | 
|  |                                         layout.substring(body, i++)); | 
|  |                 break; | 
|  |             case 'T':  | 
|  |                 kind = EK_UN; | 
|  |                 i = tokenizeSInt(e, layout, i); | 
|  |                 List<Layout.Element> cases = new ArrayList<>(); | 
|  |                 for (;;) { | 
|  |                      | 
|  |                     if (layout.charAt(i++) != '(') | 
|  |                         { i = -i; break; }  | 
|  |                     int beg = i; | 
|  |                     i = layout.indexOf(')', i); | 
|  |                     String cstr = layout.substring(beg, i++); | 
|  |                     int cstrlen = cstr.length(); | 
|  |                     if (layout.charAt(i++) != '[') | 
|  |                         { i = -i; break; }  | 
|  |                      | 
|  |                     if (layout.charAt(i) == ']') | 
|  |                         body = i;   | 
|  |                     else | 
|  |                         i = skipBody(layout, body = i); | 
|  |                     Layout.Element[] cbody | 
|  |                         = tokenizeLayout(self, curCble, | 
|  |                                          layout.substring(body, i++)); | 
|  |                     if (cstrlen == 0) { | 
|  |                         Layout.Element ce = self.new Element(); | 
|  |                         ce.body = cbody; | 
|  |                         ce.kind = EK_CASE; | 
|  |                         ce.removeBand(); | 
|  |                         cases.add(ce); | 
|  |                         break;   | 
|  |                     } else { | 
|  |                          | 
|  |                         boolean firstCaseNum = true; | 
|  |                         for (int cp = 0, endp;; cp = endp+1) { | 
|  |                              | 
|  |                             endp = cstr.indexOf(',', cp); | 
|  |                             if (endp < 0)  endp = cstrlen; | 
|  |                             String cstr1 = cstr.substring(cp, endp); | 
|  |                             if (cstr1.length() == 0) | 
|  |                                 cstr1 = "empty";   | 
|  |                             int value0, value1; | 
|  |                              | 
|  |                             int dash = findCaseDash(cstr1, 0); | 
|  |                             if (dash >= 0) { | 
|  |                                 value0 = parseIntBefore(cstr1, dash); | 
|  |                                 value1 = parseIntAfter(cstr1, dash); | 
|  |                                 if (value0 >= value1) | 
|  |                                     { i = -i; break; }  | 
|  |                             } else { | 
|  |                                 value0 = value1 = Integer.parseInt(cstr1); | 
|  |                             } | 
|  |                              | 
|  |                             for (;; value0++) { | 
|  |                                 Layout.Element ce = self.new Element(); | 
|  |                                 ce.body = cbody;   | 
|  |                                 ce.kind = EK_CASE; | 
|  |                                 ce.removeBand(); | 
|  |                                 if (!firstCaseNum) | 
|  |                                      | 
|  |                                     ce.flags |= EF_BACK; | 
|  |                                 firstCaseNum = false; | 
|  |                                 ce.value = value0; | 
|  |                                 cases.add(ce); | 
|  |                                 if (value0 == value1)  break; | 
|  |                             } | 
|  |                             if (endp == cstrlen) { | 
|  |                                 break;   | 
|  |                             } | 
|  |                         } | 
|  |                     } | 
|  |                 } | 
|  |                 e.body = new Layout.Element[cases.size()]; | 
|  |                 cases.toArray(e.body); | 
|  |                 e.kind = kind; | 
|  |                 for (int j = 0; j < e.body.length-1; j++) { | 
|  |                     Layout.Element ce = e.body[j]; | 
|  |                     if (matchCase(e, ce.value) != ce) { | 
|  |                         // Duplicate tag. | 
|  |                         { i = -i; break; }  | 
|  |                     } | 
|  |                 } | 
|  |                 break; | 
|  |             case '(':  | 
|  |                 kind = EK_CALL; | 
|  |                 e.removeBand(); | 
|  |                 i = layout.indexOf(')', i); | 
|  |                 String cstr = layout.substring(start+1, i++); | 
|  |                 int offset = Integer.parseInt(cstr); | 
|  |                 int target = curCble + offset; | 
|  |                 if (!(offset+"").equals(cstr) || | 
|  |                     self.elems == null || | 
|  |                     target < 0 || | 
|  |                     target >= self.elems.length) | 
|  |                     { i = -i; continue; }  | 
|  |                 Layout.Element ce = self.elems[target]; | 
|  |                 assert(ce.kind == EK_CBLE); | 
|  |                 e.value = target; | 
|  |                 e.body = new Layout.Element[]{ ce }; | 
|  |                  | 
|  |                 if (offset <= 0) { | 
|  |                      | 
|  |                     e.flags  |= EF_BACK; | 
|  |                     ce.flags |= EF_BACK; | 
|  |                 } | 
|  |                 break; | 
|  |             case 'K':   | 
|  |                 kind = EK_REF; | 
|  |                 switch (layout.charAt(i++)) { | 
|  |                 case 'I': e.refKind = CONSTANT_Integer; break; | 
|  |                 case 'J': e.refKind = CONSTANT_Long; break; | 
|  |                 case 'F': e.refKind = CONSTANT_Float; break; | 
|  |                 case 'D': e.refKind = CONSTANT_Double; break; | 
|  |                 case 'S': e.refKind = CONSTANT_String; break; | 
|  |                 case 'Q': e.refKind = CONSTANT_FieldSpecific; break; | 
|  |  | 
|  |                  | 
|  |                 case 'M': e.refKind = CONSTANT_MethodHandle; break; | 
|  |                 case 'T': e.refKind = CONSTANT_MethodType; break; | 
|  |                 case 'L': e.refKind = CONSTANT_LoadableValue; break; | 
|  |                 default: { i = -i; continue; }  | 
|  |                 } | 
|  |                 break; | 
|  |             case 'R':  | 
|  |                 kind = EK_REF; | 
|  |                 switch (layout.charAt(i++)) { | 
|  |                 case 'C': e.refKind = CONSTANT_Class; break; | 
|  |                 case 'S': e.refKind = CONSTANT_Signature; break; | 
|  |                 case 'D': e.refKind = CONSTANT_NameandType; break; | 
|  |                 case 'F': e.refKind = CONSTANT_Fieldref; break; | 
|  |                 case 'M': e.refKind = CONSTANT_Methodref; break; | 
|  |                 case 'I': e.refKind = CONSTANT_InterfaceMethodref; break; | 
|  |  | 
|  |                 case 'U': e.refKind = CONSTANT_Utf8; break;  | 
|  |                 case 'Q': e.refKind = CONSTANT_All; break;  | 
|  |  | 
|  |                  | 
|  |                 case 'Y': e.refKind = CONSTANT_InvokeDynamic; break; | 
|  |                 case 'B': e.refKind = CONSTANT_BootstrapMethod; break; | 
|  |                 case 'N': e.refKind = CONSTANT_AnyMember; break; | 
|  |  | 
|  |                 default: { i = -i; continue; }  | 
|  |                 } | 
|  |                 break; | 
|  |             default: { i = -i; continue; }  | 
|  |             } | 
|  |  | 
|  |              | 
|  |             if (kind == EK_REF) { | 
|  |                  | 
|  |                 if (layout.charAt(i++) == 'N') { | 
|  |                     e.flags |= EF_NULL; | 
|  |                     i++;  | 
|  |                 } | 
|  |                 --i;  | 
|  |                 i = tokenizeUInt(e, layout, i); | 
|  |                 self.hasRefs = true; | 
|  |             } | 
|  |  | 
|  |             prevBCI = (kind == EK_BCI); | 
|  |  | 
|  |              | 
|  |             e.kind = kind; | 
|  |             e.layout = layout.substring(start, i); | 
|  |             col.add(e); | 
|  |         } | 
|  |     } | 
|  |     static  | 
|  |     String[] splitBodies(String layout) { | 
|  |         List<String> bodies = new ArrayList<>(); | 
|  |          | 
|  |         for (int i = 0; i < layout.length(); i++) { | 
|  |             if (layout.charAt(i++) != '[') | 
|  |                 layout.charAt(-i);   | 
|  |             int body; | 
|  |             i = skipBody(layout, body = i); | 
|  |             bodies.add(layout.substring(body, i)); | 
|  |         } | 
|  |         String[] res = new String[bodies.size()]; | 
|  |         bodies.toArray(res); | 
|  |         return res; | 
|  |     } | 
|  |     private static | 
|  |     int skipBody(String layout, int i) { | 
|  |         assert(layout.charAt(i-1) == '['); | 
|  |         if (layout.charAt(i) == ']') | 
|  |              | 
|  |             return -i; | 
|  |          | 
|  |         for (int depth = 1; depth > 0; ) { | 
|  |             switch (layout.charAt(i++)) { | 
|  |             case '[': depth++; break; | 
|  |             case ']': depth--; break; | 
|  |             } | 
|  |         } | 
|  |         --i;   | 
|  |         assert(layout.charAt(i) == ']'); | 
|  |         return i;   | 
|  |     } | 
|  |     private static | 
|  |     int tokenizeUInt(Layout.Element e, String layout, int i) { | 
|  |         switch (layout.charAt(i++)) { | 
|  |         case 'V': e.len = 0; break; | 
|  |         case 'B': e.len = 1; break; | 
|  |         case 'H': e.len = 2; break; | 
|  |         case 'I': e.len = 4; break; | 
|  |         default: return -i; | 
|  |         } | 
|  |         return i; | 
|  |     } | 
|  |     private static | 
|  |     int tokenizeSInt(Layout.Element e, String layout, int i) { | 
|  |         if (layout.charAt(i) == 'S') { | 
|  |             e.flags |= EF_SIGN; | 
|  |             ++i; | 
|  |         } | 
|  |         return tokenizeUInt(e, layout, i); | 
|  |     } | 
|  |  | 
|  |     private static | 
|  |     boolean isDigit(char c) { | 
|  |         return c >= '0' && c <= '9'; | 
|  |     } | 
|  |  | 
|  |      | 
|  |     static  | 
|  |     int findCaseDash(String layout, int fromIndex) { | 
|  |         if (fromIndex <= 0)  fromIndex = 1;   | 
|  |         int lastDash = layout.length() - 2;   | 
|  |         for (;;) { | 
|  |             int dash = layout.indexOf('-', fromIndex); | 
|  |             if (dash < 0 || dash > lastDash)  return -1; | 
|  |             if (isDigit(layout.charAt(dash-1))) { | 
|  |                 char afterDash = layout.charAt(dash+1); | 
|  |                 if (afterDash == '-' && dash+2 < layout.length()) | 
|  |                     afterDash = layout.charAt(dash+2); | 
|  |                 if (isDigit(afterDash)) { | 
|  |                      | 
|  |                     return dash; | 
|  |                 } | 
|  |             } | 
|  |             fromIndex = dash+1; | 
|  |         } | 
|  |     } | 
|  |     static | 
|  |     int parseIntBefore(String layout, int dash) { | 
|  |         int end = dash; | 
|  |         int beg = end; | 
|  |         while (beg > 0 && isDigit(layout.charAt(beg-1))) { | 
|  |             --beg; | 
|  |         } | 
|  |         if (beg == end)  return Integer.parseInt("empty"); | 
|  |          | 
|  |         if (beg >= 1 && layout.charAt(beg-1) == '-')  --beg; | 
|  |         assert(beg == 0 || !isDigit(layout.charAt(beg-1))); | 
|  |         return Integer.parseInt(layout.substring(beg, end)); | 
|  |     } | 
|  |     static | 
|  |     int parseIntAfter(String layout, int dash) { | 
|  |         int beg = dash+1; | 
|  |         int end = beg; | 
|  |         int limit = layout.length(); | 
|  |         if (end < limit && layout.charAt(end) == '-')  ++end; | 
|  |         while (end < limit && isDigit(layout.charAt(end))) { | 
|  |             ++end; | 
|  |         } | 
|  |         if (beg == end)  return Integer.parseInt("empty"); | 
|  |         return Integer.parseInt(layout.substring(beg, end)); | 
|  |     } | 
|  |      | 
|  |     static | 
|  |     String expandCaseDashNotation(String layout) { | 
|  |         int dash = findCaseDash(layout, 0); | 
|  |         if (dash < 0)  return layout;   | 
|  |         StringBuilder result = new StringBuilder(layout.length() * 3); | 
|  |         int sofar = 0;   | 
|  |         for (;;) { | 
|  |              | 
|  |             result.append(layout, sofar, dash); | 
|  |             sofar = dash+1;   | 
|  |              | 
|  |             int value0 = parseIntBefore(layout, dash); | 
|  |             int value1 = parseIntAfter(layout, dash); | 
|  |             assert(value0 < value1); | 
|  |             result.append(",");   | 
|  |             for (int i = value0+1; i < value1; i++) { | 
|  |                 result.append(i); | 
|  |                 result.append(",");   | 
|  |             } | 
|  |             dash = findCaseDash(layout, sofar); | 
|  |             if (dash < 0)  break; | 
|  |         } | 
|  |         result.append(layout, sofar, layout.length());   | 
|  |         return result.toString(); | 
|  |     } | 
|  |     static { | 
|  |         assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5")); | 
|  |         assert(expandCaseDashNotation("-2--1").equals("-2,-1")); | 
|  |         assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1")); | 
|  |         assert(expandCaseDashNotation("-1-0").equals("-1,0")); | 
|  |     } | 
|  |  | 
|  |     // Parse attribute bytes, putting values into bands.  Returns new pos. | 
|  |     // Used when reading a class file (local refs resolved with local cpMap). | 
|  |      | 
|  |     static | 
|  |     int parseUsing(Layout.Element[] elems, Holder holder, | 
|  |                    byte[] bytes, int pos, int len, ValueStream out) { | 
|  |         int prevBCI = 0; | 
|  |         int prevRBCI = 0; | 
|  |         int end = pos + len; | 
|  |         int[] buf = { 0 };   | 
|  |         for (int i = 0; i < elems.length; i++) { | 
|  |             Layout.Element e = elems[i]; | 
|  |             int bandIndex = e.bandIndex; | 
|  |             int value; | 
|  |             int BCI, RBCI; | 
|  |             switch (e.kind) { | 
|  |             case EK_INT: | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 value = buf[0]; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 break; | 
|  |             case EK_BCI:   | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 BCI = buf[0]; | 
|  |                 RBCI = out.encodeBCI(BCI); | 
|  |                 if (!e.flagTest(EF_DELTA)) { | 
|  |                      | 
|  |                     value = RBCI; | 
|  |                 } else { | 
|  |                      | 
|  |                     value = RBCI - prevRBCI; | 
|  |                 } | 
|  |                 prevBCI = BCI; | 
|  |                 prevRBCI = RBCI; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 break; | 
|  |             case EK_BCO:   | 
|  |                 assert(e.flagTest(EF_DELTA)); | 
|  |                  | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 BCI = prevBCI + buf[0]; | 
|  |                 RBCI = out.encodeBCI(BCI); | 
|  |                 value = RBCI - prevRBCI; | 
|  |                 prevBCI = BCI; | 
|  |                 prevRBCI = RBCI; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 break; | 
|  |             case EK_FLAG: | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 value = buf[0]; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 break; | 
|  |             case EK_REPL: | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 value = buf[0]; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 for (int j = 0; j < value; j++) { | 
|  |                     pos = parseUsing(e.body, holder, bytes, pos, end-pos, out); | 
|  |                 } | 
|  |                 break;   | 
|  |             case EK_UN: | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 value = buf[0]; | 
|  |                 out.putInt(bandIndex, value); | 
|  |                 Layout.Element ce = matchCase(e, value); | 
|  |                 pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out); | 
|  |  | 
|  |                 break;   | 
|  |             case EK_CALL: | 
|  |                  | 
|  |                 assert(e.body.length == 1); | 
|  |                 assert(e.body[0].kind == EK_CBLE); | 
|  |                 if (e.flagTest(EF_BACK)) | 
|  |                     out.noteBackCall(e.value); | 
|  |                 pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out); | 
|  |                 break;   | 
|  |             case EK_REF: | 
|  |                 pos = parseInt(e, bytes, pos, buf); | 
|  |                 int localRef = buf[0]; | 
|  |                 Entry globalRef; | 
|  |                 if (localRef == 0) { | 
|  |                     globalRef = null;   | 
|  |                 } else { | 
|  |                     Entry[] cpMap = holder.getCPMap(); | 
|  |                     globalRef = (localRef >= 0 && localRef < cpMap.length | 
|  |                                     ? cpMap[localRef] | 
|  |                                     : null); | 
|  |                     byte tag = e.refKind; | 
|  |                     if (globalRef != null && tag == CONSTANT_Signature | 
|  |                         && globalRef.getTag() == CONSTANT_Utf8) { | 
|  |                          | 
|  |                         String typeName = globalRef.stringValue(); | 
|  |                         globalRef = ConstantPool.getSignatureEntry(typeName); | 
|  |                     } | 
|  |                     String got = (globalRef == null | 
|  |                         ? "invalid CP index" | 
|  |                         : "type=" + ConstantPool.tagName(globalRef.tag)); | 
|  |                     if (globalRef == null || !globalRef.tagMatches(tag)) { | 
|  |                         throw new IllegalArgumentException( | 
|  |                                 "Bad constant, expected type=" + | 
|  |                                 ConstantPool.tagName(tag) + " got " + got); | 
|  |                     } | 
|  |                 } | 
|  |                 out.putRef(bandIndex, globalRef); | 
|  |                 break; | 
|  |             default: assert(false); | 
|  |             } | 
|  |         } | 
|  |         return pos; | 
|  |     } | 
|  |  | 
|  |     static | 
|  |     Layout.Element matchCase(Layout.Element e, int value) { | 
|  |         assert(e.kind == EK_UN); | 
|  |         int lastj = e.body.length-1; | 
|  |         for (int j = 0; j < lastj; j++) { | 
|  |             Layout.Element ce = e.body[j]; | 
|  |             assert(ce.kind == EK_CASE); | 
|  |             if (value == ce.value) | 
|  |                 return ce; | 
|  |         } | 
|  |         return e.body[lastj]; | 
|  |     } | 
|  |  | 
|  |     private static | 
|  |     int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) { | 
|  |         int value = 0; | 
|  |         int loBits = e.len * 8; | 
|  |          | 
|  |         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { | 
|  |             value += (bytes[pos++] & 0xFF) << bitPos; | 
|  |         } | 
|  |         if (loBits < 32 && e.flagTest(EF_SIGN)) { | 
|  |              | 
|  |             int hiBits = 32 - loBits; | 
|  |             value = (value << hiBits) >> hiBits; | 
|  |         } | 
|  |         buf[0] = value; | 
|  |         return pos; | 
|  |     } | 
|  |  | 
|  |     // Format attribute bytes, drawing values from bands. | 
|  |     // Used when emptying attribute bands into a package model. | 
|  |      | 
|  |     static | 
|  |     void unparseUsing(Layout.Element[] elems, Object[] fixups, | 
|  |                       ValueStream in, ByteArrayOutputStream out) { | 
|  |         int prevBCI = 0; | 
|  |         int prevRBCI = 0; | 
|  |         for (int i = 0; i < elems.length; i++) { | 
|  |             Layout.Element e = elems[i]; | 
|  |             int bandIndex = e.bandIndex; | 
|  |             int value; | 
|  |             int BCI, RBCI;   | 
|  |             switch (e.kind) { | 
|  |             case EK_INT: | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 unparseInt(e, value, out); | 
|  |                 break; | 
|  |             case EK_BCI:   | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 if (!e.flagTest(EF_DELTA)) { | 
|  |                      | 
|  |                     RBCI = value; | 
|  |                 } else { | 
|  |                      | 
|  |                     RBCI = prevRBCI + value; | 
|  |                 } | 
|  |                 assert(prevBCI == in.decodeBCI(prevRBCI)); | 
|  |                 BCI = in.decodeBCI(RBCI); | 
|  |                 unparseInt(e, BCI, out); | 
|  |                 prevBCI = BCI; | 
|  |                 prevRBCI = RBCI; | 
|  |                 break; | 
|  |             case EK_BCO:   | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 assert(e.flagTest(EF_DELTA)); | 
|  |                  | 
|  |                 assert(prevBCI == in.decodeBCI(prevRBCI)); | 
|  |                 RBCI = prevRBCI + value; | 
|  |                 BCI = in.decodeBCI(RBCI); | 
|  |                 unparseInt(e, BCI - prevBCI, out); | 
|  |                 prevBCI = BCI; | 
|  |                 prevRBCI = RBCI; | 
|  |                 break; | 
|  |             case EK_FLAG: | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 unparseInt(e, value, out); | 
|  |                 break; | 
|  |             case EK_REPL: | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 unparseInt(e, value, out); | 
|  |                 for (int j = 0; j < value; j++) { | 
|  |                     unparseUsing(e.body, fixups, in, out); | 
|  |                 } | 
|  |                 break; | 
|  |             case EK_UN: | 
|  |                 value = in.getInt(bandIndex); | 
|  |                 unparseInt(e, value, out); | 
|  |                 Layout.Element ce = matchCase(e, value); | 
|  |                 unparseUsing(ce.body, fixups, in, out); | 
|  |                 break; | 
|  |             case EK_CALL: | 
|  |                 assert(e.body.length == 1); | 
|  |                 assert(e.body[0].kind == EK_CBLE); | 
|  |                 unparseUsing(e.body[0].body, fixups, in, out); | 
|  |                 break; | 
|  |             case EK_REF: | 
|  |                 Entry globalRef = in.getRef(bandIndex); | 
|  |                 int localRef; | 
|  |                 if (globalRef != null) { | 
|  |                      | 
|  |                     fixups[0] = Fixups.addRefWithLoc(fixups[0], out.size(), globalRef); | 
|  |                     localRef = 0;  | 
|  |                 } else { | 
|  |                     localRef = 0;  | 
|  |                 } | 
|  |                 unparseInt(e, localRef, out); | 
|  |                 break; | 
|  |             default: assert(false); continue; | 
|  |             } | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     private static | 
|  |     void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) { | 
|  |         int loBits = e.len * 8; | 
|  |         if (loBits == 0) { | 
|  |              | 
|  |             return; | 
|  |         } | 
|  |         if (loBits < 32) { | 
|  |             int hiBits = 32 - loBits; | 
|  |             int codedValue; | 
|  |             if (e.flagTest(EF_SIGN)) | 
|  |                 codedValue = (value << hiBits) >> hiBits; | 
|  |             else | 
|  |                 codedValue = (value << hiBits) >>> hiBits; | 
|  |             if (codedValue != value) | 
|  |                 throw new InternalError("cannot code in "+e.len+" bytes: "+value); | 
|  |         } | 
|  |          | 
|  |         for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { | 
|  |             out.write((byte)(value >>> bitPos)); | 
|  |         } | 
|  |     } | 
|  |  | 
|  | /* | 
|  |     /// Testing. | 
|  |     public static void main(String av[]) { | 
|  |         int maxVal = 12; | 
|  |         int iters = 0; | 
|  |         boolean verbose; | 
|  |         int ap = 0; | 
|  |         while (ap < av.length) { | 
|  |             if (!av[ap].startsWith("-"))  break; | 
|  |             if (av[ap].startsWith("-m")) | 
|  |                 maxVal = Integer.parseInt(av[ap].substring(2)); | 
|  |             else if (av[ap].startsWith("-i")) | 
|  |                 iters = Integer.parseInt(av[ap].substring(2)); | 
|  |             else | 
|  |                 throw new RuntimeException("Bad option: "+av[ap]); | 
|  |             ap++; | 
|  |         } | 
|  |         verbose = (iters == 0); | 
|  |         if (iters <= 0)  iters = 1; | 
|  |         if (ap == av.length) { | 
|  |             av = new String[] { | 
|  |                 "HH",         // ClassFile.version | 
|  |                 "RUH",        // SourceFile | 
|  |                 "RCHRDNH",    // EnclosingMethod | 
|  |                 "KQH",        // ConstantValue | 
|  |                 "NH[RCH]",    // Exceptions | 
|  |                 "NH[PHH]",    // LineNumberTable | 
|  |                 "NH[PHOHRUHRSHH]",      // LocalVariableTable | 
|  |                 "NH[PHPOHIIH]",         // CharacterRangeTable | 
|  |                 "NH[PHHII]",            // CoverageTable | 
|  |                 "NH[RCHRCNHRUNHFH]",    // InnerClasses | 
|  |                 "NH[RMHNH[KLH]]",       // BootstrapMethods | 
|  |                 "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code | 
|  |                 "=AnnotationDefault", | 
|  |                 // Like metadata, but with a compact tag set: | 
|  |                 "[NH[(1)]]" | 
|  |                 +"[NH[(1)]]" | 
|  |                 +"[RSHNH[RUH(1)]]" | 
|  |                 +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]", | 
|  |                 "" | 
|  |             }; | 
|  |             ap = 0; | 
|  |         } | 
|  |         Utils.currentInstance.set(new PackerImpl()); | 
|  |         final int[][] counts = new int[2][3];  // int bci ref | 
|  |         final Entry[] cpMap = new Entry[maxVal+1]; | 
|  |         for (int i = 0; i < cpMap.length; i++) { | 
|  |             if (i == 0)  continue;  // 0 => null | 
|  |             cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); | 
|  |         } | 
|  |         Package.Class cls = new Package().new Class(""); | 
|  |         cls.cpMap = cpMap; | 
|  |         class TestValueStream extends ValueStream { | 
|  |             java.util.Random rand = new java.util.Random(0); | 
|  |             ArrayList history = new ArrayList(); | 
|  |             int ckidx = 0; | 
|  |             int maxVal; | 
|  |             boolean verbose; | 
|  |             void reset() { history.clear(); ckidx = 0; } | 
|  |             public int getInt(int bandIndex) { | 
|  |                 counts[0][0]++; | 
|  |                 int value = rand.nextInt(maxVal+1); | 
|  |                 history.add(new Integer(bandIndex)); | 
|  |                 history.add(new Integer(value)); | 
|  |                 return value; | 
|  |             } | 
|  |             public void putInt(int bandIndex, int token) { | 
|  |                 counts[1][0]++; | 
|  |                 if (verbose) | 
|  |                     System.out.print(" "+bandIndex+":"+token); | 
|  |                 // Make sure this put parallels a previous get: | 
|  |                 int check0 = ((Integer)history.get(ckidx+0)).intValue(); | 
|  |                 int check1 = ((Integer)history.get(ckidx+1)).intValue(); | 
|  |                 if (check0 != bandIndex || check1 != token) { | 
|  |                     if (!verbose) | 
|  |                         System.out.println(history.subList(0, ckidx)); | 
|  |                     System.out.println(" *** Should be "+check0+":"+check1); | 
|  |                     throw new RuntimeException("Failed test!"); | 
|  |                 } | 
|  |                 ckidx += 2; | 
|  |             } | 
|  |             public Entry getRef(int bandIndex) { | 
|  |                 counts[0][2]++; | 
|  |                 int value = getInt(bandIndex); | 
|  |                 if (value < 0 || value > maxVal) { | 
|  |                     System.out.println(" *** Unexpected ref code "+value); | 
|  |                     return ConstantPool.getLiteralEntry(new Integer(value)); | 
|  |                 } | 
|  |                 return cpMap[value]; | 
|  |             } | 
|  |             public void putRef(int bandIndex, Entry ref) { | 
|  |                 counts[1][2]++; | 
|  |                 if (ref == null) { | 
|  |                     putInt(bandIndex, 0); | 
|  |                     return; | 
|  |                 } | 
|  |                 Number refValue = null; | 
|  |                 if (ref instanceof ConstantPool.NumberEntry) | 
|  |                     refValue = ((ConstantPool.NumberEntry)ref).numberValue(); | 
|  |                 int value; | 
|  |                 if (!(refValue instanceof Integer)) { | 
|  |                     System.out.println(" *** Unexpected ref "+ref); | 
|  |                     value = -1; | 
|  |                 } else { | 
|  |                     value = ((Integer)refValue).intValue(); | 
|  |                 } | 
|  |                 putInt(bandIndex, value); | 
|  |             } | 
|  |             public int encodeBCI(int bci) { | 
|  |                 counts[1][1]++; | 
|  |                 // move LSB to MSB of low byte | 
|  |                 int code = (bci >> 8) << 8;  // keep high bits | 
|  |                 code += (bci & 0xFE) >> 1; | 
|  |                 code += (bci & 0x01) << 7; | 
|  |                 return code ^ (8<<8);  // mark it clearly as coded | 
|  |             } | 
|  |             public int decodeBCI(int bciCode) { | 
|  |                 counts[0][1]++; | 
|  |                 bciCode ^= (8<<8);  // remove extra mark | 
|  |                 int bci = (bciCode >> 8) << 8;  // keep high bits | 
|  |                 bci += (bciCode & 0x7F) << 1; | 
|  |                 bci += (bciCode & 0x80) >> 7; | 
|  |                 return bci; | 
|  |             } | 
|  |         } | 
|  |         TestValueStream tts = new TestValueStream(); | 
|  |         tts.maxVal = maxVal; | 
|  |         tts.verbose = verbose; | 
|  |         ByteArrayOutputStream buf = new ByteArrayOutputStream(); | 
|  |         for (int i = 0; i < (1 << 30); i = (i + 1) * 5) { | 
|  |             int ei = tts.encodeBCI(i); | 
|  |             int di = tts.decodeBCI(ei); | 
|  |             if (di != i)  System.out.println("i="+Integer.toHexString(i)+ | 
|  |                                              " ei="+Integer.toHexString(ei)+ | 
|  |                                              " di="+Integer.toHexString(di)); | 
|  |         } | 
|  |         while (iters-- > 0) { | 
|  |             for (int i = ap; i < av.length; i++) { | 
|  |                 String layout = av[i]; | 
|  |                 if (layout.startsWith("=")) { | 
|  |                     String name = layout.substring(1); | 
|  |                     for (Attribute a : standardDefs.values()) { | 
|  |                         if (a.name().equals(name)) { | 
|  |                             layout = a.layout().layout(); | 
|  |                             break; | 
|  |                         } | 
|  |                     } | 
|  |                     if (layout.startsWith("=")) { | 
|  |                         System.out.println("Could not find "+name+" in "+standardDefs.values()); | 
|  |                     } | 
|  |                 } | 
|  |                 Layout self = new Layout(0, "Foo", layout); | 
|  |                 if (verbose) { | 
|  |                     System.out.print("/"+layout+"/ => "); | 
|  |                     System.out.println(Arrays.asList(self.elems)); | 
|  |                 } | 
|  |                 buf.reset(); | 
|  |                 tts.reset(); | 
|  |                 Object fixups = self.unparse(tts, buf); | 
|  |                 byte[] bytes = buf.toByteArray(); | 
|  |                 // Attach the references to the byte array. | 
|  |                 Fixups.setBytes(fixups, bytes); | 
|  |                 // Patch the references to their frozen values. | 
|  |                 Fixups.finishRefs(fixups, bytes, new Index("test", cpMap)); | 
|  |                 if (verbose) { | 
|  |                     System.out.print("  bytes: {"); | 
|  |                     for (int j = 0; j < bytes.length; j++) { | 
|  |                         System.out.print(" "+bytes[j]); | 
|  |                     } | 
|  |                     System.out.println("}"); | 
|  |                 } | 
|  |                 if (verbose) { | 
|  |                     System.out.print("  parse: {"); | 
|  |                 } | 
|  |                 self.parse(cls, bytes, 0, bytes.length, tts); | 
|  |                 if (verbose) { | 
|  |                     System.out.println("}"); | 
|  |                 } | 
|  |             } | 
|  |         } | 
|  |         for (int j = 0; j <= 1; j++) { | 
|  |             System.out.print("values "+(j==0?"read":"written")+": {"); | 
|  |             for (int k = 0; k < counts[j].length; k++) { | 
|  |                 System.out.print(" "+counts[j][k]); | 
|  |             } | 
|  |             System.out.println(" }"); | 
|  |         } | 
|  |     } | 
|  | //*/ | 
|  | } |