Mirror of the JODE repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
jode/jode/jode/obfuscator/LocalOptimizer.java.in

907 lines
26 KiB

/* LocalOptimizer Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.obfuscator;
import java.util.*;
import jode.bytecode.*;
import jode.AssertError;
import jode.GlobalOptions;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.ListIterator;
/**
* This class takes some bytecode and tries to minimize the number
* of locals used. It will also remove unnecessary stores.
*
* This class can only work on verified code. There should also be no
* deadcode, since the verifier doesn't check that deadcode behaves
* okay.
*
* This is done in two phases. First we determine which locals are
* the same, and which locals have a overlapping life time. In the
* second phase we will then redistribute the locals with a coloring
* graph algorithm.
*
* The idea for the first phase is: For each read we follow the
* instruction flow backward to find the corresponding writes. We can
* also merge with another control flow that has a different read, in
* this case we merge with that read, too.
*
* The tricky part is the subroutine handling. We follow the local
* that is used in a ret and find the corresponding jsr target (there
* must be only one, if the verifier should accept this class). While
* we do this we remember in the info of the ret, which locals are
* used in that subroutine.
*
* When we know the jsr target<->ret correlation, we promote from the
* nextByAddr of every jsr the locals that are accessed by the
* subroutine to the corresponding ret and the others to the jsr. Also
* we will promote all reads from the jsr targets to the jsr.
*
* If you think this might be to complicated, keep in mind that jsr's
* are not only left by the ret instructions, but also "spontanously"
* (by not reading the return address again).
*/
public class LocalOptimizer implements Opcodes, CodeTransformer {
/**
* This class keeps track of which locals must be the same, which
* name and type each local (if there is a local variable table) and
* which other locals have an intersecting life time.
*/
class LocalInfo {
LocalInfo shadow = null;
public LocalInfo getReal() {
LocalInfo real = this;
while (real.shadow != null)
real = real.shadow;
return real;
}
String name;
String type;
Vector usingInstrs = new Vector();
Vector conflictingLocals = new Vector();
int size;
int newSlot = -1;
LocalInfo() {
}
LocalInfo(InstrInfo instr) {
usingInstrs.addElement(instr);
}
void conflictsWith(LocalInfo l) {
if (shadow != null) {
getReal().conflictsWith(l);
} else {
l = l.getReal();
if (!conflictingLocals.contains(l)) {
conflictingLocals.addElement(l);
l.conflictingLocals.addElement(this);
}
}
}
void combineInto(LocalInfo l) {
if (shadow != null) {
getReal().combineInto(l);
return;
}
l = l.getReal();
if (this == l)
return;
shadow = l;
if (shadow.name == null) {
shadow.name = name;
shadow.type = type;
}
Enumeration enum = usingInstrs.elements();
while (enum.hasMoreElements()) {
InstrInfo instr = (InstrInfo) enum.nextElement();
instr.local = l;
l.usingInstrs.addElement(instr);
}
}
public int getFirstAddr() {
int minAddr = Integer.MAX_VALUE;
Enumeration enum = usingInstrs.elements();
while (enum.hasMoreElements()) {
InstrInfo info = (InstrInfo) enum.nextElement();
if (info.instr.getAddr() < minAddr)
minAddr = info.instr.getAddr();
}
return minAddr;
}
}
/**
* This class contains information for each instruction.
*/
class InstrInfo {
/**
* The LocalInfo that this instruction manipulates, or null
* if this is not an ret, iinc, load or store instruction.
*/
LocalInfo local;
/**
* For each slot, this contains the InstrInfo of one of the
* next Instruction, that may read from that slot, without
* prior writing. */
InstrInfo[] nextReads;
/**
* This only has a value for ret instructions. In that case
* this bitset contains all locals, that may be used between
* jsr and ret.
*/
BitSet usedBySub;
/**
* For each slot if get() is true, no instruction may read
* this slot, since it may contain different locals, depending
* on flow.
*/
LocalInfo[] lifeLocals;
/**
* If instruction is the destination of a jsr, this contains
* the single allowed ret instruction info, or null if there
* is no ret at all (or not yet detected).
*/
InstrInfo retInfo;
/**
* If this instruction is a ret, this contains the single
* allowed jsr target to which this ret belongs.
*/
InstrInfo jsrTargetInfo;
/**
* The Instruction of this info
*/
Instruction instr;
/**
* The next info in the chain.
*/
InstrInfo nextInfo;
}
BytecodeInfo bc;
InstrInfo firstInfo;
Stack changedInfos;
Hashtable instrInfos;
boolean produceLVT;
int maxlocals;
LocalInfo[] paramLocals;
public LocalOptimizer() {
}
/**
* Merges the given vector to a new vector. Both vectors may
* be null in which case they are interpreted as empty vectors.
* The vectors will never changed, but the result may be one
* of the given vectors.
*/
Vector merge(Vector v1, Vector v2) {
if (v1 == null || v1.isEmpty())
return v2;
if (v2 == null || v2.isEmpty())
return v1;
Vector result = (Vector) v1.clone();
Enumeration enum = v2.elements();
while (enum.hasMoreElements()) {
Object elem = enum.nextElement();
if (!result.contains(elem))
result.addElement(elem);
}
return result;
}
void promoteReads(InstrInfo info, Instruction preInstr,
BitSet mergeSet, boolean inverted) {
InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr);
int omitLocal = -1;
if (preInstr.getOpcode() >= opc_istore
&& preInstr.getOpcode() <= opc_astore) {
/* This is a store */
omitLocal = preInstr.getLocalSlot();
if (info.nextReads[omitLocal] != null)
preInfo.local.combineInto(info.nextReads[omitLocal].local);
}
for (int i=0; i < maxlocals; i++) {
if (info.nextReads[i] != null && i != omitLocal
&& (mergeSet == null || mergeSet.get(i) != inverted)) {
if (preInfo.nextReads[i] == null) {
preInfo.nextReads[i] = info.nextReads[i];
changedInfos.push(preInfo);
} else {
preInfo.nextReads[i].local
.combineInto(info.nextReads[i].local);
}
}
}
}
void promoteReads(InstrInfo info, Instruction preInstr) {
promoteReads(info, preInstr, null, false);
}
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt,
int slot, int addr) {
LocalVariableInfo match = null;
for (int i=0; i < lvt.length; i++) {
if (lvt[i].slot == slot
&& lvt[i].start.getAddr() <= addr
&& lvt[i].end.getAddr() >= addr) {
if (match != null
&& (!match.name.equals(lvt[i].name)
|| !match.type.equals(lvt[i].type))) {
/* Multiple matches..., give no info */
return null;
}
match = lvt[i];
}
}
return match;
}
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt,
Instruction instr) {
int addr;
if (instr.getOpcode() >= opc_istore
&& instr.getOpcode() <= opc_astore)
addr = instr.getNextAddr();
else
addr = instr.getAddr();
return findLVTEntry(lvt, instr.getLocalSlot(), addr);
}
public void calcLocalInfo() {
maxlocals = bc.getMaxLocals();
Handler[] handlers = bc.getExceptionHandlers();
LocalVariableInfo[] lvt = bc.getLocalVariableTable();
if (lvt != null)
produceLVT = true;
/* Initialize paramLocals */
{
String methodType = bc.getMethodInfo().getType();
int paramCount = (bc.getMethodInfo().isStatic() ? 0 : 1)
+ TypeSignature.getArgumentSize(methodType);
paramLocals = new LocalInfo[paramCount];
int slot = 0;
if (!bc.getMethodInfo().isStatic()) {
LocalInfo local = new LocalInfo();
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, 0, 0);
if (lvi != null) {
local.name = lvi.name;
local.type = lvi.type;
}
}
local.size = 1;
paramLocals[slot++] = local;
}
int pos = 1;
while (pos < methodType.length()
&& methodType.charAt(pos) != ')') {
LocalInfo local = new LocalInfo();
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, slot, 0);
if (lvi != null) {
local.name = lvi.name;
}
}
int start = pos;
pos = TypeSignature.skipType(methodType, pos);
local.type = methodType.substring(start, pos);
local.size = TypeSignature.getTypeSize(local.type);
paramLocals[slot] = local;
slot += local.size;
}
}
/* Initialize the InstrInfos and LocalInfos
*/
changedInfos = new Stack();
instrInfos = new Hashtable();
{
InstrInfo info = firstInfo = new InstrInfo();
Iterator i = bc.getInstructions().iterator();
while (true) {
Instruction instr = (Instruction) i.next();
instrInfos.put(instr, info);
info.instr = instr;
info.nextReads = new InstrInfo[maxlocals];
if (instr.hasLocalSlot()) {
info.local = new LocalInfo(info);
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, instr);
if (lvi != null) {
info.local.name = lvi.name;
info.local.type = lvi.type;
}
}
info.local.size = 1;
switch (instr.getOpcode()) {
case opc_lload: case opc_dload:
info.local.size = 2;
/* fall through */
case opc_iload: case opc_fload: case opc_aload:
case opc_iinc:
/* this is a load instruction */
info.nextReads[instr.getLocalSlot()] = info;
changedInfos.push(info);
break;
case opc_ret:
/* this is a ret instruction */
info.usedBySub = new BitSet();
info.nextReads[instr.getLocalSlot()] = info;
changedInfos.push(info);
break;
case opc_lstore: case opc_dstore:
info.local.size = 2;
//case opc_istore: case opc_fstore: case opc_astore:
}
}
if (!i.hasNext())
break;
info = info.nextInfo = new InstrInfo();
}
}
/* find out which locals are the same.
*/
while (!changedInfos.isEmpty()) {
InstrInfo info = (InstrInfo) changedInfos.pop();
Instruction instr = info.instr;
/* Mark the local as used in all ret instructions */
if (instr.hasLocalSlot()) {
int slot = instr.getLocalSlot();
for (int i=0; i< maxlocals; i++) {
InstrInfo retInfo = info.nextReads[i];
if (retInfo != null
&& retInfo.instr.getOpcode() == opc_ret
&& !retInfo.usedBySub.get(slot)) {
retInfo.usedBySub.set(slot);
if (retInfo.jsrTargetInfo != null)
changedInfos.push(retInfo.jsrTargetInfo);
}
}
}
Instruction prevInstr = instr.getPrevByAddr();
if (prevInstr != null) {
if (prevInstr.getOpcode() == opc_jsr) {
/* Prev instr is a jsr, promote reads to the
* corresponding ret.
*/
InstrInfo jsrInfo =
(InstrInfo) instrInfos.get(prevInstr.getSingleSucc());
if (jsrInfo.retInfo != null) {
/* Now promote reads that are modified by the
* subroutine to the ret, and those that are not
* to the jsr instruction.
*/
promoteReads(info, jsrInfo.retInfo.instr,
jsrInfo.retInfo.usedBySub, false);
promoteReads(info, prevInstr,
jsrInfo.retInfo.usedBySub, true);
}
} else if (!prevInstr.doesAlwaysJump())
promoteReads(info, prevInstr);
}
if (instr.getPreds() != null) {
for (int i = 0; i < instr.getPreds().length; i++) {
Instruction predInstr = instr.getPreds()[i];
if (instr.getPreds()[i].getOpcode() == opc_jsr) {
/* This is the target of a jsr instr.
*/
if (info.instr.getOpcode() != opc_astore) {
/* XXX Grrr, the bytecode verifier doesn't
* test if a jsr starts with astore. So
* it is possible to do something else
* before putting the ret address into a
* local. */
throw new AssertError("Non standard jsr");
}
InstrInfo retInfo = info.nextInfo.nextReads
[info.instr.getLocalSlot()];
if (retInfo != null) {
if (retInfo.instr.getOpcode() != opc_ret)
throw new AssertError
("reading return address");
info.retInfo = retInfo;
retInfo.jsrTargetInfo = info;
/* Now promote reads from the instruction
* after the jsr to the ret instruction if
* they are modified by the subroutine,
* and to the jsr instruction otherwise.
*/
Instruction nextInstr = predInstr.getNextByAddr();
InstrInfo nextInfo
= (InstrInfo) instrInfos.get(nextInstr);
promoteReads(nextInfo, retInfo.instr,
retInfo.usedBySub, false);
promoteReads(nextInfo, predInstr,
retInfo.usedBySub, true);
}
}
promoteReads(info, instr.getPreds()[i]);
}
}
for (int i=0; i < handlers.length; i++) {
if (handlers[i].catcher == instr) {
for (Instruction preInstr = handlers[i].start;
preInstr != handlers[i].end.getNextByAddr();
preInstr = preInstr.getNextByAddr()) {
promoteReads(info, preInstr);
}
}
}
}
changedInfos = null;
/* Now merge with the parameters
* The params should be the locals in firstInfo.nextReads
*/
for (int i=0; i< paramLocals.length; i++) {
if (firstInfo.nextReads[i] != null) {
firstInfo.nextReads[i].local.combineInto(paramLocals[i]);
paramLocals[i] = paramLocals[i].getReal();
}
}
}
public void stripLocals() {
ListIterator iter = bc.getInstructions().listIterator();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
Instruction instr = (Instruction) iter.next();
if (info.local != null && info.local.usingInstrs.size() == 1) {
/* If this is a store, whose value is never read; it can
* be removed, i.e replaced by a pop. */
switch (instr.getOpcode()) {
case opc_istore:
case opc_fstore:
case opc_astore:
iter.set(new Instruction(opc_pop));
break;
case opc_lstore:
case opc_dstore:
iter.set(new Instruction(opc_pop2));
break;
default:
}
}
}
}
void distributeLocals(Vector locals) {
if (locals.size() == 0)
return;
/* Find the local with the least conflicts. */
int min = Integer.MAX_VALUE;
LocalInfo bestLocal = null;
Enumeration enum = locals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo) enum.nextElement();
int conflicts = 0;
Enumeration conflenum = li.conflictingLocals.elements();
while (conflenum.hasMoreElements()) {
if (((LocalInfo)conflenum.nextElement()).newSlot != -2)
conflicts++;
}
if (conflicts < min) {
min = conflicts;
bestLocal = li;
}
}
/* Mark the local as taken */
locals.removeElement(bestLocal);
bestLocal.newSlot = -2;
/* Now distribute the remaining locals recursively. */
distributeLocals(locals);
/* Finally find a new slot */
next_slot:
for (int slot = 0; ; slot++) {
Enumeration conflenum = bestLocal.conflictingLocals.elements();
while (conflenum.hasMoreElements()) {
LocalInfo conflLocal = (LocalInfo)conflenum.nextElement();
if (bestLocal.size == 2 && conflLocal.newSlot == slot+1) {
slot++;
continue next_slot;
}
if (conflLocal.size == 2 && conflLocal.newSlot+1 == slot)
continue next_slot;
if (conflLocal.newSlot == slot) {
if (conflLocal.size == 2)
slot++;
continue next_slot;
}
}
bestLocal.newSlot = slot;
break;
}
}
public void distributeLocals() {
/* give locals new slots. This is a graph coloring
* algorithm (the optimal solution is NP complete, but this
* should be a good approximation).
*/
/* first give the params the same slot as they had before.
*/
for (int i=0; i<paramLocals.length; i++)
if (paramLocals[i] != null)
paramLocals[i].newSlot = i;
/* Now calculate the conflict settings.
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.instr.getOpcode() >= BytecodeInfo.opc_istore
&& info.instr.getOpcode() <= BytecodeInfo.opc_astore) {
/* This is a store. It conflicts with every local, whose
* value will be read without write.
*
* If this is inside a ret, it also conflicts with
* locals, that are not used inside, and where any jsr
* would conflict with.
*/
for (int i=0; i < maxlocals; i++) {
if (i != info.instr.getLocalSlot()
&& info.nextReads[i] != null)
info.local.conflictsWith(info.nextReads[i].local);
if (info.nextInfo.nextReads[i] != null
&& info.nextInfo.nextReads[i].jsrTargetInfo != null) {
Instruction[] jsrs = info.nextInfo.nextReads[i]
.jsrTargetInfo.instr.getPreds();
for (int j=0; j< jsrs.length; j++) {
InstrInfo jsrInfo
= (InstrInfo) instrInfos.get(jsrs[j]);
for (int k=0; k < maxlocals; k++) {
if (!info.nextInfo.nextReads[i].usedBySub
.get(k)
&& jsrInfo.nextReads[k] != null)
info.local.conflictsWith
(jsrInfo.nextReads[k].local);
}
}
}
}
}
}
/* Now put the locals that need a color into a vector.
*/
Vector locals = new Vector();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.local != null
&& info.local.newSlot == -1
&& !locals.contains(info.local))
locals.addElement(info.local);
}
/* Now distribute slots recursive.
*/
distributeLocals(locals);
/* Update the instructions and calculate new maxlocals.
*/
maxlocals = paramLocals.length;
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.local != null) {
if (info.local.newSlot+info.local.size > maxlocals)
maxlocals = info.local.newSlot + info.local.size;
info.instr.setLocalSlot(info.local.newSlot);
}
}
bc.setMaxLocals(maxlocals);
/* Update LocalVariableTable
*/
if (produceLVT)
buildNewLVT();
}
private InstrInfo CONFLICT = new InstrInfo();
boolean promoteLifeLocals(LocalInfo[] newLife, InstrInfo nextInfo) {
if (nextInfo.lifeLocals == null) {
nextInfo.lifeLocals = (LocalInfo[]) newLife.clone();
return true;
}
boolean changed = false;
for (int i=0; i< maxlocals; i++) {
LocalInfo local = nextInfo.lifeLocals[i];
if (local == null)
/* A conflict has already happened, or this slot
* may not have been initialized. */
continue;
local = local.getReal();
LocalInfo newLocal = newLife[i];
if (newLocal != null)
newLocal = newLocal.getReal();
if (local != newLocal) {
nextInfo.lifeLocals[i] = null;
changed = true;
}
}
return changed;
}
public void buildNewLVT() {
/* First we recalculate the usedBySub, to use the new local numbers.
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo)
if (info.usedBySub != null)
info.usedBySub = new BitSet();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.local != null) {
for (int i=0; i < info.nextReads.length; i++) {
if (info.nextReads[i] != null
&& info.nextReads[i].instr.getOpcode() == opc_ret)
info.nextReads[i].usedBySub.set(info.local.newSlot);
}
}
}
/* Now we begin with the first Instruction and follow program flow.
* We remember which locals are life in lifeLocals.
*/
firstInfo.lifeLocals = new LocalInfo[maxlocals];
for (int i=0; i < paramLocals.length; i++)
firstInfo.lifeLocals[i] = paramLocals[i];
Stack changedInfo = new Stack();
changedInfo.push(firstInfo);
Handler[] handlers = bc.getExceptionHandlers();
while (!changedInfo.isEmpty()) {
InstrInfo info = (InstrInfo) changedInfo.pop();
Instruction instr = info.instr;
LocalInfo[] newLife = info.lifeLocals;
if (instr.hasLocalSlot()) {
int slot = instr.getLocalSlot();
LocalInfo instrLocal = info.local.getReal();
newLife = (LocalInfo[]) newLife.clone();
newLife[slot] = instrLocal;
if (instrLocal.name != null) {
for (int j=0; j< newLife.length; j++) {
if (j != slot
&& newLife[j] != null
&& instrLocal.name.equals(newLife[j].name)) {
/* This local changed the slot. */
newLife[j] = null;
}
}
}
}
if (!instr.doesAlwaysJump()) {
InstrInfo nextInfo = info.nextInfo;
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
if (instr.hasSuccs()) {
Instruction[] succs = instr.getSuccs();
for (int i = 0; i < succs.length; i++) {
InstrInfo nextInfo
= (InstrInfo) instrInfos.get(succs[i]);
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
}
for (int i=0; i < handlers.length; i++) {
if (handlers[i].start.compareTo(instr) <= 0
&& handlers[i].end.compareTo(instr) >= 0) {
InstrInfo nextInfo
= (InstrInfo) instrInfos.get(handlers[i].catcher);
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
}
if (info.instr.getOpcode() == opc_jsr) {
/* On a jsr we do a special merge */
Instruction jsrTargetInstr = info.instr.getSingleSucc();
InstrInfo jsrTargetInfo
= (InstrInfo) instrInfos.get(jsrTargetInstr);
InstrInfo retInfo = jsrTargetInfo.retInfo;
if (retInfo != null && retInfo.lifeLocals != null) {
LocalInfo[] retLife = (LocalInfo[]) newLife.clone();
for (int i=0; i< maxlocals; i++) {
if (retInfo.usedBySub.get(i))
retLife[i] = retInfo.lifeLocals[i];
}
if (promoteLifeLocals(retLife, info.nextInfo))
changedInfo.push(info.nextInfo);
}
}
if (info.jsrTargetInfo != null) {
/* On a ret we do a special merge */
Instruction jsrTargetInstr = info.jsrTargetInfo.instr;
for (int j=0; j< jsrTargetInstr.getPreds().length; j++) {
InstrInfo jsrInfo
= (InstrInfo) instrInfos.get(jsrTargetInstr.getPreds()[j]);
if (jsrInfo.lifeLocals == null)
/* life locals are not calculated, yet */
continue;
LocalInfo[] retLife = (LocalInfo[]) newLife.clone();
for (int i=0; i< maxlocals; i++) {
if (!info.usedBySub.get(i))
retLife[i] = jsrInfo.lifeLocals[i];
}
if (promoteLifeLocals(retLife, jsrInfo.nextInfo))
changedInfo.push(jsrInfo.nextInfo);
}
}
}
Vector lvtEntries = new Vector();
LocalVariableInfo[] lvi = new LocalVariableInfo[maxlocals];
LocalInfo[] currentLocal = new LocalInfo[maxlocals];
for (int i=0; i< paramLocals.length; i++) {
if (paramLocals[i] != null) {
currentLocal[i] = paramLocals[i];
if (currentLocal[i].name != null) {
lvi[i] = new LocalVariableInfo();
lvtEntries.addElement(lvi[i]);
lvi[i].name = currentLocal[i].name; /* XXX obfuscation? */
lvi[i].type = Main.getClassBundle()
.getTypeAlias(currentLocal[i].type);
lvi[i].start = (Instruction) bc.getInstructions().get(0);
lvi[i].slot = i;
}
}
}
Instruction lastInstr = null;
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
for (int i=0; i< maxlocals; i++) {
LocalInfo lcl = info.lifeLocals != null ? info.lifeLocals[i]
: null;
if (lcl != currentLocal[i]
&& (lcl == null || currentLocal[i] == null
|| lcl.name == null || lcl.type == null
|| !lcl.name.equals(currentLocal[i].name)
|| !lcl.type.equals(currentLocal[i].type))) {
if (lvi[i] != null) {
lvi[i].end = info.instr.getPrevByAddr();
}
lvi[i] = null;
currentLocal[i] = lcl;
if (currentLocal[i] != null
&& currentLocal[i].name != null
&& currentLocal[i].type != null) {
lvi[i] = new LocalVariableInfo();
lvtEntries.addElement(lvi[i]);
lvi[i].name = currentLocal[i].name;
lvi[i].type = Main.getClassBundle()
.getTypeAlias(currentLocal[i].type);
lvi[i].start = info.instr;
lvi[i].slot = i;
}
}
}
lastInstr = info.instr;
}
for (int i=0; i< maxlocals; i++) {
if (lvi[i] != null)
lvi[i].end = lastInstr;
}
LocalVariableInfo[] lvt = new LocalVariableInfo[lvtEntries.size()];
lvtEntries.copyInto(lvt);
bc.setLocalVariableTable(lvt);
}
public void dumpLocals() {
Vector locals = new Vector();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
GlobalOptions.err.println(info.instr.getDescription());
GlobalOptions.err.print("nextReads: ");
for (int i=0; i<maxlocals; i++)
if (info.nextReads[i] == null)
GlobalOptions.err.print("-,");
else
GlobalOptions.err.print(info.nextReads[i].instr.getAddr()+",");
if (info.usedBySub != null)
GlobalOptions.err.print(" usedBySub: "+info.usedBySub);
if (info.retInfo != null)
GlobalOptions.err.print(" ret info: "
+info.retInfo.instr.getAddr());
if (info.jsrTargetInfo != null)
GlobalOptions.err.print(" jsr info: "
+info.jsrTargetInfo.instr.getAddr());
GlobalOptions.err.println();
if (info.local != null && !locals.contains(info.local))
locals.addElement(info.local);
}
Enumeration enum = locals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo) enum.nextElement();
int slot = ((InstrInfo)li.usingInstrs.elementAt(0))
.instr.getLocalSlot();
GlobalOptions.err.print("Slot: "+slot+" conflicts:");
Enumeration enum1 = li.conflictingLocals.elements();
while (enum1.hasMoreElements()) {
LocalInfo cfl = (LocalInfo)enum1.nextElement();
GlobalOptions.err.print(cfl.getFirstAddr()+", ");
}
GlobalOptions.err.println();
GlobalOptions.err.print(li.getFirstAddr());
GlobalOptions.err.print(" instrs: ");
Enumeration enum2 = li.usingInstrs.elements();
while (enum2.hasMoreElements())
GlobalOptions.err.print(((InstrInfo)enum2.nextElement())
.instr.getAddr()+", ");
GlobalOptions.err.println();
}
GlobalOptions.err.println("-----------");
}
public void transformCode(BytecodeInfo bytecode) {
this.bc = bytecode;
calcLocalInfo();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_LOCALS) != 0) {
GlobalOptions.err.println("Before Local Optimization: ");
dumpLocals();
}
stripLocals();
distributeLocals();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_LOCALS) != 0) {
GlobalOptions.err.println("After Local Optimization: ");
dumpLocals();
}
firstInfo = null;
changedInfos = null;
instrInfos = null;
paramLocals = null;
}
}