/*
* @(#)CodeHacker.java 1.41 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 vm;
import components.*;
import java.io.PrintStream;
import consts.Const;
import consts.CVMConst;
import util.DataFormatException;
import util.Localizer;
import util.SignatureIterator;
/*
* This class "quickens" Java bytecodes.
* That is, it looks at the instructions having symbolic references,
* such as get/putfield and methodcalls, attempts to resolve the references
* and re-writes the instructions in their JVM-specific, quick form.
*
* Resolution failures are not (any longer) considered fatal errors, they
* just result in code that cannot be considered as read-only, as more
* resolution will be required at runtime, as we refuse to re-write any
* instructions having unresolved references.
*
* The following is an exception to the above rule:
* References to array classes (having names starting in "[") are
* quickened anyway, as we have confidence that these classes, though not
* instantiated when this code is run, will be instantiated in time.
* (Perhaps this should be under control of a flag?)
*
*/
public
class CodeHacker {
boolean verbose;
PrintStream log;
boolean useLosslessOpcodes = false;
boolean jitOn = false;
ClassInfo java_lang_Object;
private boolean success;
private byte newopcode;
public static int quickenings = 0;
public static int checkinitQuickenings = 0;
public CodeHacker( boolean lossless, boolean jitOn, boolean v ){
verbose = v;
log = System.err;
java_lang_Object = ClassTable.lookupClass("java/lang/Object");
if ( java_lang_Object == null ){
log.println(Localizer.getString(
"codehacker.lookup_failed", "", "java.lang.Object"));
}
useLosslessOpcodes = lossless;
this.jitOn = jitOn;
}
private void lookupFailed( ClassConstant me, FMIrefConstant target ){
log.println(Localizer.getString(
"codehacker.lookup_failed", me.name, target));
success = false;
}
private static int getInt( byte code[], int w ){
return ( (int)code[w] << 24 ) |
(( (int)code[w+1] &0xff ) << 16 ) |
(( (int)code[w+2] &0xff ) << 8 ) |
( (int)code[w+3] &0xff );
}
private static int getUnsignedShort( byte code[], int w ){
return (( (int)code[w] &0xff ) << 8 ) | ( (int)code[w+1] &0xff );
}
private static void putShort( byte code[], int w, short v ){
code[w] = (byte)(v>>>8);
code[w+1] = (byte)v;
}
/*
* Returns true if the reference was fully quickened.
* Returns false if the reference could not be deleted, either because
* the lookup failed, or because we had to use the wide variant.
* Sets this.success to false if the lookup failed or there was no
* usable variant.
*/
private boolean quickenFieldAccess(
MethodInfo m,
ClassConstant me,
byte code[],
int loc,
boolean isStatic,
ConstantObject c[],
int oneWord,
int twoWords,
int refReference,
int wideReference,
int oneWord_checkinit,
int twoWords_checkinit,
int refReference_checkinit
) {
FieldConstant fc = (FieldConstant)c[ getUnsignedShort( code, loc+1 ) ];
FieldInfo fi = fc.find();
if ( fi == null ){
//
// never even try to quicken anything we cannot resolve.
//
lookupFailed( me, fc );
return false;
}
byte newCode;
boolean useCheckinit =
(isStatic && fi.parent.vmClass.hasStaticInitializer);
switch (fc.sig.type.string.charAt(0) ){
case '[':
case 'L':
newCode = (byte)((useCheckinit)
? (refReference_checkinit) : (refReference));
break;
case 'D':
case 'J':
newCode = (byte)((useCheckinit)
? (twoWords_checkinit) : (twoWords));
break;
default:
newCode = (byte)((useCheckinit)
? (oneWord_checkinit) : (oneWord));
break;
}
if ( isStatic ) {
if ( useCheckinit ) {
CodeHacker.checkinitQuickenings++;
m.hasCheckinits = true;
}
CodeHacker.quickenings++;
code[loc] = newCode;
return false; // still references constant pool!
} else {
int fieldOffset = fi.instanceOffset;
// CVM wants field offset as actual WORD offset
// from the start of the object. This requires
// knowing the size of the header.
// JVM just wanted a 0-based word index.
//
fieldOffset += CVMConst.ObjHeaderWords;
if ( fieldOffset < 0 ){
lookupFailed( me, fc );
log.println(Localizer.getString(
"codehacker.negative_field_offset"));
return false;
}
//
// Disallow lossy field access quickening if
// 1 Non-lossy opcodes are requested
// 2 The offset won't fit in a byte
// 3 The JIT is on, and the field is volatile
//
if ( fieldOffset > 0xFF || useLosslessOpcodes ||
(jitOn && fi.isVolatile())) {
if ( wideReference == 0 ){
lookupFailed( me, fc );
log.println(Localizer.getString(
"codehacker.need_wide_op"));
}
code[loc] = (byte)wideReference;
return false; // still references constant pool!
}
code[loc+1] = (byte)fieldOffset;
code[loc+2] = 0;
code[loc] = newCode;
return true;
}
}
/*
* Should a call to the specified methodblock be turned into
* an invokesuper_quick instead of a invokenonvirtual_quick?
*
* The four conditions that have to be satisfied:
* The method isn't private
* The method isn't an <init> method
* The ACC_SUPER flag is set in the current class
* The method's class is a superclass (and not equal) to
* the current class.
*/
private boolean
isSpecialSuperCall( MethodInfo me, MethodInfo callee ){
String name = callee.name.string;
if ( (callee.access&Const.ACC_PRIVATE) != 0 ) return false;
if ( name.equals("<init>") ) return false;
ClassInfo myclass = me.parent;
ClassInfo hisclass = callee.parent;
if ( myclass == hisclass ) return false;
// walk up my chain, looking for other's class
while ( myclass != null ){
myclass = myclass.superClassInfo;
if ( myclass == hisclass ) return true;
}
return false;
}
private boolean quickenMethod(MethodInfo m, ConstantObject c[])
throws DataFormatException {
byte code[] = m.code;
int list[] = m.getLdcInstructions();
ConstantObject constObj;
FieldConstant fc;
MethodConstant mc;
NameAndTypeConstant nt;
ClassConstant classConst;
String t;
ClassConstant me = m.parent.thisClass;
MethodInfo mi;
ClassInfo classInfo;
success = true;
if ( list != null ){
for ( int i = 0; i < list.length; i++ ){
int loc = list[i];
if ( loc < 0 ) continue;
switch( (int)code[loc]&0xff ){
case Const.opc_ldc:
//
// no danger of lookup failure here,
// so don't even examine the referenced object.
//
constObj = c[(int)code[loc+1]&0xff];
if (constObj instanceof StringConstant) {
code[loc] = (byte)Const.opc_aldc_quick;
} else if (!(constObj instanceof ClassConstant)) {
code[loc] = (byte)Const.opc_ldc_quick;
}
constObj.incLdcReference();
break;
default:
throw new DataFormatException( "unexpected opcode in ldc="+
((int)code[loc]&0xff)+" at loc="+loc+
" in "+m.qualifiedName() );
}
}
}
list = m.getWideConstantRefInstructions();
if ( list != null ){
MethodInfo[] tList = null;
int tListIndex = 0; // index into tList
if (VMMethodInfo.SAVE_TARGET_METHODS) {
tList = new MethodInfo[list.length];
}
for ( int i = 0; i < list.length; i++ ){
int loc = list[i];
if ( loc < 0 ) continue;
constObj = c[getUnsignedShort(code, loc+1)];
if (!constObj.isResolved()) {
//
// don't try to quicken unresolved references.
// this is not fatal!
//
if (verbose){
log.println(Localizer.getString(
"codehacker.could_not_quicken",
m.qualifiedName(), constObj));
}
continue;
}
switch( (int)code[loc]&0xff ){
case Const.opc_ldc_w:
if (constObj instanceof StringConstant) {
code[loc] = (byte)Const.opc_aldc_w_quick;
} else if (!(constObj instanceof ClassConstant)) {
code[loc] = (byte)Const.opc_ldc_w_quick;
}
constObj.incLdcReference();
break;
case Const.opc_ldc2_w:
code[loc] = (byte)Const.opc_ldc2_w_quick;
break;
case Const.opc_getstatic:
// All the accesses to static field are done using
// checkinit version of opcodes.
quickenFieldAccess( m, me, code, loc, true, c,
Const.opc_getstatic_quick,
Const.opc_getstatic2_quick,
Const.opc_agetstatic_quick, 0,
Const.opc_getstatic_checkinit_quick,
Const.opc_getstatic2_checkinit_quick,
Const.opc_agetstatic_checkinit_quick);
break;
case Const.opc_putstatic:
// All the accesses to static field are done using
// checkinit version of opcodes.
quickenFieldAccess( m, me, code, loc, true, c,
Const.opc_putstatic_quick,
Const.opc_putstatic2_quick,
Const.opc_aputstatic_quick, 0,
Const.opc_putstatic_checkinit_quick,
Const.opc_putstatic2_checkinit_quick,
Const.opc_aputstatic_checkinit_quick);
break;
case Const.opc_getfield:
if (quickenFieldAccess( m, me, code, loc, false, c,
Const.opc_getfield_quick,
Const.opc_getfield2_quick,
Const.opc_agetfield_quick,
Const.opc_getfield_quick_w, 0, 0, 0) ){
// doesn't reference constant pool any more
list[i] = -1;
}
break;
case Const.opc_putfield:
if (quickenFieldAccess( m, me, code, loc, false, c,
Const.opc_putfield_quick,
Const.opc_putfield2_quick,
Const.opc_aputfield_quick,
Const.opc_putfield_quick_w, 0, 0, 0) ){
// doesn't reference constant pool any more
list[i] = -1;
}
break;
case Const.opc_invokevirtual:
mc = (MethodConstant)constObj;
mi = mc.find(); // must succeed, if isResolved succeeded!
int x = -1;
if (mi.parent.isFinal() || mi.isFinalMember()) {
code[loc] = (byte)Const.opc_invokenonvirtual_quick;
} else if (!jitOn && (x=mi.methodTableIndex )<= 255 &&
! useLosslessOpcodes ){
// Do really quick quickening, but only if the JIT
// is not on
if ( mi.parent == java_lang_Object ){
newopcode = (byte)Const.opc_invokevirtualobject_quick;
} else {
String sig = mc.sig.type.string;
new SignatureIterator(sig) {
public void do_array(int d, int st, int end) {
newopcode =
(byte)Const.opc_ainvokevirtual_quick;
}
public void do_object(int st, int end) {
newopcode =
(byte)Const.opc_ainvokevirtual_quick;
}
public void do_scalar(char c) {
switch(c) {
case Const.SIGC_LONG:
case Const.SIGC_DOUBLE:
newopcode = (byte)
Const.opc_dinvokevirtual_quick;
break;
case Const.SIGC_VOID:
newopcode = (byte)
Const.opc_vinvokevirtual_quick;
break;
default:
newopcode = (byte)
Const.opc_invokevirtual_quick;
break;
}
}
}.iterate_returntype();
}
code[loc] = newopcode;
code[loc+1] = (byte) x;
code[loc+2] = (byte)mi.argsSize;
list[i] = -1; // doesn't reference constant pool any more
if (VMMethodInfo.SAVE_TARGET_METHODS) {
// Save the target method info for inlining
tList[tListIndex++] = mi;
}
} else {
//
// big index OR useLosslessOpcodes OR jit
//
code[loc] = (byte)Const.opc_invokevirtual_quick_w;
}
break;
case Const.opc_invokeinterface:
code[loc] = (byte)Const.opc_invokeinterface_quick;
break;
case Const.opc_invokestatic:
mc = (MethodConstant)constObj;
mi = mc.find(); // must succeed, if isResolved succeeded!
if (mi.parent.vmClass.hasStaticInitializer) {
CodeHacker.checkinitQuickenings++;
m.hasCheckinits = true;
}
CodeHacker.quickenings++;
code[loc] = (byte)((mi.parent.vmClass.hasStaticInitializer)
? Const.opc_invokestatic_checkinit_quick
: Const.opc_invokestatic_quick);
break;
case Const.opc_new:
/*
* If the class to be instantiated has a static
* initializer, we must quicken opc_new into
* opc_new_checkinit_quick rather than
* opc_new_quick.
*/
classConst = (ClassConstant)constObj;
// must succeed, if isResolved succeeded!
classInfo = classConst.find();
if (classInfo.vmClass.hasStaticInitializer) {
CodeHacker.checkinitQuickenings++;
m.hasCheckinits = true;
}
CodeHacker.quickenings++;
code[loc] = (byte)((classInfo.vmClass.hasStaticInitializer)
? Const.opc_new_checkinit_quick
: Const.opc_new_quick);
break;
case Const.opc_anewarray:
code[loc] = (byte)Const.opc_anewarray_quick;
break;
case Const.opc_checkcast:
code[loc] = (byte)Const.opc_checkcast_quick;
break;
case Const.opc_instanceof:
code[loc] = (byte)Const.opc_instanceof_quick;
break;
case Const.opc_multianewarray:
code[loc] = (byte)Const.opc_multianewarray_quick;
break;
case Const.opc_invokespecial:
mc = (MethodConstant)constObj;
mi = mc.find(); // must succeed.
byte newop;
if ( false ){
newop = (byte)Const.opc_invokesuper_quick;
} else {
newop = (byte)Const.opc_invokenonvirtual_quick;
}
code[loc] = newop;
break;
default:
throw new DataFormatException( "unexpected opcode in wideConstantRef="+
((int)code[loc]&0xff)+" at loc="+loc+
" in "+m.qualifiedName() );
}
}
// Alloc and copy to new targetMethods array
if (VMMethodInfo.SAVE_TARGET_METHODS) {
m.targetMethods = new MethodInfo[tListIndex];
System.arraycopy(tList, 0, m.targetMethods, 0, tListIndex);
}
}
return success;
}
public boolean
quickenAllMethodsInClass(ClassInfo c) {
ConstantObject constants[] = c.getConstantPool().getConstants();
MethodInfo methods[]= c.methods;
int numberOfMethods = methods.length;
int i = 0;
boolean result = true;
try {
for (i = 0; i < numberOfMethods; i++) {
if (!quickenMethod(methods[i], constants)) {
result = false;
}
}
} catch (DataFormatException e) {
System.err.println("Quickening "+methods[i].qualifiedName()+
" got exception:");
e.printStackTrace();
return false;
}
return result;
}
}