/* Copyright (c) 2006, Sriram Srinivasan * * You may distribute this software under the terms of the license * specified in the file "License" */ package kilim.analysis; import static kilim.Constants.D_DOUBLE; import static kilim.Constants.D_FLOAT; import static kilim.Constants.D_LONG; import static kilim.Constants.D_OBJECT; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.DLOAD; import static org.objectweb.asm.Opcodes.FLOAD; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.LLOAD; import org.objectweb.asm.tree.MethodNode; /** * An activation frame. * */ public class Frame { Value[] locals; Value[] stack; int numMonitorsActive = 0; int stacklen = 0; private Frame(int nLocals, int nStack, boolean init) { this.locals = new Value[nLocals]; if (init) { for (int i = 0; i < nLocals; i++) { locals[i] = Value.V_UNDEFINED; } } this.stack = new Value[nStack]; } public Frame(int nLocals, int nStack) { this(nLocals, nStack, true); } /** * Merge the local variables and stack from the incoming frame * into the current frame. * @param inframe -- incoming frame * @param localsOnly -- true for exception handlers, because the * stack is cleared. * @param usage -- Only those locals are merged that are deemed * live (@see Usage#isLiveIn(int)) * @return this, if the merge didn't change anything * or a new Frame if the operation changed a slot on the stack * or a local variable */ public Frame merge(Frame inframe, boolean localsOnly, Usage usage) { int slen = stacklen; Value[] nst = null; // new stack. allocated if needed if (!localsOnly) { Value[] st = stack; Value[] ist = inframe.stack; for (int i = 0; i < slen; i++) { Value va = st[i]; Value vb = ist[i]; if (va == vb || va.equals(vb)) continue; Value newval = va.merge(vb); if (newval != va) { if (nst == null) nst = dupArray(st); nst[i] = newval; } } } Value[] lo = locals; Value[] ilo = inframe.locals; Value[] nlo = null; // new locals array. allocated if needed for (int i = 0; i < lo.length; i++) { if (!usage.isLiveIn(i)) continue; Value va = lo[i]; Value vb = ilo[i]; if (va == vb || va.equals(vb)) continue; Value newval = va.merge(vb); if (newval != va) { if (nlo == null) nlo = dupArray(lo); nlo[i] = newval; } } if (nst == null && nlo == null) { return this; } else { // One or both of locals and stacks have new values if (nst == null) nst = dupArray(stack); if (nlo == null) nlo = dupArray(locals); return new Frame(nlo, nst, slen, numMonitorsActive); } } public static Value[] dupArray(Value[] a) { Value[] ret = new Value[a.length]; System.arraycopy(a, 0, ret, 0, a.length); return ret; } private Frame(Value[] alocals, Value[] astack, int astacklen, int aNumMonitorsActive) { this.locals = alocals; this.stack = astack; this.stacklen = astacklen; this.numMonitorsActive = aNumMonitorsActive; } public Frame dup() { return new Frame(dupArray(locals), dupArray(stack), stacklen, numMonitorsActive); } public Frame(String classDesc, MethodNode method) { this(method.maxLocals, method.maxStack, false); String[] argTypeDescs = TypeDesc.getArgumentTypes(method.desc); for (int i = 0; i < method.maxLocals; i++) { setLocal(i, Value.V_UNDEFINED); } int local = 0; int paramPos = 100000; if ((method.access & ACC_STATIC) == 0) { // 0th local is "this" setLocal(local++, Value.make(paramPos++,classDesc)); } for (int i = 0; i < argTypeDescs.length; i++) { local += setLocal(local, Value.make(paramPos++, argTypeDescs[i])); } if ((method.access & ACC_SYNCHRONIZED) != 0) { numMonitorsActive = 1; } } private boolean checkType(String desc) { if (desc.equals("Ljava/lang/Object;") && desc != D_OBJECT) return false; switch(desc.charAt(0)) { case 'L': case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': case 'N': case '[': case 'A': case 'U': return true; default: return false; } } public int setLocal(int local, Value v) { assert checkType(v.getTypeDesc()) : "Invalid type: " + v.getTypeDesc(); locals[local] = v; if (v.isCategory2()) { locals[local+1] = v; return 2; } return 1; } public Value getLocal(int local, int opcode) { Value v = locals[local]; String desc = v.getTypeDesc(); String expected = null; switch(opcode) { case ILOAD: { if (TypeDesc.isIntType(desc)) { return v; } else { expected = "int"; } break; } case LLOAD: { if (desc == D_LONG) { return v; } else { expected = "long"; } break; } case DLOAD: { if (desc == D_DOUBLE) { return v; } else { expected = "double"; } break; } case FLOAD: { if (desc == D_FLOAT) { return v; } else { expected = "float"; } break; } case ALOAD: { if (TypeDesc.isRefType(desc)) { return v; } else { expected = "ref"; } } } throw new AssertionError("Expected " + expected + " in local# " + local + ", got " + desc); } public Value getLocal(int local) { return locals[local]; } public Value getStack(int pos) { // for testing return stack[pos]; } public Value push(Value v) { assert (v != Value.V_UNDEFINED) : "UNDEFINED type pushed"; assert checkType(v.getTypeDesc()) : "Invalid type: " + v.getTypeDesc(); stack[stacklen++] = v; return v; } public Value pop() { try { return stack[--stacklen]; } catch (ArrayIndexOutOfBoundsException e) { throw new RuntimeException("Verify error. Expected word in stack, but stack is empty"); } } public Value popWord() { Value v = pop(); assert v.isCategory1() : "double word present where single expected"; return v; } public void popn(int n) { stacklen -= n; } void clearStack() { stacklen = 0; } @Override public boolean equals(Object other) { Frame that = (Frame)other; for (int i = 0; i < locals.length; i++) { if (!locals[i].equals(that.locals[i])) return false; } for (int i = 0; i < stacklen; i++) { if (!stack[i].equals(that.stack[i])) return false; } return true; } @Override public int hashCode() { int hash = 0; for (int i = 0; i < this.locals.length;i++) hash ^= this.locals[i].hashCode(); for (int i = 0; i < this.stacklen;i++) hash ^= this.locals[i].hashCode(); return hash; } @Override public String toString() { StringBuffer sb = new StringBuffer(100); int numDefined = 0; sb.append("): "); for (int i = 0; i < this.locals.length;i++) { Value v = locals[i]; if (v != Value.V_UNDEFINED) { numDefined++; sb.append(i).append(':').append(this.locals[i]).append(" "); } } sb.insert(0, numDefined); sb.insert(0, "Locals("); sb.append("\n").append("Stack(").append(stacklen).append("): "); for (int i = 0; i < this.stacklen;i++) { sb.append(this.stack[i]).append(" "); } return sb.toString(); } public int getMaxLocals() { return locals.length; } public int getStackLen() { return stacklen; } }