0N/A/*
2667N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.java.util.jar.pack;
0N/A
3063N/Aimport com.sun.java.util.jar.pack.ConstantPool.Entry;
3063N/Aimport com.sun.java.util.jar.pack.ConstantPool.Index;
3063N/Aimport java.io.ByteArrayOutputStream;
3063N/Aimport java.io.IOException;
3063N/Aimport java.util.ArrayList;
3063N/Aimport java.util.Arrays;
3063N/Aimport java.util.Collection;
3063N/Aimport java.util.Collections;
3063N/Aimport java.util.HashMap;
3063N/Aimport java.util.List;
3063N/Aimport java.util.Map;
3315N/Aimport static com.sun.java.util.jar.pack.Constants.*;
0N/A
0N/A/**
0N/A * Represents an attribute in a class-file.
0N/A * Takes care to remember where constant pool indexes occur.
0N/A * Implements the "little language" of Pack200 for describing
0N/A * attribute layouts.
0N/A * @author John Rose
0N/A */
3315N/Aclass Attribute implements Comparable {
0N/A // Attribute instance fields.
0N/A
0N/A Layout def; // the name and format of this attr
0N/A byte[] bytes; // the actual bytes
0N/A Object fixups; // reference relocations, if any are required
0N/A
0N/A public String name() { return def.name(); }
0N/A public Layout layout() { return def; }
0N/A public byte[] bytes() { return bytes; }
0N/A public int size() { return bytes.length; }
0N/A public Entry getNameRef() { return def.getNameRef(); }
0N/A
0N/A private Attribute(Attribute old) {
0N/A this.def = old.def;
0N/A this.bytes = old.bytes;
0N/A this.fixups = old.fixups;
0N/A }
0N/A
0N/A public Attribute(Layout def, byte[] bytes, Object fixups) {
0N/A this.def = def;
0N/A this.bytes = bytes;
0N/A this.fixups = fixups;
0N/A Fixups.setBytes(fixups, bytes);
0N/A }
0N/A public Attribute(Layout def, byte[] bytes) {
0N/A this(def, bytes, null);
0N/A }
0N/A
0N/A public Attribute addContent(byte[] bytes, Object fixups) {
0N/A assert(isCanonical());
0N/A if (bytes.length == 0 && fixups == null)
0N/A return this;
0N/A Attribute res = new Attribute(this);
0N/A res.bytes = bytes;
0N/A res.fixups = fixups;
0N/A Fixups.setBytes(fixups, bytes);
0N/A return res;
0N/A }
0N/A public Attribute addContent(byte[] bytes) {
0N/A return addContent(bytes, null);
0N/A }
0N/A
0N/A public void finishRefs(Index ix) {
0N/A if (fixups != null) {
0N/A Fixups.finishRefs(fixups, bytes, ix);
0N/A fixups = null;
0N/A }
0N/A }
0N/A
0N/A public boolean isCanonical() {
0N/A return this == def.canon;
0N/A }
0N/A
0N/A public int compareTo(Object o) {
0N/A Attribute that = (Attribute) o;
0N/A return this.def.compareTo(that.def);
0N/A }
0N/A
2667N/A private static final Map<List<Attribute>, List<Attribute>> canonLists = new HashMap<>();
2667N/A private static final Map<Layout, Attribute> attributes = new HashMap<>();
2667N/A private static final Map<Layout, Attribute> standardDefs = new HashMap<>();
0N/A
0N/A // Canonicalized lists of trivial attrs (Deprecated, etc.)
0N/A // are used by trimToSize, in order to reduce footprint
0N/A // of some common cases. (Note that Code attributes are
0N/A // always zero size.)
3315N/A public static List<Attribute> getCanonList(List<Attribute> al) {
0N/A synchronized (canonLists) {
2667N/A List<Attribute> cl = canonLists.get(al);
0N/A if (cl == null) {
2667N/A cl = new ArrayList<>(al.size());
0N/A cl.addAll(al);
0N/A cl = Collections.unmodifiableList(cl);
0N/A canonLists.put(al, cl);
0N/A }
0N/A return cl;
0N/A }
0N/A }
0N/A
0N/A // Find the canonical empty attribute with the given ctype, name, layout.
0N/A public static Attribute find(int ctype, String name, String layout) {
0N/A Layout key = Layout.makeKey(ctype, name, layout);
0N/A synchronized (attributes) {
2667N/A Attribute a = attributes.get(key);
0N/A if (a == null) {
0N/A a = new Layout(ctype, name, layout).canonicalInstance();
0N/A attributes.put(key, a);
0N/A }
0N/A return a;
0N/A }
0N/A }
0N/A
2667N/A public static Layout keyForLookup(int ctype, String name) {
0N/A return Layout.makeKey(ctype, name);
0N/A }
0N/A
0N/A // Find canonical empty attribute with given ctype and name,
0N/A // and with the standard layout.
2667N/A public static Attribute lookup(Map<Layout, Attribute> defs, int ctype,
2667N/A String name) {
2667N/A if (defs == null) {
2667N/A defs = standardDefs;
2667N/A }
2667N/A return defs.get(Layout.makeKey(ctype, name));
0N/A }
2667N/A
2667N/A public static Attribute define(Map<Layout, Attribute> defs, int ctype,
2667N/A String name, String layout) {
0N/A Attribute a = find(ctype, name, layout);
0N/A defs.put(Layout.makeKey(ctype, name), a);
0N/A return a;
0N/A }
0N/A
0N/A static {
2667N/A Map<Layout, Attribute> sd = standardDefs;
0N/A define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH");
0N/A define(sd, ATTR_CONTEXT_CLASS, "Synthetic", "");
0N/A define(sd, ATTR_CONTEXT_CLASS, "Deprecated", "");
0N/A define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH");
0N/A define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH");
0N/A define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]");
0N/A
0N/A define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH");
0N/A define(sd, ATTR_CONTEXT_FIELD, "Synthetic", "");
0N/A define(sd, ATTR_CONTEXT_FIELD, "Deprecated", "");
0N/A define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH");
0N/A
0N/A define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH");
0N/A define(sd, ATTR_CONTEXT_METHOD, "Synthetic", "");
0N/A define(sd, ATTR_CONTEXT_METHOD, "Deprecated", "");
0N/A define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]");
0N/A //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]");
0N/A
0N/A define(sd, ATTR_CONTEXT_CODE, "StackMapTable",
0N/A ("[NH[(1)]]" +
0N/A "[TB" +
0N/A "(64-127)[(2)]" +
0N/A "(247)[(1)(2)]" +
0N/A "(248-251)[(1)]" +
0N/A "(252)[(1)(2)]" +
0N/A "(253)[(1)(2)(2)]" +
0N/A "(254)[(1)(2)(2)(2)]" +
0N/A "(255)[(1)NH[(2)]NH[(2)]]" +
0N/A "()[]" +
0N/A "]" +
0N/A "[H]" +
0N/A "[TB(7)[RCH](8)[PH]()[]]"));
0N/A
0N/A define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]");
0N/A define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]");
0N/A define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable", "NH[PHOHRUHRSHH]");
0N/A //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]");
0N/A //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]");
0N/A
0N/A // Note: Code and InnerClasses are special-cased elsewhere.
0N/A // Their layout specs. are given here for completeness.
0N/A // The Code spec is incomplete, in that it does not distinguish
0N/A // bytecode bytes or locate CP references.
0N/A }
0N/A
0N/A // Metadata.
0N/A //
0N/A // We define metadata using similar layouts
0N/A // for all five kinds of metadata attributes.
0N/A //
0N/A // Regular annotations are a counted list of [RSHNH[RUH(1)]][...]
0N/A // pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...]
0N/A //
0N/A // Parameter annotations are a counted list of regular annotations.
0N/A // pack.method.attribute.RuntimeVisibleParameterAnnotations=[NH[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...]
0N/A //
0N/A // RuntimeInvisible annotations are defined similarly...
0N/A // Non-method annotations are defined similarly...
0N/A //
0N/A // Annotation are a simple tagged value [TB...]
0N/A // pack.attribute.method.AnnotationDefault=[TB...]
0N/A //
0N/A static {
0N/A String mdLayouts[] = {
0N/A Attribute.normalizeLayoutString
0N/A (""
0N/A +"\n # parameter_annotations :="
0N/A +"\n [ NB[(1)] ] # forward call to annotations"
0N/A ),
0N/A Attribute.normalizeLayoutString
0N/A (""
0N/A +"\n # annotations :="
0N/A +"\n [ NH[(1)] ] # forward call to annotation"
0N/A +"\n "
0N/A +"\n # annotation :="
0N/A +"\n [RSH"
0N/A +"\n NH[RUH (1)] # forward call to value"
0N/A +"\n ]"
0N/A ),
0N/A Attribute.normalizeLayoutString
0N/A (""
0N/A +"\n # value :="
0N/A +"\n [TB # Callable 2 encodes one tagged value."
0N/A +"\n (\\B,\\C,\\I,\\S,\\Z)[KIH]"
0N/A +"\n (\\D)[KDH]"
0N/A +"\n (\\F)[KFH]"
0N/A +"\n (\\J)[KJH]"
0N/A +"\n (\\c)[RSH]"
0N/A +"\n (\\e)[RSH RUH]"
0N/A +"\n (\\s)[RUH]"
0N/A +"\n (\\[)[NH[(0)]] # backward self-call to value"
0N/A +"\n (\\@)[RSH NH[RUH (0)]] # backward self-call to value"
0N/A +"\n ()[] ]"
0N/A )
0N/A };
2667N/A Map<Layout, Attribute> sd = standardDefs;
0N/A String defaultLayout = mdLayouts[2];
0N/A String annotationsLayout = mdLayouts[1] + mdLayouts[2];
0N/A String paramsLayout = mdLayouts[0] + annotationsLayout;
0N/A for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) {
0N/A if (ctype == ATTR_CONTEXT_CODE) continue;
0N/A define(sd, ctype,
0N/A "RuntimeVisibleAnnotations", annotationsLayout);
0N/A define(sd, ctype,
0N/A "RuntimeInvisibleAnnotations", annotationsLayout);
0N/A if (ctype == ATTR_CONTEXT_METHOD) {
0N/A define(sd, ctype,
0N/A "RuntimeVisibleParameterAnnotations", paramsLayout);
0N/A define(sd, ctype,
0N/A "RuntimeInvisibleParameterAnnotations", paramsLayout);
0N/A define(sd, ctype,
0N/A "AnnotationDefault", defaultLayout);
0N/A }
0N/A }
0N/A }
0N/A
0N/A public static String contextName(int ctype) {
0N/A switch (ctype) {
0N/A case ATTR_CONTEXT_CLASS: return "class";
0N/A case ATTR_CONTEXT_FIELD: return "field";
0N/A case ATTR_CONTEXT_METHOD: return "method";
0N/A case ATTR_CONTEXT_CODE: return "code";
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /** Base class for any attributed object (Class, Field, Method, Code).
0N/A * Flags are included because they are used to help transmit the
0N/A * presence of attributes. That is, flags are a mix of modifier
0N/A * bits and attribute indicators.
0N/A */
0N/A public static abstract
0N/A class Holder {
0N/A
0N/A // We need this abstract method to interpret embedded CP refs.
0N/A protected abstract Entry[] getCPMap();
0N/A
0N/A protected int flags; // defined here for convenience
2667N/A protected List<Attribute> attributes;
0N/A
0N/A public int attributeSize() {
0N/A return (attributes == null) ? 0 : attributes.size();
0N/A }
0N/A
0N/A public void trimToSize() {
0N/A if (attributes == null) {
0N/A return;
0N/A }
2667N/A if (attributes.isEmpty()) {
0N/A attributes = null;
0N/A return;
0N/A }
0N/A if (attributes instanceof ArrayList) {
2667N/A ArrayList<Attribute> al = (ArrayList<Attribute>)attributes;
0N/A al.trimToSize();
0N/A boolean allCanon = true;
2667N/A for (Attribute a : al) {
0N/A if (!a.isCanonical()) {
0N/A allCanon = false;
0N/A }
0N/A if (a.fixups != null) {
0N/A assert(!a.isCanonical());
0N/A a.fixups = Fixups.trimToSize(a.fixups);
0N/A }
0N/A }
0N/A if (allCanon) {
0N/A // Replace private writable attribute list
0N/A // with only trivial entries by public unique
0N/A // immutable attribute list with the same entries.
0N/A attributes = getCanonList(al);
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void addAttribute(Attribute a) {
0N/A if (attributes == null)
2667N/A attributes = new ArrayList<>(3);
0N/A else if (!(attributes instanceof ArrayList))
2667N/A attributes = new ArrayList<>(attributes); // unfreeze it
0N/A attributes.add(a);
0N/A }
0N/A
0N/A public Attribute removeAttribute(Attribute a) {
0N/A if (attributes == null) return null;
0N/A if (!attributes.contains(a)) return null;
0N/A if (!(attributes instanceof ArrayList))
2667N/A attributes = new ArrayList<>(attributes); // unfreeze it
0N/A attributes.remove(a);
0N/A return a;
0N/A }
0N/A
0N/A public Attribute getAttribute(int n) {
2667N/A return attributes.get(n);
0N/A }
0N/A
2667N/A protected void visitRefs(int mode, Collection<Entry> refs) {
0N/A if (attributes == null) return;
2667N/A for (Attribute a : attributes) {
0N/A a.visitRefs(this, mode, refs);
0N/A }
0N/A }
0N/A
2667N/A static final List<Attribute> noAttributes = Arrays.asList(new Attribute[0]);
0N/A
2667N/A public List<Attribute> getAttributes() {
0N/A if (attributes == null)
0N/A return noAttributes;
0N/A return attributes;
0N/A }
0N/A
2667N/A public void setAttributes(List<Attribute> attrList) {
0N/A if (attrList.isEmpty())
0N/A attributes = null;
0N/A else
0N/A attributes = attrList;
0N/A }
0N/A
0N/A public Attribute getAttribute(String attrName) {
0N/A if (attributes == null) return null;
2667N/A for (Attribute a : attributes) {
0N/A if (a.name().equals(attrName))
0N/A return a;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A public Attribute getAttribute(Layout attrDef) {
0N/A if (attributes == null) return null;
2667N/A for (Attribute a : attributes) {
0N/A if (a.layout() == attrDef)
0N/A return a;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A public Attribute removeAttribute(String attrName) {
0N/A return removeAttribute(getAttribute(attrName));
0N/A }
0N/A
0N/A public Attribute removeAttribute(Layout attrDef) {
0N/A return removeAttribute(getAttribute(attrDef));
0N/A }
0N/A
0N/A public void strip(String attrName) {
0N/A removeAttribute(getAttribute(attrName));
0N/A }
0N/A }
0N/A
0N/A // Lightweight interface to hide details of band structure.
0N/A // Also used for testing.
0N/A public static abstract
0N/A class ValueStream {
0N/A public int getInt(int bandIndex) { throw undef(); }
0N/A public void putInt(int bandIndex, int value) { throw undef(); }
0N/A public Entry getRef(int bandIndex) { throw undef(); }
0N/A public void putRef(int bandIndex, Entry ref) { throw undef(); }
0N/A // Note: decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref
0N/A public int decodeBCI(int bciCode) { throw undef(); }
0N/A public int encodeBCI(int bci) { throw undef(); }
0N/A public void noteBackCall(int whichCallable) { /* ignore by default */ }
0N/A private RuntimeException undef() {
0N/A return new UnsupportedOperationException("ValueStream method");
0N/A }
0N/A }
0N/A
0N/A // Element kinds:
0N/A static final byte EK_INT = 1; // B H I SH etc.
0N/A static final byte EK_BCI = 2; // PH POH etc.
0N/A static final byte EK_BCO = 3; // OH etc.
0N/A static final byte EK_FLAG = 4; // FH etc.
0N/A static final byte EK_REPL = 5; // NH[...] etc.
0N/A static final byte EK_REF = 6; // RUH, RUNH, KQH, etc.
0N/A static final byte EK_UN = 7; // TB(...)[...] etc.
0N/A static final byte EK_CASE = 8; // (...)[...] etc.
0N/A static final byte EK_CALL = 9; // (0), (1), etc.
0N/A static final byte EK_CBLE = 10; // [...][...] etc.
0N/A static final byte EF_SIGN = 1<<0; // INT is signed
0N/A static final byte EF_DELTA = 1<<1; // BCI/BCI value is diff'ed w/ previous
0N/A static final byte EF_NULL = 1<<2; // null REF is expected/allowed
0N/A static final byte EF_BACK = 1<<3; // call, callable, case is backward
0N/A static final int NO_BAND_INDEX = -1;
0N/A
0N/A /** A "class" of attributes, characterized by a context-type, name
0N/A * and format. The formats are specified in a "little language".
0N/A */
0N/A public static
0N/A class Layout implements Comparable {
0N/A int ctype; // attribute context type, e.g., ATTR_CONTEXT_CODE
0N/A String name; // name of attribute
0N/A boolean hasRefs; // this kind of attr contains CP refs?
0N/A String layout; // layout specification
0N/A int bandCount; // total number of elems
0N/A Element[] elems; // tokenization of layout
0N/A Attribute canon; // canonical instance of this layout
0N/A
0N/A public int ctype() { return ctype; }
0N/A public String name() { return name; }
0N/A public String layout() { return layout; }
0N/A public Attribute canonicalInstance() { return canon; }
0N/A
0N/A public Entry getNameRef() {
2667N/A return ConstantPool.getUtf8Entry(name());
0N/A }
0N/A
3315N/A public boolean isEmpty() {
3315N/A return layout.isEmpty();
3315N/A }
0N/A
0N/A public Layout(int ctype, String name, String layout) {
0N/A this.ctype = ctype;
0N/A this.name = name.intern();
0N/A this.layout = layout.intern();
0N/A assert(ctype < ATTR_CONTEXT_LIMIT);
0N/A boolean hasCallables = layout.startsWith("[");
0N/A try {
0N/A if (!hasCallables) {
0N/A this.elems = tokenizeLayout(this, -1, layout);
0N/A } else {
0N/A String[] bodies = splitBodies(layout);
0N/A // Make the callables now, so they can be linked immediately.
3315N/A Element[] lelems = new Element[bodies.length];
3315N/A this.elems = lelems;
3315N/A for (int i = 0; i < lelems.length; i++) {
0N/A Element ce = this.new Element();
0N/A ce.kind = EK_CBLE;
0N/A ce.removeBand();
0N/A ce.bandIndex = NO_BAND_INDEX;
0N/A ce.layout = bodies[i];
3315N/A lelems[i] = ce;
0N/A }
0N/A // Next fill them in.
3315N/A for (int i = 0; i < lelems.length; i++) {
3315N/A Element ce = lelems[i];
0N/A ce.body = tokenizeLayout(this, i, bodies[i]);
0N/A }
0N/A //System.out.println(Arrays.asList(elems));
0N/A }
0N/A } catch (StringIndexOutOfBoundsException ee) {
0N/A // simplest way to catch syntax errors...
0N/A throw new RuntimeException("Bad attribute layout: "+layout, ee);
0N/A }
0N/A // Some uses do not make a fresh one for each occurrence.
0N/A // For example, if layout == "", we only need one attr to share.
0N/A canon = new Attribute(this, noBytes);
0N/A }
0N/A private Layout() {}
0N/A static Layout makeKey(int ctype, String name, String layout) {
0N/A Layout def = new Layout();
0N/A def.ctype = ctype;
0N/A def.name = name.intern();
0N/A def.layout = layout.intern();
0N/A assert(ctype < ATTR_CONTEXT_LIMIT);
0N/A return def;
0N/A }
0N/A static Layout makeKey(int ctype, String name) {
0N/A return makeKey(ctype, name, "");
0N/A }
0N/A
0N/A public Attribute addContent(byte[] bytes, Object fixups) {
0N/A return canon.addContent(bytes, fixups);
0N/A }
0N/A public Attribute addContent(byte[] bytes) {
0N/A return canon.addContent(bytes, null);
0N/A }
0N/A
0N/A public boolean equals(Object x) {
3315N/A return ( x != null) && ( x.getClass() == Layout.class ) &&
3315N/A equals((Layout)x);
0N/A }
0N/A public boolean equals(Layout that) {
3315N/A return this.name.equals(that.name)
3315N/A && this.layout.equals(that.layout)
0N/A && this.ctype == that.ctype;
0N/A }
0N/A public int hashCode() {
0N/A return (((17 + name.hashCode())
0N/A * 37 + layout.hashCode())
0N/A * 37 + ctype);
0N/A }
0N/A public int compareTo(Object o) {
0N/A Layout that = (Layout) o;
0N/A int r;
0N/A r = this.name.compareTo(that.name);
0N/A if (r != 0) return r;
0N/A r = this.layout.compareTo(that.layout);
0N/A if (r != 0) return r;
0N/A return this.ctype - that.ctype;
0N/A }
0N/A public String toString() {
0N/A String str = contextName(ctype)+"."+name+"["+layout+"]";
0N/A // If -ea, print out more informative strings!
0N/A assert((str = stringForDebug()) != null);
0N/A return str;
0N/A }
0N/A private String stringForDebug() {
0N/A return contextName(ctype)+"."+name+Arrays.asList(elems);
0N/A }
0N/A
0N/A public
0N/A class Element {
0N/A String layout; // spelling in the little language
0N/A byte flags; // EF_SIGN, etc.
0N/A byte kind; // EK_UINT, etc.
0N/A byte len; // scalar length of element
0N/A byte refKind; // CONSTANT_String, etc.
0N/A int bandIndex; // which band does this element govern?
0N/A int value; // extra parameter
0N/A Element[] body; // extra data (for replications, unions, calls)
0N/A
0N/A boolean flagTest(byte mask) { return (flags & mask) != 0; }
0N/A
0N/A Element() {
0N/A bandIndex = bandCount++;
0N/A }
0N/A
0N/A void removeBand() {
0N/A --bandCount;
0N/A assert(bandIndex == bandCount);
0N/A bandIndex = NO_BAND_INDEX;
0N/A }
0N/A
0N/A public boolean hasBand() {
0N/A return bandIndex >= 0;
0N/A }
0N/A public String toString() {
0N/A String str = layout;
0N/A // If -ea, print out more informative strings!
0N/A assert((str = stringForDebug()) != null);
0N/A return str;
0N/A }
0N/A private String stringForDebug() {
3315N/A Element[] lbody = this.body;
0N/A switch (kind) {
0N/A case EK_CALL:
3315N/A lbody = null;
0N/A break;
0N/A case EK_CASE:
0N/A if (flagTest(EF_BACK))
3315N/A lbody = null;
0N/A break;
0N/A }
0N/A return layout
0N/A + (!hasBand()?"":"#"+bandIndex)
0N/A + "<"+ (flags==0?"":""+flags)+kind+len
0N/A + (refKind==0?"":""+refKind) + ">"
0N/A + (value==0?"":"("+value+")")
3315N/A + (lbody==null?"": ""+Arrays.asList(lbody));
0N/A }
0N/A }
0N/A
0N/A public boolean hasCallables() {
0N/A return (elems.length > 0 && elems[0].kind == EK_CBLE);
0N/A }
0N/A static private final Element[] noElems = {};
0N/A public Element[] getCallables() {
3315N/A if (hasCallables()) {
3315N/A Element[] nelems = Arrays.copyOf(elems, elems.length);
3315N/A return nelems;
3315N/A } else
0N/A return noElems; // no callables at all
0N/A }
0N/A public Element[] getEntryPoint() {
0N/A if (hasCallables())
0N/A return elems[0].body; // body of first callable
3315N/A else {
3315N/A Element[] nelems = Arrays.copyOf(elems, elems.length);
3315N/A return nelems; // no callables; whole body
3315N/A }
0N/A }
0N/A
0N/A /** Return a sequence of tokens from the given attribute bytes.
0N/A * Sequence elements will be 1-1 correspondent with my layout tokens.
0N/A */
0N/A public void parse(Holder holder,
0N/A byte[] bytes, int pos, int len, ValueStream out) {
0N/A int end = parseUsing(getEntryPoint(),
0N/A holder, bytes, pos, len, out);
0N/A if (end != pos + len)
0N/A throw new InternalError("layout parsed "+(end-pos)+" out of "+len+" bytes");
0N/A }
0N/A /** Given a sequence of tokens, return the attribute bytes.
0N/A * Sequence elements must be 1-1 correspondent with my layout tokens.
0N/A * The returned object is a cookie for Fixups.finishRefs, which
0N/A * must be used to harden any references into integer indexes.
0N/A */
0N/A public Object unparse(ValueStream in, ByteArrayOutputStream out) {
0N/A Object[] fixups = { null };
0N/A unparseUsing(getEntryPoint(), fixups, in, out);
0N/A return fixups[0]; // return ref-bearing cookie, if any
0N/A }
0N/A
0N/A public String layoutForPackageMajver(int majver) {
0N/A if (majver <= JAVA5_PACKAGE_MAJOR_VERSION) {
0N/A // Disallow layout syntax in the oldest protocol version.
0N/A return expandCaseDashNotation(layout);
0N/A }
0N/A return layout;
0N/A }
0N/A }
0N/A
0N/A public static
0N/A class FormatException extends IOException {
0N/A private int ctype;
0N/A private String name;
0N/A String layout;
0N/A public FormatException(String message,
0N/A int ctype, String name, String layout) {
2935N/A super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" +
2935N/A (message == null? "" : (": " + message)));
0N/A this.ctype = ctype;
0N/A this.name = name;
0N/A this.layout = layout;
0N/A }
0N/A public FormatException(String message,
0N/A int ctype, String name) {
0N/A this(message, ctype, name, null);
0N/A }
0N/A }
0N/A
3315N/A void visitRefs(Holder holder, int mode, final Collection<Entry> refs) {
0N/A if (mode == VRM_CLASSIC) {
0N/A refs.add(getNameRef());
0N/A }
0N/A // else the name is owned by the layout, and is processed elsewhere
0N/A if (bytes.length == 0) return; // quick exit
0N/A if (!def.hasRefs) return; // quick exit
0N/A if (fixups != null) {
0N/A Fixups.visitRefs(fixups, refs);
0N/A return;
0N/A }
0N/A // References (to a local cpMap) are embedded in the bytes.
0N/A def.parse(holder, bytes, 0, bytes.length,
0N/A new ValueStream() {
0N/A public void putInt(int bandIndex, int value) {
0N/A }
0N/A public void putRef(int bandIndex, Entry ref) {
0N/A refs.add(ref);
0N/A }
0N/A public int encodeBCI(int bci) {
0N/A return bci;
0N/A }
0N/A });
0N/A }
0N/A
0N/A public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) {
0N/A def.parse(holder, bytes, pos, len, out);
0N/A }
0N/A public Object unparse(ValueStream in, ByteArrayOutputStream out) {
0N/A return def.unparse(in, out);
0N/A }
0N/A
0N/A public String toString() {
0N/A return def
0N/A +"{"+(bytes == null ? -1 : size())+"}"
0N/A +(fixups == null? "": fixups.toString());
0N/A }
0N/A
0N/A /** Remove any informal "pretty printing" from the layout string.
0N/A * Removes blanks and control chars.
0N/A * Removes '#' comments (to end of line).
0N/A * Replaces '\c' by the decimal code of the character c.
0N/A * Replaces '0xNNN' by the decimal code of the hex number NNN.
0N/A */
0N/A static public
0N/A String normalizeLayoutString(String layout) {
3315N/A StringBuilder buf = new StringBuilder();
0N/A for (int i = 0, len = layout.length(); i < len; ) {
0N/A char ch = layout.charAt(i++);
0N/A if (ch <= ' ') {
0N/A // Skip whitespace and control chars
0N/A continue;
0N/A } else if (ch == '#') {
0N/A // Skip to end of line.
0N/A int end1 = layout.indexOf('\n', i);
0N/A int end2 = layout.indexOf('\r', i);
0N/A if (end1 < 0) end1 = len;
0N/A if (end2 < 0) end2 = len;
0N/A i = Math.min(end1, end2);
0N/A } else if (ch == '\\') {
0N/A // Map a character reference to its decimal code.
0N/A buf.append((int) layout.charAt(i++));
0N/A } else if (ch == '0' && layout.startsWith("0x", i-1)) {
0N/A // Map a hex numeral to its decimal code.
0N/A int start = i-1;
0N/A int end = start+2;
0N/A while (end < len) {
0N/A int dig = layout.charAt(end);
0N/A if ((dig >= '0' && dig <= '9') ||
0N/A (dig >= 'a' && dig <= 'f'))
0N/A ++end;
0N/A else
0N/A break;
0N/A }
0N/A if (end > start) {
0N/A String num = layout.substring(start, end);
0N/A buf.append(Integer.decode(num));
0N/A i = end;
0N/A } else {
0N/A buf.append(ch);
0N/A }
0N/A } else {
0N/A buf.append(ch);
0N/A }
0N/A }
0N/A String result = buf.toString();
0N/A if (false && !result.equals(layout)) {
0N/A Utils.log.info("Normalizing layout string");
0N/A Utils.log.info(" From: "+layout);
0N/A Utils.log.info(" To: "+result);
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A /// Subroutines for parsing and unparsing:
0N/A
0N/A /** Parse the attribute layout language.
0N/A<pre>
0N/A attribute_layout:
0N/A ( layout_element )* | ( callable )+
0N/A layout_element:
0N/A ( integral | replication | union | call | reference )
0N/A
0N/A callable:
0N/A '[' body ']'
0N/A body:
0N/A ( layout_element )+
0N/A
0N/A integral:
0N/A ( unsigned_int | signed_int | bc_index | bc_offset | flag )
0N/A unsigned_int:
0N/A uint_type
0N/A signed_int:
0N/A 'S' uint_type
0N/A any_int:
0N/A ( unsigned_int | signed_int )
0N/A bc_index:
0N/A ( 'P' uint_type | 'PO' uint_type )
0N/A bc_offset:
0N/A 'O' any_int
0N/A flag:
0N/A 'F' uint_type
0N/A uint_type:
0N/A ( 'B' | 'H' | 'I' | 'V' )
0N/A
0N/A replication:
0N/A 'N' uint_type '[' body ']'
0N/A
0N/A union:
0N/A 'T' any_int (union_case)* '(' ')' '[' (body)? ']'
0N/A union_case:
0N/A '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
0N/A union_case_tag:
0N/A ( numeral | numeral '-' numeral )
0N/A call:
0N/A '(' numeral ')'
0N/A
0N/A reference:
0N/A reference_type ( 'N' )? uint_type
0N/A reference_type:
0N/A ( constant_ref | schema_ref | utf8_ref | untyped_ref )
0N/A constant_ref:
0N/A ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' )
0N/A schema_ref:
0N/A ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' )
0N/A utf8_ref:
0N/A 'RU'
0N/A untyped_ref:
0N/A 'RQ'
0N/A
0N/A numeral:
0N/A '(' ('-')? (digit)+ ')'
0N/A digit:
0N/A ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
0N/A </pre>
0N/A */
0N/A static //private
0N/A Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) {
3315N/A List<Layout.Element> col = new ArrayList<>(layout.length());
0N/A tokenizeLayout(self, curCble, layout, col);
0N/A Layout.Element[] res = new Layout.Element[col.size()];
0N/A col.toArray(res);
0N/A return res;
0N/A }
0N/A static //private
3315N/A void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) {
0N/A boolean prevBCI = false;
0N/A for (int len = layout.length(), i = 0; i < len; ) {
0N/A int start = i;
0N/A int body;
0N/A Layout.Element e = self.new Element();
0N/A byte kind;
0N/A //System.out.println("at "+i+": ..."+layout.substring(i));
0N/A // strip a prefix
0N/A switch (layout.charAt(i++)) {
0N/A /// layout_element: integral
0N/A case 'B': case 'H': case 'I': case 'V': // unsigned_int
0N/A kind = EK_INT;
0N/A --i; // reparse
0N/A i = tokenizeUInt(e, layout, i);
0N/A break;
0N/A case 'S': // signed_int
0N/A kind = EK_INT;
0N/A --i; // reparse
0N/A i = tokenizeSInt(e, layout, i);
0N/A break;
0N/A case 'P': // bc_index
0N/A kind = EK_BCI;
0N/A if (layout.charAt(i++) == 'O') {
0N/A // bc_index: 'PO' tokenizeUInt
0N/A e.flags |= EF_DELTA;
0N/A // must follow P or PO:
0N/A if (!prevBCI)
0N/A { i = -i; continue; } // fail
0N/A i++; // move forward
0N/A }
0N/A --i; // reparse
0N/A i = tokenizeUInt(e, layout, i);
0N/A break;
0N/A case 'O': // bc_offset
0N/A kind = EK_BCO;
0N/A e.flags |= EF_DELTA;
0N/A // must follow P or PO:
0N/A if (!prevBCI)
0N/A { i = -i; continue; } // fail
0N/A i = tokenizeSInt(e, layout, i);
0N/A break;
0N/A case 'F': // flag
0N/A kind = EK_FLAG;
0N/A i = tokenizeUInt(e, layout, i);
0N/A break;
0N/A case 'N': // replication: 'N' uint '[' elem ... ']'
0N/A kind = EK_REPL;
0N/A i = tokenizeUInt(e, layout, i);
0N/A if (layout.charAt(i++) != '[')
0N/A { i = -i; continue; } // fail
0N/A i = skipBody(layout, body = i);
0N/A e.body = tokenizeLayout(self, curCble,
0N/A layout.substring(body, i++));
0N/A break;
0N/A case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
0N/A kind = EK_UN;
0N/A i = tokenizeSInt(e, layout, i);
3315N/A List<Layout.Element> cases = new ArrayList<>();
0N/A for (;;) {
0N/A // Keep parsing cases until we hit the default case.
0N/A if (layout.charAt(i++) != '(')
0N/A { i = -i; break; } // fail
0N/A int beg = i;
0N/A i = layout.indexOf(')', i);
0N/A String cstr = layout.substring(beg, i++);
0N/A int cstrlen = cstr.length();
0N/A if (layout.charAt(i++) != '[')
0N/A { i = -i; break; } // fail
0N/A // Check for duplication.
0N/A if (layout.charAt(i) == ']')
0N/A body = i; // missing body, which is legal here
0N/A else
0N/A i = skipBody(layout, body = i);
0N/A Layout.Element[] cbody
0N/A = tokenizeLayout(self, curCble,
0N/A layout.substring(body, i++));
0N/A if (cstrlen == 0) {
0N/A Layout.Element ce = self.new Element();
0N/A ce.body = cbody;
0N/A ce.kind = EK_CASE;
0N/A ce.removeBand();
0N/A cases.add(ce);
0N/A break; // done with the whole union
0N/A } else {
0N/A // Parse a case string.
0N/A boolean firstCaseNum = true;
0N/A for (int cp = 0, endp;; cp = endp+1) {
0N/A // Look for multiple case tags:
0N/A endp = cstr.indexOf(',', cp);
0N/A if (endp < 0) endp = cstrlen;
0N/A String cstr1 = cstr.substring(cp, endp);
0N/A if (cstr1.length() == 0)
0N/A cstr1 = "empty"; // will fail parse
0N/A int value0, value1;
0N/A // Check for a case range (new in 1.6).
0N/A int dash = findCaseDash(cstr1, 0);
0N/A if (dash >= 0) {
0N/A value0 = parseIntBefore(cstr1, dash);
0N/A value1 = parseIntAfter(cstr1, dash);
0N/A if (value0 >= value1)
0N/A { i = -i; break; } // fail
0N/A } else {
0N/A value0 = value1 = Integer.parseInt(cstr1);
0N/A }
0N/A // Add a case for each value in value0..value1
0N/A for (;; value0++) {
0N/A Layout.Element ce = self.new Element();
0N/A ce.body = cbody; // all cases share one body
0N/A ce.kind = EK_CASE;
0N/A ce.removeBand();
0N/A if (!firstCaseNum)
0N/A // "backward case" repeats a body
0N/A ce.flags |= EF_BACK;
0N/A firstCaseNum = false;
0N/A ce.value = value0;
0N/A cases.add(ce);
0N/A if (value0 == value1) break;
0N/A }
0N/A if (endp == cstrlen) {
0N/A break; // done with this case
0N/A }
0N/A }
0N/A }
0N/A }
0N/A e.body = new Layout.Element[cases.size()];
0N/A cases.toArray(e.body);
0N/A e.kind = kind;
0N/A for (int j = 0; j < e.body.length-1; j++) {
0N/A Layout.Element ce = e.body[j];
0N/A if (matchCase(e, ce.value) != ce) {
0N/A // Duplicate tag.
0N/A { i = -i; break; } // fail
0N/A }
0N/A }
0N/A break;
0N/A case '(': // call: '(' '-'? digit+ ')'
0N/A kind = EK_CALL;
0N/A e.removeBand();
0N/A i = layout.indexOf(')', i);
0N/A String cstr = layout.substring(start+1, i++);
0N/A int offset = Integer.parseInt(cstr);
0N/A int target = curCble + offset;
0N/A if (!(offset+"").equals(cstr) ||
0N/A self.elems == null ||
0N/A target < 0 ||
0N/A target >= self.elems.length)
0N/A { i = -i; continue; } // fail
0N/A Layout.Element ce = self.elems[target];
0N/A assert(ce.kind == EK_CBLE);
0N/A e.value = target;
0N/A e.body = new Layout.Element[]{ ce };
0N/A // Is it a (recursive) backward call?
0N/A if (offset <= 0) {
0N/A // Yes. Mark both caller and callee backward.
0N/A e.flags |= EF_BACK;
0N/A ce.flags |= EF_BACK;
0N/A }
0N/A break;
0N/A case 'K': // reference_type: constant_ref
0N/A kind = EK_REF;
0N/A switch (layout.charAt(i++)) {
0N/A case 'I': e.refKind = CONSTANT_Integer; break;
0N/A case 'J': e.refKind = CONSTANT_Long; break;
0N/A case 'F': e.refKind = CONSTANT_Float; break;
0N/A case 'D': e.refKind = CONSTANT_Double; break;
0N/A case 'S': e.refKind = CONSTANT_String; break;
0N/A case 'Q': e.refKind = CONSTANT_Literal; break;
0N/A default: { i = -i; continue; } // fail
0N/A }
0N/A break;
0N/A case 'R': // schema_ref
0N/A kind = EK_REF;
0N/A switch (layout.charAt(i++)) {
0N/A case 'C': e.refKind = CONSTANT_Class; break;
0N/A case 'S': e.refKind = CONSTANT_Signature; break;
0N/A case 'D': e.refKind = CONSTANT_NameandType; break;
0N/A case 'F': e.refKind = CONSTANT_Fieldref; break;
0N/A case 'M': e.refKind = CONSTANT_Methodref; break;
0N/A case 'I': e.refKind = CONSTANT_InterfaceMethodref; break;
0N/A
0N/A case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref
0N/A case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref
0N/A
0N/A default: { i = -i; continue; } // fail
0N/A }
0N/A break;
0N/A default: { i = -i; continue; } // fail
0N/A }
0N/A
0N/A // further parsing of refs
0N/A if (kind == EK_REF) {
0N/A // reference: reference_type -><- ( 'N' )? tokenizeUInt
0N/A if (layout.charAt(i++) == 'N') {
0N/A e.flags |= EF_NULL;
0N/A i++; // move forward
0N/A }
0N/A --i; // reparse
0N/A i = tokenizeUInt(e, layout, i);
0N/A self.hasRefs = true;
0N/A }
0N/A
0N/A prevBCI = (kind == EK_BCI);
0N/A
0N/A // store the new element
0N/A e.kind = kind;
0N/A e.layout = layout.substring(start, i);
0N/A col.add(e);
0N/A }
0N/A }
0N/A static //private
0N/A String[] splitBodies(String layout) {
3315N/A List<String> bodies = new ArrayList<>();
0N/A // Parse several independent layout bodies: "[foo][bar]...[baz]"
0N/A for (int i = 0; i < layout.length(); i++) {
0N/A if (layout.charAt(i++) != '[')
0N/A layout.charAt(-i); // throw error
0N/A int body;
0N/A i = skipBody(layout, body = i);
0N/A bodies.add(layout.substring(body, i));
0N/A }
0N/A String[] res = new String[bodies.size()];
0N/A bodies.toArray(res);
0N/A return res;
0N/A }
0N/A static private
0N/A int skipBody(String layout, int i) {
0N/A assert(layout.charAt(i-1) == '[');
0N/A if (layout.charAt(i) == ']')
0N/A // No empty bodies, please.
0N/A return -i;
0N/A // skip balanced [...[...]...]
0N/A for (int depth = 1; depth > 0; ) {
0N/A switch (layout.charAt(i++)) {
0N/A case '[': depth++; break;
0N/A case ']': depth--; break;
0N/A }
0N/A }
0N/A --i; // get before bracket
0N/A assert(layout.charAt(i) == ']');
0N/A return i; // return closing bracket
0N/A }
0N/A static private
0N/A int tokenizeUInt(Layout.Element e, String layout, int i) {
0N/A switch (layout.charAt(i++)) {
0N/A case 'V': e.len = 0; break;
0N/A case 'B': e.len = 1; break;
0N/A case 'H': e.len = 2; break;
0N/A case 'I': e.len = 4; break;
0N/A default: return -i;
0N/A }
0N/A return i;
0N/A }
0N/A static private
0N/A int tokenizeSInt(Layout.Element e, String layout, int i) {
0N/A if (layout.charAt(i) == 'S') {
0N/A e.flags |= EF_SIGN;
0N/A ++i;
0N/A }
0N/A return tokenizeUInt(e, layout, i);
0N/A }
0N/A
0N/A static private
0N/A boolean isDigit(char c) {
0N/A return c >= '0' && c <= '9';
0N/A }
0N/A
0N/A /** Find an occurrence of hyphen '-' between two numerals. */
0N/A static //private
0N/A int findCaseDash(String layout, int fromIndex) {
0N/A if (fromIndex <= 0) fromIndex = 1; // minimum dash pos
0N/A int lastDash = layout.length() - 2; // maximum dash pos
0N/A for (;;) {
0N/A int dash = layout.indexOf('-', fromIndex);
0N/A if (dash < 0 || dash > lastDash) return -1;
0N/A if (isDigit(layout.charAt(dash-1))) {
0N/A char afterDash = layout.charAt(dash+1);
0N/A if (afterDash == '-' && dash+2 < layout.length())
0N/A afterDash = layout.charAt(dash+2);
0N/A if (isDigit(afterDash)) {
0N/A // matched /[0-9]--?[0-9]/; return position of dash
0N/A return dash;
0N/A }
0N/A }
0N/A fromIndex = dash+1;
0N/A }
0N/A }
0N/A static
0N/A int parseIntBefore(String layout, int dash) {
0N/A int end = dash;
0N/A int beg = end;
2667N/A while (beg > 0 && isDigit(layout.charAt(beg-1))) {
2667N/A --beg;
2667N/A }
0N/A if (beg == end) return Integer.parseInt("empty");
0N/A // skip backward over a sign
0N/A if (beg >= 1 && layout.charAt(beg-1) == '-') --beg;
0N/A assert(beg == 0 || !isDigit(layout.charAt(beg-1)));
0N/A return Integer.parseInt(layout.substring(beg, end));
0N/A }
0N/A static
0N/A int parseIntAfter(String layout, int dash) {
0N/A int beg = dash+1;
0N/A int end = beg;
0N/A int limit = layout.length();
0N/A if (end < limit && layout.charAt(end) == '-') ++end;
2667N/A while (end < limit && isDigit(layout.charAt(end))) {
2667N/A ++end;
2667N/A }
0N/A if (beg == end) return Integer.parseInt("empty");
0N/A return Integer.parseInt(layout.substring(beg, end));
0N/A }
0N/A /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */
0N/A static
0N/A String expandCaseDashNotation(String layout) {
0N/A int dash = findCaseDash(layout, 0);
0N/A if (dash < 0) return layout; // no dashes (the common case)
3315N/A StringBuilder result = new StringBuilder(layout.length() * 3);
0N/A int sofar = 0; // how far have we processed the layout?
0N/A for (;;) {
0N/A // for each dash, collect everything up to the dash
0N/A result.append(layout.substring(sofar, dash));
0N/A sofar = dash+1; // skip the dash
0N/A // then collect intermediate values
0N/A int value0 = parseIntBefore(layout, dash);
0N/A int value1 = parseIntAfter(layout, dash);
0N/A assert(value0 < value1);
0N/A result.append(","); // close off value0 numeral
0N/A for (int i = value0+1; i < value1; i++) {
0N/A result.append(i);
0N/A result.append(","); // close off i numeral
0N/A }
0N/A dash = findCaseDash(layout, sofar);
0N/A if (dash < 0) break;
0N/A }
0N/A result.append(layout.substring(sofar)); // collect the rest
0N/A return result.toString();
0N/A }
0N/A static {
0N/A assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5"));
0N/A assert(expandCaseDashNotation("-2--1").equals("-2,-1"));
0N/A assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1"));
0N/A assert(expandCaseDashNotation("-1-0").equals("-1,0"));
0N/A }
0N/A
0N/A // Parse attribute bytes, putting values into bands. Returns new pos.
0N/A // Used when reading a class file (local refs resolved with local cpMap).
0N/A // Also used for ad hoc scanning.
0N/A static
0N/A int parseUsing(Layout.Element[] elems, Holder holder,
0N/A byte[] bytes, int pos, int len, ValueStream out) {
0N/A int prevBCI = 0;
0N/A int prevRBCI = 0;
0N/A int end = pos + len;
0N/A int[] buf = { 0 }; // for calls to parseInt, holds 2nd result
0N/A for (int i = 0; i < elems.length; i++) {
0N/A Layout.Element e = elems[i];
0N/A int bandIndex = e.bandIndex;
0N/A int value;
0N/A int BCI, RBCI;
0N/A switch (e.kind) {
0N/A case EK_INT:
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A value = buf[0];
0N/A out.putInt(bandIndex, value);
0N/A break;
0N/A case EK_BCI: // PH, POH
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A BCI = buf[0];
0N/A RBCI = out.encodeBCI(BCI);
0N/A if (!e.flagTest(EF_DELTA)) {
0N/A // PH: transmit R(bci), store bci
0N/A value = RBCI;
0N/A } else {
0N/A // POH: transmit D(R(bci)), store bci
0N/A value = RBCI - prevRBCI;
0N/A }
0N/A prevBCI = BCI;
0N/A prevRBCI = RBCI;
0N/A out.putInt(bandIndex, value);
0N/A break;
0N/A case EK_BCO: // OH
0N/A assert(e.flagTest(EF_DELTA));
0N/A // OH: transmit D(R(bci)), store D(bci)
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A BCI = prevBCI + buf[0];
0N/A RBCI = out.encodeBCI(BCI);
0N/A value = RBCI - prevRBCI;
0N/A prevBCI = BCI;
0N/A prevRBCI = RBCI;
0N/A out.putInt(bandIndex, value);
0N/A break;
0N/A case EK_FLAG:
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A value = buf[0];
0N/A out.putInt(bandIndex, value);
0N/A break;
0N/A case EK_REPL:
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A value = buf[0];
0N/A out.putInt(bandIndex, value);
0N/A for (int j = 0; j < value; j++) {
0N/A pos = parseUsing(e.body, holder, bytes, pos, end-pos, out);
0N/A }
0N/A break; // already transmitted the scalar value
0N/A case EK_UN:
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A value = buf[0];
0N/A out.putInt(bandIndex, value);
0N/A Layout.Element ce = matchCase(e, value);
0N/A pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out);
0N/A
0N/A break; // already transmitted the scalar value
0N/A case EK_CALL:
0N/A // Adjust band offset if it is a backward call.
0N/A assert(e.body.length == 1);
0N/A assert(e.body[0].kind == EK_CBLE);
0N/A if (e.flagTest(EF_BACK))
0N/A out.noteBackCall(e.value);
0N/A pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out);
0N/A break; // no additional scalar value to transmit
0N/A case EK_REF:
0N/A pos = parseInt(e, bytes, pos, buf);
0N/A int localRef = buf[0];
0N/A Entry globalRef;
0N/A if (localRef == 0) {
0N/A globalRef = null; // N.B. global null reference is -1
0N/A } else {
0N/A globalRef = holder.getCPMap()[localRef];
0N/A if (e.refKind == CONSTANT_Signature
0N/A && globalRef.getTag() == CONSTANT_Utf8) {
0N/A // Cf. ClassReader.readSignatureRef.
0N/A String typeName = globalRef.stringValue();
0N/A globalRef = ConstantPool.getSignatureEntry(typeName);
0N/A } else if (e.refKind == CONSTANT_Literal) {
0N/A assert(globalRef.getTag() >= CONSTANT_Integer);
0N/A assert(globalRef.getTag() <= CONSTANT_String);
0N/A } else if (e.refKind != CONSTANT_All) {
0N/A assert(e.refKind == globalRef.getTag());
0N/A }
0N/A }
0N/A out.putRef(bandIndex, globalRef);
0N/A break;
0N/A default: assert(false); continue;
0N/A }
0N/A }
0N/A return pos;
0N/A }
0N/A
0N/A static
0N/A Layout.Element matchCase(Layout.Element e, int value) {
0N/A assert(e.kind == EK_UN);
0N/A int lastj = e.body.length-1;
0N/A for (int j = 0; j < lastj; j++) {
0N/A Layout.Element ce = e.body[j];
0N/A assert(ce.kind == EK_CASE);
0N/A if (value == ce.value)
0N/A return ce;
0N/A }
0N/A return e.body[lastj];
0N/A }
0N/A
0N/A static private
0N/A int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) {
0N/A int value = 0;
0N/A int loBits = e.len * 8;
0N/A // Read in big-endian order:
0N/A for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
0N/A value += (bytes[pos++] & 0xFF) << bitPos;
0N/A }
0N/A if (loBits < 32 && e.flagTest(EF_SIGN)) {
0N/A // sign-extend subword value
0N/A int hiBits = 32 - loBits;
0N/A value = (value << hiBits) >> hiBits;
0N/A }
0N/A buf[0] = value;
0N/A return pos;
0N/A }
0N/A
0N/A // Format attribute bytes, drawing values from bands.
0N/A // Used when emptying attribute bands into a package model.
0N/A // (At that point CP refs. are not yet assigned indexes.)
0N/A static
0N/A void unparseUsing(Layout.Element[] elems, Object[] fixups,
0N/A ValueStream in, ByteArrayOutputStream out) {
0N/A int prevBCI = 0;
0N/A int prevRBCI = 0;
0N/A for (int i = 0; i < elems.length; i++) {
0N/A Layout.Element e = elems[i];
0N/A int bandIndex = e.bandIndex;
0N/A int value;
0N/A int BCI, RBCI; // "RBCI" is R(BCI), BCI's coded representation
0N/A switch (e.kind) {
0N/A case EK_INT:
0N/A value = in.getInt(bandIndex);
0N/A unparseInt(e, value, out);
0N/A break;
0N/A case EK_BCI: // PH, POH
0N/A value = in.getInt(bandIndex);
0N/A if (!e.flagTest(EF_DELTA)) {
0N/A // PH: transmit R(bci), store bci
0N/A RBCI = value;
0N/A } else {
0N/A // POH: transmit D(R(bci)), store bci
0N/A RBCI = prevRBCI + value;
0N/A }
0N/A assert(prevBCI == in.decodeBCI(prevRBCI));
0N/A BCI = in.decodeBCI(RBCI);
0N/A unparseInt(e, BCI, out);
0N/A prevBCI = BCI;
0N/A prevRBCI = RBCI;
0N/A break;
0N/A case EK_BCO: // OH
0N/A value = in.getInt(bandIndex);
0N/A assert(e.flagTest(EF_DELTA));
0N/A // OH: transmit D(R(bci)), store D(bci)
0N/A assert(prevBCI == in.decodeBCI(prevRBCI));
0N/A RBCI = prevRBCI + value;
0N/A BCI = in.decodeBCI(RBCI);
0N/A unparseInt(e, BCI - prevBCI, out);
0N/A prevBCI = BCI;
0N/A prevRBCI = RBCI;
0N/A break;
0N/A case EK_FLAG:
0N/A value = in.getInt(bandIndex);
0N/A unparseInt(e, value, out);
0N/A break;
0N/A case EK_REPL:
0N/A value = in.getInt(bandIndex);
0N/A unparseInt(e, value, out);
0N/A for (int j = 0; j < value; j++) {
0N/A unparseUsing(e.body, fixups, in, out);
0N/A }
0N/A break;
0N/A case EK_UN:
0N/A value = in.getInt(bandIndex);
0N/A unparseInt(e, value, out);
0N/A Layout.Element ce = matchCase(e, value);
0N/A unparseUsing(ce.body, fixups, in, out);
0N/A break;
0N/A case EK_CALL:
0N/A assert(e.body.length == 1);
0N/A assert(e.body[0].kind == EK_CBLE);
0N/A unparseUsing(e.body[0].body, fixups, in, out);
0N/A break;
0N/A case EK_REF:
0N/A Entry globalRef = in.getRef(bandIndex);
0N/A int localRef;
0N/A if (globalRef != null) {
0N/A // It's a one-element array, really an lvalue.
0N/A fixups[0] = Fixups.add(fixups[0], null, out.size(),
0N/A Fixups.U2_FORMAT, globalRef);
0N/A localRef = 0; // placeholder for fixups
0N/A } else {
0N/A localRef = 0; // fixed null value
0N/A }
0N/A unparseInt(e, localRef, out);
0N/A break;
0N/A default: assert(false); continue;
0N/A }
0N/A }
0N/A }
0N/A
0N/A static private
0N/A void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) {
0N/A int loBits = e.len * 8;
0N/A if (loBits == 0) {
0N/A // It is not stored at all ('V' layout).
0N/A return;
0N/A }
0N/A if (loBits < 32) {
0N/A int hiBits = 32 - loBits;
0N/A int codedValue;
0N/A if (e.flagTest(EF_SIGN))
0N/A codedValue = (value << hiBits) >> hiBits;
0N/A else
0N/A codedValue = (value << hiBits) >>> hiBits;
0N/A if (codedValue != value)
0N/A throw new InternalError("cannot code in "+e.len+" bytes: "+value);
0N/A }
0N/A // Write in big-endian order:
0N/A for (int bitPos = loBits; (bitPos -= 8) >= 0; ) {
0N/A out.write((byte)(value >>> bitPos));
0N/A }
0N/A }
0N/A
0N/A/*
0N/A /// Testing.
0N/A public static void main(String av[]) {
0N/A int maxVal = 12;
0N/A int iters = 0;
0N/A boolean verbose;
0N/A int ap = 0;
0N/A while (ap < av.length) {
0N/A if (!av[ap].startsWith("-")) break;
0N/A if (av[ap].startsWith("-m"))
0N/A maxVal = Integer.parseInt(av[ap].substring(2));
0N/A else if (av[ap].startsWith("-i"))
0N/A iters = Integer.parseInt(av[ap].substring(2));
0N/A else
0N/A throw new RuntimeException("Bad option: "+av[ap]);
0N/A ap++;
0N/A }
0N/A verbose = (iters == 0);
0N/A if (iters <= 0) iters = 1;
0N/A if (ap == av.length) {
0N/A av = new String[] {
0N/A "HH", // ClassFile.version
0N/A "RUH", // SourceFile
0N/A "RCHRDNH", // EnclosingMethod
0N/A "KQH", // ConstantValue
0N/A "NH[RCH]", // Exceptions
0N/A "NH[PHH]", // LineNumberTable
0N/A "NH[PHOHRUHRSHH]", // LocalVariableTable
0N/A "NH[PHPOHIIH]", // CharacterRangeTable
0N/A "NH[PHHII]", // CoverageTable
0N/A "NH[RCHRCNHRUNHFH]", // InnerClasses
0N/A "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
0N/A "=AnnotationDefault",
0N/A // Like metadata, but with a compact tag set:
0N/A "[NH[(1)]]"
0N/A +"[NH[(2)]]"
0N/A +"[RSHNH[RUH(3)]]"
0N/A +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(2)](6)[NH[(3)]]()[]]",
0N/A ""
0N/A };
0N/A ap = 0;
0N/A }
0N/A final int[][] counts = new int[2][3]; // int bci ref
0N/A final Entry[] cpMap = new Entry[maxVal+1];
0N/A for (int i = 0; i < cpMap.length; i++) {
0N/A if (i == 0) continue; // 0 => null
0N/A cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i));
0N/A }
0N/A Class cls = new Package().new Class("");
0N/A cls.cpMap = cpMap;
0N/A class TestValueStream extends ValueStream {
0N/A Random rand = new Random(0);
0N/A ArrayList history = new ArrayList();
0N/A int ckidx = 0;
0N/A int maxVal;
0N/A boolean verbose;
0N/A void reset() { history.clear(); ckidx = 0; }
0N/A public int getInt(int bandIndex) {
0N/A counts[0][0]++;
0N/A int value = rand.nextInt(maxVal+1);
0N/A history.add(new Integer(bandIndex));
0N/A history.add(new Integer(value));
0N/A return value;
0N/A }
0N/A public void putInt(int bandIndex, int token) {
0N/A counts[1][0]++;
0N/A if (verbose)
0N/A System.out.print(" "+bandIndex+":"+token);
0N/A // Make sure this put parallels a previous get:
0N/A int check0 = ((Integer)history.get(ckidx+0)).intValue();
0N/A int check1 = ((Integer)history.get(ckidx+1)).intValue();
0N/A if (check0 != bandIndex || check1 != token) {
0N/A if (!verbose)
0N/A System.out.println(history.subList(0, ckidx));
0N/A System.out.println(" *** Should be "+check0+":"+check1);
0N/A throw new RuntimeException("Failed test!");
0N/A }
0N/A ckidx += 2;
0N/A }
0N/A public Entry getRef(int bandIndex) {
0N/A counts[0][2]++;
0N/A int value = getInt(bandIndex);
0N/A if (value < 0 || value > maxVal) {
0N/A System.out.println(" *** Unexpected ref code "+value);
0N/A return ConstantPool.getLiteralEntry(new Integer(value));
0N/A }
0N/A return cpMap[value];
0N/A }
0N/A public void putRef(int bandIndex, Entry ref) {
0N/A counts[1][2]++;
0N/A if (ref == null) {
0N/A putInt(bandIndex, 0);
0N/A return;
0N/A }
0N/A Number refValue = null;
0N/A if (ref instanceof ConstantPool.NumberEntry)
0N/A refValue = ((ConstantPool.NumberEntry)ref).numberValue();
0N/A int value;
0N/A if (!(refValue instanceof Integer)) {
0N/A System.out.println(" *** Unexpected ref "+ref);
0N/A value = -1;
0N/A } else {
0N/A value = ((Integer)refValue).intValue();
0N/A }
0N/A putInt(bandIndex, value);
0N/A }
0N/A public int encodeBCI(int bci) {
0N/A counts[1][1]++;
0N/A // move LSB to MSB of low byte
0N/A int code = (bci >> 8) << 8; // keep high bits
0N/A code += (bci & 0xFE) >> 1;
0N/A code += (bci & 0x01) << 7;
0N/A return code ^ (8<<8); // mark it clearly as coded
0N/A }
0N/A public int decodeBCI(int bciCode) {
0N/A counts[0][1]++;
0N/A bciCode ^= (8<<8); // remove extra mark
0N/A int bci = (bciCode >> 8) << 8; // keep high bits
0N/A bci += (bciCode & 0x7F) << 1;
0N/A bci += (bciCode & 0x80) >> 7;
0N/A return bci;
0N/A }
0N/A }
0N/A TestValueStream tts = new TestValueStream();
0N/A tts.maxVal = maxVal;
0N/A tts.verbose = verbose;
0N/A ByteArrayOutputStream buf = new ByteArrayOutputStream();
0N/A for (int i = 0; i < (1 << 30); i = (i + 1) * 5) {
0N/A int ei = tts.encodeBCI(i);
0N/A int di = tts.decodeBCI(ei);
0N/A if (di != i) System.out.println("i="+Integer.toHexString(i)+
0N/A " ei="+Integer.toHexString(ei)+
0N/A " di="+Integer.toHexString(di));
0N/A }
0N/A while (iters-- > 0) {
0N/A for (int i = ap; i < av.length; i++) {
0N/A String layout = av[i];
0N/A if (layout.startsWith("=")) {
0N/A String name = layout.substring(1);
0N/A for (Iterator j = standardDefs.values().iterator(); j.hasNext(); ) {
0N/A Attribute a = (Attribute) j.next();
0N/A if (a.name().equals(name)) {
0N/A layout = a.layout().layout();
0N/A break;
0N/A }
0N/A }
0N/A if (layout.startsWith("=")) {
0N/A System.out.println("Could not find "+name+" in "+standardDefs.values());
0N/A }
0N/A }
0N/A Layout self = new Layout(0, "Foo", layout);
0N/A if (verbose) {
0N/A System.out.print("/"+layout+"/ => ");
0N/A System.out.println(Arrays.asList(self.elems));
0N/A }
0N/A buf.reset();
0N/A tts.reset();
0N/A Object fixups = self.unparse(tts, buf);
0N/A byte[] bytes = buf.toByteArray();
0N/A // Attach the references to the byte array.
0N/A Fixups.setBytes(fixups, bytes);
0N/A // Patch the references to their frozen values.
0N/A Fixups.finishRefs(fixups, bytes, new Index("test", cpMap));
0N/A if (verbose) {
0N/A System.out.print(" bytes: {");
0N/A for (int j = 0; j < bytes.length; j++) {
0N/A System.out.print(" "+bytes[j]);
0N/A }
0N/A System.out.println("}");
0N/A }
0N/A if (verbose) {
0N/A System.out.print(" parse: {");
0N/A }
0N/A self.parse(0, cls, bytes, 0, bytes.length, tts);
0N/A if (verbose) {
0N/A System.out.println("}");
0N/A }
0N/A }
0N/A }
0N/A for (int j = 0; j <= 1; j++) {
0N/A System.out.print("values "+(j==0?"read":"written")+": {");
0N/A for (int k = 0; k < counts[j].length; k++) {
0N/A System.out.print(" "+counts[j][k]);
0N/A }
0N/A System.out.println(" }");
0N/A }
0N/A }
0N/A//*/
0N/A}