| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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 org.jetbrains.java.decompiler.modules.decompiler; |
| |
| import org.jetbrains.java.decompiler.code.CodeConstants; |
| import org.jetbrains.java.decompiler.code.Instruction; |
| import org.jetbrains.java.decompiler.code.InstructionSequence; |
| import org.jetbrains.java.decompiler.code.cfg.BasicBlock; |
| import org.jetbrains.java.decompiler.main.DecompilerContext; |
| import org.jetbrains.java.decompiler.modules.decompiler.exps.*; |
| import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; |
| import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; |
| import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; |
| import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; |
| import org.jetbrains.java.decompiler.modules.decompiler.stats.*; |
| import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; |
| import org.jetbrains.java.decompiler.struct.StructClass; |
| import org.jetbrains.java.decompiler.struct.attr.StructBootstrapMethodsAttribute; |
| import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; |
| import org.jetbrains.java.decompiler.struct.consts.ConstantPool; |
| import org.jetbrains.java.decompiler.struct.consts.LinkConstant; |
| import org.jetbrains.java.decompiler.struct.consts.PooledConstant; |
| import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; |
| import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; |
| import org.jetbrains.java.decompiler.struct.gen.VarType; |
| import org.jetbrains.java.decompiler.util.InterpreterUtil; |
| |
| import java.util.*; |
| |
| public class ExprProcessor implements CodeConstants { |
| |
| public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>"; |
| public static final String UNKNOWN_TYPE_STRING = "<unknown>"; |
| public static final String NULL_TYPE_STRING = "<null>"; |
| |
| private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>(); |
| |
| static { |
| |
| // mapConsts.put(new Integer(opc_i2l), new |
| // Integer(FunctionExprent.FUNCTION_I2L)); |
| // mapConsts.put(new Integer(opc_i2f), new |
| // Integer(FunctionExprent.FUNCTION_I2F)); |
| // mapConsts.put(new Integer(opc_i2d), new |
| // Integer(FunctionExprent.FUNCTION_I2D)); |
| // mapConsts.put(new Integer(opc_l2i), new |
| // Integer(FunctionExprent.FUNCTION_L2I)); |
| // mapConsts.put(new Integer(opc_l2f), new |
| // Integer(FunctionExprent.FUNCTION_L2F)); |
| // mapConsts.put(new Integer(opc_l2d), new |
| // Integer(FunctionExprent.FUNCTION_L2D)); |
| // mapConsts.put(new Integer(opc_f2i), new |
| // Integer(FunctionExprent.FUNCTION_F2I)); |
| // mapConsts.put(new Integer(opc_f2l), new |
| // Integer(FunctionExprent.FUNCTION_F2L)); |
| // mapConsts.put(new Integer(opc_f2d), new |
| // Integer(FunctionExprent.FUNCTION_F2D)); |
| // mapConsts.put(new Integer(opc_d2i), new |
| // Integer(FunctionExprent.FUNCTION_D2I)); |
| // mapConsts.put(new Integer(opc_d2l), new |
| // Integer(FunctionExprent.FUNCTION_D2L)); |
| // mapConsts.put(new Integer(opc_d2f), new |
| // Integer(FunctionExprent.FUNCTION_D2F)); |
| // mapConsts.put(new Integer(opc_i2b), new |
| // Integer(FunctionExprent.FUNCTION_I2B)); |
| // mapConsts.put(new Integer(opc_i2c), new |
| // Integer(FunctionExprent.FUNCTION_I2C)); |
| // mapConsts.put(new Integer(opc_i2s), new |
| // Integer(FunctionExprent.FUNCTION_I2S)); |
| |
| mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAYLENGTH)); |
| mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST)); |
| mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF)); |
| } |
| |
| private static final VarType[] consts = |
| new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, |
| VarType.VARTYPE_STRING}; |
| |
| private static final VarType[] vartypes = |
| new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT}; |
| |
| private static final VarType[] arrtypes = |
| new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT, |
| VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT}; |
| |
| private static final int[] func1 = |
| new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV, |
| FunctionExprent.FUNCTION_REM}; |
| |
| private static final int[] func2 = |
| new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND, |
| FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR}; |
| |
| private static final int[] func3 = |
| new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I, |
| FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L, |
| FunctionExprent.FUNCTION_F2D, |
| FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B, |
| FunctionExprent.FUNCTION_I2C, |
| FunctionExprent.FUNCTION_I2S}; |
| |
| private static final int[] func4 = |
| new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL, |
| FunctionExprent.FUNCTION_DCMPG}; |
| |
| private static final int[] func5 = |
| new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE}; |
| |
| private static final int[] func6 = |
| new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE, |
| IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE}; |
| |
| private static final int[] func7 = new int[]{IfExprent.IF_NULL, IfExprent.IF_NONNULL}; |
| |
| private static final int[] func8 = new int[]{MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT}; |
| |
| private static final int[] arr_type = |
| new int[]{CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE, |
| CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG}; |
| |
| private static final int[] negifs = |
| new int[]{IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL, |
| IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE, |
| IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, |
| IfExprent.IF_ACMPEQ}; |
| |
| private static final String[] typeNames = new String[]{"byte", "char", "double", "float", "int", "long", "short", "boolean",}; |
| |
| private VarProcessor varProcessor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); |
| |
| public void processStatement(RootStatement root, StructClass cl) { |
| |
| FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); |
| DirectGraph dgraph = flatthelper.buildDirectGraph(root); |
| |
| // try { |
| // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); |
| // } catch (Exception ex) { |
| // ex.printStackTrace(); |
| // } |
| |
| // collect finally entry points |
| Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>(); |
| for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) { |
| for (FinallyPathWrapper finwrap : lst) { |
| setFinallyShortRangeEntryPoints.add(finwrap.entry); |
| } |
| } |
| |
| Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>(); |
| for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) { |
| for (FinallyPathWrapper finwrap : lst) { |
| setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry); |
| } |
| } |
| |
| Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>(); |
| collectCatchVars(root, flatthelper, mapCatch); |
| |
| Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<DirectNode, Map<String, PrimitiveExprsList>>(); |
| |
| LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); |
| LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>(); |
| |
| stack.add(dgraph.first); |
| stackEntryPoint.add(new LinkedList<String>()); |
| |
| Map<String, PrimitiveExprsList> map = new HashMap<String, PrimitiveExprsList>(); |
| map.put(null, new PrimitiveExprsList()); |
| mapData.put(dgraph.first, map); |
| |
| while (!stack.isEmpty()) { |
| |
| DirectNode node = stack.removeFirst(); |
| LinkedList<String> entrypoints = stackEntryPoint.removeFirst(); |
| |
| PrimitiveExprsList data; |
| if (mapCatch.containsKey(node.id)) { |
| data = getExpressionData(mapCatch.get(node.id)); |
| } |
| else { |
| data = mapData.get(node).get(buildEntryPointKey(entrypoints)); |
| } |
| |
| BasicBlockStatement block = node.block; |
| if (block != null) { |
| processBlock(block, data, cl); |
| block.setExprents(data.getLstExprents()); |
| } |
| |
| String currentEntrypoint = entrypoints.isEmpty() ? null : entrypoints.getLast(); |
| |
| for (DirectNode nd : node.succs) { |
| |
| boolean isSuccessor = true; |
| if (currentEntrypoint != null && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) { |
| isSuccessor = false; |
| for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(node.id)) { |
| if (finwraplong.source.equals(currentEntrypoint) && finwraplong.destination.equals(nd.id)) { |
| isSuccessor = true; |
| break; |
| } |
| } |
| } |
| |
| if (isSuccessor) { |
| |
| Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd); |
| if (mapSucc == null) { |
| mapData.put(nd, mapSucc = new HashMap<String, PrimitiveExprsList>()); |
| } |
| |
| LinkedList<String> ndentrypoints = new LinkedList<String>(entrypoints); |
| |
| if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) { |
| ndentrypoints.addLast(node.id); |
| } |
| else if (!setFinallyShortRangeEntryPoints.contains(nd.id) && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) { |
| ndentrypoints.removeLast(); // currentEntrypoint should |
| // not be null at this point |
| } |
| |
| // handling of entry point loops |
| int succ_entry_index = ndentrypoints.indexOf(nd.id); |
| if (succ_entry_index >= |
| 0) { // we are in a loop (e.g. continue in a finally block), drop all entry points in the list beginning with succ_entry_index |
| for (int elements_to_remove = ndentrypoints.size() - succ_entry_index; elements_to_remove > 0; elements_to_remove--) { |
| ndentrypoints.removeLast(); |
| } |
| } |
| |
| String ndentrykey = buildEntryPointKey(ndentrypoints); |
| if (!mapSucc.containsKey(ndentrykey)) { |
| |
| mapSucc.put(ndentrykey, copyVarExprents(data.copyStack())); |
| |
| stack.add(nd); |
| stackEntryPoint.add(ndentrypoints); |
| } |
| } |
| } |
| } |
| |
| initStatementExprents(root); |
| } |
| |
| // FIXME: Ugly code, to be rewritten. A tuple class is needed. |
| private static String buildEntryPointKey(LinkedList<String> entrypoints) { |
| if (entrypoints.isEmpty()) { |
| return null; |
| } |
| else { |
| StringBuilder buffer = new StringBuilder(); |
| for (String point : entrypoints) { |
| buffer.append(point); |
| buffer.append(":"); |
| } |
| return buffer.toString(); |
| } |
| } |
| |
| private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) { |
| ExprentStack stack = data.getStack(); |
| for (int i = 0; i < stack.size(); i++) { |
| stack.set(i, stack.get(i).copy()); |
| } |
| return data; |
| } |
| |
| private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) { |
| |
| List<VarExprent> lst = null; |
| |
| if (stat.type == Statement.TYPE_CATCHALL) { |
| CatchAllStatement catchall = (CatchAllStatement)stat; |
| if (!catchall.isFinally()) { |
| lst = catchall.getVars(); |
| } |
| } |
| else if (stat.type == Statement.TYPE_TRYCATCH) { |
| lst = ((CatchStatement)stat).getVars(); |
| } |
| |
| if (lst != null) { |
| for (int i = 1; i < stat.getStats().size(); i++) { |
| map.put(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0], lst.get(i - 1)); |
| } |
| } |
| |
| for (Statement st : stat.getStats()) { |
| collectCatchVars(st, flatthelper, map); |
| } |
| } |
| |
| private static void initStatementExprents(Statement stat) { |
| stat.initExprents(); |
| |
| for (Statement st : stat.getStats()) { |
| initStatementExprents(st); |
| } |
| } |
| |
| public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) { |
| |
| ConstantPool pool = cl.getPool(); |
| StructBootstrapMethodsAttribute bootstrap = |
| (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); |
| |
| BasicBlock block = stat.getBlock(); |
| |
| ExprentStack stack = data.getStack(); |
| List<Exprent> exprlist = data.getLstExprents(); |
| |
| InstructionSequence seq = block.getSeq(); |
| |
| for (int i = 0; i < seq.length(); i++) { |
| |
| Instruction instr = seq.getInstr(i); |
| |
| switch (instr.opcode) { |
| case opc_aconst_null: |
| pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null)); |
| break; |
| case opc_bipush: |
| case opc_sipush: |
| pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true)); |
| break; |
| case opc_lconst_0: |
| case opc_lconst_1: |
| pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0))); |
| break; |
| case opc_fconst_0: |
| case opc_fconst_1: |
| case opc_fconst_2: |
| pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0))); |
| break; |
| case opc_dconst_0: |
| case opc_dconst_1: |
| pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0))); |
| break; |
| case opc_ldc: |
| case opc_ldc_w: |
| case opc_ldc2_w: |
| PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0)); |
| pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value)); |
| break; |
| case opc_iload: |
| case opc_lload: |
| case opc_fload: |
| case opc_dload: |
| case opc_aload: |
| pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor)); |
| break; |
| case opc_iaload: |
| case opc_laload: |
| case opc_faload: |
| case opc_daload: |
| case opc_aaload: |
| case opc_baload: |
| case opc_caload: |
| case opc_saload: |
| Exprent index = stack.pop(); |
| Exprent arr = stack.pop(); |
| |
| VarType vartype = null; |
| switch (instr.opcode) { |
| case opc_laload: |
| vartype = VarType.VARTYPE_LONG; |
| break; |
| case opc_daload: |
| vartype = VarType.VARTYPE_DOUBLE; |
| } |
| pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload]), vartype); |
| break; |
| case opc_istore: |
| case opc_lstore: |
| case opc_fstore: |
| case opc_dstore: |
| case opc_astore: |
| Exprent top = stack.pop(); |
| int varindex = instr.getOperand(0); |
| AssignmentExprent assign = |
| new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top); |
| exprlist.add(assign); |
| break; |
| case opc_iastore: |
| case opc_lastore: |
| case opc_fastore: |
| case opc_dastore: |
| case opc_aastore: |
| case opc_bastore: |
| case opc_castore: |
| case opc_sastore: |
| Exprent value = stack.pop(); |
| Exprent index_store = stack.pop(); |
| Exprent arr_store = stack.pop(); |
| AssignmentExprent arrassign = |
| new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value); |
| exprlist.add(arrassign); |
| break; |
| case opc_iadd: |
| case opc_ladd: |
| case opc_fadd: |
| case opc_dadd: |
| case opc_isub: |
| case opc_lsub: |
| case opc_fsub: |
| case opc_dsub: |
| case opc_imul: |
| case opc_lmul: |
| case opc_fmul: |
| case opc_dmul: |
| case opc_idiv: |
| case opc_ldiv: |
| case opc_fdiv: |
| case opc_ddiv: |
| case opc_irem: |
| case opc_lrem: |
| case opc_frem: |
| case opc_drem: |
| pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack)); |
| break; |
| case opc_ishl: |
| case opc_lshl: |
| case opc_ishr: |
| case opc_lshr: |
| case opc_iushr: |
| case opc_lushr: |
| case opc_iand: |
| case opc_land: |
| case opc_ior: |
| case opc_lor: |
| case opc_ixor: |
| case opc_lxor: |
| pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack)); |
| break; |
| case opc_ineg: |
| case opc_lneg: |
| case opc_fneg: |
| case opc_dneg: |
| pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack)); |
| break; |
| case opc_iinc: |
| VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor); |
| exprlist.add(new AssignmentExprent(vevar, new FunctionExprent( |
| instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays |
| .asList(new Exprent[]{vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, new Integer(Math.abs(instr.getOperand(1))))})))); |
| break; |
| case opc_i2l: |
| case opc_i2f: |
| case opc_i2d: |
| case opc_l2i: |
| case opc_l2f: |
| case opc_l2d: |
| case opc_f2i: |
| case opc_f2l: |
| case opc_f2d: |
| case opc_d2i: |
| case opc_d2l: |
| case opc_d2f: |
| case opc_i2b: |
| case opc_i2c: |
| case opc_i2s: |
| pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack)); |
| break; |
| case opc_lcmp: |
| case opc_fcmpl: |
| case opc_fcmpg: |
| case opc_dcmpl: |
| case opc_dcmpg: |
| pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack)); |
| break; |
| case opc_ifeq: |
| case opc_ifne: |
| case opc_iflt: |
| case opc_ifge: |
| case opc_ifgt: |
| case opc_ifle: |
| exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack)); |
| break; |
| case opc_if_icmpeq: |
| case opc_if_icmpne: |
| case opc_if_icmplt: |
| case opc_if_icmpge: |
| case opc_if_icmpgt: |
| case opc_if_icmple: |
| case opc_if_acmpeq: |
| case opc_if_acmpne: |
| exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack)); |
| break; |
| case opc_ifnull: |
| case opc_ifnonnull: |
| exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack)); |
| break; |
| case opc_tableswitch: |
| case opc_lookupswitch: |
| exprlist.add(new SwitchExprent(stack.pop())); |
| break; |
| case opc_ireturn: |
| case opc_lreturn: |
| case opc_freturn: |
| case opc_dreturn: |
| case opc_areturn: |
| case opc_return: |
| case opc_athrow: |
| exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN, |
| instr.opcode == opc_return ? null : stack.pop(), |
| instr.opcode == opc_athrow |
| ? null |
| : ((MethodDescriptor)DecompilerContext |
| .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret)); |
| break; |
| case opc_monitorenter: |
| case opc_monitorexit: |
| exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop())); |
| break; |
| case opc_checkcast: |
| case opc_instanceof: |
| stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null)); |
| case opc_arraylength: |
| pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack)); |
| break; |
| case opc_getstatic: |
| case opc_getfield: |
| pushEx(stack, exprlist, |
| new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop())); |
| break; |
| case opc_putstatic: |
| case opc_putfield: |
| Exprent valfield = stack.pop(); |
| Exprent exprfield = |
| new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop()); |
| exprlist.add(new AssignmentExprent(exprfield, valfield)); |
| break; |
| case opc_invokevirtual: |
| case opc_invokespecial: |
| case opc_invokestatic: |
| case opc_invokeinterface: |
| case opc_invokedynamic: |
| if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { |
| |
| LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0)); |
| int dynamic_invokation_type = -1; |
| |
| if (instr.opcode == opc_invokedynamic && bootstrap != null) { |
| List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1); |
| LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); |
| |
| dynamic_invokation_type = content_method_handle.index1; |
| } |
| |
| InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type); |
| if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { |
| exprlist.add(exprinv); |
| } |
| else { |
| pushEx(stack, exprlist, exprinv); |
| } |
| } |
| break; |
| case opc_new: |
| case opc_anewarray: |
| case opc_multianewarray: |
| int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1); |
| VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true); |
| if (instr.opcode != opc_multianewarray) { |
| arrtype.arraydim += arrdims; |
| } |
| pushEx(stack, exprlist, new NewExprent(arrtype, stack, arrdims)); |
| break; |
| case opc_newarray: |
| pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1)); |
| break; |
| case opc_dup: |
| pushEx(stack, exprlist, stack.getByOffset(-1).copy()); |
| break; |
| case opc_dup_x1: |
| insertByOffsetEx(-2, stack, exprlist, -1); |
| break; |
| case opc_dup_x2: |
| if (stack.getByOffset(-2).getExprType().stack_size == 2) { |
| insertByOffsetEx(-2, stack, exprlist, -1); |
| } |
| else { |
| insertByOffsetEx(-3, stack, exprlist, -1); |
| } |
| break; |
| case opc_dup2: |
| if (stack.getByOffset(-1).getExprType().stack_size == 2) { |
| pushEx(stack, exprlist, stack.getByOffset(-1).copy()); |
| } |
| else { |
| pushEx(stack, exprlist, stack.getByOffset(-2).copy()); |
| pushEx(stack, exprlist, stack.getByOffset(-2).copy()); |
| } |
| break; |
| case opc_dup2_x1: |
| if (stack.getByOffset(-1).getExprType().stack_size == 2) { |
| insertByOffsetEx(-2, stack, exprlist, -1); |
| } |
| else { |
| insertByOffsetEx(-3, stack, exprlist, -2); |
| insertByOffsetEx(-3, stack, exprlist, -1); |
| } |
| break; |
| case opc_dup2_x2: |
| if (stack.getByOffset(-1).getExprType().stack_size == 2) { |
| if (stack.getByOffset(-2).getExprType().stack_size == 2) { |
| insertByOffsetEx(-2, stack, exprlist, -1); |
| } |
| else { |
| insertByOffsetEx(-3, stack, exprlist, -1); |
| } |
| } |
| else { |
| if (stack.getByOffset(-3).getExprType().stack_size == 2) { |
| insertByOffsetEx(-3, stack, exprlist, -2); |
| insertByOffsetEx(-3, stack, exprlist, -1); |
| } |
| else { |
| insertByOffsetEx(-4, stack, exprlist, -2); |
| insertByOffsetEx(-4, stack, exprlist, -1); |
| } |
| } |
| break; |
| case opc_swap: |
| insertByOffsetEx(-2, stack, exprlist, -1); |
| stack.pop(); |
| break; |
| case opc_pop: |
| case opc_pop2: |
| stack.pop(); |
| } |
| } |
| } |
| |
| private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) { |
| pushEx(stack, exprlist, exprent, null); |
| } |
| |
| private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent, VarType vartype) { |
| int varindex = VarExprent.STACK_BASE + stack.size(); |
| VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor); |
| var.setStack(true); |
| |
| exprlist.add(new AssignmentExprent(var, exprent)); |
| stack.push(var.copy()); |
| } |
| |
| private void insertByOffsetEx(int offset, ExprentStack stack, List<Exprent> exprlist, int copyoffset) { |
| |
| int base = VarExprent.STACK_BASE + stack.size(); |
| |
| LinkedList<VarExprent> lst = new LinkedList<VarExprent>(); |
| |
| for (int i = -1; i >= offset; i--) { |
| Exprent varex = stack.pop(); |
| VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor); |
| varnew.setStack(true); |
| exprlist.add(new AssignmentExprent(varnew, varex)); |
| lst.add(0, (VarExprent)varnew.copy()); |
| } |
| |
| Exprent exprent = lst.get(lst.size() + copyoffset).copy(); |
| VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor); |
| var.setStack(true); |
| exprlist.add(new AssignmentExprent(var, exprent)); |
| lst.add(0, (VarExprent)var.copy()); |
| |
| for (VarExprent expr : lst) { |
| stack.push(expr); |
| } |
| } |
| |
| public static String getTypeName(VarType type) { |
| return getTypeName(type, true); |
| } |
| |
| public static String getTypeName(VarType type, boolean getShort) { |
| |
| int tp = type.type; |
| if (tp <= CodeConstants.TYPE_BOOLEAN) { |
| return typeNames[tp]; |
| } |
| else if (tp == CodeConstants.TYPE_UNKNOWN) { |
| return UNKNOWN_TYPE_STRING; // INFO: should not occur |
| } |
| else if (tp == CodeConstants.TYPE_NULL) { |
| return NULL_TYPE_STRING; // INFO: should not occur |
| } |
| else if (tp == CodeConstants.TYPE_VOID) { |
| return "void"; |
| } |
| else if (tp == CodeConstants.TYPE_OBJECT) { |
| String ret = buildJavaClassName(type.value); |
| if (getShort) { |
| ret = DecompilerContext.getImportCollector().getShortName(ret); |
| } |
| |
| if (ret == null) { |
| // FIXME: a warning should be logged |
| ret = UNDEFINED_TYPE_STRING; |
| } |
| return ret; |
| } |
| |
| throw new RuntimeException("invalid type"); |
| } |
| |
| public static String getCastTypeName(VarType type) { |
| return getCastTypeName(type, true); |
| } |
| |
| public static String getCastTypeName(VarType type, boolean getShort) { |
| String s = getTypeName(type, getShort); |
| int dim = type.arraydim; |
| while (dim-- > 0) { |
| s += "[]"; |
| } |
| return s; |
| } |
| |
| public static PrimitiveExprsList getExpressionData(VarExprent var) { |
| PrimitiveExprsList prlst = new PrimitiveExprsList(); |
| VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor()); |
| vartmp.setStack(true); |
| |
| prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy())); |
| prlst.getStack().push(vartmp.copy()); |
| return prlst; |
| } |
| |
| public static boolean endsWithSemikolon(Exprent expr) { |
| int type = expr.type; |
| return !(type == Exprent.EXPRENT_SWITCH || |
| type == Exprent.EXPRENT_MONITOR || |
| type == Exprent.EXPRENT_IF || |
| (type == Exprent.EXPRENT_VAR && ((VarExprent)expr) |
| .isClassdef())); |
| } |
| |
| public static String jmpWrapper(Statement stat, int indent, boolean semicolon) { |
| StringBuilder buf = new StringBuilder(stat.toJava(indent)); |
| |
| String new_line_separator = DecompilerContext.getNewLineSeparator(); |
| |
| List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); |
| if (lstSuccs.size() == 1) { |
| StatEdge edge = lstSuccs.get(0); |
| if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { |
| buf.append(InterpreterUtil.getIndentString(indent)); |
| |
| switch (edge.getType()) { |
| case StatEdge.TYPE_BREAK: |
| buf.append("break"); |
| break; |
| case StatEdge.TYPE_CONTINUE: |
| buf.append("continue"); |
| } |
| |
| if (edge.labeled) { |
| buf.append(" label").append(edge.closure.id); |
| } |
| buf.append(";").append(new_line_separator); |
| } |
| } |
| |
| if (buf.length() == 0 && semicolon) { |
| buf.append(InterpreterUtil.getIndentString(indent)).append(";").append(new_line_separator); |
| } |
| |
| return buf.toString(); |
| } |
| |
| public static String buildJavaClassName(String name) { |
| String res = name.replace('/', '.'); |
| |
| if (res.contains("$")) { // attempt to invoke foreign member |
| // classes correctly |
| StructClass cl = DecompilerContext.getStructContext().getClass(name); |
| if (cl == null || !cl.isOwn()) { |
| res = res.replace('$', '.'); |
| } |
| } |
| |
| return res; |
| } |
| |
| public static String listToJava(List<Exprent> lst, int indent) { |
| if (lst == null || lst.isEmpty()) { |
| return ""; |
| } |
| |
| String indstr = InterpreterUtil.getIndentString(indent); |
| String new_line_separator = DecompilerContext.getNewLineSeparator(); |
| |
| StringBuilder buf = new StringBuilder(); |
| |
| for (Exprent expr : lst) { |
| String content = expr.toJava(indent); |
| if (content.length() > 0) { |
| if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassdef()) { |
| buf.append(indstr); |
| } |
| buf.append(content); |
| if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMontype() == MonitorExprent.MONITOR_ENTER) { |
| buf.append("{}"); // empty synchronized block |
| } |
| if (endsWithSemikolon(expr)) { |
| buf.append(";"); |
| } |
| buf.append(new_line_separator); |
| } |
| } |
| |
| return buf.toString(); |
| } |
| |
| public static ConstExprent getDefaultArrayValue(VarType arrtype) { |
| |
| ConstExprent defaultval; |
| if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) { |
| defaultval = new ConstExprent(VarType.VARTYPE_NULL, null); |
| } |
| else if (arrtype.type == CodeConstants.TYPE_FLOAT) { |
| defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); |
| } |
| else if (arrtype.type == CodeConstants.TYPE_LONG) { |
| defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); |
| } |
| else if (arrtype.type == CodeConstants.TYPE_DOUBLE) { |
| defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); |
| } |
| else { // integer types |
| defaultval = new ConstExprent(0, true); |
| } |
| |
| return defaultval; |
| } |
| |
| public static boolean getCastedExprent(Exprent exprent, VarType leftType, StringBuilder buffer, int indent, boolean castNull) { |
| return getCastedExprent(exprent, leftType, buffer, indent, castNull, false); |
| } |
| |
| public static boolean getCastedExprent(Exprent exprent, |
| VarType leftType, |
| StringBuilder buffer, |
| int indent, |
| boolean castNull, |
| boolean castAlways) { |
| |
| boolean ret = false; |
| VarType rightType = exprent.getExprType(); |
| |
| String res = exprent.toJava(indent); |
| |
| boolean cast = |
| !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT); |
| cast |= castAlways; |
| |
| if (!cast && castNull && rightType.type == CodeConstants.TYPE_NULL) { |
| // check for a nameless anonymous class |
| cast = !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType)); |
| } |
| if (!cast) { |
| cast = isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType); |
| } |
| |
| if (cast) { |
| if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { |
| res = "(" + res + ")"; |
| } |
| |
| res = "(" + getCastTypeName(leftType) + ")" + res; |
| ret = true; |
| } |
| |
| buffer.append(res); |
| |
| return ret; |
| } |
| |
| private static boolean isIntConstant(Exprent exprent) { |
| |
| if (exprent.type == Exprent.EXPRENT_CONST) { |
| ConstExprent cexpr = (ConstExprent)exprent; |
| switch (cexpr.getConsttype().type) { |
| case CodeConstants.TYPE_BYTE: |
| case CodeConstants.TYPE_BYTECHAR: |
| case CodeConstants.TYPE_SHORT: |
| case CodeConstants.TYPE_SHORTCHAR: |
| case CodeConstants.TYPE_INT: |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |