0001: /*
0002: * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.java.util.jar.pack;
0027:
0028: import java.io.*;
0029: import java.util.*;
0030: import com.sun.java.util.jar.pack.Package.Class;
0031: import com.sun.java.util.jar.pack.ConstantPool.*;
0032:
0033: /**
0034: * Represents an attribute in a class-file.
0035: * Takes care to remember where constant pool indexes occur.
0036: * Implements the "little language" of Pack200 for describing
0037: * attribute layouts.
0038: * @author John Rose
0039: * @version 1.27, 05/05/07
0040: */
0041: class Attribute implements Comparable, Constants {
0042: // Attribute instance fields.
0043:
0044: Layout def; // the name and format of this attr
0045: byte[] bytes; // the actual bytes
0046: Object fixups; // reference relocations, if any are required
0047:
0048: public String name() {
0049: return def.name();
0050: }
0051:
0052: public Layout layout() {
0053: return def;
0054: }
0055:
0056: public byte[] bytes() {
0057: return bytes;
0058: }
0059:
0060: public int size() {
0061: return bytes.length;
0062: }
0063:
0064: public Entry getNameRef() {
0065: return def.getNameRef();
0066: }
0067:
0068: private Attribute(Attribute old) {
0069: this .def = old.def;
0070: this .bytes = old.bytes;
0071: this .fixups = old.fixups;
0072: }
0073:
0074: public Attribute(Layout def, byte[] bytes, Object fixups) {
0075: this .def = def;
0076: this .bytes = bytes;
0077: this .fixups = fixups;
0078: Fixups.setBytes(fixups, bytes);
0079: }
0080:
0081: public Attribute(Layout def, byte[] bytes) {
0082: this (def, bytes, null);
0083: }
0084:
0085: public Attribute addContent(byte[] bytes, Object fixups) {
0086: assert (isCanonical());
0087: if (bytes.length == 0 && fixups == null)
0088: return this ;
0089: Attribute res = new Attribute(this );
0090: res.bytes = bytes;
0091: res.fixups = fixups;
0092: Fixups.setBytes(fixups, bytes);
0093: return res;
0094: }
0095:
0096: public Attribute addContent(byte[] bytes) {
0097: return addContent(bytes, null);
0098: }
0099:
0100: public void finishRefs(Index ix) {
0101: if (fixups != null) {
0102: Fixups.finishRefs(fixups, bytes, ix);
0103: fixups = null;
0104: }
0105: }
0106:
0107: public boolean isCanonical() {
0108: return this == def.canon;
0109: }
0110:
0111: public int compareTo(Object o) {
0112: Attribute that = (Attribute) o;
0113: return this .def.compareTo(that.def);
0114: }
0115:
0116: static private final byte[] noBytes = {};
0117: static private final HashMap canonLists = new HashMap();
0118: static private final HashMap attributes = new HashMap();
0119: static private final HashMap standardDefs = new HashMap();
0120:
0121: // Canonicalized lists of trivial attrs (Deprecated, etc.)
0122: // are used by trimToSize, in order to reduce footprint
0123: // of some common cases. (Note that Code attributes are
0124: // always zero size.)
0125: public static List getCanonList(List al) {
0126: synchronized (canonLists) {
0127: List cl = (List) canonLists.get(al);
0128: if (cl == null) {
0129: cl = new ArrayList(al.size());
0130: cl.addAll(al);
0131: cl = Collections.unmodifiableList(cl);
0132: canonLists.put(al, cl);
0133: }
0134: return cl;
0135: }
0136: }
0137:
0138: // Find the canonical empty attribute with the given ctype, name, layout.
0139: public static Attribute find(int ctype, String name, String layout) {
0140: Layout key = Layout.makeKey(ctype, name, layout);
0141: synchronized (attributes) {
0142: Attribute a = (Attribute) attributes.get(key);
0143: if (a == null) {
0144: a = new Layout(ctype, name, layout).canonicalInstance();
0145: attributes.put(key, a);
0146: }
0147: return a;
0148: }
0149: }
0150:
0151: public static Object keyForLookup(int ctype, String name) {
0152: return Layout.makeKey(ctype, name);
0153: }
0154:
0155: // Find canonical empty attribute with given ctype and name,
0156: // and with the standard layout.
0157: public static Attribute lookup(Map defs, int ctype, String name) {
0158: if (defs == null)
0159: defs = standardDefs;
0160: return (Attribute) defs.get(Layout.makeKey(ctype, name));
0161: }
0162:
0163: public static Attribute define(Map defs, int ctype, String name,
0164: String layout) {
0165: Attribute a = find(ctype, name, layout);
0166: defs.put(Layout.makeKey(ctype, name), a);
0167: return a;
0168: }
0169:
0170: static {
0171: Map sd = standardDefs;
0172: define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH");
0173: define(sd, ATTR_CONTEXT_CLASS, "Synthetic", "");
0174: define(sd, ATTR_CONTEXT_CLASS, "Deprecated", "");
0175: define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH");
0176: define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH");
0177: define(sd, ATTR_CONTEXT_CLASS, "InnerClasses",
0178: "NH[RCHRCNHRUNHFH]");
0179:
0180: define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH");
0181: define(sd, ATTR_CONTEXT_FIELD, "Synthetic", "");
0182: define(sd, ATTR_CONTEXT_FIELD, "Deprecated", "");
0183: define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH");
0184:
0185: define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH");
0186: define(sd, ATTR_CONTEXT_METHOD, "Synthetic", "");
0187: define(sd, ATTR_CONTEXT_METHOD, "Deprecated", "");
0188: define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]");
0189: //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]");
0190:
0191: define(sd, ATTR_CONTEXT_CODE, "StackMapTable", ("[NH[(1)]]"
0192: + "[TB" + "(64-127)[(2)]" + "(247)[(1)(2)]"
0193: + "(248-251)[(1)]" + "(252)[(1)(2)]"
0194: + "(253)[(1)(2)(2)]" + "(254)[(1)(2)(2)(2)]"
0195: + "(255)[(1)NH[(2)]NH[(2)]]" + "()[]" + "]" + "[H]"
0196: + "[TB(7)[RCH](8)[PH]()[]]"));
0197:
0198: define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]");
0199: define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable",
0200: "NH[PHOHRUHRSHH]");
0201: define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable",
0202: "NH[PHOHRUHRSHH]");
0203: //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]");
0204: //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]");
0205:
0206: // Note: Code and InnerClasses are special-cased elsewhere.
0207: // Their layout specs. are given here for completeness.
0208: // The Code spec is incomplete, in that it does not distinguish
0209: // bytecode bytes or locate CP references.
0210: }
0211:
0212: // Metadata.
0213: //
0214: // We define metadata using similar layouts
0215: // for all five kinds of metadata attributes.
0216: //
0217: // Regular annotations are a counted list of [RSHNH[RUH(1)]][...]
0218: // pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...]
0219: //
0220: // Parameter annotations are a counted list of regular annotations.
0221: // pack.method.attribute.RuntimeVisibleParameterAnnotations=[NH[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...]
0222: //
0223: // RuntimeInvisible annotations are defined similarly...
0224: // Non-method annotations are defined similarly...
0225: //
0226: // Annotation are a simple tagged value [TB...]
0227: // pack.attribute.method.AnnotationDefault=[TB...]
0228: //
0229: static {
0230: String mdLayouts[] = {
0231: Attribute
0232: .normalizeLayoutString(""
0233: + "\n # parameter_annotations :="
0234: + "\n [ NB[(1)] ] # forward call to annotations"),
0235: Attribute
0236: .normalizeLayoutString(""
0237: + "\n # annotations :="
0238: + "\n [ NH[(1)] ] # forward call to annotation"
0239: + "\n "
0240: + "\n # annotation :="
0241: + "\n [RSH"
0242: + "\n NH[RUH (1)] # forward call to value"
0243: + "\n ]"),
0244: Attribute
0245: .normalizeLayoutString(""
0246: + "\n # value :="
0247: + "\n [TB # Callable 2 encodes one tagged value."
0248: + "\n (\\B,\\C,\\I,\\S,\\Z)[KIH]"
0249: + "\n (\\D)[KDH]"
0250: + "\n (\\F)[KFH]"
0251: + "\n (\\J)[KJH]"
0252: + "\n (\\c)[RSH]"
0253: + "\n (\\e)[RSH RUH]"
0254: + "\n (\\s)[RUH]"
0255: + "\n (\\[)[NH[(0)]] # backward self-call to value"
0256: + "\n (\\@)[RSH NH[RUH (0)]] # backward self-call to value"
0257: + "\n ()[] ]") };
0258: Map sd = standardDefs;
0259: String defaultLayout = mdLayouts[2];
0260: String annotationsLayout = mdLayouts[1] + mdLayouts[2];
0261: String paramsLayout = mdLayouts[0] + annotationsLayout;
0262: for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) {
0263: if (ctype == ATTR_CONTEXT_CODE)
0264: continue;
0265: define(sd, ctype, "RuntimeVisibleAnnotations",
0266: annotationsLayout);
0267: define(sd, ctype, "RuntimeInvisibleAnnotations",
0268: annotationsLayout);
0269: if (ctype == ATTR_CONTEXT_METHOD) {
0270: define(sd, ctype, "RuntimeVisibleParameterAnnotations",
0271: paramsLayout);
0272: define(sd, ctype,
0273: "RuntimeInvisibleParameterAnnotations",
0274: paramsLayout);
0275: define(sd, ctype, "AnnotationDefault", defaultLayout);
0276: }
0277: }
0278: }
0279:
0280: public static String contextName(int ctype) {
0281: switch (ctype) {
0282: case ATTR_CONTEXT_CLASS:
0283: return "class";
0284: case ATTR_CONTEXT_FIELD:
0285: return "field";
0286: case ATTR_CONTEXT_METHOD:
0287: return "method";
0288: case ATTR_CONTEXT_CODE:
0289: return "code";
0290: }
0291: return null;
0292: }
0293:
0294: public static Map getStandardDefs() {
0295: return new HashMap(standardDefs);
0296: }
0297:
0298: /** Base class for any attributed object (Class, Field, Method, Code).
0299: * Flags are included because they are used to help transmit the
0300: * presence of attributes. That is, flags are a mix of modifier
0301: * bits and attribute indicators.
0302: */
0303: public static abstract class Holder {
0304:
0305: // We need this abstract method to interpret embedded CP refs.
0306: protected abstract Entry[] getCPMap();
0307:
0308: protected int flags; // defined here for convenience
0309: protected List attributes;
0310:
0311: public int attributeSize() {
0312: return (attributes == null) ? 0 : attributes.size();
0313: }
0314:
0315: public void trimToSize() {
0316: if (attributes == null) {
0317: return;
0318: }
0319: if (attributes.size() == 0) {
0320: attributes = null;
0321: return;
0322: }
0323: if (attributes instanceof ArrayList) {
0324: ArrayList al = (ArrayList) attributes;
0325: al.trimToSize();
0326: boolean allCanon = true;
0327: for (Iterator i = al.iterator(); i.hasNext();) {
0328: Attribute a = (Attribute) i.next();
0329: if (!a.isCanonical()) {
0330: allCanon = false;
0331: }
0332: if (a.fixups != null) {
0333: assert (!a.isCanonical());
0334: a.fixups = Fixups.trimToSize(a.fixups);
0335: }
0336: }
0337: if (allCanon) {
0338: // Replace private writable attribute list
0339: // with only trivial entries by public unique
0340: // immutable attribute list with the same entries.
0341: attributes = getCanonList(al);
0342: }
0343: }
0344: }
0345:
0346: public void addAttribute(Attribute a) {
0347: if (attributes == null)
0348: attributes = new ArrayList(3);
0349: else if (!(attributes instanceof ArrayList))
0350: attributes = new ArrayList(attributes); // unfreeze it
0351: attributes.add(a);
0352: }
0353:
0354: public Attribute removeAttribute(Attribute a) {
0355: if (attributes == null)
0356: return null;
0357: if (!attributes.contains(a))
0358: return null;
0359: if (!(attributes instanceof ArrayList))
0360: attributes = new ArrayList(attributes); // unfreeze it
0361: attributes.remove(a);
0362: return a;
0363: }
0364:
0365: public Attribute getAttribute(int n) {
0366: return (Attribute) attributes.get(n);
0367: }
0368:
0369: protected void visitRefs(int mode, Collection refs) {
0370: if (attributes == null)
0371: return;
0372: for (Iterator i = attributes.iterator(); i.hasNext();) {
0373: Attribute a = (Attribute) i.next();
0374: a.visitRefs(this , mode, refs);
0375: }
0376: }
0377:
0378: static final List noAttributes = Arrays.asList(new Object[0]);
0379:
0380: public List getAttributes() {
0381: if (attributes == null)
0382: return noAttributes;
0383: return attributes;
0384: }
0385:
0386: public void setAttributes(List attrList) {
0387: if (attrList.isEmpty())
0388: attributes = null;
0389: else
0390: attributes = attrList;
0391: }
0392:
0393: public Attribute getAttribute(String attrName) {
0394: if (attributes == null)
0395: return null;
0396: for (Iterator i = attributes.iterator(); i.hasNext();) {
0397: Attribute a = (Attribute) i.next();
0398: if (a.name().equals(attrName))
0399: return a;
0400: }
0401: return null;
0402: }
0403:
0404: public Attribute getAttribute(Layout attrDef) {
0405: if (attributes == null)
0406: return null;
0407: for (Iterator i = attributes.iterator(); i.hasNext();) {
0408: Attribute a = (Attribute) i.next();
0409: if (a.layout() == attrDef)
0410: return a;
0411: }
0412: return null;
0413: }
0414:
0415: public Attribute removeAttribute(String attrName) {
0416: return removeAttribute(getAttribute(attrName));
0417: }
0418:
0419: public Attribute removeAttribute(Layout attrDef) {
0420: return removeAttribute(getAttribute(attrDef));
0421: }
0422:
0423: public void strip(String attrName) {
0424: removeAttribute(getAttribute(attrName));
0425: }
0426: }
0427:
0428: // Lightweight interface to hide details of band structure.
0429: // Also used for testing.
0430: public static abstract class ValueStream {
0431: public int getInt(int bandIndex) {
0432: throw undef();
0433: }
0434:
0435: public void putInt(int bandIndex, int value) {
0436: throw undef();
0437: }
0438:
0439: public Entry getRef(int bandIndex) {
0440: throw undef();
0441: }
0442:
0443: public void putRef(int bandIndex, Entry ref) {
0444: throw undef();
0445: }
0446:
0447: // Note: decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref
0448: public int decodeBCI(int bciCode) {
0449: throw undef();
0450: }
0451:
0452: public int encodeBCI(int bci) {
0453: throw undef();
0454: }
0455:
0456: public void noteBackCall(int whichCallable) { /* ignore by default */
0457: }
0458:
0459: private RuntimeException undef() {
0460: return new UnsupportedOperationException(
0461: "ValueStream method");
0462: }
0463: }
0464:
0465: // Element kinds:
0466: static final byte EK_INT = 1; // B H I SH etc.
0467: static final byte EK_BCI = 2; // PH POH etc.
0468: static final byte EK_BCO = 3; // OH etc.
0469: static final byte EK_FLAG = 4; // FH etc.
0470: static final byte EK_REPL = 5; // NH[...] etc.
0471: static final byte EK_REF = 6; // RUH, RUNH, KQH, etc.
0472: static final byte EK_UN = 7; // TB(...)[...] etc.
0473: static final byte EK_CASE = 8; // (...)[...] etc.
0474: static final byte EK_CALL = 9; // (0), (1), etc.
0475: static final byte EK_CBLE = 10; // [...][...] etc.
0476: static final byte EF_SIGN = 1 << 0; // INT is signed
0477: static final byte EF_DELTA = 1 << 1; // BCI/BCI value is diff'ed w/ previous
0478: static final byte EF_NULL = 1 << 2; // null REF is expected/allowed
0479: static final byte EF_BACK = 1 << 3; // call, callable, case is backward
0480: static final int NO_BAND_INDEX = -1;
0481:
0482: /** A "class" of attributes, characterized by a context-type, name
0483: * and format. The formats are specified in a "little language".
0484: */
0485: public static class Layout implements Comparable {
0486: int ctype; // attribute context type, e.g., ATTR_CONTEXT_CODE
0487: String name; // name of attribute
0488: boolean hasRefs; // this kind of attr contains CP refs?
0489: String layout; // layout specification
0490: int bandCount; // total number of elems
0491: Element[] elems; // tokenization of layout
0492: Attribute canon; // canonical instance of this layout
0493:
0494: public int ctype() {
0495: return ctype;
0496: }
0497:
0498: public String name() {
0499: return name;
0500: }
0501:
0502: public String layout() {
0503: return layout;
0504: }
0505:
0506: public Attribute canonicalInstance() {
0507: return canon;
0508: }
0509:
0510: // Cache of name reference.
0511: private Entry nameRef; // name, for use by visitRefs
0512:
0513: public Entry getNameRef() {
0514: Entry nameRef = this .nameRef;
0515: if (nameRef == null) {
0516: this .nameRef = nameRef = ConstantPool
0517: .getUtf8Entry(name());
0518: }
0519: return nameRef;
0520: }
0521:
0522: public boolean isEmpty() {
0523: return layout == "";
0524: }
0525:
0526: public Layout(int ctype, String name, String layout) {
0527: this .ctype = ctype;
0528: this .name = name.intern();
0529: this .layout = layout.intern();
0530: assert (ctype < ATTR_CONTEXT_LIMIT);
0531: boolean hasCallables = layout.startsWith("[");
0532: try {
0533: if (!hasCallables) {
0534: this .elems = tokenizeLayout(this , -1, layout);
0535: } else {
0536: String[] bodies = splitBodies(layout);
0537: // Make the callables now, so they can be linked immediately.
0538: Element[] elems = new Element[bodies.length];
0539: this .elems = elems;
0540: for (int i = 0; i < elems.length; i++) {
0541: Element ce = this .new Element();
0542: ce.kind = EK_CBLE;
0543: ce.removeBand();
0544: ce.bandIndex = NO_BAND_INDEX;
0545: ce.layout = bodies[i];
0546: elems[i] = ce;
0547: }
0548: // Next fill them in.
0549: for (int i = 0; i < elems.length; i++) {
0550: Element ce = elems[i];
0551: ce.body = tokenizeLayout(this , i, bodies[i]);
0552: }
0553: //System.out.println(Arrays.asList(elems));
0554: }
0555: } catch (StringIndexOutOfBoundsException ee) {
0556: // simplest way to catch syntax errors...
0557: throw new RuntimeException("Bad attribute layout: "
0558: + layout, ee);
0559: }
0560: // Some uses do not make a fresh one for each occurrence.
0561: // For example, if layout == "", we only need one attr to share.
0562: canon = new Attribute(this , noBytes);
0563: }
0564:
0565: private Layout() {
0566: }
0567:
0568: static Layout makeKey(int ctype, String name, String layout) {
0569: Layout def = new Layout();
0570: def.ctype = ctype;
0571: def.name = name.intern();
0572: def.layout = layout.intern();
0573: assert (ctype < ATTR_CONTEXT_LIMIT);
0574: return def;
0575: }
0576:
0577: static Layout makeKey(int ctype, String name) {
0578: return makeKey(ctype, name, "");
0579: }
0580:
0581: public Attribute addContent(byte[] bytes, Object fixups) {
0582: return canon.addContent(bytes, fixups);
0583: }
0584:
0585: public Attribute addContent(byte[] bytes) {
0586: return canon.addContent(bytes, null);
0587: }
0588:
0589: public boolean equals(Object x) {
0590: return x instanceof Layout && equals((Layout) x);
0591: }
0592:
0593: public boolean equals(Layout that) {
0594: return this .name == that.name && this .layout == that.layout
0595: && this .ctype == that.ctype;
0596: }
0597:
0598: public int hashCode() {
0599: return (((17 + name.hashCode()) * 37 + layout.hashCode()) * 37 + ctype);
0600: }
0601:
0602: public int compareTo(Object o) {
0603: Layout that = (Layout) o;
0604: int r;
0605: r = this .name.compareTo(that.name);
0606: if (r != 0)
0607: return r;
0608: r = this .layout.compareTo(that.layout);
0609: if (r != 0)
0610: return r;
0611: return this .ctype - that.ctype;
0612: }
0613:
0614: public String toString() {
0615: String str = contextName(ctype) + "." + name + "[" + layout
0616: + "]";
0617: // If -ea, print out more informative strings!
0618: assert ((str = stringForDebug()) != null);
0619: return str;
0620: }
0621:
0622: private String stringForDebug() {
0623: return contextName(ctype) + "." + name
0624: + Arrays.asList(elems);
0625: }
0626:
0627: public class Element {
0628: String layout; // spelling in the little language
0629: byte flags; // EF_SIGN, etc.
0630: byte kind; // EK_UINT, etc.
0631: byte len; // scalar length of element
0632: byte refKind; // CONSTANT_String, etc.
0633: int bandIndex; // which band does this element govern?
0634: int value; // extra parameter
0635: Element[] body; // extra data (for replications, unions, calls)
0636:
0637: boolean flagTest(byte mask) {
0638: return (flags & mask) != 0;
0639: }
0640:
0641: Element() {
0642: bandIndex = bandCount++;
0643: }
0644:
0645: void removeBand() {
0646: --bandCount;
0647: assert (bandIndex == bandCount);
0648: bandIndex = NO_BAND_INDEX;
0649: }
0650:
0651: public boolean hasBand() {
0652: return bandIndex >= 0;
0653: }
0654:
0655: public String toString() {
0656: String str = layout;
0657: // If -ea, print out more informative strings!
0658: assert ((str = stringForDebug()) != null);
0659: return str;
0660: }
0661:
0662: private String stringForDebug() {
0663: Element[] body = this .body;
0664: switch (kind) {
0665: case EK_CALL:
0666: body = null;
0667: break;
0668: case EK_CASE:
0669: if (flagTest(EF_BACK))
0670: body = null;
0671: break;
0672: }
0673: return layout
0674: + (!hasBand() ? "" : "#" + bandIndex)
0675: + "<"
0676: + (flags == 0 ? "" : "" + flags)
0677: + kind
0678: + len
0679: + (refKind == 0 ? "" : "" + refKind)
0680: + ">"
0681: + (value == 0 ? "" : "(" + value + ")")
0682: + (body == null ? "" : "" + Arrays.asList(body));
0683: }
0684: }
0685:
0686: public boolean hasCallables() {
0687: return (elems.length > 0 && elems[0].kind == EK_CBLE);
0688: }
0689:
0690: static private final Element[] noElems = {};
0691:
0692: public Element[] getCallables() {
0693: if (hasCallables())
0694: return elems;
0695: else
0696: return noElems; // no callables at all
0697: }
0698:
0699: public Element[] getEntryPoint() {
0700: if (hasCallables())
0701: return elems[0].body; // body of first callable
0702: else
0703: return elems; // no callables; whole body
0704: }
0705:
0706: /** Return a sequence of tokens from the given attribute bytes.
0707: * Sequence elements will be 1-1 correspondent with my layout tokens.
0708: */
0709: public void parse(Holder holder, byte[] bytes, int pos,
0710: int len, ValueStream out) {
0711: int end = parseUsing(getEntryPoint(), holder, bytes, pos,
0712: len, out);
0713: if (end != pos + len)
0714: throw new InternalError("layout parsed " + (end - pos)
0715: + " out of " + len + " bytes");
0716: }
0717:
0718: /** Given a sequence of tokens, return the attribute bytes.
0719: * Sequence elements must be 1-1 correspondent with my layout tokens.
0720: * The returned object is a cookie for Fixups.finishRefs, which
0721: * must be used to harden any references into integer indexes.
0722: */
0723: public Object unparse(ValueStream in, ByteArrayOutputStream out) {
0724: Object[] fixups = { null };
0725: unparseUsing(getEntryPoint(), fixups, in, out);
0726: return fixups[0]; // return ref-bearing cookie, if any
0727: }
0728:
0729: public String layoutForPackageMajver(int majver) {
0730: if (majver <= JAVA5_PACKAGE_MAJOR_VERSION) {
0731: // Disallow layout syntax in the oldest protocol version.
0732: return expandCaseDashNotation(layout);
0733: }
0734: return layout;
0735: }
0736: }
0737:
0738: public static class FormatException extends IOException {
0739: private int ctype;
0740: private String name;
0741: String layout;
0742:
0743: public FormatException(String message, int ctype, String name,
0744: String layout) {
0745: super (ATTR_CONTEXT_NAME[ctype] + "." + name
0746: + (message == null ? "" : (": " + message)));
0747: this .ctype = ctype;
0748: this .name = name;
0749: this .layout = layout;
0750: }
0751:
0752: public FormatException(String message, int ctype, String name) {
0753: this (message, ctype, name, null);
0754: }
0755: }
0756:
0757: void visitRefs(Holder holder, int mode, final Collection refs) {
0758: if (mode == VRM_CLASSIC) {
0759: refs.add(getNameRef());
0760: }
0761: // else the name is owned by the layout, and is processed elsewhere
0762: if (bytes.length == 0)
0763: return; // quick exit
0764: if (!def.hasRefs)
0765: return; // quick exit
0766: if (fixups != null) {
0767: Fixups.visitRefs(fixups, refs);
0768: return;
0769: }
0770: // References (to a local cpMap) are embedded in the bytes.
0771: def.parse(holder, bytes, 0, bytes.length, new ValueStream() {
0772: public void putInt(int bandIndex, int value) {
0773: }
0774:
0775: public void putRef(int bandIndex, Entry ref) {
0776: refs.add(ref);
0777: }
0778:
0779: public int encodeBCI(int bci) {
0780: return bci;
0781: }
0782: });
0783: }
0784:
0785: public void parse(Holder holder, byte[] bytes, int pos, int len,
0786: ValueStream out) {
0787: def.parse(holder, bytes, pos, len, out);
0788: }
0789:
0790: public Object unparse(ValueStream in, ByteArrayOutputStream out) {
0791: return def.unparse(in, out);
0792: }
0793:
0794: public String toString() {
0795: return def + "{" + (bytes == null ? -1 : size()) + "}"
0796: + (fixups == null ? "" : fixups.toString());
0797: }
0798:
0799: /** Remove any informal "pretty printing" from the layout string.
0800: * Removes blanks and control chars.
0801: * Removes '#' comments (to end of line).
0802: * Replaces '\c' by the decimal code of the character c.
0803: * Replaces '0xNNN' by the decimal code of the hex number NNN.
0804: */
0805: static public String normalizeLayoutString(String layout) {
0806: StringBuffer buf = new StringBuffer();
0807: for (int i = 0, len = layout.length(); i < len;) {
0808: char ch = layout.charAt(i++);
0809: if (ch <= ' ') {
0810: // Skip whitespace and control chars
0811: continue;
0812: } else if (ch == '#') {
0813: // Skip to end of line.
0814: int end1 = layout.indexOf('\n', i);
0815: int end2 = layout.indexOf('\r', i);
0816: if (end1 < 0)
0817: end1 = len;
0818: if (end2 < 0)
0819: end2 = len;
0820: i = Math.min(end1, end2);
0821: } else if (ch == '\\') {
0822: // Map a character reference to its decimal code.
0823: buf.append((int) layout.charAt(i++));
0824: } else if (ch == '0' && layout.startsWith("0x", i - 1)) {
0825: // Map a hex numeral to its decimal code.
0826: int start = i - 1;
0827: int end = start + 2;
0828: while (end < len) {
0829: int dig = layout.charAt(end);
0830: if ((dig >= '0' && dig <= '9')
0831: || (dig >= 'a' && dig <= 'f'))
0832: ++end;
0833: else
0834: break;
0835: }
0836: if (end > start) {
0837: String num = layout.substring(start, end);
0838: buf.append(Integer.decode(num));
0839: i = end;
0840: } else {
0841: buf.append(ch);
0842: }
0843: } else {
0844: buf.append(ch);
0845: }
0846: }
0847: String result = buf.toString();
0848: if (false && !result.equals(layout)) {
0849: Utils.log.info("Normalizing layout string");
0850: Utils.log.info(" From: " + layout);
0851: Utils.log.info(" To: " + result);
0852: }
0853: return result;
0854: }
0855:
0856: /// Subroutines for parsing and unparsing:
0857:
0858: /** Parse the attribute layout language.
0859: <pre>
0860: attribute_layout:
0861: ( layout_element )* | ( callable )+
0862: layout_element:
0863: ( integral | replication | union | call | reference )
0864:
0865: callable:
0866: '[' body ']'
0867: body:
0868: ( layout_element )+
0869:
0870: integral:
0871: ( unsigned_int | signed_int | bc_index | bc_offset | flag )
0872: unsigned_int:
0873: uint_type
0874: signed_int:
0875: 'S' uint_type
0876: any_int:
0877: ( unsigned_int | signed_int )
0878: bc_index:
0879: ( 'P' uint_type | 'PO' uint_type )
0880: bc_offset:
0881: 'O' any_int
0882: flag:
0883: 'F' uint_type
0884: uint_type:
0885: ( 'B' | 'H' | 'I' | 'V' )
0886:
0887: replication:
0888: 'N' uint_type '[' body ']'
0889:
0890: union:
0891: 'T' any_int (union_case)* '(' ')' '[' (body)? ']'
0892: union_case:
0893: '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
0894: union_case_tag:
0895: ( numeral | numeral '-' numeral )
0896: call:
0897: '(' numeral ')'
0898:
0899: reference:
0900: reference_type ( 'N' )? uint_type
0901: reference_type:
0902: ( constant_ref | schema_ref | utf8_ref | untyped_ref )
0903: constant_ref:
0904: ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' )
0905: schema_ref:
0906: ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' )
0907: utf8_ref:
0908: 'RU'
0909: untyped_ref:
0910: 'RQ'
0911:
0912: numeral:
0913: '(' ('-')? (digit)+ ')'
0914: digit:
0915: ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
0916: </pre>
0917: */
0918: static//private
0919: Layout.Element[] tokenizeLayout(Layout self, int curCble,
0920: String layout) {
0921: ArrayList col = new ArrayList(layout.length());
0922: tokenizeLayout(self, curCble, layout, col);
0923: Layout.Element[] res = new Layout.Element[col.size()];
0924: col.toArray(res);
0925: return res;
0926: }
0927:
0928: static//private
0929: void tokenizeLayout(Layout self, int curCble, String layout,
0930: ArrayList col) {
0931: boolean prevBCI = false;
0932: for (int len = layout.length(), i = 0; i < len;) {
0933: int start = i;
0934: int body;
0935: Layout.Element e = self.new Element();
0936: byte kind;
0937: //System.out.println("at "+i+": ..."+layout.substring(i));
0938: // strip a prefix
0939: switch (layout.charAt(i++)) {
0940: /// layout_element: integral
0941: case 'B':
0942: case 'H':
0943: case 'I':
0944: case 'V': // unsigned_int
0945: kind = EK_INT;
0946: --i; // reparse
0947: i = tokenizeUInt(e, layout, i);
0948: break;
0949: case 'S': // signed_int
0950: kind = EK_INT;
0951: --i; // reparse
0952: i = tokenizeSInt(e, layout, i);
0953: break;
0954: case 'P': // bc_index
0955: kind = EK_BCI;
0956: if (layout.charAt(i++) == 'O') {
0957: // bc_index: 'PO' tokenizeUInt
0958: e.flags |= EF_DELTA;
0959: // must follow P or PO:
0960: if (!prevBCI) {
0961: i = -i;
0962: continue;
0963: } // fail
0964: i++; // move forward
0965: }
0966: --i; // reparse
0967: i = tokenizeUInt(e, layout, i);
0968: break;
0969: case 'O': // bc_offset
0970: kind = EK_BCO;
0971: e.flags |= EF_DELTA;
0972: // must follow P or PO:
0973: if (!prevBCI) {
0974: i = -i;
0975: continue;
0976: } // fail
0977: i = tokenizeSInt(e, layout, i);
0978: break;
0979: case 'F': // flag
0980: kind = EK_FLAG;
0981: i = tokenizeUInt(e, layout, i);
0982: break;
0983: case 'N': // replication: 'N' uint '[' elem ... ']'
0984: kind = EK_REPL;
0985: i = tokenizeUInt(e, layout, i);
0986: if (layout.charAt(i++) != '[') {
0987: i = -i;
0988: continue;
0989: } // fail
0990: i = skipBody(layout, body = i);
0991: e.body = tokenizeLayout(self, curCble, layout
0992: .substring(body, i++));
0993: break;
0994: case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
0995: kind = EK_UN;
0996: i = tokenizeSInt(e, layout, i);
0997: ArrayList cases = new ArrayList();
0998: for (;;) {
0999: // Keep parsing cases until we hit the default case.
1000: if (layout.charAt(i++) != '(') {
1001: i = -i;
1002: break;
1003: } // fail
1004: int beg = i;
1005: i = layout.indexOf(')', i);
1006: String cstr = layout.substring(beg, i++);
1007: int cstrlen = cstr.length();
1008: if (layout.charAt(i++) != '[') {
1009: i = -i;
1010: break;
1011: } // fail
1012: // Check for duplication.
1013: if (layout.charAt(i) == ']')
1014: body = i; // missing body, which is legal here
1015: else
1016: i = skipBody(layout, body = i);
1017: Layout.Element[] cbody = tokenizeLayout(self,
1018: curCble, layout.substring(body, i++));
1019: if (cstrlen == 0) {
1020: Layout.Element ce = self.new Element();
1021: ce.body = cbody;
1022: ce.kind = EK_CASE;
1023: ce.removeBand();
1024: cases.add(ce);
1025: break; // done with the whole union
1026: } else {
1027: // Parse a case string.
1028: boolean firstCaseNum = true;
1029: for (int cp = 0, endp;; cp = endp + 1) {
1030: // Look for multiple case tags:
1031: endp = cstr.indexOf(',', cp);
1032: if (endp < 0)
1033: endp = cstrlen;
1034: String cstr1 = cstr.substring(cp, endp);
1035: if (cstr1.length() == 0)
1036: cstr1 = "empty"; // will fail parse
1037: int value0, value1;
1038: // Check for a case range (new in 1.6).
1039: int dash = findCaseDash(cstr1, 0);
1040: if (dash >= 0) {
1041: value0 = parseIntBefore(cstr1, dash);
1042: value1 = parseIntAfter(cstr1, dash);
1043: if (value0 >= value1) {
1044: i = -i;
1045: break;
1046: } // fail
1047: } else {
1048: value0 = value1 = Integer
1049: .parseInt(cstr1);
1050: }
1051: // Add a case for each value in value0..value1
1052: for (;; value0++) {
1053: Layout.Element ce = self.new Element();
1054: ce.body = cbody; // all cases share one body
1055: ce.kind = EK_CASE;
1056: ce.removeBand();
1057: if (!firstCaseNum)
1058: // "backward case" repeats a body
1059: ce.flags |= EF_BACK;
1060: firstCaseNum = false;
1061: ce.value = value0;
1062: cases.add(ce);
1063: if (value0 == value1)
1064: break;
1065: }
1066: if (endp == cstrlen) {
1067: break; // done with this case
1068: }
1069: }
1070: }
1071: }
1072: e.body = new Layout.Element[cases.size()];
1073: cases.toArray(e.body);
1074: e.kind = kind;
1075: for (int j = 0; j < e.body.length - 1; j++) {
1076: Layout.Element ce = e.body[j];
1077: if (matchCase(e, ce.value) != ce) {
1078: // Duplicate tag.
1079: {
1080: i = -i;
1081: break;
1082: } // fail
1083: }
1084: }
1085: break;
1086: case '(': // call: '(' '-'? digit+ ')'
1087: kind = EK_CALL;
1088: e.removeBand();
1089: i = layout.indexOf(')', i);
1090: String cstr = layout.substring(start + 1, i++);
1091: int offset = Integer.parseInt(cstr);
1092: int target = curCble + offset;
1093: if (!(offset + "").equals(cstr) || self.elems == null
1094: || target < 0 || target >= self.elems.length) {
1095: i = -i;
1096: continue;
1097: } // fail
1098: Layout.Element ce = self.elems[target];
1099: assert (ce.kind == EK_CBLE);
1100: e.value = target;
1101: e.body = new Layout.Element[] { ce };
1102: // Is it a (recursive) backward call?
1103: if (offset <= 0) {
1104: // Yes. Mark both caller and callee backward.
1105: e.flags |= EF_BACK;
1106: ce.flags |= EF_BACK;
1107: }
1108: break;
1109: case 'K': // reference_type: constant_ref
1110: kind = EK_REF;
1111: switch (layout.charAt(i++)) {
1112: case 'I':
1113: e.refKind = CONSTANT_Integer;
1114: break;
1115: case 'J':
1116: e.refKind = CONSTANT_Long;
1117: break;
1118: case 'F':
1119: e.refKind = CONSTANT_Float;
1120: break;
1121: case 'D':
1122: e.refKind = CONSTANT_Double;
1123: break;
1124: case 'S':
1125: e.refKind = CONSTANT_String;
1126: break;
1127: case 'Q':
1128: e.refKind = CONSTANT_Literal;
1129: break;
1130: default: {
1131: i = -i;
1132: continue;
1133: } // fail
1134: }
1135: break;
1136: case 'R': // schema_ref
1137: kind = EK_REF;
1138: switch (layout.charAt(i++)) {
1139: case 'C':
1140: e.refKind = CONSTANT_Class;
1141: break;
1142: case 'S':
1143: e.refKind = CONSTANT_Signature;
1144: break;
1145: case 'D':
1146: e.refKind = CONSTANT_NameandType;
1147: break;
1148: case 'F':
1149: e.refKind = CONSTANT_Fieldref;
1150: break;
1151: case 'M':
1152: e.refKind = CONSTANT_Methodref;
1153: break;
1154: case 'I':
1155: e.refKind = CONSTANT_InterfaceMethodref;
1156: break;
1157:
1158: case 'U':
1159: e.refKind = CONSTANT_Utf8;
1160: break; //utf8_ref
1161: case 'Q':
1162: e.refKind = CONSTANT_All;
1163: break; //untyped_ref
1164:
1165: default: {
1166: i = -i;
1167: continue;
1168: } // fail
1169: }
1170: break;
1171: default: {
1172: i = -i;
1173: continue;
1174: } // fail
1175: }
1176:
1177: // further parsing of refs
1178: if (kind == EK_REF) {
1179: // reference: reference_type -><- ( 'N' )? tokenizeUInt
1180: if (layout.charAt(i++) == 'N') {
1181: e.flags |= EF_NULL;
1182: i++; // move forward
1183: }
1184: --i; // reparse
1185: i = tokenizeUInt(e, layout, i);
1186: self.hasRefs = true;
1187: }
1188:
1189: prevBCI = (kind == EK_BCI);
1190:
1191: // store the new element
1192: e.kind = kind;
1193: e.layout = layout.substring(start, i);
1194: col.add(e);
1195: }
1196: }
1197:
1198: static//private
1199: String[] splitBodies(String layout) {
1200: ArrayList bodies = new ArrayList();
1201: // Parse several independent layout bodies: "[foo][bar]...[baz]"
1202: for (int i = 0; i < layout.length(); i++) {
1203: if (layout.charAt(i++) != '[')
1204: layout.charAt(-i); // throw error
1205: int body;
1206: i = skipBody(layout, body = i);
1207: bodies.add(layout.substring(body, i));
1208: }
1209: String[] res = new String[bodies.size()];
1210: bodies.toArray(res);
1211: return res;
1212: }
1213:
1214: static private int skipBody(String layout, int i) {
1215: assert (layout.charAt(i - 1) == '[');
1216: if (layout.charAt(i) == ']')
1217: // No empty bodies, please.
1218: return -i;
1219: // skip balanced [...[...]...]
1220: for (int depth = 1; depth > 0;) {
1221: switch (layout.charAt(i++)) {
1222: case '[':
1223: depth++;
1224: break;
1225: case ']':
1226: depth--;
1227: break;
1228: }
1229: }
1230: --i; // get before bracket
1231: assert (layout.charAt(i) == ']');
1232: return i; // return closing bracket
1233: }
1234:
1235: static private int tokenizeUInt(Layout.Element e, String layout,
1236: int i) {
1237: switch (layout.charAt(i++)) {
1238: case 'V':
1239: e.len = 0;
1240: break;
1241: case 'B':
1242: e.len = 1;
1243: break;
1244: case 'H':
1245: e.len = 2;
1246: break;
1247: case 'I':
1248: e.len = 4;
1249: break;
1250: default:
1251: return -i;
1252: }
1253: return i;
1254: }
1255:
1256: static private int tokenizeSInt(Layout.Element e, String layout,
1257: int i) {
1258: if (layout.charAt(i) == 'S') {
1259: e.flags |= EF_SIGN;
1260: ++i;
1261: }
1262: return tokenizeUInt(e, layout, i);
1263: }
1264:
1265: static private boolean isDigit(char c) {
1266: return c >= '0' && c <= '9';
1267: }
1268:
1269: /** Find an occurrence of hyphen '-' between two numerals. */
1270: static//private
1271: int findCaseDash(String layout, int fromIndex) {
1272: if (fromIndex <= 0)
1273: fromIndex = 1; // minimum dash pos
1274: int lastDash = layout.length() - 2; // maximum dash pos
1275: for (;;) {
1276: int dash = layout.indexOf('-', fromIndex);
1277: if (dash < 0 || dash > lastDash)
1278: return -1;
1279: if (isDigit(layout.charAt(dash - 1))) {
1280: char afterDash = layout.charAt(dash + 1);
1281: if (afterDash == '-' && dash + 2 < layout.length())
1282: afterDash = layout.charAt(dash + 2);
1283: if (isDigit(afterDash)) {
1284: // matched /[0-9]--?[0-9]/; return position of dash
1285: return dash;
1286: }
1287: }
1288: fromIndex = dash + 1;
1289: }
1290: }
1291:
1292: static int parseIntBefore(String layout, int dash) {
1293: int end = dash;
1294: int beg = end;
1295: while (beg > 0 && isDigit(layout.charAt(beg - 1)))
1296: --beg;
1297: if (beg == end)
1298: return Integer.parseInt("empty");
1299: // skip backward over a sign
1300: if (beg >= 1 && layout.charAt(beg - 1) == '-')
1301: --beg;
1302: assert (beg == 0 || !isDigit(layout.charAt(beg - 1)));
1303: return Integer.parseInt(layout.substring(beg, end));
1304: }
1305:
1306: static int parseIntAfter(String layout, int dash) {
1307: int beg = dash + 1;
1308: int end = beg;
1309: int limit = layout.length();
1310: if (end < limit && layout.charAt(end) == '-')
1311: ++end;
1312: while (end < limit && isDigit(layout.charAt(end)))
1313: ++end;
1314: if (beg == end)
1315: return Integer.parseInt("empty");
1316: return Integer.parseInt(layout.substring(beg, end));
1317: }
1318:
1319: /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */
1320: static String expandCaseDashNotation(String layout) {
1321: int dash = findCaseDash(layout, 0);
1322: if (dash < 0)
1323: return layout; // no dashes (the common case)
1324: StringBuffer result = new StringBuffer(layout.length() * 3);
1325: int sofar = 0; // how far have we processed the layout?
1326: for (;;) {
1327: // for each dash, collect everything up to the dash
1328: result.append(layout.substring(sofar, dash));
1329: sofar = dash + 1; // skip the dash
1330: // then collect intermediate values
1331: int value0 = parseIntBefore(layout, dash);
1332: int value1 = parseIntAfter(layout, dash);
1333: assert (value0 < value1);
1334: result.append(","); // close off value0 numeral
1335: for (int i = value0 + 1; i < value1; i++) {
1336: result.append(i);
1337: result.append(","); // close off i numeral
1338: }
1339: dash = findCaseDash(layout, sofar);
1340: if (dash < 0)
1341: break;
1342: }
1343: result.append(layout.substring(sofar)); // collect the rest
1344: return result.toString();
1345: }
1346:
1347: static {
1348: assert (expandCaseDashNotation("1-5").equals("1,2,3,4,5"));
1349: assert (expandCaseDashNotation("-2--1").equals("-2,-1"));
1350: assert (expandCaseDashNotation("-2-1").equals("-2,-1,0,1"));
1351: assert (expandCaseDashNotation("-1-0").equals("-1,0"));
1352: }
1353:
1354: // Parse attribute bytes, putting values into bands. Returns new pos.
1355: // Used when reading a class file (local refs resolved with local cpMap).
1356: // Also used for ad hoc scanning.
1357: static int parseUsing(Layout.Element[] elems, Holder holder,
1358: byte[] bytes, int pos, int len, ValueStream out) {
1359: int prevBCI = 0;
1360: int prevRBCI = 0;
1361: int end = pos + len;
1362: int[] buf = { 0 }; // for calls to parseInt, holds 2nd result
1363: for (int i = 0; i < elems.length; i++) {
1364: Layout.Element e = elems[i];
1365: int bandIndex = e.bandIndex;
1366: int value;
1367: int BCI, RBCI;
1368: switch (e.kind) {
1369: case EK_INT:
1370: pos = parseInt(e, bytes, pos, buf);
1371: value = buf[0];
1372: out.putInt(bandIndex, value);
1373: break;
1374: case EK_BCI: // PH, POH
1375: pos = parseInt(e, bytes, pos, buf);
1376: BCI = buf[0];
1377: RBCI = out.encodeBCI(BCI);
1378: if (!e.flagTest(EF_DELTA)) {
1379: // PH: transmit R(bci), store bci
1380: value = RBCI;
1381: } else {
1382: // POH: transmit D(R(bci)), store bci
1383: value = RBCI - prevRBCI;
1384: }
1385: prevBCI = BCI;
1386: prevRBCI = RBCI;
1387: out.putInt(bandIndex, value);
1388: break;
1389: case EK_BCO: // OH
1390: assert (e.flagTest(EF_DELTA));
1391: // OH: transmit D(R(bci)), store D(bci)
1392: pos = parseInt(e, bytes, pos, buf);
1393: BCI = prevBCI + buf[0];
1394: RBCI = out.encodeBCI(BCI);
1395: value = RBCI - prevRBCI;
1396: prevBCI = BCI;
1397: prevRBCI = RBCI;
1398: out.putInt(bandIndex, value);
1399: break;
1400: case EK_FLAG:
1401: pos = parseInt(e, bytes, pos, buf);
1402: value = buf[0];
1403: out.putInt(bandIndex, value);
1404: break;
1405: case EK_REPL:
1406: pos = parseInt(e, bytes, pos, buf);
1407: value = buf[0];
1408: out.putInt(bandIndex, value);
1409: for (int j = 0; j < value; j++) {
1410: pos = parseUsing(e.body, holder, bytes, pos, end
1411: - pos, out);
1412: }
1413: break; // already transmitted the scalar value
1414: case EK_UN:
1415: pos = parseInt(e, bytes, pos, buf);
1416: value = buf[0];
1417: out.putInt(bandIndex, value);
1418: Layout.Element ce = matchCase(e, value);
1419: pos = parseUsing(ce.body, holder, bytes, pos,
1420: end - pos, out);
1421:
1422: break; // already transmitted the scalar value
1423: case EK_CALL:
1424: // Adjust band offset if it is a backward call.
1425: assert (e.body.length == 1);
1426: assert (e.body[0].kind == EK_CBLE);
1427: if (e.flagTest(EF_BACK))
1428: out.noteBackCall(e.value);
1429: pos = parseUsing(e.body[0].body, holder, bytes, pos,
1430: end - pos, out);
1431: break; // no additional scalar value to transmit
1432: case EK_REF:
1433: pos = parseInt(e, bytes, pos, buf);
1434: int localRef = buf[0];
1435: Entry globalRef;
1436: if (localRef == 0) {
1437: globalRef = null; // N.B. global null reference is -1
1438: } else {
1439: globalRef = holder.getCPMap()[localRef];
1440: if (e.refKind == CONSTANT_Signature
1441: && globalRef.getTag() == CONSTANT_Utf8) {
1442: // Cf. ClassReader.readSignatureRef.
1443: String typeName = globalRef.stringValue();
1444: globalRef = ConstantPool
1445: .getSignatureEntry(typeName);
1446: } else if (e.refKind == CONSTANT_Literal) {
1447: assert (globalRef.getTag() >= CONSTANT_Integer);
1448: assert (globalRef.getTag() <= CONSTANT_String);
1449: } else if (e.refKind != CONSTANT_All) {
1450: assert (e.refKind == globalRef.getTag());
1451: }
1452: }
1453: out.putRef(bandIndex, globalRef);
1454: break;
1455: default:
1456: assert (false);
1457: continue;
1458: }
1459: }
1460: return pos;
1461: }
1462:
1463: static Layout.Element matchCase(Layout.Element e, int value) {
1464: assert (e.kind == EK_UN);
1465: int lastj = e.body.length - 1;
1466: for (int j = 0; j < lastj; j++) {
1467: Layout.Element ce = e.body[j];
1468: assert (ce.kind == EK_CASE);
1469: if (value == ce.value)
1470: return ce;
1471: }
1472: return e.body[lastj];
1473: }
1474:
1475: static private int parseInt(Layout.Element e, byte[] bytes,
1476: int pos, int[] buf) {
1477: int value = 0;
1478: int loBits = e.len * 8;
1479: // Read in big-endian order:
1480: for (int bitPos = loBits; (bitPos -= 8) >= 0;) {
1481: value += (bytes[pos++] & 0xFF) << bitPos;
1482: }
1483: if (loBits < 32 && e.flagTest(EF_SIGN)) {
1484: // sign-extend subword value
1485: int hiBits = 32 - loBits;
1486: value = (value << hiBits) >> hiBits;
1487: }
1488: buf[0] = value;
1489: return pos;
1490: }
1491:
1492: // Format attribute bytes, drawing values from bands.
1493: // Used when emptying attribute bands into a package model.
1494: // (At that point CP refs. are not yet assigned indexes.)
1495: static void unparseUsing(Layout.Element[] elems, Object[] fixups,
1496: ValueStream in, ByteArrayOutputStream out) {
1497: int prevBCI = 0;
1498: int prevRBCI = 0;
1499: for (int i = 0; i < elems.length; i++) {
1500: Layout.Element e = elems[i];
1501: int bandIndex = e.bandIndex;
1502: int value;
1503: int BCI, RBCI; // "RBCI" is R(BCI), BCI's coded representation
1504: switch (e.kind) {
1505: case EK_INT:
1506: value = in.getInt(bandIndex);
1507: unparseInt(e, value, out);
1508: break;
1509: case EK_BCI: // PH, POH
1510: value = in.getInt(bandIndex);
1511: if (!e.flagTest(EF_DELTA)) {
1512: // PH: transmit R(bci), store bci
1513: RBCI = value;
1514: } else {
1515: // POH: transmit D(R(bci)), store bci
1516: RBCI = prevRBCI + value;
1517: }
1518: assert (prevBCI == in.decodeBCI(prevRBCI));
1519: BCI = in.decodeBCI(RBCI);
1520: unparseInt(e, BCI, out);
1521: prevBCI = BCI;
1522: prevRBCI = RBCI;
1523: break;
1524: case EK_BCO: // OH
1525: value = in.getInt(bandIndex);
1526: assert (e.flagTest(EF_DELTA));
1527: // OH: transmit D(R(bci)), store D(bci)
1528: assert (prevBCI == in.decodeBCI(prevRBCI));
1529: RBCI = prevRBCI + value;
1530: BCI = in.decodeBCI(RBCI);
1531: unparseInt(e, BCI - prevBCI, out);
1532: prevBCI = BCI;
1533: prevRBCI = RBCI;
1534: break;
1535: case EK_FLAG:
1536: value = in.getInt(bandIndex);
1537: unparseInt(e, value, out);
1538: break;
1539: case EK_REPL:
1540: value = in.getInt(bandIndex);
1541: unparseInt(e, value, out);
1542: for (int j = 0; j < value; j++) {
1543: unparseUsing(e.body, fixups, in, out);
1544: }
1545: break;
1546: case EK_UN:
1547: value = in.getInt(bandIndex);
1548: unparseInt(e, value, out);
1549: Layout.Element ce = matchCase(e, value);
1550: unparseUsing(ce.body, fixups, in, out);
1551: break;
1552: case EK_CALL:
1553: assert (e.body.length == 1);
1554: assert (e.body[0].kind == EK_CBLE);
1555: unparseUsing(e.body[0].body, fixups, in, out);
1556: break;
1557: case EK_REF:
1558: Entry globalRef = in.getRef(bandIndex);
1559: int localRef;
1560: if (globalRef != null) {
1561: // It's a one-element array, really an lvalue.
1562: fixups[0] = Fixups.add(fixups[0], null, out.size(),
1563: Fixups.U2_FORMAT, globalRef);
1564: localRef = 0; // placeholder for fixups
1565: } else {
1566: localRef = 0; // fixed null value
1567: }
1568: unparseInt(e, localRef, out);
1569: break;
1570: default:
1571: assert (false);
1572: continue;
1573: }
1574: }
1575: }
1576:
1577: static private void unparseInt(Layout.Element e, int value,
1578: ByteArrayOutputStream out) {
1579: int loBits = e.len * 8;
1580: if (loBits == 0) {
1581: // It is not stored at all ('V' layout).
1582: return;
1583: }
1584: if (loBits < 32) {
1585: int hiBits = 32 - loBits;
1586: int codedValue;
1587: if (e.flagTest(EF_SIGN))
1588: codedValue = (value << hiBits) >> hiBits;
1589: else
1590: codedValue = (value << hiBits) >>> hiBits;
1591: if (codedValue != value)
1592: throw new InternalError("cannot code in " + e.len
1593: + " bytes: " + value);
1594: }
1595: // Write in big-endian order:
1596: for (int bitPos = loBits; (bitPos -= 8) >= 0;) {
1597: out.write((byte) (value >>> bitPos));
1598: }
1599: }
1600:
1601: /*
1602: /// Testing.
1603: public static void main(String av[]) {
1604: int maxVal = 12;
1605: int iters = 0;
1606: boolean verbose;
1607: int ap = 0;
1608: while (ap < av.length) {
1609: if (!av[ap].startsWith("-")) break;
1610: if (av[ap].startsWith("-m"))
1611: maxVal = Integer.parseInt(av[ap].substring(2));
1612: else if (av[ap].startsWith("-i"))
1613: iters = Integer.parseInt(av[ap].substring(2));
1614: else
1615: throw new RuntimeException("Bad option: "+av[ap]);
1616: ap++;
1617: }
1618: verbose = (iters == 0);
1619: if (iters <= 0) iters = 1;
1620: if (ap == av.length) {
1621: av = new String[] {
1622: "HH", // ClassFile.version
1623: "RUH", // SourceFile
1624: "RCHRDNH", // EnclosingMethod
1625: "KQH", // ConstantValue
1626: "NH[RCH]", // Exceptions
1627: "NH[PHH]", // LineNumberTable
1628: "NH[PHOHRUHRSHH]", // LocalVariableTable
1629: "NH[PHPOHIIH]", // CharacterRangeTable
1630: "NH[PHHII]", // CoverageTable
1631: "NH[RCHRCNHRUNHFH]", // InnerClasses
1632: "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
1633: "=AnnotationDefault",
1634: // Like metadata, but with a compact tag set:
1635: "[NH[(1)]]"
1636: +"[NH[(2)]]"
1637: +"[RSHNH[RUH(3)]]"
1638: +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(2)](6)[NH[(3)]]()[]]",
1639: ""
1640: };
1641: ap = 0;
1642: }
1643: final int[][] counts = new int[2][3]; // int bci ref
1644: final Entry[] cpMap = new Entry[maxVal+1];
1645: for (int i = 0; i < cpMap.length; i++) {
1646: if (i == 0) continue; // 0 => null
1647: cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i));
1648: }
1649: Class cls = new Package().new Class("");
1650: cls.cpMap = cpMap;
1651: class TestValueStream extends ValueStream {
1652: Random rand = new Random(0);
1653: ArrayList history = new ArrayList();
1654: int ckidx = 0;
1655: int maxVal;
1656: boolean verbose;
1657: void reset() { history.clear(); ckidx = 0; }
1658: public int getInt(int bandIndex) {
1659: counts[0][0]++;
1660: int value = rand.nextInt(maxVal+1);
1661: history.add(new Integer(bandIndex));
1662: history.add(new Integer(value));
1663: return value;
1664: }
1665: public void putInt(int bandIndex, int token) {
1666: counts[1][0]++;
1667: if (verbose)
1668: System.out.print(" "+bandIndex+":"+token);
1669: // Make sure this put parallels a previous get:
1670: int check0 = ((Integer)history.get(ckidx+0)).intValue();
1671: int check1 = ((Integer)history.get(ckidx+1)).intValue();
1672: if (check0 != bandIndex || check1 != token) {
1673: if (!verbose)
1674: System.out.println(history.subList(0, ckidx));
1675: System.out.println(" *** Should be "+check0+":"+check1);
1676: throw new RuntimeException("Failed test!");
1677: }
1678: ckidx += 2;
1679: }
1680: public Entry getRef(int bandIndex) {
1681: counts[0][2]++;
1682: int value = getInt(bandIndex);
1683: if (value < 0 || value > maxVal) {
1684: System.out.println(" *** Unexpected ref code "+value);
1685: return ConstantPool.getLiteralEntry(new Integer(value));
1686: }
1687: return cpMap[value];
1688: }
1689: public void putRef(int bandIndex, Entry ref) {
1690: counts[1][2]++;
1691: if (ref == null) {
1692: putInt(bandIndex, 0);
1693: return;
1694: }
1695: Number refValue = null;
1696: if (ref instanceof ConstantPool.NumberEntry)
1697: refValue = ((ConstantPool.NumberEntry)ref).numberValue();
1698: int value;
1699: if (!(refValue instanceof Integer)) {
1700: System.out.println(" *** Unexpected ref "+ref);
1701: value = -1;
1702: } else {
1703: value = ((Integer)refValue).intValue();
1704: }
1705: putInt(bandIndex, value);
1706: }
1707: public int encodeBCI(int bci) {
1708: counts[1][1]++;
1709: // move LSB to MSB of low byte
1710: int code = (bci >> 8) << 8; // keep high bits
1711: code += (bci & 0xFE) >> 1;
1712: code += (bci & 0x01) << 7;
1713: return code ^ (8<<8); // mark it clearly as coded
1714: }
1715: public int decodeBCI(int bciCode) {
1716: counts[0][1]++;
1717: bciCode ^= (8<<8); // remove extra mark
1718: int bci = (bciCode >> 8) << 8; // keep high bits
1719: bci += (bciCode & 0x7F) << 1;
1720: bci += (bciCode & 0x80) >> 7;
1721: return bci;
1722: }
1723: }
1724: TestValueStream tts = new TestValueStream();
1725: tts.maxVal = maxVal;
1726: tts.verbose = verbose;
1727: ByteArrayOutputStream buf = new ByteArrayOutputStream();
1728: for (int i = 0; i < (1 << 30); i = (i + 1) * 5) {
1729: int ei = tts.encodeBCI(i);
1730: int di = tts.decodeBCI(ei);
1731: if (di != i) System.out.println("i="+Integer.toHexString(i)+
1732: " ei="+Integer.toHexString(ei)+
1733: " di="+Integer.toHexString(di));
1734: }
1735: while (iters-- > 0) {
1736: for (int i = ap; i < av.length; i++) {
1737: String layout = av[i];
1738: if (layout.startsWith("=")) {
1739: String name = layout.substring(1);
1740: for (Iterator j = standardDefs.values().iterator(); j.hasNext(); ) {
1741: Attribute a = (Attribute) j.next();
1742: if (a.name().equals(name)) {
1743: layout = a.layout().layout();
1744: break;
1745: }
1746: }
1747: if (layout.startsWith("=")) {
1748: System.out.println("Could not find "+name+" in "+standardDefs.values());
1749: }
1750: }
1751: Layout self = new Layout(0, "Foo", layout);
1752: if (verbose) {
1753: System.out.print("/"+layout+"/ => ");
1754: System.out.println(Arrays.asList(self.elems));
1755: }
1756: buf.reset();
1757: tts.reset();
1758: Object fixups = self.unparse(tts, buf);
1759: byte[] bytes = buf.toByteArray();
1760: // Attach the references to the byte array.
1761: Fixups.setBytes(fixups, bytes);
1762: // Patch the references to their frozen values.
1763: Fixups.finishRefs(fixups, bytes, new Index("test", cpMap));
1764: if (verbose) {
1765: System.out.print(" bytes: {");
1766: for (int j = 0; j < bytes.length; j++) {
1767: System.out.print(" "+bytes[j]);
1768: }
1769: System.out.println("}");
1770: }
1771: if (verbose) {
1772: System.out.print(" parse: {");
1773: }
1774: self.parse(0, cls, bytes, 0, bytes.length, tts);
1775: if (verbose) {
1776: System.out.println("}");
1777: }
1778: }
1779: }
1780: for (int j = 0; j <= 1; j++) {
1781: System.out.print("values "+(j==0?"read":"written")+": {");
1782: for (int k = 0; k < counts[j].length; k++) {
1783: System.out.print(" "+counts[j][k]);
1784: }
1785: System.out.println(" }");
1786: }
1787: }
1788: //*/
1789: }
|