|
|
|
/* VariableStack Copyright (C) 1999-2002 Jochen Hoenicke.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser 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 Lesser General Public License
|
|
|
|
* along with this program; see the file COPYING.LESSER. If not, write to
|
|
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
package net.sf.jode.flow;
|
|
|
|
import net.sf.jode.decompiler.LocalInfo;
|
|
|
|
import net.sf.jode.expr.Expression;
|
|
|
|
import net.sf.jode.expr.LocalLoadOperator;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class represents the state of the stack at various points in
|
|
|
|
* the program. Each entry is a anonymous local, which is used instead
|
|
|
|
* of the PUSH / stack_i statements. <p>
|
|
|
|
*
|
|
|
|
* This class is immutable, but note, that the local infos can get merged.
|
Documentation updates (INSTALL, javadoc).
Added JUnit Test cases.
* build.xml: Big update.
* net/sf/jode/bytecode/BasicBlock.java:
(updateMaxStackLocals): new method to calculate maxStack and
maxLocals.
(setBlocks): fixed calculation of handlers, call updateMaxLocals.
* net/sf/jode/bytecode/BasicBlockReader.java:
(maxLocals, maxStack): new fields.
(readCode): read maxStack/Locals into private fields.
(convert): check that maxStack/Locals match what we calculate.
* net/sf/jode/bytecode/BinaryInfo.java:
(getKnownAttributeCount): renamed to...
(getAttributeCount): ... this, and also count internal attributes.
Made it protected.
(readAttribute): made protected.
(drop): made protected.
(prepareAttributes): made protected.
(writeKnownAttributes): removed.
(writeAttributes): made protected, use getAttributeCount.
Changed policy: it doesn't call writeKnownAttribute, but instead
it expects sub classes to override this method.
(getAttributeSize): made protected, subclasses should override it.
Changed all subclasses to new policy.
* net/sf/jode/bytecode/Block.java:
(lineNr): Removed, it wasn't used.
(pop,push): Removed, replaced by ...
(maxpop,maxpush,delta): ... these, with slightly changed semantics.
(stackHeight): New variable.
(Block): Default Constructor doesn't initialize fields now.
(getCatchers): Renamed to ...
(getHandlers): ... this, changed all callers.
(initCode): Calculate maxpop, maxpush, delta correctly.
(getStackPopPush): Changed accordingly to new fields.
(setCode): Removed debugging output for illegal contents.
* net/sf/jode/bytecode/Classes.java: Reworked handling of inner
classes.
(innerClasses): Field mustn't be null anymore when loaded.
(setName): Update class in classpath.
* net/sf/jode/bytecode/ClassPath.java:
(renameClassInfo): new function, should only used by ClassInfo.
* net/sf/jode/bytecode/ConstantPool.java: made public.
(getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
(iterateClassNames): New method.
* net/sf/jode/decompiler/Main.java:
(decompileClass): Catch ClassFormatExceptions and decompile
remaining classes.
* net/sf/jode/obfuscator/ClassIdentifier.java:
Updated handling of inner/extra classes to new ClassInfo behaviour.
(initSuperClasses): Load DECLARATION of super classes.
* net/sf/jode/obfuscator/PackageIdentifier.java:
Replace deprecated methods of ClassInfo with corresponding classpath
calls.
(loadMatchingClasses): Initialize packages loaded on demand if we
are initialize.
* net/sf/jode/obfuscator/modules/ConstantAnalyzer.java:
Now extends SimpleAnalyzer.
(canonizeIfaceRef): Removed; it is now inherited.
(canonizeRef): likewise.
Big updates to handle jsr correctly.
(handleOpcode): Moved method to BlockInfo.
* net/sf/jode/obfuscator/modules/SimpleAnalyzer.java:
(canonizeIfaceRef): New method, copied from ConstantAnalyzer.
(canonizeRef): call canonizeIfaceRef for interfaces.
* net/sf/jode/util/UnifyHash.java
(iterateHashCode): iterator now supports remove().
(remove): New method.
git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1337 379699f6-c40d-0410-875b-85095c16579e
23 years ago
|
|
|
* @see FlowBlock#mapStackToLocal
|
|
|
|
* @see FlowBlock#removePush
|
|
|
|
*/
|
|
|
|
public class VariableStack {
|
|
|
|
public final static VariableStack EMPTY = new VariableStack();
|
|
|
|
|
|
|
|
final LocalInfo[] stackMap;
|
|
|
|
|
|
|
|
private VariableStack() {
|
|
|
|
stackMap = new LocalInfo[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
private VariableStack(LocalInfo[] stack) {
|
|
|
|
stackMap = stack;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
return stackMap.length == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableStack pop(int count) {
|
|
|
|
LocalInfo[] newStack = new LocalInfo[stackMap.length - count];
|
|
|
|
System.arraycopy(stackMap, 0, newStack, 0, stackMap.length - count);
|
|
|
|
return new VariableStack(newStack);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableStack push(LocalInfo li) {
|
|
|
|
return poppush(0, li);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableStack poppush(int count, LocalInfo li) {
|
|
|
|
LocalInfo[] newStack = new LocalInfo[stackMap.length - count + 1];
|
|
|
|
System.arraycopy(stackMap, 0, newStack, 0, stackMap.length - count);
|
|
|
|
newStack[stackMap.length - count] = li;
|
|
|
|
return new VariableStack(newStack);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableStack peek(int count) {
|
|
|
|
LocalInfo[] peeked = new LocalInfo[count];
|
|
|
|
System.arraycopy(stackMap, stackMap.length - count, peeked, 0, count);
|
|
|
|
return new VariableStack(peeked);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void merge(VariableStack other) {
|
|
|
|
if (stackMap.length != other.stackMap.length)
|
|
|
|
throw new IllegalArgumentException("stack length differs");
|
|
|
|
for (int i=0; i<stackMap.length; i++) {
|
|
|
|
if (stackMap[i].getType().stackSize()
|
|
|
|
!= other.stackMap[i].getType().stackSize())
|
|
|
|
throw new IllegalArgumentException
|
|
|
|
("stack element length differs at "+i);
|
|
|
|
stackMap[i].combineWith(other.stackMap[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge to VariableStacks. Either one may be null, in which case
|
|
|
|
* the other is returned.
|
|
|
|
*/
|
|
|
|
public static VariableStack merge(VariableStack first,
|
|
|
|
VariableStack second) {
|
|
|
|
if (first == null)
|
|
|
|
return second;
|
|
|
|
else if (second == null)
|
|
|
|
return first;
|
|
|
|
first.merge(second);
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Expression mergeIntoExpression(Expression expr) {
|
|
|
|
/* assert expr.getFreeOperandCount() == stackMap.length */
|
|
|
|
|
|
|
|
for (int i = stackMap.length-1; i >= 0; i--) {
|
|
|
|
// if (!used.contains(stackMap[i]))
|
|
|
|
// used.addElement(stackMap[i]);
|
|
|
|
expr = expr.addOperand
|
|
|
|
(new LocalLoadOperator(stackMap[i].getType(), null,
|
|
|
|
stackMap[i]));
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableStack executeSpecial(SpecialBlock special) {
|
|
|
|
if (special.type == SpecialBlock.POP) {
|
|
|
|
int popped = 0;
|
|
|
|
int newLength = stackMap.length;
|
|
|
|
while (popped < special.count) {
|
|
|
|
newLength--;
|
|
|
|
popped += stackMap[newLength].getType().stackSize();
|
|
|
|
}
|
|
|
|
if (popped != special.count)
|
|
|
|
throw new IllegalArgumentException("wrong POP");
|
|
|
|
LocalInfo[] newStack = new LocalInfo[newLength];
|
|
|
|
System.arraycopy(stackMap, 0, newStack, 0, newLength);
|
|
|
|
return new VariableStack(newStack);
|
|
|
|
} else if (special.type == SpecialBlock.DUP) {
|
|
|
|
int popped = 0;
|
|
|
|
int numDup = 0;
|
|
|
|
int startDup = stackMap.length;
|
|
|
|
while (popped < special.count) {
|
|
|
|
startDup--;
|
|
|
|
numDup++;
|
|
|
|
popped += stackMap[startDup].getType().stackSize();
|
|
|
|
}
|
|
|
|
if (popped != special.count)
|
|
|
|
throw new IllegalArgumentException("wrong DUP");
|
|
|
|
int destDup = startDup;
|
|
|
|
int depth = 0;
|
|
|
|
while (depth < special.depth) {
|
|
|
|
destDup--;
|
|
|
|
depth += stackMap[destDup].getType().stackSize();
|
|
|
|
}
|
|
|
|
if (depth != special.depth)
|
|
|
|
throw new IllegalArgumentException("wrong DUP");
|
|
|
|
LocalInfo[] newStack = new LocalInfo[stackMap.length + numDup];
|
|
|
|
System.arraycopy(stackMap, 0, newStack, 0, destDup);
|
|
|
|
System.arraycopy(stackMap, startDup, newStack, destDup, numDup);
|
|
|
|
System.arraycopy(stackMap, destDup, newStack, destDup + numDup,
|
|
|
|
startDup - destDup);
|
|
|
|
System.arraycopy(stackMap, startDup, newStack, startDup + numDup,
|
|
|
|
numDup);
|
|
|
|
return new VariableStack(newStack);
|
|
|
|
} else if (special.type == SpecialBlock.SWAP) {
|
|
|
|
LocalInfo[] newStack = new LocalInfo[stackMap.length];
|
|
|
|
System.arraycopy(stackMap, 0, newStack, 0, stackMap.length - 2);
|
|
|
|
if (stackMap[stackMap.length-2].getType().stackSize() != 1
|
|
|
|
|| stackMap[stackMap.length-1].getType().stackSize() != 1)
|
|
|
|
throw new IllegalArgumentException("wrong SWAP");
|
|
|
|
newStack[stackMap.length-2] = stackMap[stackMap.length-1];
|
|
|
|
newStack[stackMap.length-1] = stackMap[stackMap.length-2];
|
|
|
|
return new VariableStack(newStack);
|
|
|
|
} else
|
|
|
|
throw new InternalError("Unknown SpecialBlock");
|
|
|
|
}
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
StringBuffer result = new StringBuffer("[");
|
|
|
|
for (int i=0; i < stackMap.length; i++) {
|
|
|
|
if (i>0)
|
|
|
|
result.append(", ");
|
|
|
|
result.append(stackMap[i].getName());
|
|
|
|
}
|
|
|
|
return result.append("]").toString();
|
|
|
|
}
|
|
|
|
}
|