com.googlecode.d2j.converter.IR2JConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.d2j.converter.IR2JConverter.java

Source

/*
 * dex2jar - Tools to work with android .dex and java .class files
 * Copyright (c) 2009-2013 Panxiaobo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.d2j.converter;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;

import com.googlecode.d2j.asm.LdcOptimizeAdapter;
import com.googlecode.dex2jar.ir.stmt.*;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.googlecode.dex2jar.ir.IrMethod;
import com.googlecode.dex2jar.ir.Trap;
import com.googlecode.dex2jar.ir.expr.ArrayExpr;
import com.googlecode.dex2jar.ir.expr.CastExpr;
import com.googlecode.dex2jar.ir.expr.Constant;
import com.googlecode.dex2jar.ir.expr.Exprs;
import com.googlecode.dex2jar.ir.expr.FieldExpr;
import com.googlecode.dex2jar.ir.expr.FilledArrayExpr;
import com.googlecode.dex2jar.ir.expr.InvokeExpr;
import com.googlecode.dex2jar.ir.expr.Local;
import com.googlecode.dex2jar.ir.expr.NewExpr;
import com.googlecode.dex2jar.ir.expr.NewMutiArrayExpr;
import com.googlecode.dex2jar.ir.expr.StaticFieldExpr;
import com.googlecode.dex2jar.ir.expr.TypeExpr;
import com.googlecode.dex2jar.ir.expr.Value;
import com.googlecode.dex2jar.ir.expr.Value.E1Expr;
import com.googlecode.dex2jar.ir.expr.Value.E2Expr;
import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
import com.googlecode.dex2jar.ir.expr.Value.VT;
import com.googlecode.dex2jar.ir.stmt.Stmt.E2Stmt;
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
import com.googlecode.dex2jar.ir.ts.Cfg;
import com.googlecode.dex2jar.ir.ts.Cfg.TravelCallBack;

@SuppressWarnings("incomplete-switch")
public class IR2JConverter implements Opcodes {

    private boolean optimizeSynchronized = false;

    public IR2JConverter() {
        super();
    }

    public IR2JConverter(boolean optimizeSynchronized) {
        super();
        this.optimizeSynchronized = optimizeSynchronized;
    }

    public void convert(IrMethod ir, MethodVisitor asm) {
        mapLabelStmt(ir);
        reBuildInstructions(ir, asm);
        reBuildTryCatchBlocks(ir, asm);
    }

    private void mapLabelStmt(IrMethod ir) {
        for (Stmt p : ir.stmts) {
            if (p.st == ST.LABEL) {
                LabelStmt labelStmt = (LabelStmt) p;
                labelStmt.tag = new Label();
            }
        }
    }

    /**
     * an empty try-catch block will cause other crash, we check this by finding non-label stmts between
     * {@link Trap#start} and {@link Trap#end}. if find we add the try-catch or we drop the try-catch.
     * 
     * @param ir
     * @param asm
     */
    private void reBuildTryCatchBlocks(IrMethod ir, MethodVisitor asm) {
        for (Trap trap : ir.traps) {
            boolean needAdd = false;
            for (Stmt p = trap.start.getNext(); p != null && p != trap.end; p = p.getNext()) {
                if (p.st != ST.LABEL) {
                    needAdd = true;
                    break;
                }
            }
            if (needAdd) {
                for (int i = 0; i < trap.handlers.length; i++) {
                    String type = trap.types[i];
                    asm.visitTryCatchBlock((Label) trap.start.tag, (Label) trap.end.tag,
                            (Label) trap.handlers[i].tag, type == null ? null : toInternal(type));
                }
            }
        }
    }

    static String toInternal(String n) {
        // TODO replace
        return Type.getType(n).getInternalName();
    }

    private void reBuildInstructions(IrMethod ir, MethodVisitor asm) {
        asm = new LdcOptimizeAdapter(asm);
        int maxLocalIndex = 0;
        for (Local local : ir.locals) {
            maxLocalIndex = Math.max(maxLocalIndex, local._ls_index);
        }
        Map<String, Integer> lockMap = new HashMap<String, Integer>();
        for (Stmt st : ir.stmts) {
            switch (st.st) {
            case LABEL:
                LabelStmt labelStmt = (LabelStmt) st;
                Label label = (Label) labelStmt.tag;
                asm.visitLabel(label);
                if (labelStmt.lineNumber >= 0) {
                    asm.visitLineNumber(labelStmt.lineNumber, label);
                }
                break;
            case ASSIGN: {
                E2Stmt e2 = (E2Stmt) st;
                Value v1 = e2.op1;
                Value v2 = e2.op2;
                switch (v1.vt) {
                case LOCAL:

                    Local local = ((Local) v1);
                    int i = local._ls_index;

                    if (v2.vt == VT.LOCAL && (i == ((Local) v2)._ls_index)) {//
                        continue;
                    }

                    boolean skipOrg = false;
                    if (v1.valueType.charAt(0) == 'I') {// check for IINC
                        if (v2.vt == VT.ADD) {
                            E2Expr e = (E2Expr) v2;
                            if ((e.op1 == local && e.op2.vt == VT.CONSTANT)
                                    || (e.op2 == local && e.op1.vt == VT.CONSTANT)) {
                                int increment = (Integer) ((Constant) (e.op1 == local ? e.op2 : e.op1)).value;
                                if (increment >= Short.MIN_VALUE && increment <= Short.MAX_VALUE) {
                                    asm.visitIincInsn(i, increment);
                                    skipOrg = true;
                                }
                            }
                        } else if (v2.vt == VT.SUB) {
                            E2Expr e = (E2Expr) v2;
                            if (e.op1 == local && e.op2.vt == VT.CONSTANT) {
                                int increment = -(Integer) ((Constant) e.op2).value;
                                if (increment >= Short.MIN_VALUE && increment <= Short.MAX_VALUE) {
                                    asm.visitIincInsn(i, increment);
                                    skipOrg = true;
                                }
                            }
                        }
                    }
                    if (!skipOrg) {
                        accept(v2, asm);
                        if (i >= 0) {
                            asm.visitVarInsn(getOpcode(v1, ISTORE), i);
                        } else if (!v1.valueType.equals("V")) { // skip void type locals
                            switch (v1.valueType.charAt(0)) {
                            case 'J':
                            case 'D':
                                asm.visitInsn(POP2);
                                break;
                            default:
                                asm.visitInsn(POP);
                                break;
                            }
                        }
                    }
                    break;
                case STATIC_FIELD: {
                    StaticFieldExpr fe = (StaticFieldExpr) v1;
                    accept(v2, asm);
                    insertI2x(v2.valueType, fe.type, asm);
                    asm.visitFieldInsn(PUTSTATIC, toInternal(fe.owner), fe.name, fe.type);
                    break;
                }
                case FIELD: {
                    FieldExpr fe = (FieldExpr) v1;
                    accept(fe.op, asm);
                    accept(v2, asm);
                    insertI2x(v2.valueType, fe.type, asm);
                    asm.visitFieldInsn(PUTFIELD, toInternal(fe.owner), fe.name, fe.type);
                    break;
                }
                case ARRAY:
                    ArrayExpr ae = (ArrayExpr) v1;
                    accept(ae.op1, asm);
                    accept(ae.op2, asm);
                    accept(v2, asm);
                    String tp1 = ae.op1.valueType;
                    String tp2 = ae.valueType;
                    if (tp1.charAt(0) == '[') {
                        String arrayElementType = tp1.substring(1);
                        insertI2x(v2.valueType, arrayElementType, asm);
                        asm.visitInsn(getOpcode(arrayElementType, IASTORE));
                    } else {
                        asm.visitInsn(getOpcode(tp2, IASTORE));
                    }
                    break;
                }
            }
                break;
            case IDENTITY: {
                E2Stmt e2 = (E2Stmt) st;
                if (e2.op2.vt == VT.EXCEPTION_REF) {
                    int index = ((Local) e2.op1)._ls_index;
                    if (index >= 0) {
                        asm.visitVarInsn(ASTORE, index);
                    } else {
                        asm.visitInsn(POP);
                    }
                }
            }
                break;

            case FILL_ARRAY_DATA: {
                E2Stmt e2 = (E2Stmt) st;
                Object arrayData = ((Constant) e2.getOp2()).value;
                int arraySize = Array.getLength(arrayData);
                String arrayValueType = e2.getOp1().valueType;
                String elementType;
                if (arrayValueType.charAt(0) == '[') {
                    elementType = arrayValueType.substring(1);
                } else {
                    elementType = "I";
                }
                int iastoreOP = getOpcode(elementType, IASTORE);
                accept(e2.getOp1(), asm);
                for (int i = 0; i < arraySize; i++) {
                    asm.visitInsn(DUP);
                    asm.visitLdcInsn(i);
                    asm.visitLdcInsn(Array.get(arrayData, i));
                    asm.visitInsn(iastoreOP);
                }
                asm.visitInsn(POP);
            }
                break;
            case GOTO:
                asm.visitJumpInsn(GOTO, (Label) ((GotoStmt) st).target.tag);
                break;
            case IF:
                reBuildJumpInstructions((IfStmt) st, asm);
                break;
            case LOCK: {
                Value v = ((UnopStmt) st).op;
                accept(v, asm);
                if (optimizeSynchronized) {
                    switch (v.vt) {
                    case LOCAL:
                        // FIXME do we have to disable local due to OptSyncTest ?
                        // break;
                    case CONSTANT: {
                        String key;
                        if (v.vt == VT.LOCAL) {
                            key = "L" + ((Local) v)._ls_index;
                        } else {
                            key = "C" + ((Constant) v).value;
                        }
                        Integer integer = lockMap.get(key);
                        int nIndex = integer != null ? integer : ++maxLocalIndex;
                        asm.visitInsn(DUP);
                        asm.visitVarInsn(getOpcode(v, ISTORE), nIndex);
                        lockMap.put(key, nIndex);
                    }
                        break;
                    default:
                        throw new RuntimeException();
                    }
                }
                asm.visitInsn(MONITORENTER);
            }
                break;
            case UNLOCK: {
                Value v = ((UnopStmt) st).op;
                if (optimizeSynchronized) {
                    switch (v.vt) {
                    case LOCAL:
                    case CONSTANT: {
                        String key;
                        if (v.vt == VT.LOCAL) {
                            key = "L" + ((Local) v)._ls_index;
                        } else {
                            key = "C" + ((Constant) v).value;
                        }
                        Integer integer = lockMap.get(key);
                        if (integer != null) {
                            asm.visitVarInsn(getOpcode(v, ILOAD), integer);
                        } else {
                            accept(v, asm);
                        }
                    }
                        break;
                    // TODO other
                    default: {
                        accept(v, asm);
                        break;
                    }
                    }
                } else {
                    accept(v, asm);
                }
                asm.visitInsn(MONITOREXIT);
            }
                break;
            case NOP:
                break;
            case RETURN: {
                Value v = ((UnopStmt) st).op;
                accept(v, asm);
                insertI2x(v.valueType, ir.ret, asm);
                asm.visitInsn(getOpcode(v, IRETURN));
            }
                break;
            case RETURN_VOID:
                asm.visitInsn(RETURN);
                break;
            case LOOKUP_SWITCH: {
                LookupSwitchStmt lss = (LookupSwitchStmt) st;
                accept(lss.op, asm);
                Label targets[] = new Label[lss.targets.length];
                for (int i = 0; i < targets.length; i++) {
                    targets[i] = (Label) lss.targets[i].tag;
                }
                asm.visitLookupSwitchInsn((Label) lss.defaultTarget.tag, lss.lookupValues, targets);
            }
                break;
            case TABLE_SWITCH: {
                TableSwitchStmt tss = (TableSwitchStmt) st;
                accept(tss.op, asm);
                Label targets[] = new Label[tss.targets.length];
                for (int i = 0; i < targets.length; i++) {
                    targets[i] = (Label) tss.targets[i].tag;
                }
                asm.visitTableSwitchInsn(tss.lowIndex, tss.lowIndex + targets.length - 1,
                        (Label) tss.defaultTarget.tag, targets);
            }
                break;
            case THROW:
                accept(((UnopStmt) st).op, asm);
                asm.visitInsn(ATHROW);
                break;
            case VOID_INVOKE:
                InvokeExpr invokeExpr = (InvokeExpr) st.getOp();
                accept(invokeExpr, asm);
                String ret = invokeExpr.ret;
                if (invokeExpr.vt == VT.INVOKE_NEW) {
                    asm.visitInsn(POP);
                } else if (!"V".equals(ret)) {
                    switch (ret.charAt(0)) {
                    case 'J':
                    case 'D':
                        asm.visitInsn(POP2);
                        break;
                    default:
                        asm.visitInsn(POP);
                        break;
                    }
                }
                break;
            default:
                throw new RuntimeException("not support st: " + st.st);
            }

        }
    }

    private int[] buildLocalUsage(IrMethod ir) {
        int maxLocal = 0;
        for (Local local : ir.locals) {
            if (local._ls_index > maxLocal) {
                maxLocal = local._ls_index;
            }
        }
        final int[] localUsages = new int[maxLocal + 1]; // maxIndex+1 to get the size
        Cfg.travel(ir.stmts, new TravelCallBack() {

            @Override
            public Value onAssign(Local v, AssignStmt as) {
                return v;
            }

            @Override
            public Value onUse(Local v) {
                localUsages[v._ls_index]++;
                return v;
            }
        }, true);
        return localUsages;
    }

    /**
     * insert I2x instruction
     * 
     * @param tos
     * @param expect
     * @param mv
     */
    private static void insertI2x(String tos, String expect, MethodVisitor mv) {
        switch (expect.charAt(0)) {
        case 'B':
            switch (tos.charAt(0)) {
            case 'S':
            case 'C':
            case 'I':
                mv.visitInsn(I2B);
            }
            break;
        case 'S':
            switch (tos.charAt(0)) {
            case 'C':
            case 'I':
                mv.visitInsn(I2S);
            }
            break;
        case 'C':
            switch (tos.charAt(0)) {
            case 'I':
                mv.visitInsn(I2C);
            }
            break;
        }
    }

    static boolean isZeroOrNull(Value v1) {
        if (v1.vt == VT.CONSTANT) {
            Object v = ((Constant) v1).value;
            return Integer.valueOf(0).equals(v) || Constant.Null.equals(v);
        }
        return false;
    }

    private void reBuildJumpInstructions(IfStmt st, MethodVisitor asm) {
        Label target = (Label) st.target.tag;
        Value v = st.op;
        Value v1 = v.getOp1();
        Value v2 = v.getOp2();

        String type = v1.valueType;

        switch (type.charAt(0)) {
        case '[':
        case 'L':
            // IF_ACMPx
            // IF[non]null
            if (isZeroOrNull(v1) || isZeroOrNull(v2)) { // IF[non]null
                if (isZeroOrNull(v2)) {// v2 is null
                    accept(v1, asm);
                } else {
                    accept(v2, asm);
                }
                asm.visitJumpInsn(v.vt == VT.EQ ? IFNULL : IFNONNULL, target);
            } else {
                accept(v1, asm);
                accept(v2, asm);
                asm.visitJumpInsn(v.vt == VT.EQ ? IF_ACMPEQ : IF_ACMPNE, target);
            }
            break;
        default:
            // IFx
            // IF_ICMPx
            if (isZeroOrNull(v1) || isZeroOrNull(v2)) { // IFx
                if (isZeroOrNull(v2)) {// v2 is zero
                    accept(v1, asm);
                } else {
                    accept(v2, asm);
                }
                switch (v.vt) {
                case NE:
                    asm.visitJumpInsn(IFNE, target);
                    break;
                case EQ:
                    asm.visitJumpInsn(IFEQ, target);
                    break;
                case GE:
                    asm.visitJumpInsn(IFGE, target);
                    break;
                case GT:
                    asm.visitJumpInsn(IFGT, target);
                    break;
                case LE:
                    asm.visitJumpInsn(IFLE, target);
                    break;
                case LT:
                    asm.visitJumpInsn(IFLT, target);
                    break;
                }
            } else { // IF_ICMPx
                accept(v1, asm);
                accept(v2, asm);
                switch (v.vt) {
                case NE:
                    asm.visitJumpInsn(IF_ICMPNE, target);
                    break;
                case EQ:
                    asm.visitJumpInsn(IF_ICMPEQ, target);
                    break;
                case GE:
                    asm.visitJumpInsn(IF_ICMPGE, target);
                    break;
                case GT:
                    asm.visitJumpInsn(IF_ICMPGT, target);
                    break;
                case LE:
                    asm.visitJumpInsn(IF_ICMPLE, target);
                    break;
                case LT:
                    asm.visitJumpInsn(IF_ICMPLT, target);
                    break;
                }
            }
            break;
        }
    }

    /**
     * 
     * @param v
     * @param op
     *            DUP
     * @return
     */
    static int getOpcode(Value v, int op) {
        return getOpcode(v.valueType, op);
    }

    static int getOpcode(String v, int op) {
        return Type.getType(v).getOpcode(op);
    }

    private static void accept(Value value, MethodVisitor asm) {

        switch (value.et) {
        case E0:
            switch (value.vt) {
            case LOCAL:
                asm.visitVarInsn(getOpcode(value, ILOAD), ((Local) value)._ls_index);
                break;
            case CONSTANT:
                Constant cst = (Constant) value;
                if (cst.value.equals(Constant.Null)) {
                    asm.visitInsn(ACONST_NULL);
                } else if (cst.value instanceof Constant.Type) {
                    asm.visitLdcInsn(Type.getType(((Constant.Type) cst.value).desc));
                } else {
                    asm.visitLdcInsn(cst.value);
                }
                break;
            case NEW:
                asm.visitTypeInsn(NEW, toInternal(((NewExpr) value).type));
                break;
            case STATIC_FIELD:
                StaticFieldExpr sfe = (StaticFieldExpr) value;
                asm.visitFieldInsn(GETSTATIC, toInternal(sfe.owner), sfe.name, sfe.type);
                break;
            }
            break;
        case E1:
            reBuildE1Expression((E1Expr) value, asm);
            break;
        case E2:
            reBuildE2Expression((E2Expr) value, asm);
            break;
        case En:
            reBuildEnExpression((EnExpr) value, asm);
            break;
        }
    }

    private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) {
        if (value.vt == VT.FILLED_ARRAY) {
            FilledArrayExpr fae = (FilledArrayExpr) value;
            reBuildE1Expression(Exprs.nNewArray(fae.type, Exprs.nInt(fae.ops.length)), asm);
            String tp1 = fae.valueType;
            int xastore = IASTORE;
            String elementType = null;
            if (tp1.charAt(0) == '[') {
                elementType = tp1.substring(1);
                xastore = getOpcode(elementType, IASTORE);
            }

            for (int i = 0; i < fae.ops.length; i++) {
                if (fae.ops[i] == null)
                    continue;
                asm.visitInsn(DUP);
                asm.visitLdcInsn(i);
                accept(fae.ops[i], asm);
                String tp2 = fae.ops[i].valueType;
                if (elementType != null) {
                    insertI2x(tp2, elementType, asm);
                }
                asm.visitInsn(xastore);
            }
            return;
        }

        switch (value.vt) {
        case NEW_MUTI_ARRAY:
            for (Value vb : value.ops) {
                accept(vb, asm);
            }
            NewMutiArrayExpr nmae = (NewMutiArrayExpr) value;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < nmae.dimension; i++) {
                sb.append('[');
            }
            sb.append(nmae.baseType);
            asm.visitMultiANewArrayInsn(sb.toString(), nmae.dimension);
            break;
        case INVOKE_NEW:
            asm.visitTypeInsn(NEW, toInternal(((InvokeExpr) value).owner));
            asm.visitInsn(DUP);
            // pass through
        case INVOKE_INTERFACE:
        case INVOKE_SPECIAL:
        case INVOKE_STATIC:
        case INVOKE_VIRTUAL:
            InvokeExpr ie = (InvokeExpr) value;
            int i = 0;
            if (value.vt != VT.INVOKE_STATIC && value.vt != VT.INVOKE_NEW) {
                i = 1;
                accept(value.ops[0], asm);
            }
            for (int j = 0; i < value.ops.length; i++, j++) {
                Value vb = value.ops[i];
                accept(vb, asm);
                insertI2x(vb.valueType, ie.args[j], asm);
            }

            int opcode;
            switch (value.vt) {
            case INVOKE_VIRTUAL:
                opcode = INVOKEVIRTUAL;
                break;
            case INVOKE_INTERFACE:
                opcode = INVOKEINTERFACE;
                break;
            case INVOKE_NEW:
            case INVOKE_SPECIAL:
                opcode = INVOKESPECIAL;
                break;
            case INVOKE_STATIC:
                opcode = INVOKESTATIC;
                break;
            default:
                opcode = -1;
            }

            asm.visitMethodInsn(opcode, toInternal(ie.owner), ie.name,
                    buildMethodDesc(ie.vt == VT.INVOKE_NEW ? "V" : ie.ret, ie.args));
            break;
        }
    }

    static String buildMethodDesc(String ret, String... ps) {
        StringBuilder sb = new StringBuilder();
        sb.append('(');
        for (String p : ps) {
            sb.append(p);
        }
        sb.append(')');
        sb.append(ret);
        return sb.toString();
    }

    private static void reBuildE1Expression(E1Expr e1, MethodVisitor asm) {
        accept(e1.getOp(), asm);
        switch (e1.vt) {
        case STATIC_FIELD: {
            FieldExpr fe = (FieldExpr) e1;
            asm.visitFieldInsn(GETSTATIC, toInternal(fe.owner), fe.name, fe.type);
            break;
        }
        case FIELD: {
            FieldExpr fe = (FieldExpr) e1;
            asm.visitFieldInsn(GETFIELD, toInternal(fe.owner), fe.name, fe.type);
            break;
        }
        case NEW_ARRAY: {
            TypeExpr te = (TypeExpr) e1;
            switch (te.type.charAt(0)) {
            case '[':
            case 'L':
                asm.visitTypeInsn(ANEWARRAY, toInternal(te.type));
                break;
            case 'Z':
                asm.visitIntInsn(NEWARRAY, T_BOOLEAN);
                break;
            case 'B':
                asm.visitIntInsn(NEWARRAY, T_BYTE);
                break;
            case 'S':
                asm.visitIntInsn(NEWARRAY, T_SHORT);
                break;
            case 'C':
                asm.visitIntInsn(NEWARRAY, T_CHAR);
                break;
            case 'I':
                asm.visitIntInsn(NEWARRAY, T_INT);
                break;
            case 'F':
                asm.visitIntInsn(NEWARRAY, T_FLOAT);
                break;
            case 'J':
                asm.visitIntInsn(NEWARRAY, T_LONG);
                break;
            case 'D':
                asm.visitIntInsn(NEWARRAY, T_DOUBLE);
                break;
            }
        }
            break;
        case CHECK_CAST:
        case INSTANCE_OF: {
            TypeExpr te = (TypeExpr) e1;
            asm.visitTypeInsn(e1.vt == VT.CHECK_CAST ? CHECKCAST : INSTANCEOF, toInternal(te.type));
        }
            break;
        case CAST: {
            CastExpr te = (CastExpr) e1;
            cast2(e1.op.valueType, te.to, asm);
        }
            break;
        case LENGTH:
            asm.visitInsn(ARRAYLENGTH);
            break;
        case NEG:
            asm.visitInsn(getOpcode(e1, INEG));
            break;
        }
    }

    private static void reBuildE2Expression(E2Expr e2, MethodVisitor asm) {
        String type = e2.op2.valueType;
        accept(e2.op1, asm);
        if ((e2.vt == VT.ADD || e2.vt == VT.SUB) && e2.op2.vt == VT.CONSTANT) {
            // [x + (-1)] to [x - 1]
            // [x - (-1)] to [x + 1]
            Constant constant = (Constant) e2.op2;
            String t = constant.valueType;
            switch (t.charAt(0)) {
            case 'S':
            case 'B':
            case 'I': {
                int s = (Integer) constant.value;
                if (s < 0) {
                    asm.visitLdcInsn(-s);
                    asm.visitInsn(getOpcode(type, e2.vt == VT.ADD ? ISUB : IADD));
                    return;
                }
            }
                break;
            case 'F': {
                float s = (Float) constant.value;
                if (s < 0) {
                    asm.visitLdcInsn(-s);
                    asm.visitInsn(getOpcode(type, e2.vt == VT.ADD ? ISUB : IADD));
                    return;
                }
            }
                break;
            case 'J': {
                long s = (Long) constant.value;
                if (s < 0) {
                    asm.visitLdcInsn(-s);
                    asm.visitInsn(getOpcode(type, e2.vt == VT.ADD ? ISUB : IADD));
                    return;
                }
            }
                break;
            case 'D': {
                double s = (Double) constant.value;
                if (s < 0) {
                    asm.visitLdcInsn(-s);
                    asm.visitInsn(getOpcode(type, e2.vt == VT.ADD ? ISUB : IADD));
                    return;
                }
            }
                break;
            }
        }

        accept(e2.op2, asm);

        String tp1 = e2.op1.valueType;
        switch (e2.vt) {
        case ARRAY:
            String tp2 = e2.valueType;
            if (tp1.charAt(0) == '[') {
                asm.visitInsn(getOpcode(tp1.substring(1), IALOAD));
            } else {
                asm.visitInsn(getOpcode(tp2, IALOAD));
            }
            break;
        case ADD:
            asm.visitInsn(getOpcode(type, IADD));
            break;
        case SUB:
            asm.visitInsn(getOpcode(type, ISUB));
            break;
        case DIV:
            asm.visitInsn(getOpcode(type, IDIV));
            break;
        case MUL:
            asm.visitInsn(getOpcode(type, IMUL));
            break;
        case REM:
            asm.visitInsn(getOpcode(type, IREM));
            break;
        case AND:
            asm.visitInsn(getOpcode(type, IAND));
            break;
        case OR:
            asm.visitInsn(getOpcode(type, IOR));
            break;
        case XOR:
            asm.visitInsn(getOpcode(type, IXOR));
            break;

        case SHL:
            asm.visitInsn(getOpcode(tp1, ISHL));
            break;
        case SHR:
            asm.visitInsn(getOpcode(tp1, ISHR));
            break;
        case USHR:
            asm.visitInsn(getOpcode(tp1, IUSHR));
            break;
        case LCMP:
            asm.visitInsn(LCMP);
            break;
        case FCMPG:
            asm.visitInsn(FCMPG);
            break;
        case DCMPG:
            asm.visitInsn(DCMPG);
            break;
        case FCMPL:
            asm.visitInsn(FCMPL);
            break;
        case DCMPL:
            asm.visitInsn(DCMPL);
            break;
        }
    }

    private static void cast2(String t1, String t2, MethodVisitor asm) {
        if (t1.equals(t2)) {
            return;
        }
        switch (t1.charAt(0)) {
        case 'Z':
        case 'B':
        case 'C':
        case 'S':
        case 'I': {
            switch (t2.charAt(0)) {
            case 'F':
                asm.visitInsn(I2F);
                break;
            case 'J':
                asm.visitInsn(I2L);
                break;
            case 'D':
                asm.visitInsn(I2D);
                break;
            case 'C':
                asm.visitInsn(I2C);
                break;
            case 'B':
                asm.visitInsn(I2B);
                break;
            case 'S':
                asm.visitInsn(I2S);
                break;
            }
        }
            break;
        case 'J': {
            switch (t2.charAt(0)) {
            case 'I':
                asm.visitInsn(L2I);
                break;
            case 'F':
                asm.visitInsn(L2F);
                break;
            case 'D':
                asm.visitInsn(L2D);
                break;
            }
        }
            break;
        case 'D': {
            switch (t2.charAt(0)) {
            case 'I':
                asm.visitInsn(D2I);
                break;
            case 'F':
                asm.visitInsn(D2F);
                break;
            case 'J':
                asm.visitInsn(D2L);
                break;
            }
        }
            break;
        case 'F': {
            switch (t2.charAt(0)) {
            case 'I':
                asm.visitInsn(F2I);
                break;
            case 'J':
                asm.visitInsn(F2L);
                break;
            case 'D':
                asm.visitInsn(F2D);
                break;
            }
            break;
        }
        }
    }
}