package net.jhorstmann.i18n.xgettext.asm.poc;
import org.objectweb.asm.tree.MethodNode;
import java.util.Iterator;
import org.objectweb.asm.tree.analysis.Analyzer;
import java.io.IOException;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import java.io.InputStream;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Interpreter;
import org.objectweb.asm.tree.analysis.Value;
import static org.objectweb.asm.Opcodes.*;
public class ConstantTracker extends Interpreter {
public ConstantTracker() {
super( 1 );
}
static final class UninitializedValue implements Value {
@Override
public int getSize() {
return 1;
}
}
static final class SimpleValue implements Value {
@Override
public int getSize() {
return 1;
}
}
/**
* A value that uses 2 stack slots, long or double.
*/
static final class LongValue implements Value {
@Override
public int getSize() {
return 2;
}
}
static final class ConstantValue implements Value {
private String string;
public ConstantValue(String string) {
this.string = string;
}
public String getString() {
return string;
}
@Override
public int getSize() {
return 1;
}
}
private static final Value UNINITIALIZED_VALUE = new UninitializedValue();
private static final Value SIMPLE_VALUE = new SimpleValue();
private static final Value LONG_VALUE = new LongValue();
public static void findConstantArgumentsToPrintln(InputStream in) throws IOException, AnalyzerException {
ClassReader classReader = new ClassReader(in);
ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0);
findConstantArgumentsToPrintln(classNode);
}
public static void findConstantArgumentsToPrintln(ClassNode classNode) throws AnalyzerException {
Interpreter interpreter = new ConstantTracker();
Analyzer analyzer = new Analyzer(interpreter);
for (Iterator i = classNode.methods.iterator(); i.hasNext();) {
MethodNode methodNode = (MethodNode)i.next();
analyzer.analyze(classNode.name, methodNode);
}
}
@Override
public Value newValue(Type type) {
if (type == null) {
return UNINITIALIZED_VALUE;
} else {
switch (type.getSort()) {
case Type.VOID:
return null;
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
case Type.FLOAT:
case Type.ARRAY:
case Type.OBJECT:
return SIMPLE_VALUE;
case Type.LONG:
case Type.DOUBLE:
return LONG_VALUE;
default:
throw new IllegalStateException("Unhandled type " + type);
}
}
}
@Override
public Value newOperation(final AbstractInsnNode insn) throws AnalyzerException {
int opcode = insn.getOpcode();
switch (opcode) {
case ACONST_NULL:
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
case FCONST_0:
case FCONST_1:
case FCONST_2:
case BIPUSH:
case SIPUSH:
return SIMPLE_VALUE;
case LCONST_0:
case LCONST_1:
case DCONST_0:
case DCONST_1:
return LONG_VALUE;
case LDC:
Object cst = ((LdcInsnNode)insn).cst;
if (cst instanceof String) {
return new ConstantValue((String)cst);
} else {
return newValue(Type.getType(cst.getClass()));
}
case JSR:
return SIMPLE_VALUE;
case GETSTATIC:
return newValue(Type.getType(((FieldInsnNode)insn).desc));
case NEW:
return newValue(Type.getObjectType(((TypeInsnNode)insn).desc));
default:
throw new IllegalStateException("Unhandled opcode " + opcode);
}
}
@Override
public Value copyOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
return value;
}
@Override
public Value unaryOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
int opcode = insn.getOpcode();
switch (opcode) {
case INEG:
case IINC:
case L2I:
case F2I:
case D2I:
case I2B:
case I2C:
case I2S:
case FNEG:
case I2F:
case L2F:
case D2F:
case NEWARRAY:
case ANEWARRAY:
case ARRAYLENGTH:
case INSTANCEOF:
return SIMPLE_VALUE;
case LNEG:
case I2L:
case F2L:
case D2L:
case DNEG:
case I2D:
case L2D:
case F2D:
return LONG_VALUE;
case GETFIELD:
return newValue(Type.getType(((FieldInsnNode)insn).desc));
case CHECKCAST:
return newValue(Type.getObjectType(((TypeInsnNode)insn).desc));
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
case PUTSTATIC:
case MONITORENTER:
case MONITOREXIT:
case IFNULL:
case IFNONNULL:
case ATHROW:
return null;
default:
throw new IllegalStateException("Unhandled opcode " + opcode);
}
}
@Override
public Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2) throws AnalyzerException {
int opcode = insn.getOpcode();
switch (opcode) {
case IALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
case IADD:
case ISUB:
case IMUL:
case IDIV:
case IREM:
case ISHL:
case ISHR:
case IUSHR:
case IAND:
case IOR:
case IXOR:
case FALOAD:
case FADD:
case FSUB:
case FMUL:
case FDIV:
case FREM:
case AALOAD:
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
return SIMPLE_VALUE;
case LALOAD:
case LADD:
case LSUB:
case LMUL:
case LDIV:
case LREM:
case LSHL:
case LSHR:
case LUSHR:
case LAND:
case LOR:
case LXOR:
case DALOAD:
case DADD:
case DSUB:
case DMUL:
case DDIV:
case DREM:
return LONG_VALUE;
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
case PUTFIELD:
return null;
default:
throw new IllegalStateException("Unhandled opcode " + opcode);
}
}
@Override
public Value ternaryOperation(AbstractInsnNode insn, Value value1, Value value2, Value value3) throws AnalyzerException {
return null;
}
@Override
public Value naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
return SIMPLE_VALUE;
} else {
MethodInsnNode methodInsn = (MethodInsnNode)insn;
String owner = methodInsn.owner;
String name = methodInsn.name;
String desc = methodInsn.desc;
if (opcode == INVOKEVIRTUAL && "java/io/PrintStream".equals(owner) && "println".equals(name) && "(Ljava/lang/String;)V".equals(desc)) {
Value arg = (Value)values.get(1);
if (arg instanceof ConstantValue) {
ConstantValue cons = (ConstantValue)arg;
System.out.println(cons.getString());
}
}
return newValue(Type.getReturnType(methodInsn.desc));
}
}
@Override
public void returnOperation(AbstractInsnNode insn, Value value, Value expected) throws AnalyzerException {
}
@Override
public Value merge(Value v, Value w) {
if (v != w) {
return UNINITIALIZED_VALUE;
} else {
return v;
}
}
}