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