/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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
*/
// Attribute instance fields.
}
}
}
assert(isCanonical());
return this;
return res;
}
}
}
}
public boolean isCanonical() {
}
}
// 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.)
synchronized (canonLists) {
}
return cl;
}
}
// Find the canonical empty attribute with the given ctype, name, layout.
synchronized (attributes) {
if (a == null) {
}
return a;
}
}
}
// Find canonical empty attribute with given ctype and name,
// and with the standard layout.
defs = standardDefs;
}
}
return a;
}
static {
//define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]");
("[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, "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.
}
// Metadata.
//
// We define metadata using similar layouts
// for all five kinds of 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=[NH[(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 {
(""
+"\n # parameter_annotations :="
+"\n [ NB[(1)] ] # forward call to annotations"
),
(""
+"\n # annotations :="
+"\n [ NH[(1)] ] # forward call to annotation"
+"\n "
+"\n # annotation :="
+"\n [RSH"
+"\n NH[RUH (1)] # forward call to value"
+"\n ]"
),
(""
+"\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 ()[] ]"
)
};
if (ctype == ATTR_CONTEXT_CODE) continue;
"RuntimeVisibleAnnotations", annotationsLayout);
"RuntimeInvisibleAnnotations", annotationsLayout);
if (ctype == ATTR_CONTEXT_METHOD) {
"RuntimeVisibleParameterAnnotations", paramsLayout);
"RuntimeInvisibleParameterAnnotations", paramsLayout);
"AnnotationDefault", defaultLayout);
}
}
}
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.
public int attributeSize() {
}
public void trimToSize() {
if (attributes == null) {
return;
}
if (attributes.isEmpty()) {
attributes = null;
return;
}
if (attributes instanceof ArrayList) {
al.trimToSize();
boolean allCanon = true;
if (!a.isCanonical()) {
allCanon = false;
}
assert(!a.isCanonical());
}
}
if (allCanon) {
// Replace private writable attribute list
// with only trivial entries by public unique
// immutable attribute list with the same entries.
}
}
}
if (attributes == null)
else if (!(attributes instanceof ArrayList))
attributes.add(a);
}
if (!(attributes instanceof ArrayList))
attributes.remove(a);
return a;
}
return attributes.get(n);
}
if (attributes == null) return;
for (Attribute a : attributes) {
}
}
if (attributes == null)
return noAttributes;
return attributes;
}
attributes = null;
else
}
for (Attribute a : attributes) {
return a;
}
return null;
}
for (Attribute a : attributes) {
return a;
}
return null;
}
}
}
}
}
// Lightweight interface to hide details of band structure.
// Also used for testing.
public static abstract
class ValueStream {
return new UnsupportedOperationException("ValueStream method");
}
}
// Element kinds:
/** A "class" of attributes, characterized by a context-type, name
* and format. The formats are specified in a "little language".
*/
public static
}
public boolean isEmpty() {
}
assert(ctype < ATTR_CONTEXT_LIMIT);
try {
if (!hasCallables) {
} else {
// Make the callables now, so they can be linked immediately.
ce.removeBand();
}
// Next fill them in.
}
//System.out.println(Arrays.asList(elems));
}
} catch (StringIndexOutOfBoundsException ee) {
// simplest way to catch syntax errors...
}
// Some uses do not make a fresh one for each occurrence.
// For example, if layout == "", we only need one attr to share.
}
private Layout() {}
assert(ctype < ATTR_CONTEXT_LIMIT);
return def;
}
}
}
}
}
}
public int hashCode() {
* 37 + ctype);
}
int r;
if (r != 0) return r;
if (r != 0) return r;
}
// If -ea, print out more informative strings!
return str;
}
}
public
class Element {
Element() {
}
void removeBand() {
--bandCount;
}
public boolean hasBand() {
return bandIndex >= 0;
}
// If -ea, print out more informative strings!
return str;
}
switch (kind) {
case EK_CALL:
break;
case EK_CASE:
break;
}
return layout
}
}
public boolean hasCallables() {
}
if (hasCallables()) {
return nelems;
} else
return noElems; // no callables at all
}
if (hasCallables())
else {
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.
*/
}
/** 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.
*/
}
if (majver <= JAVA5_PACKAGE_MAJOR_VERSION) {
// Disallow layout syntax in the oldest protocol version.
return expandCaseDashNotation(layout);
}
return layout;
}
}
public static
private int ctype;
}
}
}
if (mode == VRM_CLASSIC) {
}
// else the name is owned by the layout, and is processed elsewhere
return;
}
// References (to a local cpMap) are embedded in the bytes.
new ValueStream() {
}
}
return bci;
}
});
}
}
}
return def
}
/** 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
if (ch <= ' ') {
// Skip whitespace and control chars
continue;
} else if (ch == '#') {
// Skip to end of line.
} else if (ch == '\\') {
// Map a character reference to its decimal code.
// Map a hex numeral to its decimal code.
int start = i-1;
++end;
else
break;
}
i = end;
} else {
}
} else {
}
}
}
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' )
schema_ref:
( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' )
utf8_ref:
'RU'
untyped_ref:
'RQ'
numeral:
'(' ('-')? (digit)+ ')'
digit:
( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
</pre>
*/
static //private
return res;
}
static //private
boolean prevBCI = false;
int start = i;
int body;
byte kind;
//System.out.println("at "+i+": ..."+layout.substring(i));
// strip a prefix
/// layout_element: integral
case 'B': case 'H': case 'I': case 'V': // unsigned_int
--i; // reparse
i = tokenizeUInt(e, layout, i);
break;
case 'S': // signed_int
--i; // reparse
i = tokenizeSInt(e, layout, i);
break;
case 'P': // bc_index
// bc_index: 'PO' tokenizeUInt
// 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
// must follow P or PO:
if (!prevBCI)
{ i = -i; continue; } // fail
i = tokenizeSInt(e, layout, i);
break;
case 'F': // flag
i = tokenizeUInt(e, layout, i);
break;
case 'N': // replication: 'N' uint '[' elem ... ']'
i = tokenizeUInt(e, layout, i);
{ i = -i; continue; } // fail
break;
case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
i = tokenizeSInt(e, layout, i);
for (;;) {
// Keep parsing cases until we hit the default case.
{ i = -i; break; } // fail
int beg = i;
{ i = -i; break; } // fail
// Check for duplication.
body = i; // missing body, which is legal here
else
if (cstrlen == 0) {
ce.removeBand();
break; // done with the whole union
} else {
// Parse a case string.
boolean firstCaseNum = true;
// Look for multiple case tags:
// Check for a case range (new in 1.6).
if (dash >= 0) {
{ i = -i; break; } // fail
} else {
}
// Add a case for each value in value0..value1
for (;; value0++) {
ce.removeBand();
if (!firstCaseNum)
// "backward case" repeats a body
firstCaseNum = false;
}
break; // done with this case
}
}
}
}
// Duplicate tag.
{ i = -i; break; } // fail
}
}
break;
case '(': // call: '(' '-'? digit+ ')'
e.removeBand();
target < 0 ||
{ i = -i; continue; } // fail
// Is it a (recursive) backward call?
if (offset <= 0) {
// Yes. Mark both caller and callee backward.
}
break;
case 'K': // reference_type: constant_ref
default: { i = -i; continue; } // fail
}
break;
case 'R': // schema_ref
default: { i = -i; continue; } // fail
}
break;
default: { i = -i; continue; } // fail
}
// further parsing of refs
// reference: reference_type -><- ( 'N' )? tokenizeUInt
i++; // move forward
}
--i; // reparse
i = tokenizeUInt(e, layout, i);
}
// store the new element
}
}
static //private
// Parse several independent layout bodies: "[foo][bar]...[baz]"
int body;
}
return res;
}
static private
// No empty bodies, please.
return -i;
// skip balanced [...[...]...]
case '[': depth++; break;
case ']': depth--; break;
}
}
--i; // get before bracket
return i; // return closing bracket
}
static private
default: return -i;
}
return i;
}
static private
++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
for (;;) {
// matched /[0-9]--?[0-9]/; return position of dash
return dash;
}
}
}
}
static
--beg;
}
// skip backward over a sign
}
static
++end;
}
}
/** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */
static
for (;;) {
// for each dash, collect everything up to the dash
// then collect intermediate values
}
if (dash < 0) break;
}
}
static {
}
// 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 prevBCI = 0;
int prevRBCI = 0;
int value;
switch (e.kind) {
case EK_INT:
break;
case EK_BCI: // PH, POH
// PH: transmit R(bci), store bci
} else {
// POH: transmit D(R(bci)), store bci
}
break;
case EK_BCO: // OH
// OH: transmit D(R(bci)), store D(bci)
break;
case EK_FLAG:
break;
case EK_REPL:
for (int j = 0; j < value; j++) {
}
break; // already transmitted the scalar value
case EK_UN:
break; // already transmitted the scalar value
case EK_CALL:
// Adjust band offset if it is a backward call.
break; // no additional scalar value to transmit
case EK_REF:
if (localRef == 0) {
} else {
if (e.refKind == CONSTANT_Signature
// Cf. ClassReader.readSignatureRef.
} else if (e.refKind == CONSTANT_Literal) {
} else if (e.refKind != CONSTANT_All) {
}
}
break;
default: assert(false); continue;
}
}
return pos;
}
static
for (int j = 0; j < lastj; j++) {
return ce;
}
}
static private
int value = 0;
// Read in big-endian order:
}
// sign-extend subword 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
int prevBCI = 0;
int prevRBCI = 0;
int value;
switch (e.kind) {
case EK_INT:
break;
case EK_BCI: // PH, POH
// PH: transmit R(bci), store bci
} else {
// POH: transmit D(R(bci)), store bci
}
break;
case EK_BCO: // OH
// OH: transmit D(R(bci)), store D(bci)
break;
case EK_FLAG:
break;
case EK_REPL:
for (int j = 0; j < value; j++) {
}
break;
case EK_UN:
break;
case EK_CALL:
break;
case EK_REF:
int localRef;
// It's a one-element array, really an lvalue.
} else {
}
break;
default: assert(false); continue;
}
}
}
static private
if (loBits == 0) {
// It is not stored at all ('V' layout).
return;
}
if (loBits < 32) {
int codedValue;
else
if (codedValue != value)
}
// Write in big-endian order:
}
}
/*
/// 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
"HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
"=AnnotationDefault",
// Like metadata, but with a compact tag set:
"[NH[(1)]]"
+"[NH[(2)]]"
+"[RSHNH[RUH(3)]]"
+"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(2)](6)[NH[(3)]]()[]]",
""
};
ap = 0;
}
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));
}
Class cls = new Package().new Class("");
cls.cpMap = cpMap;
class TestValueStream extends ValueStream {
Random rand = new 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 (Iterator j = standardDefs.values().iterator(); j.hasNext(); ) {
Attribute a = (Attribute) j.next();
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(0, 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(" }");
}
}
//*/
}