/*
* @(#)MethodInfo.java 1.51 06/10/22
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program 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.
*
* This program 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 at /legal/license.txt).
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package components;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Hashtable;
import java.util.Vector;
import consts.Const;
import consts.CVMConst;
import jcc.Util;
import util.DataFormatException;
import util.ValidationException;
/*
* Class for representing every method in a class
* As with ClassInfo, this represents the VM-independent information
* (except that it seems to know a lot about quickening!)
* The truely VM-specific information is in the VMMethodInfo and its
* subclass the CVMMethodInfo. See field vmMethodInfo below.
*/
public
class MethodInfo extends ClassMemberInfo implements Const, Cloneable
{
public int argsSize;
public int stack;
public int locals;
public int methodTableIndex = -1;
public byte code[];
private Attribute methodAttributes[];
public CodeAttribute codeAttribute;
public Attribute codeAttributes[];
public ExceptionEntry exceptionTable[];
public ClassConstant exceptionsThrown[];
private boolean checkedDebugTables = false;
private LineNumberTableEntry lineNumberTable[];
private LocalVariableTableEntry localVariableTable[];
public vm.VMMethodInfo vmMethodInfo;
/* An array of the methods called by invokevirtual_quick and
* invokevirtualobject_quick. (After quickening, before inlining).
*/
public MethodInfo[] targetMethods;
/**
* The following are arrays of indexes into the
* code array of references to the constant pool:
* ldcInstructions lists the instructions with a one-byte index.
* wideConstantRefInstructions lists the instructions with a two-byte index.
* In each case, the index is that of the opcode: the actual reference
* begins with the following byte.
* Entries of value -1 are ignored.
*/
private int ldcInstructions[];
private int wideConstantRefInstructions[];
public boolean hasCheckinits = false;
// Keep track of any list of methods to be excluded during
// processing. Initialize once and then remove each entry,
// if there are any, as it is found.
static private Vector excludeList = null;
static void setExcludeList( Vector v ) {
excludeList = v;
return;
}
protected Object clone() throws CloneNotSupportedException{
MethodInfo m = (MethodInfo)super.clone();
return m;
}
public MethodInfo( int name, int sig, int access, ClassInfo p ) {
super( name, sig, access, p );
}
public void
initializeClassDebugTables() {
int nattr = (codeAttributes == null) ? 0 : codeAttributes.length;
// parse code attributes
for (int i = 0; i < nattr; i++) {
Attribute a = codeAttributes[i];
if (a.name.string.equals("LineNumberTable")) {
lineNumberTable = ((LineNumberTableAttribute)a).data;
}
if (a.name.string.equals("LocalVariableTable")) {
localVariableTable = ((LocalVariableTableAttribute)a).data;
}
}
}
public LineNumberTableEntry []
getLineNumberTable(){
if ( !checkedDebugTables ){
initializeClassDebugTables();
checkedDebugTables = true;
}
return lineNumberTable;
}
public LocalVariableTableEntry []
getLocalVariableTable(){
if ( !checkedDebugTables ){
initializeClassDebugTables();
checkedDebugTables = true;
}
return localVariableTable;
}
public boolean
hasLineNumberTable(){
if ( !checkedDebugTables ){
initializeClassDebugTables();
checkedDebugTables = true;
}
return (lineNumberTable != null) && (lineNumberTable.length != 0);
}
public boolean
hasLocalVariableTable(){
if ( !checkedDebugTables ){
initializeClassDebugTables();
checkedDebugTables = true;
}
return (localVariableTable != null) && (localVariableTable.length != 0);
}
public boolean
throwsExceptions(){
return exceptionsThrown != null;
}
public ClassConstant[]
getExceptionsThrown(){
return exceptionsThrown;
}
public int
nExceptionsThrown(){
return ( exceptionsThrown == null ) ? 0 : exceptionsThrown.length;
}
private String nativeName;
public String getNativeName(boolean isJNI) {
if (nativeName == null) {
nativeName = isJNI ? getJNIName() : getOldNativeName();
}
return nativeName;
}
public boolean isNonTrivial() {
//
// We can only tell if a method is trivial or not by looking
// at its bytecodes. Otherwise we assume non-trivial.
//
if (code != null) {
if ((code.length == 1) && (code[0] == (byte)opc_return)) {
return false;
}
}
return true;
}
private String getJNIName() {
ClassInfo ci = parent;
String classname = ci.className;
String methodname = this.name.string;
int nmethods = ci.methods.length;
String typeName = null; // by default, don't need type
for (int j = 0; j < nmethods; j ++ ){
MethodInfo m = ci.methods[j];
if ((m != this) && ( (m.access&Const.ACC_NATIVE) != 0 )) {
if (m.name.equals(this.name)) {
// Two native methods with the same name. Need type name
typeName = this.type.string;
break;
}
}
}
return Util.convertToJNIName(classname, methodname, typeName);
}
private String getOldNativeName() {
ClassInfo ci = parent;
String methodname = this.name.string;
StringBuffer sbuf = new StringBuffer(/*NOI18N*/"Java_")
.append(ci.getGenericNativeName())
.append('_');
if (methodname.indexOf('_') == -1) {
// optimization. Most methods don't have an _ in them
sbuf.append(methodname);
} else {
for (int i = 0; i < methodname.length(); i++) {
if (methodname.charAt(i) == '_') {
sbuf.append(/*NOI18N*/"_0005f");
} else {
sbuf.append(methodname.charAt(i));
}
}
}
sbuf.append("_stub");
return sbuf.toString();
}
/*
* A methods attributes are Code and Exceptions.
*/
private static Hashtable methodAttributeTypes = new Hashtable();
static {
methodAttributeTypes.put( "Code", CodeAttributeFactory.instance );
methodAttributeTypes.put( "Exceptions", ExceptionsAttributeFactory.instance );
}
// Read in method attributes from classfile
void
readAttributes(DataInput in, ConstantPool cp, boolean readCode)
throws IOException
{
methodAttributes = Attribute.readAttributes(in, cp,
methodAttributeTypes, false);
// NOTE: The above reads in the code as well. We might want to
// optimize this.
//
// parse special attributes
//
if (methodAttributes != null) {
for (int i = 0; i < methodAttributes.length; i++) {
Attribute a = methodAttributes[i];
if (a.name.string.equals("Code")) {
CodeAttribute ca = (CodeAttribute)a;
this.locals = ca.locals;
this.stack = ca.stack;
this.code = ca.code;
this.exceptionTable = ca.exceptionTable;
this.codeAttributes = ca.codeAttributes;
this.codeAttribute = ca;
} else if (a.name.string.equals("Exceptions") ) {
this.exceptionsThrown = ((ExceptionsAttribute)a).data;
}
}
}
}
public static MethodInfo
readMethod( DataInput in, ClassInfo p, boolean readCode ) throws IOException {
int access = in.readUnsignedShort();
int name = in.readUnsignedShort();
int sig = in.readUnsignedShort();
MethodInfo m = new MethodInfo( name, sig, access, p );
// the bad thing is, we really cannot go far
// without resolving. So we resolve here.
ConstantPool cp = p.getConstantPool();
m.flatten(cp);
m.argsSize = Util.argsSize(m.type.string);
if ((m.access & ACC_STATIC) == 0) {
m.argsSize++;
}
m.readAttributes(in, cp, readCode);
ConstantObject[] constants = cp.getConstants();
// Check to make sure this method isn't marked for exclusion
if (excludeList != null && excludeList.size() > 0) {
// See if this method is to be discarded. The vector holds
// the signature of methods to be excluded parsed into
// class, method & type portions.
for (int i = 0 ; i < excludeList.size() ; i++) {
String paramlist = constants[sig].toString();
paramlist = paramlist.substring(0, paramlist.indexOf(')')+1);
MemberNameTriple t =
(MemberNameTriple)excludeList.elementAt(i);
if (t.sameMember(p.className, constants[name].toString(),
paramlist)) {
excludeList.remove(i);
return (MethodInfo)null;
}
}
}
return m;
}
public void write( DataOutput o ) throws IOException{
o.writeShort( access );
o.writeShort( name.index );
o.writeShort( type.index );
Attribute.writeAttributes( methodAttributes, o, false );
}
public int getInt( int w ){
return ( (int)code[w] << 24 ) |
(( (int)code[w+1] &0xff ) << 16 ) |
(( (int)code[w+2] &0xff ) << 8 ) |
( (int)code[w+3] &0xff );
}
public int getUnsignedShort( int w ){
return (( (int)code[w] &0xff ) << 8 ) | ( (int)code[w+1] &0xff );
}
public int getShort( int w ){
return (( (int)code[w]) << 8 ) | ( (int)code[w+1] &0xff );
}
//
// Private Utility functions
private void putInt(byte array[], int offset, int val) {
array[offset] = (byte) ((val >> 24) & 0xFF);
array[offset+1] = (byte) ((val >> 16) & 0xFF);
array[offset+2] = (byte) ((val >> 8) & 0xFF);
array[offset+3] = (byte) (val & 0xFF);
}
private void putShort( int w, short v ){
code[w] = (byte)(v>>>8);
code[w+1] = (byte)v;
}
private void findConstantReferences() throws DataFormatException {
if ( code == null ) return; // no code, no references.
int ldc[] = new int[ code.length / 2 ];
int wide[] = new int[ code.length / 3 ];
int nldc = 0;
int nwide = 0;
int ncode = code.length;
int opcode;
for( int i = 0; i < ncode; /*nothing*/){
switch (opcode = (int)code[i]&0xff) {
case opc_tableswitch:
i = (i + 4) & ~3;
int low = getInt( i+4);
int high = getInt( i+8);
i += (high - low + 1) * 4 + 12;
break;
case opc_lookupswitch:
i = (i + 4) & ~3;
int pairs = getInt(i+4);
i += pairs * 8 + 8;
break;
case opc_wide:
switch ((int)code[i+1]&0xff) {
case opc_aload:
case opc_iload:
case opc_fload:
case opc_lload:
case opc_dload:
case opc_istore:
case opc_astore:
case opc_fstore:
case opc_lstore:
case opc_dstore:
case opc_ret:
i += 4;
break;
case opc_iinc:
i += 6;
break;
default:
throw new DataFormatException( parent.className + "." +
name.string + ": unknown wide " +
"instruction: " + code[i+1] );
}
break;
case opc_ldc:
case opc_ldc_quick:
case opc_aldc_quick:
case opc_aldc_ind_quick:
ldc[nldc++] = i;
i += opcLengths[opcode];
break;
case opc_ldc_w:
case opc_ldc2_w:
case opc_getstatic:
case opc_putstatic:
case opc_getfield:
case opc_putfield:
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
case opc_multianewarray:
case opc_ldc_w_quick:
case opc_aldc_w_quick:
case opc_aldc_ind_w_quick:
case opc_ldc2_w_quick:
case opc_getstatic_quick:
case opc_putstatic_quick:
case opc_agetstatic_quick:
case opc_aputstatic_quick:
case opc_agetstatic_checkinit_quick:
case opc_aputstatic_checkinit_quick:
case opc_getstatic2_quick:
case opc_putstatic2_quick:
case opc_getstatic2_checkinit_quick:
case opc_putstatic2_checkinit_quick:
case opc_invokestatic_quick:
case opc_invokenonvirtual_quick:
case opc_invokesuper_quick:
case opc_new_quick:
case opc_new_checkinit_quick:
case opc_anewarray_quick:
case opc_multianewarray_quick:
case opc_checkcast_quick:
case opc_instanceof_quick:
case opc_invokevirtual_quick_w:
case opc_getfield_quick_w:
case opc_putfield_quick_w:
wide[nwide++] = i;
i += opcLengths[opcode];
break;
default:
i += opcLengths[opcode];
break;
}
}
// not knowing any better, we allocated excess capacity.
// allocate and fill appropriately-sized arrays.
ldcInstructions = new int[ nldc ];
System.arraycopy( ldc, 0, ldcInstructions, 0, nldc );
ldc = null;
wideConstantRefInstructions = new int[ nwide ];
System.arraycopy( wide, 0, wideConstantRefInstructions, 0, nwide );
wide = null;
}
public int opcodeLength (int pc) {
int old_pc;
int opcode = (int)code[pc]&0xff;
switch (opcode) {
case opc_tableswitch:
old_pc = pc;
pc = (pc + 4) & ~3;
int low = getInt(pc + 4);
int high = getInt(pc + 8);
pc += (high - low + 1) * 4 + 12;
return pc - old_pc;
case opc_lookupswitch:
old_pc = pc;
pc = (pc + 4) & ~3;
int pairs = getInt(pc + 4);
pc += pairs * 8 + 8;
return pc - old_pc;
case opc_wide:
if (((int)code[pc + 1]&0xff) == opc_iinc)
return 6;
return 4;
default:
return opcLengths[opcode];
}
}
public void
countConstantReferences(ConstantPool cp, boolean isRelocatable) {
ConstantObject table[] = cp.getConstants();
super.countConstantReferences(isRelocatable);
Attribute.countConstantReferences(methodAttributes, isRelocatable);
Attribute.countConstantReferences(codeAttributes, isRelocatable);
if (code == null) return; // no code, no relocation
if (ldcInstructions == null) {
findConstantReferences();
}
{
int list[] = ldcInstructions;
int n = list.length;
for (int i = 0; i < n; i++){
int loc = list[i];
if ( loc==-1 ) continue;
table[ (int)code[loc+1]&0xff ].incReference();
}
}
{
int list[] = wideConstantRefInstructions;
int n = list.length;
for (int i = 0; i < n; i++){
int loc = list[i];
if ( loc==-1 ) continue;
table[ getUnsignedShort(loc+1) ].incReference();
}
}
}
/*
* Call just before writing output.
* Ensure sanity among attributes and constants.
* Some tables we do here because they need to know code length.
* Others we just deligate.
*/
public void
validate(ConstantObject constantPool[]){
int codeLength = (code == null ) ? 0 : code.length;
super.validate();
if (methodAttributes != null){
for (int i=0; i<methodAttributes.length; i++){
// this covers the exceptionsThrown and the localVariableTable
methodAttributes[i].validate();
}
}
if (codeAttributes != null){
for (int i=0; i<codeAttributes.length; i++){
codeAttributes[i].validate();
}
}
if (exceptionTable != null){
validateExceptionTable();
}
if (lineNumberTable != null){
for (int i=0; i<lineNumberTable.length; i++){
int startPC = lineNumberTable[i].startPC;
if (startPC<0 || startPC >= codeLength){
throw new ValidationException(
"Line number table entry startPC out of range in", this);
}
}
}
/*
* The big job is sweeping the code for constant pool references
* and making sure we like what we see.
*/
if (codeLength != 0){
validateCode(constantPool);
}
}
/*
* Helper for validate.
* Make sure pc range is in range
* Make sure that if there is a catch type, that we like it:
* it validates itself, and should be Throwable.
*/
private void
validateExceptionTable(){
int codeLength = (code == null ) ? 0 : code.length;
for (int i=0; i<exceptionTable.length; i++){
int startPC = exceptionTable[i].startPC;
int endPC = exceptionTable[i].endPC;
if (startPC<0 || startPC >= codeLength){
throw new ValidationException(
"ExceptionTable entry startPC out of range in", this);
}
if (endPC<0 || endPC >= codeLength){
throw new ValidationException(
"ExceptionTable entry endPC out of range in", this);
}
if (exceptionTable[i].catchType != null){
exceptionTable[i].catchType.validate();
// make sure that what we're catching is throwable!
ClassInfo throwClassInfo = exceptionTable[i].catchType.find();
ClassInfo thisClassInfo = throwClassInfo;
while (thisClassInfo != null){
if (thisClassInfo.className.equals("java/lang/Throwable")){
break; // success, we're happy
}
if (thisClassInfo.className.equals("java/lang/Object")){
// reached top of hierarchy without finding Throwable. BAD
throw new ValidationException(
"ExceptionTable entry catches non-throwable "+
throwClassInfo.className, this);
}
thisClassInfo = thisClassInfo.superClassInfo;
}
// either did break or ventured into unknown superclassdom.
// either way we stop here.
}
}
}
/*
* Helper for validateCode
*/
/*
* These are used to encode the constantType as follows:
* The base types (first 4) are never combined with each other.
* CP_FIELD can be combined using | with one of
* CP_DOUBLE, CP_SINGLE, CP_REF, as well as CP_STATIC.
* CP_METHOD can be combined with CP_STATIC.
* CP_DOUBLE or CP_SINGLE can also stand alone to represent
* references to constants in the pool.
* CP_LOADABLE cannot be combined with anything and represents
* any literal constant pool entry than can be the target of an ldc
* or ldc variant: a String literal, a single-numeric literal or a long-numeric
* literal.
*/
static final int CP_LOADABLE = 0; // any type that can be loaded directly.
static final int CP_CLASS = 1; // Class reference
static final int CP_STRING = 2; // String reference
static final int CP_METHOD = 4; // method reference
static final int CP_FIELD = 8; // field reference
static final int CP_BASE = 0xf; // all of the above.
static final int CP_DOUBLE = 0x10; // double when applied to a field numeric.
// or 64-bit const pool entry
static final int CP_REF = 0x20; // reference type when applied to a field .
static final int CP_STATIC = 0x40; // static method or field
static final int CP_SINGLE = 0x80; // non-reference field.
// or 32-bit const pool entry
private void
validateLoadable(ConstantObject constantPool[], int index, int constantType){
if (index <= 0 || index >= constantPool.length){
throw new ValidationException(
"Instruction constant pool index out of range in", this);
}
ConstantObject c = constantPool[index];
if (c == null){
throw new ValidationException(
"Instruction constant pool index to illegal entry in", this);
}
String expected;
foundBad:
switch(constantType){
case CP_LOADABLE:
if (c instanceof StringConstant) return;
if (c instanceof SingleValueConstant) return;
if (c instanceof DoubleValueConstant) return;
if (c instanceof ClassConstant) return;
expected = "Directly loadable constant";
break foundBad;
case CP_STRING:
if (c instanceof StringConstant) return;
expected = "String constant";
break foundBad;
case CP_SINGLE:
// a one-word constant. see use of CP_SINGLE as modifier below.
if (c instanceof SingleValueConstant) return;
expected = "One-word constant";
break foundBad;
case CP_DOUBLE:
// a two-word constant. see use of CP_DOUBLE as modifier below.
if (c instanceof DoubleValueConstant) return;
expected = "Two-word constant";
break foundBad;
case CP_CLASS:
if (c instanceof ClassConstant) return;
expected = "Class reference";
break foundBad;
default:
// some combination of FIELD or METHOD with modifiers.
ClassMemberInfo cm;
FMIrefConstant fmiRef;
String memberType;
fieldOrMethod:
switch (constantType & CP_BASE){
case CP_FIELD:
if (! (c instanceof FieldConstant)){
expected = "Field reference";
break foundBad;
}
fmiRef = (FMIrefConstant)c;
cm = ((FieldConstant)c).find();
break fieldOrMethod;
case CP_METHOD:
if (!(c instanceof MethodConstant)){
expected = "Method reference";
break foundBad;
}
fmiRef = (FMIrefConstant)c;
cm = ((MethodConstant)c).find();
break fieldOrMethod;
default:
expected = "verifyLoadable confusion";
break foundBad;
}
memberType = fmiRef.sig.type.string;
if ((constantType & (CP_DOUBLE|CP_SINGLE)) != 0){
if (memberType.length() != 1){
expected = "Simple type class member";
break foundBad;
}
switch(memberType.charAt(0)){
case 'B':
case 'C':
case 'F':
case 'I':
case 'S':
case 'Z':
if ((constantType& CP_SINGLE) == 0){
expected = "Long/Double type class member";
break foundBad;
}
break;
case 'D':
case 'J':
if ((constantType& CP_DOUBLE) == 0){
expected = "Single word scalar type class member";
break foundBad;
}
break;
default:
expected = "Simple type class member";
break foundBad;
}
}else if ((constantType & CP_REF) != 0){
if (memberType.length() <= 1){
expected = "Reference type class member";
break foundBad;
}
switch(memberType.charAt(0)){
case 'L':
case '[':
break;
default:
expected = "Reference type class member";
break foundBad;
}
}
if (cm != null){
// can't look at missing class!
if ((constantType & CP_STATIC) != 0){
if (!cm.isStaticMember()){
expected = "Static class member";
break foundBad;
}
} else {
if (cm.isStaticMember()){
expected = "Non-static class member";
break foundBad;
}
}
}
// got this far looking at a field/method access.
// must be successful.
return;
}
// we got here by doing a break foundBad.
// expected is set to something.
// c is set to something.
// throw something
throw new ValidationException(expected+" in "+this, c);
}
/*
* If we were more trusting, we'd use the info already computed
* on the location of constant references, such as ldcInstructions.
* It may be out of date because of code rewriting due to constant
* pool sharing or other transformations.
*/
void validateCode(ConstantObject constantPool[]) throws DataFormatException {
if ( code == null ) return; // no code, no references.
int ncode = code.length;
byte codeBytes[] = code;
int opcode;
int index;
int constType;
for( int i = 0; i < ncode; /*nothing*/){
computedLength:{
noCPAccess:{
switch (opcode = (int)code[i]&0xff) {
case opc_tableswitch:
i = (i + 4) & ~3;
int low = getInt( i+4);
int high = getInt( i+8);
i += (high - low + 1) * 4 + 12;
break computedLength;
case opc_lookupswitch:
i = (i + 4) & ~3;
int pairs = getInt(i+4);
i += pairs * 8 + 8;
break computedLength;
case opc_wide:
switch ((int)code[i+1]&0xff) {
case opc_aload:
case opc_iload:
case opc_fload:
case opc_lload:
case opc_dload:
case opc_istore:
case opc_astore:
case opc_fstore:
case opc_lstore:
case opc_dstore:
case opc_ret:
i += 4;
break computedLength;
case opc_iinc:
i += 6;
break computedLength;
default:
throw new DataFormatException( parent.className + "." +
name.string + ": unknown wide " +
"instruction: " + code[i+1] );
}
case opc_ldc:
// undifferentiated loadable type.
constType = CP_LOADABLE;
index = at(codeBytes, i+1);
break;
case opc_ldc_quick:
// single cell, not a string
constType = CP_SINGLE;
index = at(codeBytes, i+1);
break;
case opc_aldc_quick:
case opc_aldc_ind_quick:
// string
constType = CP_STRING;
index = at(codeBytes, i+1);
break;
case opc_ldc_w:
// undifferentiated loadable type.
constType = CP_LOADABLE;
index = shortAt(codeBytes, i+1);
break;
case opc_ldc2_w:
case opc_ldc2_w_quick:
// double cell
constType = CP_DOUBLE;
index = shortAt(codeBytes, i+1);
break;
case opc_aldc_w_quick:
case opc_aldc_ind_w_quick:
// string
constType = CP_STRING;
index = shortAt(codeBytes, i+1);
break;
case opc_getstatic:
case opc_putstatic:
constType = CP_FIELD | CP_STATIC;
index = shortAt(codeBytes, i+1);
break;
case opc_getfield:
case opc_putfield:
case opc_getfield_quick_w:
case opc_putfield_quick_w:
constType = CP_FIELD;
index = shortAt(codeBytes, i+1);
break;
case opc_getstatic_quick:
case opc_putstatic_quick:
constType = CP_FIELD | CP_STATIC | CP_SINGLE;
index = shortAt(codeBytes, i+1);
break;
case opc_agetstatic_quick:
case opc_aputstatic_quick:
case opc_agetstatic_checkinit_quick:
case opc_aputstatic_checkinit_quick:
constType = CP_FIELD | CP_STATIC | CP_REF;
index = shortAt(codeBytes, i+1);
break;
case opc_getstatic2_quick:
case opc_putstatic2_quick:
case opc_getstatic2_checkinit_quick:
case opc_putstatic2_checkinit_quick:
constType = CP_FIELD | CP_STATIC | CP_DOUBLE;
index = shortAt(codeBytes, i+1);
break;
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokenonvirtual_quick: // actually IS virtual.
case opc_invokeinterface:
case opc_invokevirtual_quick_w:
// TODO: interface checking
constType = CP_METHOD;
index = shortAt(codeBytes, i+1);
break;
case opc_invokestatic:
case opc_invokestatic_quick:
constType = CP_METHOD | CP_STATIC;
index = shortAt(codeBytes, i+1);
break;
case opc_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
case opc_multianewarray:
case opc_new_quick:
case opc_new_checkinit_quick:
case opc_anewarray_quick:
case opc_multianewarray_quick:
case opc_checkcast_quick:
case opc_instanceof_quick:
constType = CP_CLASS;
index = shortAt(codeBytes, i+1);
break;
default:
break noCPAccess;
}
validateLoadable(constantPool, index, constType);
}
i += opcLengths[opcode];
}
}
}
public void
relocateConstantReferences( ConstantObject table[] ) throws DataFormatException {
if ( code == null ) return; // no code, no relocation
if (ldcInstructions == null){
findConstantReferences();
}
{
int list[] = ldcInstructions;
int n = list.length;
for (int i = 0; i < n; i++){
int j = list[i]+1;
if ( j <= 0 ) continue;
ConstantObject c = table[ (int)code[j]&0xff ];
if ( c.shared )
throw new DataFormatException("code reference to shared constant");
int v = c.index;
if ( v < 0 )
throw new DataFormatException("code reference to deleted constant at "+qualifiedName()+"+"+Integer.toHexString(j));
if ( v > 255 )
throw new DataFormatException("ldc subscript out of range at "+qualifiedName()+"+"+Integer.toHexString(j));
code[j] = (byte)v;
}
}
{
int list[] = wideConstantRefInstructions;
int n = list.length;
for (int i = 0; i < n; i++){
int j = list[i]+1;
if ( j <= 0 ) continue;
ConstantObject c = table[ getUnsignedShort(j) ];
if ( c.shared )
throw new DataFormatException("code reference to shared constant at "+qualifiedName()+"+"+Integer.toHexString(j));
int v = c.index;
if ( v < 0 )
throw new DataFormatException("code reference to deleted constant at "+qualifiedName()+"+"+Integer.toHexString(j));
putShort( j, (short)v );
}
}
}
public void replaceCode(int start, int end) {
replaceCode(start, end, new byte[0]);
}
public void replaceCode(int start, int end, int op1) {
byte code[] = { (byte)op1 };
replaceCode(start, end, code);
}
public void replaceCode(int start, int end, int op1, int op2) {
byte code[] = { (byte)op1, (byte)op2 };
replaceCode(start, end, code);
}
public void replaceCode(int start, int end, int op1, int op2, int op3) {
byte code[] = { (byte)op1, (byte)op2, (byte)op3 };
replaceCode(start, end, code);
}
public java.util.BitSet getLabelTargets() {
java.util.BitSet result = new java.util.BitSet();
int ncode = code.length;
int nextpc;
for(int pc = 0; pc < ncode; pc = nextpc) {
nextpc = pc + opcodeLength(pc);
int opcode = (int)code[pc]&0xff;
switch (opcode) {
case opc_tableswitch:
case opc_lookupswitch:
int i = (pc + 4) & ~3;
int delta = (opcode == opc_tableswitch) ? 4 : 8;
result.set(pc + getInt(i)); // default
for (i = i + 12; i < nextpc; i += delta)
result.set(pc + getInt(i));
break;
case opc_jsr:
result.set(pc + 3);
case opc_goto:
case opc_ifeq: case opc_ifge: case opc_ifgt:
case opc_ifle: case opc_iflt: case opc_ifne:
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
case opc_if_icmplt: case opc_if_icmpgt: case opc_if_icmple:
case opc_if_acmpeq: case opc_if_acmpne:
case opc_ifnull: case opc_ifnonnull:
result.set(pc + getShort(pc + 1));
break;
case opc_jsr_w:
result.set(pc + 5);
case opc_goto_w:
result.set(pc + getInt(pc + 1));
break;
}
}
return result;
}
public void replaceCode(int start, int end, byte[] replaceCode) {
if (end - start < replaceCode.length) {
// System.out.println(" Cannot yet do expansion!!");
return;
}
if (exceptionTable != null && exceptionTable.length > 0) {
for (int i = 0; i < exceptionTable.length; i++) {
int startPC = exceptionTable[i].startPC;
int endPC = exceptionTable[i].endPC;
if (startPC >= start && startPC < end)
return;
if (endPC >= start && endPC < end)
return;
}
}
int startExtra = start + replaceCode.length;
int extra = end - startExtra;
System.arraycopy(replaceCode, 0, code, start, replaceCode.length);
for (int i = startExtra; i < end; i++)
code[i] = (byte)opc_nop;
}
public String disassemble(int start, int end) {
return disassemble(code, start, end);
}
/**
* Return the byte stored at a given index from the offset
* within code bytes
*/
private static final int at(byte codeBytes[], int index) {
return codeBytes[index] & 0xFF;
}
/**
* Return the short stored at a given index from the offset
* within code bytes
*/
private static final int shortAt(byte codeBytes[], int index) {
return ((codeBytes[index] & 0xFF) << 8)
| (codeBytes[index+1] & 0xFF);
}
public static String disassemble(byte[] codeBytes, int start, int end) {
// Output goes into a string
StringWriter sw = new StringWriter();
PrintWriter output = new PrintWriter(sw);
for (int offset = start; offset < end; ) {
int opcode = at(codeBytes, offset);
if (offset > start)
output.print("; ");
output.print(opcodeName(opcode));
switch (opcode) {
case opc_aload: case opc_astore:
case opc_fload: case opc_fstore:
case opc_iload: case opc_istore:
case opc_lload: case opc_lstore:
case opc_dload: case opc_dstore:
case opc_ret:
output.print(" " + at(codeBytes, offset+1));
offset += 2;
break;
case opc_iinc:
output.print(" " + at(codeBytes, offset+1) + " " +
(byte) at(codeBytes, offset +2));
offset += 3;
break;
case opc_newarray:
switch (at(codeBytes, offset+1)) {
case T_INT: output.print(" int"); break;
case T_LONG: output.print(" long"); break;
case T_FLOAT: output.print(" float"); break;
case T_DOUBLE: output.print(" double"); break;
case T_CHAR: output.print(" char"); break;
case T_SHORT: output.print(" short"); break;
case T_BYTE: output.print(" byte"); break;
case T_BOOLEAN:output.print(" boolean"); break;
default: output.print(" INVALID_TYPE"); break;
}
offset += 2;
break;
case opc_anewarray_quick:
case opc_anewarray: {
int index = shortAt(codeBytes, offset+1);
output.print(" class #" + index + " ");
offset += 3;
break;
}
case opc_sipush:
output.print(" " + (short) shortAt(codeBytes, offset+1));
offset += 3;
break;
case opc_bipush:
output.print(" " + (byte) at(codeBytes, offset+1));
offset += 2;
break;
case opc_ldc_quick:
case opc_aldc_quick:
case opc_aldc_ind_quick:
case opc_ldc: {
int index = at(codeBytes, offset+1);
output.print(" #" + index + " ");
offset += 2;
break;
}
case opc_ldc_w_quick:
case opc_aldc_w_quick:
case opc_aldc_ind_w_quick:
case opc_ldc2_w_quick:
case opc_getstatic_quick:
case opc_putstatic_quick:
case opc_getstatic2_quick:
case opc_putstatic2_quick:
case opc_agetstatic_quick:
case opc_aputstatic_quick:
case opc_agetstatic_checkinit_quick:
case opc_aputstatic_checkinit_quick:
case opc_getstatic_checkinit_quick: // CVM
case opc_getstatic2_checkinit_quick: // CVM
case opc_putstatic_checkinit_quick: // CVM
case opc_putstatic2_checkinit_quick: // CVM
case opc_invokenonvirtual_quick:
case opc_invokesuper_quick:
case opc_invokestatic_quick:
case opc_invokestatic_checkinit_quick: // CVM
case opc_new_quick:
case opc_new_checkinit_quick: // CVM
case opc_checkcast_quick:
case opc_instanceof_quick:
case opc_invokevirtual_quick_w:
case opc_getfield_quick_w:
case opc_putfield_quick_w:
case opc_ldc_w: case opc_ldc2_w:
case opc_instanceof: case opc_checkcast:
case opc_new:
case opc_putstatic: case opc_getstatic:
case opc_putfield: case opc_getfield:
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic: {
int index = shortAt(codeBytes, offset+1);
output.print(" #" + index + " ");
offset += 3;
break;
}
case opc_jsr: case opc_goto:
case opc_ifeq: case opc_ifge: case opc_ifgt:
case opc_ifle: case opc_iflt: case opc_ifne:
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
case opc_if_acmpeq: case opc_if_acmpne:
case opc_ifnull: case opc_ifnonnull: {
int target = offset + (short) shortAt(codeBytes,offset+1);
output.print(" " + target);
offset += 3;
break;
}
case opc_getfield_quick:
case opc_putfield_quick:
case opc_getfield2_quick:
case opc_putfield2_quick:
case opc_agetfield_quick: // CVM
case opc_aputfield_quick: // CVM
output.print(" #" + at(codeBytes, offset+1));
offset += 3;
break;
case opc_invokevirtualobject_quick:
case opc_invokevirtual_quick:
case opc_ainvokevirtual_quick:
case opc_dinvokevirtual_quick:
case opc_vinvokevirtual_quick:
output.print(" #" + at(codeBytes, offset+1) +
" args=" + at(codeBytes, offset+2));
offset += 3;
break;
case opc_invokeignored_quick: {
char null_check = (at(codeBytes, offset+2) != 0) ? 'T' : 'F';
output.print(" #" + at(codeBytes, offset+1) + " " + null_check);
offset += 3;
break;
}
default:
offset++;
break;
}
}
output.close();
return sw.toString();
}
public static String opcodeName (int opcode) {
return opcNames[opcode];
}
public String toString(){
String r = "Method: "+super.toString();
if ( code != null ){
r += " {"+code.length+" bytes of code}";
}
return r;
}
// Case 1: expand code.
// Convert ldc to ldc2: a. Insert extra bytes
// b. Fix all branch targets/exception ranges
// Case 2: smash code
// ldc_w_quick has index which is less than 255. Change to use
// ldc_w.
public void relocateAndPackCode (ConstantPool cp,
boolean noCodeCompaction) {
if (code == null)
return;
ConstantObject[] co = cp.getConstants();
int opcode, adjustment = 0;
int newOffsets[] = new int[code.length];
int indexByPC[] = new int[code.length];
// First figure out where we'll have to insert extra bytes in
// order to fit opc_ldc_w instead of opc_ldc instructions.
for (int pc = 0, pcindex = 0; pc < code.length;
pc = pc + opcodeLength(pc), pcindex++) {
opcode = (int) code[pc]&0xFF;
newOffsets[pcindex] = pc + adjustment;
indexByPC[pc] = pcindex;
switch (opcode) {
case opc_ldc:
case opc_ldc_quick:
case opc_aldc_quick:
case opc_aldc_ind_quick: {
// a conversion table which maps pcValue to new index.
int oldindex = (int)(code[pc+1] & 0xFF);
int index = co[oldindex].index;
if (index >= 0x100)
adjustment++;
break;
}
case opc_ldc_w:
case opc_ldc_w_quick:
case opc_aldc_w_quick:
case opc_aldc_ind_w_quick: {
// a conversion table which maps pcValue to new index.
int oldindex = (int)(((code[pc+1]&0xFF) << 8)
| (code[pc+2]&0xFF));
int index = co[oldindex].index;
if (index < 0x100)
adjustment--;
break;
}
case opc_goto: {
// Calculate the displacement, sign extend high byte
int displ = (code[pc+1] << 8) | (code[pc+2] & 0xFF);
if (!noCodeCompaction && displ == 3) {
adjustment -= 3; // remove no-use goto's.
}
break;
}
case opc_nop: {
if (!noCodeCompaction) {
adjustment--; // remove
}
break;
}
case opc_tableswitch:
case opc_lookupswitch: {
int oldExtraPC = (( pc + 4 ) & ~3);
int newExtraPC = (( pc + adjustment + 4) & ~3);
adjustment = newExtraPC - oldExtraPC;
break;
}
}
}
// Now copy the code to the new location. At the same
// time, we adjust all branch targets.
byte newCode[] = new byte[code.length + adjustment];
for (int pc = 0, pcindex = 0; pc < code.length;
pc = pc + opcodeLength(pc), pcindex++) {
int outPos = newOffsets[pcindex];
int inPos = pc;
opcode = (int) code[pc]&0xFF;
switch (opcode) {
case opc_ldc:
case opc_ldc_quick:
case opc_aldc_quick:
case opc_aldc_ind_quick: {
int oldindex = (int)(code[pc+1] & 0xFF);
int index = co[oldindex].index;
if (index >= 0x100) {
if (opcode == opc_aldc_quick) {
newCode[outPos] = (byte) opc_aldc_w_quick;
} else if (opcode == opc_aldc_ind_quick) {
newCode[outPos] = (byte) opc_aldc_ind_w_quick;
} else if (opcode == opc_ldc) {
newCode[outPos] = (byte) opc_ldc_w;
} else {
newCode[outPos] = (byte) opc_ldc_w_quick;
}
newCode[outPos +1] = (byte) ((index >> 8) & 0xFF);
newCode[outPos +2] = (byte) (index & 0xFF);
} else {
newCode[outPos] = (byte) opcode;
newCode[outPos +1] = (byte) index ;
}
break;
}
case opc_ldc_w:
case opc_ldc_w_quick:
case opc_aldc_w_quick:
case opc_aldc_ind_w_quick: {
int oldindex = (int)(((code[pc+1]&0xFF) << 8)
| (code[pc+2]&0xFF));
int index = co[oldindex].index;
if (index < 0x100) {
if (opcode == opc_aldc_w_quick) {
newCode[outPos] = (byte) opc_aldc_quick;
} else if (opcode == opc_aldc_ind_w_quick) {
newCode[outPos] = (byte) opc_aldc_ind_quick;
} else if (opcode == opc_ldc_w) {
newCode[outPos] = (byte) opc_ldc;
} else {
newCode[outPos] = (byte) opc_ldc_quick;
}
newCode[outPos+1] = (byte) index;
} else {
newCode[outPos] = (byte) opcode;
newCode[outPos+1] = (byte) ((index >> 8) & 0xFF);
newCode[outPos+2] = (byte) (index & 0xFF);
}
break;
}
// Remapping branches
case opc_ifeq:
case opc_ifne:
case opc_iflt:
case opc_ifge:
case opc_ifgt:
case opc_ifle:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmplt:
case opc_if_icmpge:
case opc_if_icmpgt:
case opc_if_icmple:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_ifnull:
case opc_ifnonnull:
case opc_goto:
case opc_jsr: {
// Calculate the displacement, sign extend high byte
int displ = (code[pc+1] << 8) | (code[pc+2] & 0xFF);
if (!noCodeCompaction && displ == 3 && opcode == opc_goto) {
break;
}
newCode[outPos] = code[pc];
int branchDest = pc + displ;
int newDest = newOffsets[indexByPC[branchDest]] - outPos;
newCode[outPos+1] = (byte) ((newDest >> 8) & 0xFF);
newCode[outPos+2] = (byte) (newDest & 0xFF);
break;
}
case opc_goto_w:
case opc_jsr_w: {
// Calculate the displacement, sign extend high byte
int displ = getInt(pc+1);
if (!noCodeCompaction &&
displ == 3 && opcode == opc_goto_w) {
break;
}
newCode[outPos] = code[pc];
int branchDest = pc + displ;
int newDest = newOffsets[indexByPC[branchDest]] - outPos;
putInt(newCode, outPos+1, newDest);
break;
}
case opc_tableswitch: {
newCode[outPos] = code[pc];
outPos = (outPos + 4) & ~3;
inPos = (inPos + 4) & ~3;
// Update the default destination
int oldDest = getInt(inPos) + pc;
int newDest = newOffsets[indexByPC[oldDest]]
- newOffsets[pcindex];
putInt(newCode, outPos, newDest);
// Update each of the destinations in the table
int low = getInt(inPos+4);
int high = getInt(inPos+8);
putInt(newCode, outPos+4, low);
putInt(newCode, outPos+8, high);
for (int j = 0; j <= high-low; j++) {
int offset = j * 4 + 12;
oldDest = getInt(inPos + offset) + pc;
newDest = newOffsets[indexByPC[oldDest]]
- newOffsets[pcindex];
putInt(newCode, outPos + offset, newDest);
}
break;
}
case opc_lookupswitch: {
newCode[outPos] = code[pc];
// 0-3 byte pads
outPos = (outPos + 4) & ~3;
inPos = (inPos + 4) & ~3;
// Update the default destination
int oldDest = getInt(inPos) + pc;
int newDest = newOffsets[indexByPC[oldDest]]
- newOffsets[pcindex];
putInt(newCode, outPos, newDest);
// Update each of the pairs of destinations in the list
int pairs = getInt(inPos+4);
putInt(newCode, outPos+4, pairs);
for (int j = 0; j < pairs; j++) {
int offset = (j + 1) * 8;
// First copy the value
putInt(newCode, outPos + offset,
getInt(inPos + offset));
offset += 4;
// Now adjust the destination
oldDest = getInt(inPos + offset) + pc;
newDest = newOffsets[indexByPC[oldDest]]
- newOffsets[pcindex];
putInt(newCode, outPos + offset, newDest);
}
break;
}
// Byte-codes with constant pool access. Remap to new indices
case opc_getfield:
case opc_checkcast:
case opc_getstatic:
case opc_instanceof:
case opc_ldc2_w:
case opc_new:
case opc_putfield:
case opc_putstatic:
case opc_invokevirtual:
case opc_invokestatic:
case opc_invokespecial:
case opc_anewarray:
case opc_anewarray_quick:
case opc_checkcast_quick:
case opc_agetstatic_quick:
case opc_getstatic_quick:
case opc_getstatic2_quick:
case opc_aputstatic_quick:
case opc_putstatic_quick:
case opc_putstatic2_quick:
case opc_agetstatic_checkinit_quick: // CVM
case opc_getstatic_checkinit_quick: // CVM
case opc_getstatic2_checkinit_quick: // CVM
case opc_aputstatic_checkinit_quick: // CVM
case opc_putstatic_checkinit_quick: // CVM
case opc_putstatic2_checkinit_quick: // CVM
case opc_instanceof_quick:
case opc_invokestatic_quick:
case opc_invokestatic_checkinit_quick: // CVM
case opc_new_quick:
case opc_new_checkinit_quick: // CVM
case opc_invokenonvirtual_quick:
case opc_invokesuper_quick:
case opc_ldc2_w_quick:
case opc_invokevirtual_quick_w:
case opc_putfield_quick_w:
case opc_getfield_quick_w: {
int oldindex = (int)(((code[pc+1]&0xFF) << 8)
| (code[pc+2]&0xFF));
int index = co[oldindex].index;
newCode[outPos] = (byte) opcode;
newCode[outPos+1] = (byte) ((index >> 8) & 0xFF);
newCode[outPos+2] = (byte) (index & 0xFF);
break;
}
case opc_multianewarray:
case opc_multianewarray_quick: {
int oldindex = (int)(((code[pc+1]&0xFF) << 8)
| (code[pc+2]&0xFF));
int index = co[oldindex].index;
newCode[outPos] = (byte) opcode;
newCode[outPos+1] = (byte) ((index >> 8) & 0xFF);
newCode[outPos+2] = (byte) (index & 0xFF);
newCode[outPos+3] = (byte) code[pc+3];
break;
}
case opc_invokeinterface:
case opc_invokeinterface_quick: {
int oldindex = (int)(((code[pc+1]&0xFF) << 8)
| (code[pc+2]&0xFF));
int index = co[oldindex].index;
newCode[outPos] = (byte) opcode;
newCode[outPos+1] = (byte) ((index >> 8) & 0xFF);
newCode[outPos+2] = (byte) (index & 0xFF);
newCode[outPos+3] = (byte) code[pc+3];
newCode[outPos+4] = (byte) code[pc+4];
break;
}
case opc_nop: {
if (!noCodeCompaction) {
break; // remove
}
}
default: {
// other bytecode
for (int i = 0; i < opcodeLength(pc); i++) {
newCode[outPos + i] = code[pc + i];
}
break;
}
}
}
// Update the exception table
for (int i = 0; i < exceptionTable.length; i++) {
ExceptionEntry e = exceptionTable[i];
e.startPC = newOffsets[indexByPC[e.startPC]];
e.endPC = newOffsets[indexByPC[e.endPC]];
e.handlerPC = newOffsets[indexByPC[e.handlerPC]];
}
// Update the line number table
LineNumberTableEntry[] lntab = getLineNumberTable();
if (lntab != null) {
for (int i = 0; i < lntab.length; i++) {
LineNumberTableEntry e = lntab[i];
e.startPC = newOffsets[indexByPC[e.startPC]];
}
}
// Update the line number table
LocalVariableTableEntry[] locvartab = getLocalVariableTable();
if (locvartab != null) {
for (int i = 0; i < locvartab.length; i++) {
LocalVariableTableEntry e = locvartab[i];
e.pc0 = newOffsets[indexByPC[e.pc0]];
}
}
// make the changes permanent
code = newCode;
}
public String getMethodName() {
return super.name.string+super.type.string;
}
public int[] getLdcInstructions() {
if (ldcInstructions == null){
findConstantReferences();
}
return ldcInstructions;
}
public int[] getWideConstantRefInstructions() {
if (wideConstantRefInstructions == null){
findConstantReferences();
}
return wideConstantRefInstructions;
}
/*
* Iterate through code and create the array class for anewarray opcode
* and friends.
*/
public void collectArrayForAnewarray(ConstantObject constantPool[], String clname) {
if ( code == null ) return; // no code.
int ncode = code.length;
int opcode;
int index;
for( int i = 0; i < ncode; /*nothing*/){
switch (opcode = (int)code[i]&0xff) {
case opc_tableswitch:
i = (i + 4) & ~3;
int low = getInt( i+4);
int high = getInt( i+8);
i += (high - low + 1) * 4 + 12;
break;
case opc_lookupswitch:
i = (i + 4) & ~3;
int pairs = getInt(i+4);
i += pairs * 8 + 8;
break;
case opc_wide:
switch ((int)code[i+1]&0xff) {
case opc_aload: case opc_iload: case opc_fload:
case opc_lload: case opc_dload: case opc_istore:
case opc_astore: case opc_fstore: case opc_lstore:
case opc_dstore: case opc_ret:
i += 4;
break;
case opc_iinc:
i += 6;
break;
default:
throw new DataFormatException( parent.className + "." +
name.string + ": unknown wide " +
"instruction: " + code[i+1] );
}
break;
case opc_anewarray:
case opc_multianewarray:
case opc_anewarray_quick:
case opc_multianewarray_quick:
index = shortAt(code, i+1);
ConstantObject co = constantPool[index];
if (co instanceof ClassConstant) {
ClassConstant cc = (ClassConstant)co;
String cname = cc.name.string;
/* Construct the name of the array. For multianewarray, the
* resolved constant should already be the array class with
* correct dimensions. */
if (opcode == opc_anewarray || opcode == opc_anewarray_quick) {
if (cname.startsWith("[")) {
cname = "[" + cname;
} else {
cname = "[L"+ cname +";";
}
}
vm.ArrayClassInfo.collectArrayClass(cname, ClassTable.getClassLoader(), false);
}
default:
i += opcLengths[opcode];
break;
}
}
}
}