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