package org.codehaus.groovy.gjit; import static org.codehaus.groovy.gjit.DebugUtils.dump; import static org.codehaus.groovy.gjit.DebugUtils.print; import static org.codehaus.groovy.gjit.DebugUtils.println; import static org.codehaus.groovy.gjit.DebugUtils.toggle; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.codehaus.groovy.gjit.db.ClassEntry; import org.codehaus.groovy.gjit.db.SiteTypePersistentCache; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.VarInsnNode; import org.objectweb.asm.util.AbstractVisitor; public class SecondTransformer extends BaseTransformer { private ConstantPack pack; private String[] siteNames; private int[] localTypes; private Integer currentSiteIndex; private Stack<Integer> callSiteIndexStack = new Stack<Integer>(); private Map<Integer, AbstractInsnNode> callSiteInsnLocations = new HashMap<Integer, AbstractInsnNode>(); private List<Integer> unusedCallSites = new ArrayList<Integer>(); //private List<AbstractInsnNode> fixed = new ArrayList<AbstractInsnNode>(); private ClassEntry ce; private HashMap<AbstractInsnNode, Integer> instToCallsiteIndex = new HashMap<AbstractInsnNode, Integer>(); private static final String SCRIPT_BYTECODE_ADAPTER = "org/codehaus/groovy/runtime/ScriptBytecodeAdapter"; private static final String CALL_SITE_INTERFACE = "org/codehaus/groovy/runtime/callsite/CallSite"; private static final String DEFAULT_TYPE_TRANSFORMATION = "org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation"; public SecondTransformer(String owner, MethodNode mn, ConstantPack pack, String[] siteNames) { super(owner, mn); this.pack = pack; this.siteNames = siteNames; this.localTypes = new int[mn.maxLocals]; this.interpreter = new MyBasicInterpreter();// new FixableInterpreter(); try { this.ce = SiteTypePersistentCache.v().find(owner); } catch (Throwable e) { DebugUtils.println("error cannot get cache entry of sitetype"); this.ce = null; } } @Override protected void pretransform() { preTransformationOnly = true; super.pretransform(); int i = -1; while (true) { i++; if (i >= units.size()) break; AbstractInsnNode s = units.get(i); DebugUtils.dump(s); if (extractCallSiteName(s)) continue; recordUnusedCallSite(s); if (unwrapBinOp(s)) continue; if (eliminateBoxCastUnbox(s)) { i--; continue; } if (unwrapConst(s)) continue; if (unwrapBooleanAndIF(s)) continue; if (unwrapBoxOrUnbox(s)) { i--; continue; } if (unwrapCompare(s)) { i--; continue; } if (clearWrapperCast(s)) { i--; continue; } if (fixALOAD(s)) { i--; continue; } if (fixASTORE(s)) { i--; continue; } if (fixHasNext(s)) continue; if (fixAASTORE(s)) continue; if (fix_XRETURN(s)) { i++; continue; } if (fix_DUP(s)) { i--; continue; } if (fix_POP(s)) continue; } DebugUtils.println("===== pre-transformed"); relocateLocalVars(); removeUnusedCallSite(); i = -1; DebugUtils.println("===== phase 2"); while (true) { i++; if (i >= units.size()) break; AbstractInsnNode s = units.get(i); if (correctCall(s)) { i = units.indexOf(s); continue; } if (correctSBAMethods(s)) { i = units.indexOf(s); continue; } if (fix_DUP(s)) { i = units.indexOf(s); continue; } if (fix_POP(s)) { i = units.indexOf(s); continue; } } } private boolean unwrapBooleanAndIF(AbstractInsnNode s) { // GETSTATIC java/lang/Boolean.TRUE : Ljava/lang/Boolean; // INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox (Ljava/lang/Object;)Z // IFEQ L15 if(s.getOpcode() != GETSTATIC) return false; AbstractInsnNode s1 = s.getNext(); if(s1.getOpcode()==-1) s1 = s1.getNext(); if(s1.getOpcode() != INVOKESTATIC) return false; AbstractInsnNode s2 = s1.getNext(); if(s2 instanceof JumpInsnNode == false) return false; FieldInsnNode f = ((FieldInsnNode)s); MethodInsnNode iv1 = (MethodInsnNode)s1; if(f.owner.equals("java/lang/Boolean") && iv1.owner.equals(DEFAULT_TYPE_TRANSFORMATION) && iv1.name.equals("booleanUnbox")) { if(f.name.equals("TRUE")) { units.set(s, new InsnNode(ICONST_1)); units.remove(s1); return true; } else if(f.name.equals("FALSE")) { units.set(s, new InsnNode(ICONST_0)); units.remove(s1); return true; } } return false; } private boolean fix_POP(AbstractInsnNode s) { if(s.getOpcode() != POP) return false; AbstractInsnNode p1 = s.getPrevious(); AbstractInsnNode p2 = p1.getPrevious(); // DebugUtils.dump = true; // DebugUtils.dump(p2); // DebugUtils.dump(p1); // DebugUtils.dump(s); // DebugUtils.dump = false; if(p2.getOpcode() == DUP2_X1) { units.set(s, new InsnNode(POP2)); return true; } else if(p2.getOpcode() == DUP2 && getBytecodeType(p1).getSize()==2) { units.set(s, new InsnNode(POP2)); return true; } else if(p1.getOpcode() == DLOAD || p1.getOpcode() == LLOAD) { units.set(s, new InsnNode(POP2)); return true; } return false; } private boolean fix_DUP(AbstractInsnNode s) { if(s.getOpcode()==DUP || s.getOpcode()==DUP2) { AbstractInsnNode p = s.getPrevious(); AbstractInsnNode s1 = s.getNext(); AbstractInsnNode s2 = s1.getNext(); Type t = getBytecodeType(p); if(s1.getOpcode() == ALOAD && s2.getOpcode() == SWAP) { if(t==null && s.getOpcode()==DUP) return false; if((t != null && t.getSize() == 2) || s.getOpcode() == DUP2) { units.remove(s); units.insertBefore(s2, new InsnNode(DUP_X2)); units.insertBefore(s2, new InsnNode(POP)); units.set(s2, new InsnNode(DUP2_X1)); return true; } } else if(p.getOpcode() == INVOKESTATIC && t == null && s.getOpcode() == DUP2) { units.set(s, new InsnNode(DUP)); return false; } } return false; } private boolean fix_XRETURN(AbstractInsnNode s) { if(s.getOpcode() >= IRETURN && s.getOpcode() <= DRETURN) { AbstractInsnNode p = s.getPrevious(); while(p instanceof LabelNode) p = p.getPrevious(); if(p.getOpcode()==ACONST_NULL) { switch (s.getOpcode()) { case IRETURN: units.set(p, new InsnNode(ICONST_0)); return true; case LRETURN: units.set(p, new InsnNode(LCONST_0)); return true; case FRETURN: units.set(p, new InsnNode(FCONST_0)); return true; case DRETURN: units.set(p, new InsnNode(DCONST_0)); return true; } } int opcode = getConverterOpCode(getBytecodeType(p), getBytecodeType(s)); if(opcode != 0) { units.insertBefore(s, new InsnNode(opcode)); return true; } // case of the result is from call(/2) if(p.getOpcode() == INVOKEINTERFACE) { MethodInsnNode m = (MethodInsnNode)p; if(Type.getArgumentTypes(m.desc).length==2 && m.name.equals("call")) { Type t = getBytecodeType(s); unbox(s, t); return true; } } } else if(s.getOpcode() == ARETURN) { AbstractInsnNode p = s.getPrevious(); while(p instanceof LabelNode) p = p.getPrevious(); if(p.getOpcode() == PUTFIELD) { FieldInsnNode f = (FieldInsnNode)p; if(f.desc.length()==1) { switch(f.desc.charAt(0)) { case 'I': box(p, Type.INT_TYPE); return true; case 'L': box(p, Type.LONG_TYPE); return true; case 'F': box(p, Type.FLOAT_TYPE); return true; case 'D': box(p, Type.DOUBLE_TYPE); return true; } } } else { switch(p.getOpcode()) { case ILOAD: box(p, Type.INT_TYPE); return true; case LLOAD: box(p, Type.LONG_TYPE); return true; case FLOAD: box(p, Type.FLOAT_TYPE); return true; case DLOAD: box(p, Type.DOUBLE_TYPE); return true; } } } return false; } private void relocateLocalVars() { int[] localIndex = new int[localTypes.length]; int j = 0; DebugUtils.println(localTypes.length); for (int i = 0; i < localIndex.length; i++) { localIndex[i] = j; if (localTypes[i] == Type.LONG || localTypes[i] == Type.DOUBLE) { j++; } j++; } int i = -1; while (true) { i++; if (i >= units.size()) break; AbstractInsnNode s = units.get(i); if (s instanceof VarInsnNode) { VarInsnNode v = ((VarInsnNode) s); v.var = localIndex[v.var]; } } } @Override protected void posttransform() { DebugUtils.println(use.size()); } private boolean correctSBAMethods(AbstractInsnNode s) { if (s.getOpcode() != INVOKESTATIC) return false; MethodInsnNode iv = ((MethodInsnNode) s); if (iv.owner.equals(SCRIPT_BYTECODE_ADAPTER) == false) return false; if (iv.name.equals("unwrap")) return false; DebugUtils.println(">>> === correctSBAMethods at " + s); fixByArguments_specialCase1(iv); unboxForCorrectCall(s); return true; } private boolean correctCall(AbstractInsnNode s) { if (s.getOpcode() != INVOKEINTERFACE) return false; MethodInsnNode iv = ((MethodInsnNode) s); if (iv.owner.equals(CALL_SITE_INTERFACE) == false) return false; if (iv.name.startsWith("call") == false) return false; DebugUtils.println(">>> === correctCall"); fixByArguments_specialCase1(iv); unboxForCorrectCall(s); return true; } private void unboxForCorrectCall(AbstractInsnNode s) { AbstractInsnNode s1 = s.getNext(); if(s1.getOpcode() == DUP) s1 = s1.getNext(); int s1_opcode = s1.getOpcode(); // TODO special case, need checking with ALOAD without SWAP if(s1_opcode>=ILOAD && s1_opcode <= DLOAD) return; if(s1_opcode>=ICONST_M1 && s1_opcode <= ICONST_5) return; if(s1_opcode>=LCONST_0 && s1_opcode <= LCONST_1) return; if(s1_opcode>=FCONST_0 && s1_opcode <= FCONST_1) return; if(s1_opcode>=DCONST_0 && s1_opcode <= DCONST_1) return; if(s1_opcode==LDC) return; if(s1_opcode==GETFIELD) return; if(s1_opcode==GETSTATIC) return; Type t = getBytecodeType(s1); if(t == null) { // try again looking for sequence of ALOAD, SWAP, XX AbstractInsnNode s2 = s1.getNext(); AbstractInsnNode s3 = s2.getNext(); if(s1_opcode==ALOAD && s2.getOpcode() == SWAP) { t = getBytecodeType(s3); } } if(t!=null) { s1 = s.getNext(); // re-check s1_opcode = s1.getOpcode(); unbox(s1, t); if(s1_opcode == DUP && t.getSize()==2) { units.set(s1, new InsnNode(DUP2)); } } } private AbstractInsnNode findStartingInsn(MethodInsnNode s) { ReverseStackDistance r = new ReverseStackDistance(s); return r.findStartingNode(); } private void fixByArguments_specialCase1(MethodInsnNode iv) { //toggle(); dump(iv); AbstractInsnNode s0 = findStartingInsn(iv); SimpleInterpreter in = new SimpleInterpreter(this.node, s0, iv); AbstractInsnNode[] useBox = in.analyse().get(iv); Type[] argTypes = Type.getArgumentTypes(iv.desc); if (iv.getOpcode() == INVOKESTATIC) { for (int i = 0; i < argTypes.length; i++) { if (useBox[i] == null) continue; print(" use box " + i); dump(useBox[i]); if (argTypes[i].getSort() == Type.OBJECT || argTypes[i].getSort() == Type.ARRAY) { Type t = getBytecodeType(useBox[i]); if (t != null) { box(useBox[i], t); } } } } else { for (int i = 0; i < argTypes.length; i++) { if (useBox[i + 1] == null) continue; print(" use box " + (i + 1)); dump(useBox[i + 1]); if (argTypes[i].getSort() == Type.OBJECT || argTypes[i].getSort() == Type.ARRAY) { Type t = getBytecodeType(useBox[i + 1]); if (t != null) { dump(iv); print(", to box "); dump(useBox[i + 1]); box(useBox[i + 1], t); } } } } //toggle(); } private boolean unwrapBinOp(AbstractInsnNode s) { if (s.getOpcode() == INVOKESTATIC) { MethodInsnNode iv = ((MethodInsnNode) s); if (iv.owner.equals(CALL_SITE_INTERFACE) == false) return false; if (iv.desc.equals(CALL_SITE_BIN_SIGNATURE) == false) return false; BinOp op = null; try { op = BinOp.valueOf(iv.name); } catch (Exception e) { op = null; } if (op == null) return false; AbstractInsnNode s_op2 = s.getPrevious(); AbstractInsnNode s_op1 = s_op2.getPrevious(); if (s_op1 instanceof MethodInsnNode || s_op2 instanceof MethodInsnNode || isString(s_op1) || isString(s_op2)) { // use op1 as InsnNode to get its index // use op2 as InsnNode to get its index Integer op1_index = instToCallsiteIndex.get(s_op1); Type op1_rtype=null; try { if(op1_index == null) { op1_rtype = getBytecodeType(s_op1); } else { op1_rtype = Type.getType(ce.getReturnType(op1_index)); } Integer op2_index = instToCallsiteIndex.get(s_op2); Type op2_rtype=null; if(op1_index == null) { op2_rtype = getBytecodeType(s_op2); } else { op2_rtype = Type.getType(ce.getReturnType(op2_index)); } // TODO dealing wtih callsite data here } catch(Throwable e) { DebugUtils.println("callsite entry not available: " + e.getMessage()); iv.setOpcode(INVOKEINTERFACE); iv.name = "call"; unusedCallSites.remove(currentSiteIndex); return true; } iv.setOpcode(INVOKEINTERFACE); iv.name = "call"; unusedCallSites.remove(currentSiteIndex); return true; } Type t2 = getBytecodeType(s_op2); Type t1 = getBytecodeType(s_op1); Type toType=t1; Type fromType = null; if (t1 != null && t2 != null) { if(t1 != t2) { AbstractInsnNode op_to_promote; if(t1.getSort() > t2.getSort()) { fromType = t2; toType = t1; op_to_promote = s_op2; } else { fromType = t1; toType = t2; op_to_promote = s_op1; } InsnNode converter=new InsnNode(getConverterOpCode(fromType, toType)); if(converter.getOpcode()!=NOP) { units.insert(op_to_promote, converter); } } int offset = 0; if (toType == Type.LONG_TYPE) offset = 1; else if (toType == Type.FLOAT_TYPE) offset = 2; else if (toType == Type.DOUBLE_TYPE) offset = 3; AbstractInsnNode newS = null; switch (op) { case minus: newS = new InsnNode(ISUB + offset); units.set(s, newS); break; case plus: newS = new InsnNode(IADD + offset); units.set(s, newS); break; case multiply: newS = new InsnNode(IMUL + offset); units.set(s, newS); break; case div: newS = new InsnNode(IDIV + offset); units.set(s, newS); break; case leftShift: newS = new InsnNode(ISHL + offset); units.set(s, newS); break; case rightShift: newS = new InsnNode(ISHR + offset); units.set(s, newS); break; } if(toType.getSize() == 2 && newS.getNext().getOpcode()==DUP) { units.set(newS.getNext(), new InsnNode(DUP2)); } return true; } } return false; } private boolean isString(AbstractInsnNode op) { if(op.getOpcode()==LDC) { return ((LdcInsnNode)op).cst instanceof String; } return false; } private Type getBytecodeType(AbstractInsnNode op) { int opcode = op.getOpcode(); if (opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC) { Type t = Type.getType(((FieldInsnNode) op).desc); if (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY) return null; return t; } if (opcode >= ICONST_M1 && opcode <= ICONST_5) return Type.INT_TYPE; if (opcode == ILOAD || opcode == BIPUSH || opcode == SIPUSH || opcode == IALOAD || opcode == IADD || opcode == ISUB || opcode == IMUL || opcode == IDIV || opcode == IRETURN || opcode == ISTORE || (opcode == LDC && ((LdcInsnNode) op).cst instanceof Integer)) return Type.INT_TYPE; if (opcode == LLOAD || opcode == LCONST_0 || opcode == LCONST_1 || opcode == LALOAD || opcode == LADD || opcode == LSUB || opcode == LMUL || opcode == LDIV || opcode == LRETURN || opcode == LSTORE || (opcode == LDC && ((LdcInsnNode) op).cst instanceof Long)) return Type.LONG_TYPE; if (opcode == FLOAD || opcode == FCONST_0 || opcode == FCONST_1 || opcode == FALOAD || opcode == FADD || opcode == FSUB || opcode == FMUL || opcode == FDIV || opcode == FRETURN || opcode == FSTORE || (opcode == LDC && ((LdcInsnNode) op).cst instanceof Float)) return Type.FLOAT_TYPE; if (opcode == DLOAD || opcode == DCONST_0 || opcode == DCONST_1 || opcode == DALOAD || opcode == DADD || opcode == DSUB || opcode == DMUL || opcode == DDIV || opcode == DRETURN || opcode == DSTORE || (opcode == LDC && ((LdcInsnNode) op).cst instanceof Double)) return Type.DOUBLE_TYPE; return null; } private void removeUnusedCallSite() { // Set<Entry<Integer, AbstractInsnNode>> set = // unusedCallSites.entrySet(); for (Integer index : unusedCallSites) { AbstractInsnNode s = callSiteInsnLocations.get(index); AbstractInsnNode s1 = s.getNext(); // LDC AbstractInsnNode s2 = s1.getNext(); // AALOAD DebugUtils.println(">>>> ==="); DebugUtils.println(index); DebugUtils.println(siteNames[index]); DebugUtils.println(AbstractVisitor.OPCODES[s.getOpcode()]); DebugUtils.println(AbstractVisitor.OPCODES[s1.getOpcode()]); DebugUtils.println(AbstractVisitor.OPCODES[s2.getOpcode()]); units.remove(s); units.remove(s1); units.remove(s2); } } private void recordUnusedCallSite(AbstractInsnNode s) { if (s.getOpcode() != INVOKEINTERFACE) return; MethodInsnNode iv = (MethodInsnNode) s; if (iv.owner.equals(CALL_SITE_INTERFACE) == false) return; if (iv.name.equals("call") == false) return; currentSiteIndex = callSiteIndexStack.pop(); instToCallsiteIndex.put(s, currentSiteIndex); // AbstractInsnNode p2 = s.getPrevious(); // AbstractInsnNode p1 = p2.getPrevious(); if (isBinOpPrimitiveCall(s) == true) { iv.setOpcode(INVOKESTATIC); iv.name = siteNames[currentSiteIndex]; unusedCallSites.add(currentSiteIndex); } } private boolean fixAASTORE(AbstractInsnNode s) { if (s.getOpcode() != AASTORE) return false; AbstractInsnNode p = s.getPrevious(); switch (p.getOpcode()) { case ILOAD: case IADD: case ISUB: case IMUL: case IDIV: case ISHL: case ISHR: box(p, Type.INT_TYPE); break; case LLOAD: case LADD: case LSUB: case LMUL: case LDIV: case LSHL: case LSHR: box(p, Type.LONG_TYPE); break; case FLOAD: case FADD: case FSUB: case FMUL: case FDIV: box(p, Type.FLOAT_TYPE); break; case DLOAD: case DADD: case DSUB: case DMUL: case DDIV: box(p, Type.DOUBLE_TYPE); break; } return true; } // @Override // public Action process(AbstractInsnNode s, // Map<AbstractInsnNode, Frame> frames) { // // if(extractCallSiteName(s)) return Action.NONE; // // if(eliminateBoxCastUnbox(s)) return Action.REMOVE; // // if(unwrapConst(s)) return Action.REPLACE; // // if(unwrapBoxOrUnbox(s)) return Action.REMOVE; // // if(unwrapBinaryPrimitiveCall(s, frames.get(s))) return // // Action.REPLACE; // // if(unwrapCompare(s,frames.get(s))) return Action.REMOVE; // // if(clearWrapperCast(s)) return Action.REMOVE; // // if(fixASTORE(s,frames.get(s))) return Action.REPLACE; // // if(fixALOAD(s)) return Action.REPLACE; // // if(fixHasNext(s)) return Action.REPLACE; // workaround ASM verifier // // if(fixAASTORE(s,frames.get(s))) return Action.ADD; // return Action.NONE; // } private boolean fixHasNext(AbstractInsnNode s) { if (s.getOpcode() != INVOKEINTERFACE) return false; MethodInsnNode m = ((MethodInsnNode) s); // mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", // "()Z"); if (m.owner.equals("java/util/Iterator") && s.getPrevious().getOpcode() != CHECKCAST) { units.insertBefore(s, new TypeInsnNode(CHECKCAST, m.owner)); return true; } return false; } // private boolean fixASTORE(AbstractInsnNode s, Frame frame) { // if(s.getOpcode()!=ASTORE) return false; // BasicValue top = (BasicValue)frame.getStack(frame.getStackSize()-1); // return fixASTORE(s, top.getType()); // } private boolean fixASTORE(AbstractInsnNode s) { if (s.getOpcode() != ASTORE) return false; int sort = localTypes[((VarInsnNode) s).var]; if (sort != 0) { // special case, done fixing before return fixASTORE(s, sort); } AbstractInsnNode p = s.getPrevious(); switch (p.getOpcode()) { case ILOAD: case IADD: case ISUB: case IMUL: case IDIV: return fixASTORE(s, Type.INT_TYPE); case LLOAD: case LADD: case LSUB: case LMUL: case LDIV: return fixASTORE(s, Type.LONG_TYPE); case FLOAD: case FADD: case FSUB: case FMUL: case FDIV: return fixASTORE(s, Type.FLOAT_TYPE); case DLOAD: case DADD: case DSUB: case DMUL: case DDIV: return fixASTORE(s, Type.DOUBLE_TYPE); } return false; } private boolean fixASTORE(AbstractInsnNode s, int sort) { VarInsnNode v = (VarInsnNode) s; Type t = null; AbstractInsnNode p = s.getPrevious(); VarInsnNode newS; switch (sort) { case Type.INT: if(p.getOpcode()==ACONST_NULL) units.set(p, new InsnNode(ICONST_0)); newS = new VarInsnNode(ISTORE, v.var); units.set(s, newS); localTypes[v.var] = Type.INT; t = Type.INT_TYPE; break; case Type.LONG: if(p.getOpcode()==ACONST_NULL) units.set(p, new InsnNode(LCONST_0)); newS = new VarInsnNode(LSTORE, v.var); units.set(s, newS); localTypes[v.var] = Type.LONG; t = Type.LONG_TYPE; break; case Type.FLOAT: if(p.getOpcode()==ACONST_NULL) units.set(p, new InsnNode(FCONST_0)); newS = new VarInsnNode(FSTORE, v.var); units.set(s, newS); localTypes[v.var] = Type.FLOAT; t = Type.FLOAT_TYPE; break; case Type.DOUBLE: if(p.getOpcode()==ACONST_NULL) units.set(p, new InsnNode(DCONST_0)); newS = new VarInsnNode(DSTORE, v.var); units.set(s, newS); localTypes[v.var] = Type.DOUBLE; t = Type.DOUBLE_TYPE; break; default: return false; } if (t != null) { p = newS.getPrevious(); if (p != null) { if (p.getOpcode() == DUP) p = p.getPrevious(); if (p instanceof MethodInsnNode) { MethodInsnNode iv = ((MethodInsnNode) p); if (iv.name.equals("call") && iv.desc.equals(CALL_SITE_BIN_SIGNATURE)) { unbox(newS, t); } else if(iv.name.endsWith("next") && iv.owner.equals("java/util/Iterator") && iv.desc.equals(ITERATOR_NEXT_SIGNATURE)) { unbox(newS, t); } } else if(getBytecodeType(p) != getBytecodeType(newS)) { // DebugUtils.dump = true; // DebugUtils.dump(p); // DebugUtils.dump(newS); // DebugUtils.dump = false; int converterOpcode = getConverterOpCode(getBytecodeType(p),getBytecodeType(newS)); if(converterOpcode != 0) { InsnNode converter = new InsnNode(converterOpcode); units.insertBefore(newS, converter); } } } } return true; } private int getConverterOpCode(Type fromType, Type toType) { if(fromType == null) return 0; if(toType == null) return 0; switch(fromType.getSort()) { case Type.INT: return getConvertIntTo(toType); case Type.LONG: return getConvertLongTo(toType); case Type.FLOAT: return getConvertFloatTo(toType); case Type.DOUBLE: return getConvertDoubleTo(toType); } return 0; } private int getConvertIntTo(Type toType) { switch(toType.getSort()) { case Type.LONG: return I2L; case Type.FLOAT: return I2F; case Type.DOUBLE: return I2D; } return 0; } private int getConvertLongTo(Type toType) { switch(toType.getSort()) { case Type.INT: return L2I; case Type.FLOAT: return L2F; case Type.DOUBLE: return L2D; } return 0; } private int getConvertFloatTo(Type toType) { switch(toType.getSort()) { case Type.INT: return F2I; case Type.LONG: return F2L; case Type.DOUBLE: return F2D; } return 0; } private int getConvertDoubleTo(Type toType) { switch(toType.getSort()) { case Type.INT: return D2I; case Type.LONG: return D2L; case Type.FLOAT: return D2F; } return 0; } private boolean fixASTORE(AbstractInsnNode s, Type type) { if (type == null) return false; // VarInsnNode v = (VarInsnNode)s; return fixASTORE(s, type.getSort()); } private boolean fixALOAD(AbstractInsnNode s) { if (s.getOpcode() != ALOAD) return false; VarInsnNode v = (VarInsnNode) s; switch (localTypes[v.var]) { case Type.INT: units.set(s, new VarInsnNode(ILOAD, v.var)); return true; case Type.LONG: units.set(s, new VarInsnNode(LLOAD, v.var)); return true; case Type.FLOAT: units.set(s, new VarInsnNode(FLOAD, v.var)); return true; case Type.DOUBLE: units.set(s, new VarInsnNode(DLOAD, v.var)); return true; default: return false; } } private void box(AbstractInsnNode source, Type t) { String boxType = null; String primType = null; switch (t.getSort()) { case Type.INT: boxType = "java/lang/Integer"; primType = "I"; break; case Type.LONG: boxType = "java/lang/Long"; primType = "J"; break; case Type.FLOAT: boxType = "java/lang/Float"; primType = "F"; break; case Type.DOUBLE: boxType = "java/lang/Double"; primType = "D"; break; default: throw new RuntimeException("I'm trying to catch you" + t); // break; } MethodInsnNode iv = new MethodInsnNode(INVOKESTATIC, boxType, "valueOf", "(" + primType + ")L" + boxType + ";"); // if(source.getOpcode()==SWAP) source = source.getPrevious(); // work // around for inserted SWAP,POP // else if(source.getOpcode()==DUP2_X1) source = // source.getNext().getNext();// POP2, POP,| units.insert(source, iv); } private void unbox(AbstractInsnNode s, Type t) { String boxType = null; String primType = null; String primTypeName = null; switch (t.getSort()) { case Type.INT: boxType = "java/lang/Integer"; primType = "I"; primTypeName = "int"; break; case Type.LONG: boxType = "java/lang/Long"; primType = "J"; primTypeName = "long"; break; case Type.FLOAT: boxType = "java/lang/Float"; primType = "F"; primTypeName = "float"; break; case Type.DOUBLE: boxType = "java/lang/Double"; primType = "D"; primTypeName = "double"; break; } TypeInsnNode cast = new TypeInsnNode(CHECKCAST, boxType); MethodInsnNode iv = new MethodInsnNode(INVOKEVIRTUAL, boxType, primTypeName + "Value", "()" + primType); AbstractInsnNode p = s.getPrevious(); if (p instanceof LabelNode) { s = p; } units.insertBefore(s, cast); units.insert(cast, iv); } private boolean extractCallSiteName(AbstractInsnNode s) { if (s.getOpcode() != ALOAD) return false; VarInsnNode v = (VarInsnNode) s; if (v.var != callSiteVar) return false; AbstractInsnNode s1 = s.getNext(); AbstractInsnNode s2 = s1.getNext(); if (s1.getOpcode() != LDC) return false; if (s2.getOpcode() != AALOAD) return false; LdcInsnNode l = (LdcInsnNode) s1; callSiteIndexStack.push((Integer) l.cst); callSiteInsnLocations.put((Integer) l.cst, s); return true; } private boolean eliminateBoxCastUnbox(AbstractInsnNode s) { if (s.getOpcode() != INVOKESTATIC) return false; AbstractInsnNode s1 = s.getNext(); if (s1 == null) return false; if (s1.getOpcode() != INVOKESTATIC) return false; AbstractInsnNode s2 = s1.getNext(); if (s2 == null) return false; if (s2.getOpcode() != INVOKESTATIC) return false; AbstractInsnNode s3 = s2.getNext(); if (s3 == null) return false; if (s3.getOpcode() != CHECKCAST) return false; AbstractInsnNode s4 = s3.getNext(); if (s4 == null) return false; if (s4.getOpcode() != INVOKESTATIC) return false; MethodInsnNode m = (MethodInsnNode) s; MethodInsnNode m1 = (MethodInsnNode) s1; MethodInsnNode m2 = (MethodInsnNode) s2; MethodInsnNode m4 = (MethodInsnNode) s4; if (m.owner.equals(DEFAULT_TYPE_TRANSFORMATION) == false) return false; if (m.name.equals("box") == false) return false; if (m1.name.startsWith("$get$$class$") == false) return false; if (m2.name.startsWith("castToType") == false) return false; if (m4.name.endsWith("Unbox") == false) return false; units.remove(s); units.remove(s1); units.remove(s2); units.remove(s3); units.remove(s4); return true; } private boolean unwrapBoxOrUnbox(AbstractInsnNode s) { if (s.getOpcode() != INVOKESTATIC) return false; MethodInsnNode m = (MethodInsnNode) s; if (m.owner.equals(DEFAULT_TYPE_TRANSFORMATION) == false) return false; if (m.name.equals("box")) { // unit_remove(s,s.getPrevious()); units.remove(s); return true; } else if (m.name.endsWith("Unbox")) { // unit_remove(s,s.getPrevious()); units.remove(s); return true; } return false; } private boolean unwrapConst(AbstractInsnNode s) { if (s.getOpcode() != GETSTATIC) return false; FieldInsnNode f = (FieldInsnNode) s; if (f.name.startsWith("$const$")) { // special case, not unwrap AbstractInsnNode s11 = s.getNext(); if(s11.getOpcode()==NEW && ((TypeInsnNode)s11).desc.equals("groovy/lang/Reference")) return false; DebugUtils.println(">>> pass $const$"); DebugUtils.println(f.name); Object constValue = pack.get(f.name); DebugUtils.println("const type: " + constValue.getClass()); AbstractInsnNode newS = new LdcInsnNode(constValue); if (constValue instanceof Integer) { int c = (Integer) constValue; if (c >= -1 && c <= 5) { switch (c) { case -1: newS = new InsnNode(ICONST_M1); break; case 0: newS = new InsnNode(ICONST_0); break; case 1: newS = new InsnNode(ICONST_1); break; case 2: newS = new InsnNode(ICONST_2); break; case 3: newS = new InsnNode(ICONST_3); break; case 4: newS = new InsnNode(ICONST_4); break; case 5: newS = new InsnNode(ICONST_5); break; } } else if (c >= -128 && c <= 127) { newS = new IntInsnNode(BIPUSH, c); } else if (c >= -32768 && c <= 32767) { newS = new IntInsnNode(SIPUSH, c); } } AbstractInsnNode s1 = s.getNext(); if(s1.getOpcode() == DUP) { AbstractInsnNode temp = s1.getNext(); // sometime the compiler use DUP to reuse TOS if(newS instanceof LdcInsnNode && (constValue instanceof Long || constValue instanceof Double)) { units.set(s1, new InsnNode(DUP2)); } s1 = temp; } units.set(s, newS); if (s1.getOpcode() == ASTORE) { Type type = Type.getType(constValue.getClass()); fixASTORE(s1, getPrimitiveType(type)); } DebugUtils.println("unwrap const"); return true; } return false; } private Type getPrimitiveType(Type type) { return getPrimitiveType(type.getDescriptor()); } private Type getPrimitiveType(String desc) { if (desc.charAt(0) != 'L') desc = 'L' + desc + ';'; if (desc.equals("Ljava/lang/Integer;")) return Type.INT_TYPE; if (desc.equals("Ljava/lang/Long;")) return Type.LONG_TYPE; if (desc.equals("Ljava/lang/Float;")) return Type.FLOAT_TYPE; if (desc.equals("Ljava/lang/Double;")) return Type.DOUBLE_TYPE; return null; } private enum BinOp { minus, plus, multiply, div, leftShift, rightShift } private static final String CALL_SITE_BIN_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; private static final String ITERATOR_NEXT_SIGNATURE = "()Ljava/lang/Object;"; private boolean isBinOpPrimitiveCall(AbstractInsnNode s) { if (s.getOpcode() != INVOKEINTERFACE) return false; MethodInsnNode iv = (MethodInsnNode) s; if (iv.owner.equals(CALL_SITE_INTERFACE) == false) return false; if (iv.name.equals("call") == false) return false; if (iv.desc.equals(CALL_SITE_BIN_SIGNATURE) == false) return false; String name = siteNames[currentSiteIndex]; BinOp op = null; try { op = BinOp.valueOf(name); } catch (IllegalArgumentException e) {} if (op == null) return false; return true; } private boolean clearWrapperCast(AbstractInsnNode s) { // INVOKESTATIC // TreeNode.$get$$class$java$lang$Integer()Ljava/lang/Class; // INVOKESTATIC // org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; // CHECKCAST java/lang/Integer if (s.getOpcode() != INVOKESTATIC) return false; MethodInsnNode m = (MethodInsnNode) s; if (m.name.startsWith("$get$$class$java$lang$") == false) return false; AbstractInsnNode s1 = s.getNext(); if (s1 == null) return false; if (s1.getOpcode() != INVOKESTATIC) return false; MethodInsnNode m1 = (MethodInsnNode) s1; if (m1.name.equals("castToType") == false) return false; AbstractInsnNode s2 = s1.getNext(); if (s2 == null) return false; if (s2.getOpcode() != CHECKCAST) return false; TypeInsnNode t2 = (TypeInsnNode) s2; if (t2.desc.startsWith("java/lang") == false) return false; AbstractInsnNode s3 = s2.getNext(); AbstractInsnNode s4 = s3.getNext(); AbstractInsnNode s0 = s.getPrevious(); if (s0 instanceof LabelNode) s0 = s0.getPrevious(); units.remove(s); units.remove(s1); units.remove(s2); // DebugUtils.println(t2.desc); // DebugUtils.print("clear >>>>>>> s0 : "); // DebugUtils.dump(s0); // DebugUtils.print("clear >>>>>>> s3 : "); // DebugUtils.dump(s3.getNext()); if (s3.getOpcode() == ASTORE) { if (s0 instanceof MethodInsnNode) { // DebugUtils.println(siteNames[currentSiteIndex]); if (isBinOpPrimitiveCall(s0) == false) { unbox(s3, getPrimitiveType(t2.desc)); } } fixASTORE(s3, getPrimitiveType(t2.desc)); } else if( s0.getOpcode() == INVOKEVIRTUAL && ((MethodInsnNode)s0).name.equals("get") && ((MethodInsnNode)s0).owner.equals("groovy/lang/Reference") && s4.getOpcode() >= IRETURN && s4.getOpcode() <= DRETURN) { switch(s4.getOpcode()) { case IRETURN: unbox(s3, Type.INT_TYPE); break; case LRETURN: unbox(s3, Type.LONG_TYPE); break; case FRETURN: unbox(s3, Type.FLOAT_TYPE); break; case DRETURN: unbox(s3, Type.DOUBLE_TYPE); break; } } return true; } private enum ComparingMethod { compareLessThan, compareGreaterThan, compareLessThanEqual, compareGreaterThanEqual }; private boolean unwrapCompare(AbstractInsnNode s) { if (s.getOpcode() != Opcodes.INVOKESTATIC) return false; MethodInsnNode m = (MethodInsnNode) s; if (m.owner.equals(SCRIPT_BYTECODE_ADAPTER) == false) return false; if (m.name.startsWith("compare") == false) return false; if (m.desc.equals("(Ljava/lang/Object;Ljava/lang/Object;)Z") == false) return false; AbstractInsnNode p2 = s.getPrevious(); AbstractInsnNode p1 = p2.getPrevious(); Type t1 = getBytecodeType(p1); Type t2 = getBytecodeType(p2); if(t1 == null || t2 == null) return false; Type fromType = null; Type toType = null; AbstractInsnNode whereToInsert = null; Type promotedType=null; // TODO doing type promotion if(t1.getSort() != t2.getSort()) { if(t1.getSort() < t2.getSort()) { fromType = t1; toType = t2; whereToInsert = p1; promotedType = t2; } else if(t2.getSort() < t1.getSort()) { fromType = t2; toType = t1; whereToInsert = p2; promotedType = t1; } InsnNode converter = new InsnNode(getConverterOpCode(fromType, toType)); if(converter.getOpcode() != NOP) { units.insert(whereToInsert, converter); } } else { promotedType = t1; } ComparingMethod compare; try { compare = ComparingMethod.valueOf(m.name); } catch (IllegalArgumentException e) { return false; } switch(promotedType.getSort()) { case Type.INT: convertCompareForInt(compare, s); break; case Type.LONG: convertCompare(LCMP, compare, s); break; case Type.FLOAT: convertCompare(FCMPL, compare, s); break; case Type.DOUBLE: convertCompare(DCMPL, compare, s); break; default: return false; } return true; } private void convertCompare(int opcode, ComparingMethod compare, AbstractInsnNode s) { JumpInsnNode s1 = (JumpInsnNode) s.getNext(); units.set(s, new InsnNode(opcode)); switch (compare) { case compareGreaterThan: units.set(s1, new JumpInsnNode(IFLE, s1.label)); break; case compareGreaterThanEqual: units.set(s1, new JumpInsnNode(IFLT, s1.label)); break; case compareLessThan: units.set(s1, new JumpInsnNode(IFGE, s1.label)); break; case compareLessThanEqual: units.set(s1, new JumpInsnNode(IFGT, s1.label)); break; } } private void convertCompareForInt(ComparingMethod compare, AbstractInsnNode s) { JumpInsnNode s1 = (JumpInsnNode) s.getNext(); switch (compare) { case compareGreaterThan: units.set(s1, new JumpInsnNode(IF_ICMPLE, s1.label)); break; case compareGreaterThanEqual: units.set(s1, new JumpInsnNode(IF_ICMPLT, s1.label)); break; case compareLessThan: units.set(s1, new JumpInsnNode(IF_ICMPGE, s1.label)); break; case compareLessThanEqual: units.set(s1, new JumpInsnNode(IF_ICMPGT, s1.label)); break; } units.remove(s); } }