diff options
Diffstat (limited to 'src')
355 files changed, 40736 insertions, 37835 deletions
diff --git a/src/org/jetbrains/java/decompiler/code/CodeConstants.java b/src/org/jetbrains/java/decompiler/code/CodeConstants.java index 78cbcf0..de5198b 100644 --- a/src/org/jetbrains/java/decompiler/code/CodeConstants.java +++ b/src/org/jetbrains/java/decompiler/code/CodeConstants.java @@ -1,371 +1,370 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; public interface CodeConstants { - // ---------------------------------------------------------------------- - // BYTECODE VERSIONS - // ---------------------------------------------------------------------- - - public final static int BYTECODE_JAVA_LE_4 = 1; - public final static int BYTECODE_JAVA_5 = 2; - public final static int BYTECODE_JAVA_6 = 3; - public final static int BYTECODE_JAVA_7 = 4; - public final static int BYTECODE_JAVA_8 = 5; - - // ---------------------------------------------------------------------- - // VARIABLE TYPES - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------------- + // BYTECODE VERSIONS + // ---------------------------------------------------------------------- + + public final static int BYTECODE_JAVA_LE_4 = 1; + public final static int BYTECODE_JAVA_5 = 2; + public final static int BYTECODE_JAVA_6 = 3; + public final static int BYTECODE_JAVA_7 = 4; + public final static int BYTECODE_JAVA_8 = 5; + + // ---------------------------------------------------------------------- + // VARIABLE TYPES + // ---------------------------------------------------------------------- + + public final static int TYPE_BYTE = 0; + public final static int TYPE_CHAR = 1; + public final static int TYPE_DOUBLE = 2; + public final static int TYPE_FLOAT = 3; + public final static int TYPE_INT = 4; + public final static int TYPE_LONG = 5; + public final static int TYPE_SHORT = 6; + public final static int TYPE_BOOLEAN = 7; + public final static int TYPE_OBJECT = 8; + public final static int TYPE_ADDRESS = 9; + public final static int TYPE_VOID = 10; + public final static int TYPE_ANY = 11; + public final static int TYPE_GROUP2EMPTY = 12; + public final static int TYPE_NULL = 13; + public final static int TYPE_NOTINITIALIZED = 14; + public final static int TYPE_BYTECHAR = 15; + public final static int TYPE_SHORTCHAR = 16; + public final static int TYPE_UNKNOWN = 17; + public final static int TYPE_GENVAR = 18; + + // ---------------------------------------------------------------------- + // VARIABLE TYPE FAMILIES + // ---------------------------------------------------------------------- + + public final static int TYPE_FAMILY_UNKNOWN = 0; + public final static int TYPE_FAMILY_BOOLEAN = 1; + public final static int TYPE_FAMILY_INTEGER = 2; + public final static int TYPE_FAMILY_FLOAT = 3; + public final static int TYPE_FAMILY_LONG = 4; + public final static int TYPE_FAMILY_DOUBLE = 5; + public final static int TYPE_FAMILY_OBJECT = 6; + + // ---------------------------------------------------------------------- + // MODULE CONSTANTS + // ---------------------------------------------------------------------- + + public final static int STACKSIZE_SIMPLE = 1; + public final static int STACKSIZE_DOUBLE = 2; + + public final static int VAR_LOCAL = 0; + public final static int VAR_STACK = 1; + + public final static int VAR_WRITE = 0; + public final static int VAR_READ = 1; + + + // ---------------------------------------------------------------------- + // ACCESS FLAGS + // ---------------------------------------------------------------------- + + public final static int ACC_PUBLIC = 0x0001; + public final static int ACC_PRIVATE = 0x0002; + public final static int ACC_PROTECTED = 0x0004; + public final static int ACC_STATIC = 0x0008; + public final static int ACC_FINAL = 0x0010; + public final static int ACC_SYNCHRONIZED = 0x0020; + public final static int ACC_NATIVE = 0x0100; + public final static int ACC_ABSTRACT = 0x0400; + public final static int ACC_STRICT = 0x0800; + public final static int ACC_VOLATILE = 0x0040; + public final static int ACC_BRIDGE = 0x0040; + public final static int ACC_TRANSIENT = 0x0080; + public final static int ACC_VARARGS = 0x0080; + public final static int ACC_SYNTHETIC = 0x1000; + public final static int ACC_ANNOTATION = 0x2000; + public final static int ACC_ENUM = 0x4000; + + // ---------------------------------------------------------------------- + // CLASS FLAGS + // ---------------------------------------------------------------------- + + public final static int ACC_SUPER = 0x0020; + public final static int ACC_INTERFACE = 0x0200; + + + // ---------------------------------------------------------------------- + // DEPENDENCY CONSTANTS + // ---------------------------------------------------------------------- - public final static int TYPE_BYTE = 0; - public final static int TYPE_CHAR = 1; - public final static int TYPE_DOUBLE = 2; - public final static int TYPE_FLOAT = 3; - public final static int TYPE_INT = 4; - public final static int TYPE_LONG = 5; - public final static int TYPE_SHORT = 6; - public final static int TYPE_BOOLEAN = 7; - public final static int TYPE_OBJECT = 8; - public final static int TYPE_ADDRESS = 9; - public final static int TYPE_VOID = 10; - public final static int TYPE_ANY = 11; - public final static int TYPE_GROUP2EMPTY = 12; - public final static int TYPE_NULL = 13; - public final static int TYPE_NOTINITIALIZED = 14; - public final static int TYPE_BYTECHAR = 15; - public final static int TYPE_SHORTCHAR = 16; - public final static int TYPE_UNKNOWN = 17; - public final static int TYPE_GENVAR = 18; + public final static int DEP_CONSTANT = 0; + public final static int DEP_UNKNOWN = 1; + public final static int DEP_GENERAL = 2; + public final static int DEP_PARAMS = 4; + public final static int DEP_STATIC = 8; - // ---------------------------------------------------------------------- - // VARIABLE TYPE FAMILIES - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------------- + // INSTRUCTION GROUPS + // ---------------------------------------------------------------------- - public final static int TYPE_FAMILY_UNKNOWN = 0; - public final static int TYPE_FAMILY_BOOLEAN = 1; - public final static int TYPE_FAMILY_INTEGER = 2; - public final static int TYPE_FAMILY_FLOAT = 3; - public final static int TYPE_FAMILY_LONG = 4; - public final static int TYPE_FAMILY_DOUBLE = 5; - public final static int TYPE_FAMILY_OBJECT = 6; - - // ---------------------------------------------------------------------- - // MODULE CONSTANTS - // ---------------------------------------------------------------------- + public final static int GROUP_GENERAL = 1; + public final static int GROUP_JUMP = 2; + public final static int GROUP_SWITCH = 3; + public final static int GROUP_INVOCATION = 4; + public final static int GROUP_FIELDACCESS = 5; + public final static int GROUP_RETURN = 6; - public final static int STACKSIZE_SIMPLE = 1; - public final static int STACKSIZE_DOUBLE = 2; - - public final static int VAR_LOCAL = 0; - public final static int VAR_STACK = 1; - - public final static int VAR_WRITE = 0; - public final static int VAR_READ = 1; - - - // ---------------------------------------------------------------------- - // ACCESS FLAGS - // ---------------------------------------------------------------------- - - public final static int ACC_PUBLIC = 0x0001; - public final static int ACC_PRIVATE = 0x0002; - public final static int ACC_PROTECTED = 0x0004; - public final static int ACC_STATIC = 0x0008; - public final static int ACC_FINAL = 0x0010; - public final static int ACC_SYNCHRONIZED = 0x0020; - public final static int ACC_NATIVE = 0x0100; - public final static int ACC_ABSTRACT = 0x0400; - public final static int ACC_STRICT = 0x0800; - public final static int ACC_VOLATILE = 0x0040; - public final static int ACC_BRIDGE = 0x0040; - public final static int ACC_TRANSIENT = 0x0080; - public final static int ACC_VARARGS = 0x0080; - public final static int ACC_SYNTHETIC = 0x1000; - public final static int ACC_ANNOTATION = 0x2000; - public final static int ACC_ENUM = 0x4000; + // ---------------------------------------------------------------------- + // POOL CONSTANTS + // ---------------------------------------------------------------------- - // ---------------------------------------------------------------------- - // CLASS FLAGS - // ---------------------------------------------------------------------- - - public final static int ACC_SUPER = 0x0020; - public final static int ACC_INTERFACE = 0x0200; - + public final static int CONSTANT_Utf8 = 1; + public final static int CONSTANT_Integer = 3; + public final static int CONSTANT_Float = 4; + public final static int CONSTANT_Long = 5; + public final static int CONSTANT_Double = 6; + public final static int CONSTANT_Class = 7; + public final static int CONSTANT_String = 8; + public final static int CONSTANT_Fieldref = 9; + public final static int CONSTANT_Methodref = 10; + public final static int CONSTANT_InterfaceMethodref = 11; + public final static int CONSTANT_NameAndType = 12; + public final static int CONSTANT_MethodHandle = 15; + public final static int CONSTANT_MethodType = 16; + public final static int CONSTANT_InvokeDynamic = 18; - // ---------------------------------------------------------------------- - // DEPENDENCY CONSTANTS - // ---------------------------------------------------------------------- - - public final static int DEP_CONSTANT = 0; - public final static int DEP_UNKNOWN = 1; - public final static int DEP_GENERAL = 2; - public final static int DEP_PARAMS = 4; - public final static int DEP_STATIC = 8; + // ---------------------------------------------------------------------- + // MethodHandle reference_kind values + // ---------------------------------------------------------------------- - // ---------------------------------------------------------------------- - // INSTRUCTION GROUPS - // ---------------------------------------------------------------------- - - public final static int GROUP_GENERAL = 1; - public final static int GROUP_JUMP = 2; - public final static int GROUP_SWITCH = 3; - public final static int GROUP_INVOCATION = 4; - public final static int GROUP_FIELDACCESS = 5; - public final static int GROUP_RETURN = 6; - - // ---------------------------------------------------------------------- - // POOL CONSTANTS - // ---------------------------------------------------------------------- - - public final static int CONSTANT_Utf8 = 1; - public final static int CONSTANT_Integer = 3; - public final static int CONSTANT_Float = 4; - public final static int CONSTANT_Long = 5; - public final static int CONSTANT_Double = 6; - public final static int CONSTANT_Class = 7; - public final static int CONSTANT_String = 8; - public final static int CONSTANT_Fieldref = 9; - public final static int CONSTANT_Methodref = 10; - public final static int CONSTANT_InterfaceMethodref = 11; - public final static int CONSTANT_NameAndType = 12; - public final static int CONSTANT_MethodHandle = 15; - public final static int CONSTANT_MethodType = 16; - public final static int CONSTANT_InvokeDynamic = 18; - - // ---------------------------------------------------------------------- - // MethodHandle reference_kind values - // ---------------------------------------------------------------------- + public final static int CONSTANT_MethodHandle_REF_getField = 1; + public final static int CONSTANT_MethodHandle_REF_getStatic = 2; + public final static int CONSTANT_MethodHandle_REF_putField = 3; + public final static int CONSTANT_MethodHandle_REF_putStatic = 4; + public final static int CONSTANT_MethodHandle_REF_invokeVirtual = 5; + public final static int CONSTANT_MethodHandle_REF_invokeStatic = 6; + public final static int CONSTANT_MethodHandle_REF_invokeSpecial = 7; + public final static int CONSTANT_MethodHandle_REF_newInvokeSpecial = 8; + public final static int CONSTANT_MethodHandle_REF_invokeInterface = 9; - public final static int CONSTANT_MethodHandle_REF_getField = 1; - public final static int CONSTANT_MethodHandle_REF_getStatic = 2; - public final static int CONSTANT_MethodHandle_REF_putField = 3; - public final static int CONSTANT_MethodHandle_REF_putStatic = 4; - public final static int CONSTANT_MethodHandle_REF_invokeVirtual = 5; - public final static int CONSTANT_MethodHandle_REF_invokeStatic = 6; - public final static int CONSTANT_MethodHandle_REF_invokeSpecial = 7; - public final static int CONSTANT_MethodHandle_REF_newInvokeSpecial = 8; - public final static int CONSTANT_MethodHandle_REF_invokeInterface = 9; - - // ---------------------------------------------------------------------- - // VM OPCODES - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------------- + // VM OPCODES + // ---------------------------------------------------------------------- - public final static int opc_nop = 0; - public final static int opc_aconst_null = 1; - public final static int opc_iconst_m1 = 2; - public final static int opc_iconst_0 = 3; - public final static int opc_iconst_1 = 4; - public final static int opc_iconst_2 = 5; - public final static int opc_iconst_3 = 6; - public final static int opc_iconst_4 = 7; - public final static int opc_iconst_5 = 8; - public final static int opc_lconst_0 = 9; - public final static int opc_lconst_1 = 10; - public final static int opc_fconst_0 = 11; - public final static int opc_fconst_1 = 12; - public final static int opc_fconst_2 = 13; - public final static int opc_dconst_0 = 14; - public final static int opc_dconst_1 = 15; - public final static int opc_bipush = 16; - public final static int opc_sipush = 17; - public final static int opc_ldc = 18; - public final static int opc_ldc_w = 19; - public final static int opc_ldc2_w = 20; - public final static int opc_iload = 21; - public final static int opc_lload = 22; - public final static int opc_fload = 23; - public final static int opc_dload = 24; - public final static int opc_aload = 25; - public final static int opc_iload_0 = 26; - public final static int opc_iload_1 = 27; - public final static int opc_iload_2 = 28; - public final static int opc_iload_3 = 29; - public final static int opc_lload_0 = 30; - public final static int opc_lload_1 = 31; - public final static int opc_lload_2 = 32; - public final static int opc_lload_3 = 33; - public final static int opc_fload_0 = 34; - public final static int opc_fload_1 = 35; - public final static int opc_fload_2 = 36; - public final static int opc_fload_3 = 37; - public final static int opc_dload_0 = 38; - public final static int opc_dload_1 = 39; - public final static int opc_dload_2 = 40; - public final static int opc_dload_3 = 41; - public final static int opc_aload_0 = 42; - public final static int opc_aload_1 = 43; - public final static int opc_aload_2 = 44; - public final static int opc_aload_3 = 45; - public final static int opc_iaload = 46; - public final static int opc_laload = 47; - public final static int opc_faload = 48; - public final static int opc_daload = 49; - public final static int opc_aaload = 50; - public final static int opc_baload = 51; - public final static int opc_caload = 52; - public final static int opc_saload = 53; - public final static int opc_istore = 54; - public final static int opc_lstore = 55; - public final static int opc_fstore = 56; - public final static int opc_dstore = 57; - public final static int opc_astore = 58; - public final static int opc_istore_0 = 59; - public final static int opc_istore_1 = 60; - public final static int opc_istore_2 = 61; - public final static int opc_istore_3 = 62; - public final static int opc_lstore_0 = 63; - public final static int opc_lstore_1 = 64; - public final static int opc_lstore_2 = 65; - public final static int opc_lstore_3 = 66; - public final static int opc_fstore_0 = 67; - public final static int opc_fstore_1 = 68; - public final static int opc_fstore_2 = 69; - public final static int opc_fstore_3 = 70; - public final static int opc_dstore_0 = 71; - public final static int opc_dstore_1 = 72; - public final static int opc_dstore_2 = 73; - public final static int opc_dstore_3 = 74; - public final static int opc_astore_0 = 75; - public final static int opc_astore_1 = 76; - public final static int opc_astore_2 = 77; - public final static int opc_astore_3 = 78; - public final static int opc_iastore = 79; - public final static int opc_lastore = 80; - public final static int opc_fastore = 81; - public final static int opc_dastore = 82; - public final static int opc_aastore = 83; - public final static int opc_bastore = 84; - public final static int opc_castore = 85; - public final static int opc_sastore = 86; - public final static int opc_pop = 87; - public final static int opc_pop2 = 88; - public final static int opc_dup = 89; - public final static int opc_dup_x1 = 90; - public final static int opc_dup_x2 = 91; - public final static int opc_dup2 = 92; - public final static int opc_dup2_x1 = 93; - public final static int opc_dup2_x2 = 94; - public final static int opc_swap = 95; - public final static int opc_iadd = 96; - public final static int opc_ladd = 97; - public final static int opc_fadd = 98; - public final static int opc_dadd = 99; - public final static int opc_isub = 100; - public final static int opc_lsub = 101; - public final static int opc_fsub = 102; - public final static int opc_dsub = 103; - public final static int opc_imul = 104; - public final static int opc_lmul = 105; - public final static int opc_fmul = 106; - public final static int opc_dmul = 107; - public final static int opc_idiv = 108; - public final static int opc_ldiv = 109; - public final static int opc_fdiv = 110; - public final static int opc_ddiv = 111; - public final static int opc_irem = 112; - public final static int opc_lrem = 113; - public final static int opc_frem = 114; - public final static int opc_drem = 115; - public final static int opc_ineg = 116; - public final static int opc_lneg = 117; - public final static int opc_fneg = 118; - public final static int opc_dneg = 119; - public final static int opc_ishl = 120; - public final static int opc_lshl = 121; - public final static int opc_ishr = 122; - public final static int opc_lshr = 123; - public final static int opc_iushr = 124; - public final static int opc_lushr = 125; - public final static int opc_iand = 126; - public final static int opc_land = 127; - public final static int opc_ior = 128; - public final static int opc_lor = 129; - public final static int opc_ixor = 130; - public final static int opc_lxor = 131; - public final static int opc_iinc = 132; - public final static int opc_i2l = 133; - public final static int opc_i2f = 134; - public final static int opc_i2d = 135; - public final static int opc_l2i = 136; - public final static int opc_l2f = 137; - public final static int opc_l2d = 138; - public final static int opc_f2i = 139; - public final static int opc_f2l = 140; - public final static int opc_f2d = 141; - public final static int opc_d2i = 142; - public final static int opc_d2l = 143; - public final static int opc_d2f = 144; - public final static int opc_i2b = 145; - public final static int opc_i2c = 146; - public final static int opc_i2s = 147; - public final static int opc_lcmp = 148; - public final static int opc_fcmpl = 149; - public final static int opc_fcmpg = 150; - public final static int opc_dcmpl = 151; - public final static int opc_dcmpg = 152; - public final static int opc_ifeq = 153; - public final static int opc_ifne = 154; - public final static int opc_iflt = 155; - public final static int opc_ifge = 156; - public final static int opc_ifgt = 157; - public final static int opc_ifle = 158; - public final static int opc_if_icmpeq = 159; - public final static int opc_if_icmpne = 160; - public final static int opc_if_icmplt = 161; - public final static int opc_if_icmpge = 162; - public final static int opc_if_icmpgt = 163; - public final static int opc_if_icmple = 164; - public final static int opc_if_acmpeq = 165; - public final static int opc_if_acmpne = 166; - public final static int opc_goto = 167; - public final static int opc_jsr = 168; - public final static int opc_ret = 169; - public final static int opc_tableswitch = 170; - public final static int opc_lookupswitch = 171; - public final static int opc_ireturn = 172; - public final static int opc_lreturn = 173; - public final static int opc_freturn = 174; - public final static int opc_dreturn = 175; - public final static int opc_areturn = 176; - public final static int opc_return = 177; - public final static int opc_getstatic = 178; - public final static int opc_putstatic = 179; - public final static int opc_getfield = 180; - public final static int opc_putfield = 181; - public final static int opc_invokevirtual = 182; - public final static int opc_invokespecial = 183; - public final static int opc_invokestatic = 184; - public final static int opc_invokeinterface = 185; - public final static int opc_invokedynamic = 186; - public final static int opc_xxxunusedxxx = 186; - public final static int opc_new = 187; - public final static int opc_newarray = 188; - public final static int opc_anewarray = 189; - public final static int opc_arraylength = 190; - public final static int opc_athrow = 191; - public final static int opc_checkcast = 192; - public final static int opc_instanceof = 193; - public final static int opc_monitorenter = 194; - public final static int opc_monitorexit = 195; - public final static int opc_wide = 196; - public final static int opc_multianewarray = 197; - public final static int opc_ifnull = 198; - public final static int opc_ifnonnull = 199; - public final static int opc_goto_w = 200; - public final static int opc_jsr_w = 201; - - + public final static int opc_nop = 0; + public final static int opc_aconst_null = 1; + public final static int opc_iconst_m1 = 2; + public final static int opc_iconst_0 = 3; + public final static int opc_iconst_1 = 4; + public final static int opc_iconst_2 = 5; + public final static int opc_iconst_3 = 6; + public final static int opc_iconst_4 = 7; + public final static int opc_iconst_5 = 8; + public final static int opc_lconst_0 = 9; + public final static int opc_lconst_1 = 10; + public final static int opc_fconst_0 = 11; + public final static int opc_fconst_1 = 12; + public final static int opc_fconst_2 = 13; + public final static int opc_dconst_0 = 14; + public final static int opc_dconst_1 = 15; + public final static int opc_bipush = 16; + public final static int opc_sipush = 17; + public final static int opc_ldc = 18; + public final static int opc_ldc_w = 19; + public final static int opc_ldc2_w = 20; + public final static int opc_iload = 21; + public final static int opc_lload = 22; + public final static int opc_fload = 23; + public final static int opc_dload = 24; + public final static int opc_aload = 25; + public final static int opc_iload_0 = 26; + public final static int opc_iload_1 = 27; + public final static int opc_iload_2 = 28; + public final static int opc_iload_3 = 29; + public final static int opc_lload_0 = 30; + public final static int opc_lload_1 = 31; + public final static int opc_lload_2 = 32; + public final static int opc_lload_3 = 33; + public final static int opc_fload_0 = 34; + public final static int opc_fload_1 = 35; + public final static int opc_fload_2 = 36; + public final static int opc_fload_3 = 37; + public final static int opc_dload_0 = 38; + public final static int opc_dload_1 = 39; + public final static int opc_dload_2 = 40; + public final static int opc_dload_3 = 41; + public final static int opc_aload_0 = 42; + public final static int opc_aload_1 = 43; + public final static int opc_aload_2 = 44; + public final static int opc_aload_3 = 45; + public final static int opc_iaload = 46; + public final static int opc_laload = 47; + public final static int opc_faload = 48; + public final static int opc_daload = 49; + public final static int opc_aaload = 50; + public final static int opc_baload = 51; + public final static int opc_caload = 52; + public final static int opc_saload = 53; + public final static int opc_istore = 54; + public final static int opc_lstore = 55; + public final static int opc_fstore = 56; + public final static int opc_dstore = 57; + public final static int opc_astore = 58; + public final static int opc_istore_0 = 59; + public final static int opc_istore_1 = 60; + public final static int opc_istore_2 = 61; + public final static int opc_istore_3 = 62; + public final static int opc_lstore_0 = 63; + public final static int opc_lstore_1 = 64; + public final static int opc_lstore_2 = 65; + public final static int opc_lstore_3 = 66; + public final static int opc_fstore_0 = 67; + public final static int opc_fstore_1 = 68; + public final static int opc_fstore_2 = 69; + public final static int opc_fstore_3 = 70; + public final static int opc_dstore_0 = 71; + public final static int opc_dstore_1 = 72; + public final static int opc_dstore_2 = 73; + public final static int opc_dstore_3 = 74; + public final static int opc_astore_0 = 75; + public final static int opc_astore_1 = 76; + public final static int opc_astore_2 = 77; + public final static int opc_astore_3 = 78; + public final static int opc_iastore = 79; + public final static int opc_lastore = 80; + public final static int opc_fastore = 81; + public final static int opc_dastore = 82; + public final static int opc_aastore = 83; + public final static int opc_bastore = 84; + public final static int opc_castore = 85; + public final static int opc_sastore = 86; + public final static int opc_pop = 87; + public final static int opc_pop2 = 88; + public final static int opc_dup = 89; + public final static int opc_dup_x1 = 90; + public final static int opc_dup_x2 = 91; + public final static int opc_dup2 = 92; + public final static int opc_dup2_x1 = 93; + public final static int opc_dup2_x2 = 94; + public final static int opc_swap = 95; + public final static int opc_iadd = 96; + public final static int opc_ladd = 97; + public final static int opc_fadd = 98; + public final static int opc_dadd = 99; + public final static int opc_isub = 100; + public final static int opc_lsub = 101; + public final static int opc_fsub = 102; + public final static int opc_dsub = 103; + public final static int opc_imul = 104; + public final static int opc_lmul = 105; + public final static int opc_fmul = 106; + public final static int opc_dmul = 107; + public final static int opc_idiv = 108; + public final static int opc_ldiv = 109; + public final static int opc_fdiv = 110; + public final static int opc_ddiv = 111; + public final static int opc_irem = 112; + public final static int opc_lrem = 113; + public final static int opc_frem = 114; + public final static int opc_drem = 115; + public final static int opc_ineg = 116; + public final static int opc_lneg = 117; + public final static int opc_fneg = 118; + public final static int opc_dneg = 119; + public final static int opc_ishl = 120; + public final static int opc_lshl = 121; + public final static int opc_ishr = 122; + public final static int opc_lshr = 123; + public final static int opc_iushr = 124; + public final static int opc_lushr = 125; + public final static int opc_iand = 126; + public final static int opc_land = 127; + public final static int opc_ior = 128; + public final static int opc_lor = 129; + public final static int opc_ixor = 130; + public final static int opc_lxor = 131; + public final static int opc_iinc = 132; + public final static int opc_i2l = 133; + public final static int opc_i2f = 134; + public final static int opc_i2d = 135; + public final static int opc_l2i = 136; + public final static int opc_l2f = 137; + public final static int opc_l2d = 138; + public final static int opc_f2i = 139; + public final static int opc_f2l = 140; + public final static int opc_f2d = 141; + public final static int opc_d2i = 142; + public final static int opc_d2l = 143; + public final static int opc_d2f = 144; + public final static int opc_i2b = 145; + public final static int opc_i2c = 146; + public final static int opc_i2s = 147; + public final static int opc_lcmp = 148; + public final static int opc_fcmpl = 149; + public final static int opc_fcmpg = 150; + public final static int opc_dcmpl = 151; + public final static int opc_dcmpg = 152; + public final static int opc_ifeq = 153; + public final static int opc_ifne = 154; + public final static int opc_iflt = 155; + public final static int opc_ifge = 156; + public final static int opc_ifgt = 157; + public final static int opc_ifle = 158; + public final static int opc_if_icmpeq = 159; + public final static int opc_if_icmpne = 160; + public final static int opc_if_icmplt = 161; + public final static int opc_if_icmpge = 162; + public final static int opc_if_icmpgt = 163; + public final static int opc_if_icmple = 164; + public final static int opc_if_acmpeq = 165; + public final static int opc_if_acmpne = 166; + public final static int opc_goto = 167; + public final static int opc_jsr = 168; + public final static int opc_ret = 169; + public final static int opc_tableswitch = 170; + public final static int opc_lookupswitch = 171; + public final static int opc_ireturn = 172; + public final static int opc_lreturn = 173; + public final static int opc_freturn = 174; + public final static int opc_dreturn = 175; + public final static int opc_areturn = 176; + public final static int opc_return = 177; + public final static int opc_getstatic = 178; + public final static int opc_putstatic = 179; + public final static int opc_getfield = 180; + public final static int opc_putfield = 181; + public final static int opc_invokevirtual = 182; + public final static int opc_invokespecial = 183; + public final static int opc_invokestatic = 184; + public final static int opc_invokeinterface = 185; + public final static int opc_invokedynamic = 186; + public final static int opc_xxxunusedxxx = 186; + public final static int opc_new = 187; + public final static int opc_newarray = 188; + public final static int opc_anewarray = 189; + public final static int opc_arraylength = 190; + public final static int opc_athrow = 191; + public final static int opc_checkcast = 192; + public final static int opc_instanceof = 193; + public final static int opc_monitorenter = 194; + public final static int opc_monitorexit = 195; + public final static int opc_wide = 196; + public final static int opc_multianewarray = 197; + public final static int opc_ifnull = 198; + public final static int opc_ifnonnull = 199; + public final static int opc_goto_w = 200; + public final static int opc_jsr_w = 201; } diff --git a/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java b/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java index dcaea77..79eecdf 100644 --- a/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java +++ b/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java @@ -1,519 +1,482 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; -import org.jetbrains.java.decompiler.code.optinstructions.ALOAD; -import org.jetbrains.java.decompiler.code.optinstructions.ANEWARRAY; -import org.jetbrains.java.decompiler.code.optinstructions.ASTORE; -import org.jetbrains.java.decompiler.code.optinstructions.BIPUSH; -import org.jetbrains.java.decompiler.code.optinstructions.CHECKCAST; -import org.jetbrains.java.decompiler.code.optinstructions.DLOAD; -import org.jetbrains.java.decompiler.code.optinstructions.DSTORE; -import org.jetbrains.java.decompiler.code.optinstructions.FLOAD; -import org.jetbrains.java.decompiler.code.optinstructions.FSTORE; -import org.jetbrains.java.decompiler.code.optinstructions.GETFIELD; -import org.jetbrains.java.decompiler.code.optinstructions.GETSTATIC; -import org.jetbrains.java.decompiler.code.optinstructions.GOTO; -import org.jetbrains.java.decompiler.code.optinstructions.GOTO_W; -import org.jetbrains.java.decompiler.code.optinstructions.IINC; -import org.jetbrains.java.decompiler.code.optinstructions.ILOAD; -import org.jetbrains.java.decompiler.code.optinstructions.INSTANCEOF; -import org.jetbrains.java.decompiler.code.optinstructions.INVOKEDYNAMIC; -import org.jetbrains.java.decompiler.code.optinstructions.INVOKEINTERFACE; -import org.jetbrains.java.decompiler.code.optinstructions.INVOKESPECIAL; -import org.jetbrains.java.decompiler.code.optinstructions.INVOKESTATIC; -import org.jetbrains.java.decompiler.code.optinstructions.INVOKEVIRTUAL; -import org.jetbrains.java.decompiler.code.optinstructions.ISTORE; -import org.jetbrains.java.decompiler.code.optinstructions.JSR; -import org.jetbrains.java.decompiler.code.optinstructions.JSR_W; -import org.jetbrains.java.decompiler.code.optinstructions.LDC; -import org.jetbrains.java.decompiler.code.optinstructions.LDC2_W; -import org.jetbrains.java.decompiler.code.optinstructions.LDC_W; -import org.jetbrains.java.decompiler.code.optinstructions.LLOAD; -import org.jetbrains.java.decompiler.code.optinstructions.LOOKUPSWITCH; -import org.jetbrains.java.decompiler.code.optinstructions.LSTORE; -import org.jetbrains.java.decompiler.code.optinstructions.MULTIANEWARRAY; -import org.jetbrains.java.decompiler.code.optinstructions.NEW; -import org.jetbrains.java.decompiler.code.optinstructions.NEWARRAY; -import org.jetbrains.java.decompiler.code.optinstructions.PUTFIELD; -import org.jetbrains.java.decompiler.code.optinstructions.PUTSTATIC; -import org.jetbrains.java.decompiler.code.optinstructions.RET; -import org.jetbrains.java.decompiler.code.optinstructions.SIPUSH; -import org.jetbrains.java.decompiler.code.optinstructions.TABLESWITCH; +import org.jetbrains.java.decompiler.code.optinstructions.*; public class ConstantsUtil { - - public static String getName(int opcode) { - return opcodeNames[opcode]; - } - public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int bytecode_version, int[] operands) { + public static String getName(int opcode) { + return opcodeNames[opcode]; + } + + public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int bytecode_version, int[] operands) { + + Instruction instr = getInstructionInstance(opcode, bytecode_version); + instr.wide = wide; + instr.group = group; + instr.bytecode_version = bytecode_version; + instr.setOperands(operands); + + return instr; + } + + private static Instruction getInstructionInstance(int opcode, int bytecode_version) { + try { + Instruction instr; + + if ((opcode >= CodeConstants.opc_ifeq && + opcode <= CodeConstants.opc_if_acmpne) || + opcode == CodeConstants.opc_ifnull || + opcode == CodeConstants.opc_ifnonnull) { + instr = new IfInstruction(); + } + else { + + Class cl = opcodeClasses[opcode]; + + if (opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) { + cl = null; // instruction unused in Java 6 and before + } - Instruction instr = getInstructionInstance(opcode, bytecode_version); - instr.wide = wide; - instr.group = group; - instr.bytecode_version = bytecode_version; - instr.setOperands(operands); + if (cl == null) { + instr = new Instruction(); + } + else { + instr = (Instruction)cl.newInstance(); + } + } - return instr; - } - - private static Instruction getInstructionInstance(int opcode, int bytecode_version) { - try { - Instruction instr; - - if((opcode >= CodeConstants.opc_ifeq && - opcode <= CodeConstants.opc_if_acmpne) || - opcode == CodeConstants.opc_ifnull || - opcode == CodeConstants.opc_ifnonnull) { - instr = new IfInstruction(); - } else { + instr.opcode = opcode; + return instr; + } + catch (Exception ex) { + return null; + } + } - Class cl = opcodeClasses[opcode]; - - if(opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) { - cl = null; // instruction unused in Java 6 and before - } - - if(cl == null) { - instr = new Instruction(); - } else { - instr = (Instruction)cl.newInstance(); - } - } - - instr.opcode = opcode; - return instr; - } catch (Exception ex) { - return null; - } - - } - - private static String[] opcodeNames = { - "nop", // "nop", - "aconst_null", // "aconst_null", - "iconst_m1", // "iconst_m1", - "iconst_0", // "iconst_0", - "iconst_1", // "iconst_1", - "iconst_2", // "iconst_2", - "iconst_3", // "iconst_3", - "iconst_4", // "iconst_4", - "iconst_5", // "iconst_5", - "lconst_0", // "lconst_0", - "lconst_1", // "lconst_1", - "fconst_0", // "fconst_0", - "fconst_1", // "fconst_1", - "fconst_2", // "fconst_2", - "dconst_0", // "dconst_0", - "dconst_1", // "dconst_1", - "bipush", // "bipush", - "sipush", // "sipush", - "ldc", // "ldc", - "ldc_w", // "ldc_w", - "ldc2_w", // "ldc2_w", - "iload", // "iload", - "lload", // "lload", - "fload", // "fload", - "dload", // "dload", - "aload", // "aload", - "iload_0", // "iload_0", - "iload_1", // "iload_1", - "iload_2", // "iload_2", - "iload_3", // "iload_3", - "lload_0", // "lload_0", - "lload_1", // "lload_1", - "lload_2", // "lload_2", - "lload_3", // "lload_3", - "fload_0", // "fload_0", - "fload_1", // "fload_1", - "fload_2", // "fload_2", - "fload_3", // "fload_3", - "dload_0", // "dload_0", - "dload_1", // "dload_1", - "dload_2", // "dload_2", - "dload_3", // "dload_3", - "aload_0", // "aload_0", - "aload_1", // "aload_1", - "aload_2", // "aload_2", - "aload_3", // "aload_3", - "iaload", // "iaload", - "laload", // "laload", - "faload", // "faload", - "daload", // "daload", - "aaload", // "aaload", - "baload", // "baload", - "caload", // "caload", - "saload", // "saload", - "istore", // "istore", - "lstore", // "lstore", - "fstore", // "fstore", - "dstore", // "dstore", - "astore", // "astore", - "istore_0", // "istore_0", - "istore_1", // "istore_1", - "istore_2", // "istore_2", - "istore_3", // "istore_3", - "lstore_0", // "lstore_0", - "lstore_1", // "lstore_1", - "lstore_2", // "lstore_2", - "lstore_3", // "lstore_3", - "fstore_0", // "fstore_0", - "fstore_1", // "fstore_1", - "fstore_2", // "fstore_2", - "fstore_3", // "fstore_3", - "dstore_0", // "dstore_0", - "dstore_1", // "dstore_1", - "dstore_2", // "dstore_2", - "dstore_3", // "dstore_3", - "astore_0", // "astore_0", - "astore_1", // "astore_1", - "astore_2", // "astore_2", - "astore_3", // "astore_3", - "iastore", // "iastore", - "lastore", // "lastore", - "fastore", // "fastore", - "dastore", // "dastore", - "aastore", // "aastore", - "bastore", // "bastore", - "castore", // "castore", - "sastore", // "sastore", - "pop", // "pop", - "pop2", // "pop2", - "dup", // "dup", - "dup_x1", // "dup_x1", - "dup_x2", // "dup_x2", - "dup2", // "dup2", - "dup2_x1", // "dup2_x1", - "dup2_x2", // "dup2_x2", - "swap", // "swap", - "iadd", // "iadd", - "ladd", // "ladd", - "fadd", // "fadd", - "dadd", // "dadd", - "isub", // "isub", - "lsub", // "lsub", - "fsub", // "fsub", - "dsub", // "dsub", - "imul", // "imul", - "lmul", // "lmul", - "fmul", // "fmul", - "dmul", // "dmul", - "idiv", // "idiv", - "ldiv", // "ldiv", - "fdiv", // "fdiv", - "ddiv", // "ddiv", - "irem", // "irem", - "lrem", // "lrem", - "frem", // "frem", - "drem", // "drem", - "ineg", // "ineg", - "lneg", // "lneg", - "fneg", // "fneg", - "dneg", // "dneg", - "ishl", // "ishl", - "lshl", // "lshl", - "ishr", // "ishr", - "lshr", // "lshr", - "iushr", // "iushr", - "lushr", // "lushr", - "iand", // "iand", - "land", // "land", - "ior", // "ior", - "lor", // "lor", - "ixor", // "ixor", - "lxor", // "lxor", - "iinc", // "iinc", - "i2l", // "i2l", - "i2f", // "i2f", - "i2d", // "i2d", - "l2i", // "l2i", - "l2f", // "l2f", - "l2d", // "l2d", - "f2i", // "f2i", - "f2l", // "f2l", - "f2d", // "f2d", - "d2i", // "d2i", - "d2l", // "d2l", - "d2f", // "d2f", - "i2b", // "i2b", - "i2c", // "i2c", - "i2s", // "i2s", - "lcmp", // "lcmp", - "fcmpl", // "fcmpl", - "fcmpg", // "fcmpg", - "dcmpl", // "dcmpl", - "dcmpg", // "dcmpg", - "ifeq", // "ifeq", - "ifne", // "ifne", - "iflt", // "iflt", - "ifge", // "ifge", - "ifgt", // "ifgt", - "ifle", // "ifle", - "if_icmpeq", // "if_icmpeq", - "if_icmpne", // "if_icmpne", - "if_icmplt", // "if_icmplt", - "if_icmpge", // "if_icmpge", - "if_icmpgt", // "if_icmpgt", - "if_icmple", // "if_icmple", - "if_acmpeq", // "if_acmpeq", - "if_acmpne", // "if_acmpne", - "goto", // "goto", - "jsr", // "jsr", - "ret", // "ret", - "tableswitch", // "tableswitch", - "lookupswitch", // "lookupswitch", - "ireturn", // "ireturn", - "lreturn", // "lreturn", - "freturn", // "freturn", - "dreturn", // "dreturn", - "areturn", // "areturn", - "return", // "return", - "getstatic", // "getstatic", - "putstatic", // "putstatic", - "getfield", // "getfield", - "putfield", // "putfield", - "invokevirtual", // "invokevirtual", - "invokespecial", // "invokespecial", - "invokestatic", // "invokestatic", - "invokeinterface", // "invokeinterface", - //"xxxunusedxxx", // "xxxunusedxxx", Java 6 and before - "invokedynamic", // "invokedynamic", Java 7 and later - "new", // "new", - "newarray", // "newarray", - "anewarray", // "anewarray", - "arraylength", // "arraylength", - "athrow", // "athrow", - "checkcast", // "checkcast", - "instanceof", // "instanceof", - "monitorenter", // "monitorenter", - "monitorexit", // "monitorexit", - "wide", // "wide", - "multianewarray", // "multianewarray", - "ifnull", // "ifnull", - "ifnonnull", // "ifnonnull", - "goto_w", // "goto_w", - "jsr_w" // "jsr_w" - }; + private static String[] opcodeNames = { + "nop", // "nop", + "aconst_null", // "aconst_null", + "iconst_m1", // "iconst_m1", + "iconst_0", // "iconst_0", + "iconst_1", // "iconst_1", + "iconst_2", // "iconst_2", + "iconst_3", // "iconst_3", + "iconst_4", // "iconst_4", + "iconst_5", // "iconst_5", + "lconst_0", // "lconst_0", + "lconst_1", // "lconst_1", + "fconst_0", // "fconst_0", + "fconst_1", // "fconst_1", + "fconst_2", // "fconst_2", + "dconst_0", // "dconst_0", + "dconst_1", // "dconst_1", + "bipush", // "bipush", + "sipush", // "sipush", + "ldc", // "ldc", + "ldc_w", // "ldc_w", + "ldc2_w", // "ldc2_w", + "iload", // "iload", + "lload", // "lload", + "fload", // "fload", + "dload", // "dload", + "aload", // "aload", + "iload_0", // "iload_0", + "iload_1", // "iload_1", + "iload_2", // "iload_2", + "iload_3", // "iload_3", + "lload_0", // "lload_0", + "lload_1", // "lload_1", + "lload_2", // "lload_2", + "lload_3", // "lload_3", + "fload_0", // "fload_0", + "fload_1", // "fload_1", + "fload_2", // "fload_2", + "fload_3", // "fload_3", + "dload_0", // "dload_0", + "dload_1", // "dload_1", + "dload_2", // "dload_2", + "dload_3", // "dload_3", + "aload_0", // "aload_0", + "aload_1", // "aload_1", + "aload_2", // "aload_2", + "aload_3", // "aload_3", + "iaload", // "iaload", + "laload", // "laload", + "faload", // "faload", + "daload", // "daload", + "aaload", // "aaload", + "baload", // "baload", + "caload", // "caload", + "saload", // "saload", + "istore", // "istore", + "lstore", // "lstore", + "fstore", // "fstore", + "dstore", // "dstore", + "astore", // "astore", + "istore_0", // "istore_0", + "istore_1", // "istore_1", + "istore_2", // "istore_2", + "istore_3", // "istore_3", + "lstore_0", // "lstore_0", + "lstore_1", // "lstore_1", + "lstore_2", // "lstore_2", + "lstore_3", // "lstore_3", + "fstore_0", // "fstore_0", + "fstore_1", // "fstore_1", + "fstore_2", // "fstore_2", + "fstore_3", // "fstore_3", + "dstore_0", // "dstore_0", + "dstore_1", // "dstore_1", + "dstore_2", // "dstore_2", + "dstore_3", // "dstore_3", + "astore_0", // "astore_0", + "astore_1", // "astore_1", + "astore_2", // "astore_2", + "astore_3", // "astore_3", + "iastore", // "iastore", + "lastore", // "lastore", + "fastore", // "fastore", + "dastore", // "dastore", + "aastore", // "aastore", + "bastore", // "bastore", + "castore", // "castore", + "sastore", // "sastore", + "pop", // "pop", + "pop2", // "pop2", + "dup", // "dup", + "dup_x1", // "dup_x1", + "dup_x2", // "dup_x2", + "dup2", // "dup2", + "dup2_x1", // "dup2_x1", + "dup2_x2", // "dup2_x2", + "swap", // "swap", + "iadd", // "iadd", + "ladd", // "ladd", + "fadd", // "fadd", + "dadd", // "dadd", + "isub", // "isub", + "lsub", // "lsub", + "fsub", // "fsub", + "dsub", // "dsub", + "imul", // "imul", + "lmul", // "lmul", + "fmul", // "fmul", + "dmul", // "dmul", + "idiv", // "idiv", + "ldiv", // "ldiv", + "fdiv", // "fdiv", + "ddiv", // "ddiv", + "irem", // "irem", + "lrem", // "lrem", + "frem", // "frem", + "drem", // "drem", + "ineg", // "ineg", + "lneg", // "lneg", + "fneg", // "fneg", + "dneg", // "dneg", + "ishl", // "ishl", + "lshl", // "lshl", + "ishr", // "ishr", + "lshr", // "lshr", + "iushr", // "iushr", + "lushr", // "lushr", + "iand", // "iand", + "land", // "land", + "ior", // "ior", + "lor", // "lor", + "ixor", // "ixor", + "lxor", // "lxor", + "iinc", // "iinc", + "i2l", // "i2l", + "i2f", // "i2f", + "i2d", // "i2d", + "l2i", // "l2i", + "l2f", // "l2f", + "l2d", // "l2d", + "f2i", // "f2i", + "f2l", // "f2l", + "f2d", // "f2d", + "d2i", // "d2i", + "d2l", // "d2l", + "d2f", // "d2f", + "i2b", // "i2b", + "i2c", // "i2c", + "i2s", // "i2s", + "lcmp", // "lcmp", + "fcmpl", // "fcmpl", + "fcmpg", // "fcmpg", + "dcmpl", // "dcmpl", + "dcmpg", // "dcmpg", + "ifeq", // "ifeq", + "ifne", // "ifne", + "iflt", // "iflt", + "ifge", // "ifge", + "ifgt", // "ifgt", + "ifle", // "ifle", + "if_icmpeq", // "if_icmpeq", + "if_icmpne", // "if_icmpne", + "if_icmplt", // "if_icmplt", + "if_icmpge", // "if_icmpge", + "if_icmpgt", // "if_icmpgt", + "if_icmple", // "if_icmple", + "if_acmpeq", // "if_acmpeq", + "if_acmpne", // "if_acmpne", + "goto", // "goto", + "jsr", // "jsr", + "ret", // "ret", + "tableswitch", // "tableswitch", + "lookupswitch", // "lookupswitch", + "ireturn", // "ireturn", + "lreturn", // "lreturn", + "freturn", // "freturn", + "dreturn", // "dreturn", + "areturn", // "areturn", + "return", // "return", + "getstatic", // "getstatic", + "putstatic", // "putstatic", + "getfield", // "getfield", + "putfield", // "putfield", + "invokevirtual", // "invokevirtual", + "invokespecial", // "invokespecial", + "invokestatic", // "invokestatic", + "invokeinterface", // "invokeinterface", + //"xxxunusedxxx", // "xxxunusedxxx", Java 6 and before + "invokedynamic", // "invokedynamic", Java 7 and later + "new", // "new", + "newarray", // "newarray", + "anewarray", // "anewarray", + "arraylength", // "arraylength", + "athrow", // "athrow", + "checkcast", // "checkcast", + "instanceof", // "instanceof", + "monitorenter", // "monitorenter", + "monitorexit", // "monitorexit", + "wide", // "wide", + "multianewarray", // "multianewarray", + "ifnull", // "ifnull", + "ifnonnull", // "ifnonnull", + "goto_w", // "goto_w", + "jsr_w" // "jsr_w" + }; - private static Class[] opcodeClasses = { - null, // "nop", - null, // "aconst_null", - null, // "iconst_m1", - null, // "iconst_0", - null, // "iconst_1", - null, // "iconst_2", - null, // "iconst_3", - null, // "iconst_4", - null, // "iconst_5", - null, // "lconst_0", - null, // "lconst_1", - null, // "fconst_0", - null, // "fconst_1", - null, // "fconst_2", - null, // "dconst_0", - null, // "dconst_1", - BIPUSH.class, // "bipush", - SIPUSH.class, // "sipush", - LDC.class, // "ldc", - LDC_W.class, // "ldc_w", - LDC2_W.class, // "ldc2_w", - ILOAD.class, // "iload", - LLOAD.class, // "lload", - FLOAD.class, // "fload", - DLOAD.class, // "dload", - ALOAD.class, // "aload", - null, // "iload_0", - null, // "iload_1", - null, // "iload_2", - null, // "iload_3", - null, // "lload_0", - null, // "lload_1", - null, // "lload_2", - null, // "lload_3", - null, // "fload_0", - null, // "fload_1", - null, // "fload_2", - null, // "fload_3", - null, // "dload_0", - null, // "dload_1", - null, // "dload_2", - null, // "dload_3", - null, // "aload_0", - null, // "aload_1", - null, // "aload_2", - null, // "aload_3", - null, // "iaload", - null, // "laload", - null, // "faload", - null, // "daload", - null, // "aaload", - null, // "baload", - null, // "caload", - null, // "saload", - ISTORE.class, // "istore", - LSTORE.class, // "lstore", - FSTORE.class, // "fstore", - DSTORE.class, // "dstore", - ASTORE.class, // "astore", - null, // "istore_0", - null, // "istore_1", - null, // "istore_2", - null, // "istore_3", - null, // "lstore_0", - null, // "lstore_1", - null, // "lstore_2", - null, // "lstore_3", - null, // "fstore_0", - null, // "fstore_1", - null, // "fstore_2", - null, // "fstore_3", - null, // "dstore_0", - null, // "dstore_1", - null, // "dstore_2", - null, // "dstore_3", - null, // "astore_0", - null, // "astore_1", - null, // "astore_2", - null, // "astore_3", - null, // "iastore", - null, // "lastore", - null, // "fastore", - null, // "dastore", - null, // "aastore", - null, // "bastore", - null, // "castore", - null, // "sastore", - null, // "pop", - null, // "pop2", - null, // "dup", - null, // "dup_x1", - null, // "dup_x2", - null, // "dup2", - null, // "dup2_x1", - null, // "dup2_x2", - null, // "swap", - null, // "iadd", - null, // "ladd", - null, // "fadd", - null, // "dadd", - null, // "isub", - null, // "lsub", - null, // "fsub", - null, // "dsub", - null, // "imul", - null, // "lmul", - null, // "fmul", - null, // "dmul", - null, // "idiv", - null, // "ldiv", - null, // "fdiv", - null, // "ddiv", - null, // "irem", - null, // "lrem", - null, // "frem", - null, // "drem", - null, // "ineg", - null, // "lneg", - null, // "fneg", - null, // "dneg", - null, // "ishl", - null, // "lshl", - null, // "ishr", - null, // "lshr", - null, // "iushr", - null, // "lushr", - null, // "iand", - null, // "land", - null, // "ior", - null, // "lor", - null, // "ixor", - null, // "lxor", - IINC.class, // "iinc", - null, // "i2l", - null, // "i2f", - null, // "i2d", - null, // "l2i", - null, // "l2f", - null, // "l2d", - null, // "f2i", - null, // "f2l", - null, // "f2d", - null, // "d2i", - null, // "d2l", - null, // "d2f", - null, // "i2b", - null, // "i2c", - null, // "i2s", - null, // "lcmp", - null, // "fcmpl", - null, // "fcmpg", - null, // "dcmpl", - null, // "dcmpg", - null, // "ifeq", - null, // "ifne", - null, // "iflt", - null, // "ifge", - null, // "ifgt", - null, // "ifle", - null, // "if_icmpeq", - null, // "if_icmpne", - null, // "if_icmplt", - null, // "if_icmpge", - null, // "if_icmpgt", - null, // "if_icmple", - null, // "if_acmpeq", - null, // "if_acmpne", - GOTO.class, // "goto", - JSR.class, // "jsr", - RET.class, // "ret", - TABLESWITCH.class, // "tableswitch", - LOOKUPSWITCH.class, // "lookupswitch", - null, // "ireturn", - null, // "lreturn", - null, // "freturn", - null, // "dreturn", - null, // "areturn", - null, // "return", - GETSTATIC.class, // "getstatic", - PUTSTATIC.class, // "putstatic", - GETFIELD.class, // "getfield", - PUTFIELD.class, // "putfield", - INVOKEVIRTUAL.class, // "invokevirtual", - INVOKESPECIAL.class, // "invokespecial", - INVOKESTATIC.class, // "invokestatic", - INVOKEINTERFACE.class, // "invokeinterface", - INVOKEDYNAMIC.class, // "xxxunusedxxx" Java 6 and before, "invokedynamic" Java 7 and later - NEW.class, // "new", - NEWARRAY.class, // "newarray", - ANEWARRAY.class, // "anewarray", - null, // "arraylength", - null, // "athrow", - CHECKCAST.class, // "checkcast", - INSTANCEOF.class, // "instanceof", - null, // "monitorenter", - null, // "monitorexit", - null, // "wide", - MULTIANEWARRAY.class, // "multianewarray", - null, // "ifnull", - null, // "ifnonnull", - GOTO_W.class, // "goto_w", - JSR_W.class // "jsr_w" - }; - - - + private static Class[] opcodeClasses = { + null, // "nop", + null, // "aconst_null", + null, // "iconst_m1", + null, // "iconst_0", + null, // "iconst_1", + null, // "iconst_2", + null, // "iconst_3", + null, // "iconst_4", + null, // "iconst_5", + null, // "lconst_0", + null, // "lconst_1", + null, // "fconst_0", + null, // "fconst_1", + null, // "fconst_2", + null, // "dconst_0", + null, // "dconst_1", + BIPUSH.class, // "bipush", + SIPUSH.class, // "sipush", + LDC.class, // "ldc", + LDC_W.class, // "ldc_w", + LDC2_W.class, // "ldc2_w", + ILOAD.class, // "iload", + LLOAD.class, // "lload", + FLOAD.class, // "fload", + DLOAD.class, // "dload", + ALOAD.class, // "aload", + null, // "iload_0", + null, // "iload_1", + null, // "iload_2", + null, // "iload_3", + null, // "lload_0", + null, // "lload_1", + null, // "lload_2", + null, // "lload_3", + null, // "fload_0", + null, // "fload_1", + null, // "fload_2", + null, // "fload_3", + null, // "dload_0", + null, // "dload_1", + null, // "dload_2", + null, // "dload_3", + null, // "aload_0", + null, // "aload_1", + null, // "aload_2", + null, // "aload_3", + null, // "iaload", + null, // "laload", + null, // "faload", + null, // "daload", + null, // "aaload", + null, // "baload", + null, // "caload", + null, // "saload", + ISTORE.class, // "istore", + LSTORE.class, // "lstore", + FSTORE.class, // "fstore", + DSTORE.class, // "dstore", + ASTORE.class, // "astore", + null, // "istore_0", + null, // "istore_1", + null, // "istore_2", + null, // "istore_3", + null, // "lstore_0", + null, // "lstore_1", + null, // "lstore_2", + null, // "lstore_3", + null, // "fstore_0", + null, // "fstore_1", + null, // "fstore_2", + null, // "fstore_3", + null, // "dstore_0", + null, // "dstore_1", + null, // "dstore_2", + null, // "dstore_3", + null, // "astore_0", + null, // "astore_1", + null, // "astore_2", + null, // "astore_3", + null, // "iastore", + null, // "lastore", + null, // "fastore", + null, // "dastore", + null, // "aastore", + null, // "bastore", + null, // "castore", + null, // "sastore", + null, // "pop", + null, // "pop2", + null, // "dup", + null, // "dup_x1", + null, // "dup_x2", + null, // "dup2", + null, // "dup2_x1", + null, // "dup2_x2", + null, // "swap", + null, // "iadd", + null, // "ladd", + null, // "fadd", + null, // "dadd", + null, // "isub", + null, // "lsub", + null, // "fsub", + null, // "dsub", + null, // "imul", + null, // "lmul", + null, // "fmul", + null, // "dmul", + null, // "idiv", + null, // "ldiv", + null, // "fdiv", + null, // "ddiv", + null, // "irem", + null, // "lrem", + null, // "frem", + null, // "drem", + null, // "ineg", + null, // "lneg", + null, // "fneg", + null, // "dneg", + null, // "ishl", + null, // "lshl", + null, // "ishr", + null, // "lshr", + null, // "iushr", + null, // "lushr", + null, // "iand", + null, // "land", + null, // "ior", + null, // "lor", + null, // "ixor", + null, // "lxor", + IINC.class, // "iinc", + null, // "i2l", + null, // "i2f", + null, // "i2d", + null, // "l2i", + null, // "l2f", + null, // "l2d", + null, // "f2i", + null, // "f2l", + null, // "f2d", + null, // "d2i", + null, // "d2l", + null, // "d2f", + null, // "i2b", + null, // "i2c", + null, // "i2s", + null, // "lcmp", + null, // "fcmpl", + null, // "fcmpg", + null, // "dcmpl", + null, // "dcmpg", + null, // "ifeq", + null, // "ifne", + null, // "iflt", + null, // "ifge", + null, // "ifgt", + null, // "ifle", + null, // "if_icmpeq", + null, // "if_icmpne", + null, // "if_icmplt", + null, // "if_icmpge", + null, // "if_icmpgt", + null, // "if_icmple", + null, // "if_acmpeq", + null, // "if_acmpne", + GOTO.class, // "goto", + JSR.class, // "jsr", + RET.class, // "ret", + TABLESWITCH.class, // "tableswitch", + LOOKUPSWITCH.class, // "lookupswitch", + null, // "ireturn", + null, // "lreturn", + null, // "freturn", + null, // "dreturn", + null, // "areturn", + null, // "return", + GETSTATIC.class, // "getstatic", + PUTSTATIC.class, // "putstatic", + GETFIELD.class, // "getfield", + PUTFIELD.class, // "putfield", + INVOKEVIRTUAL.class, // "invokevirtual", + INVOKESPECIAL.class, // "invokespecial", + INVOKESTATIC.class, // "invokestatic", + INVOKEINTERFACE.class, // "invokeinterface", + INVOKEDYNAMIC.class, // "xxxunusedxxx" Java 6 and before, "invokedynamic" Java 7 and later + NEW.class, // "new", + NEWARRAY.class, // "newarray", + ANEWARRAY.class, // "anewarray", + null, // "arraylength", + null, // "athrow", + CHECKCAST.class, // "checkcast", + INSTANCEOF.class, // "instanceof", + null, // "monitorenter", + null, // "monitorexit", + null, // "wide", + MULTIANEWARRAY.class, // "multianewarray", + null, // "ifnull", + null, // "ifnonnull", + GOTO_W.class, // "goto_w", + JSR_W.class // "jsr_w" + }; } diff --git a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java index 87598ac..5c5b659 100644 --- a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java +++ b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java @@ -1,62 +1,63 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; +import org.jetbrains.java.decompiler.main.DecompilerContext; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.main.DecompilerContext; - public class ExceptionHandler { - public int from = 0; - public int to = 0; - public int handler = 0; - - public int from_instr = 0; - public int to_instr = 0; - public int handler_instr = 0; - - public int class_index = 0; - public String exceptionClass = null; - - public ExceptionHandler(){} - - public ExceptionHandler(int from_raw, int to_raw, int handler_raw, String exceptionClass) { - this.from = from_raw; - this.to = to_raw; - this.handler = handler_raw; - this.exceptionClass = exceptionClass; - } - - public void writeToStream(DataOutputStream out) throws IOException { - out.writeShort(from); - out.writeShort(to); - out.writeShort(handler); - out.writeShort(class_index); - } - - public String toString() { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - buf.append("from: "+from+" to: "+to+" handler: "+handler+new_line_separator); - buf.append("from_instr: "+from_instr+" to_instr: "+to_instr+" handler_instr: "+handler_instr+new_line_separator); - buf.append("exceptionClass: "+exceptionClass+new_line_separator); - return buf.toString(); - } - + public int from = 0; + public int to = 0; + public int handler = 0; + + public int from_instr = 0; + public int to_instr = 0; + public int handler_instr = 0; + + public int class_index = 0; + public String exceptionClass = null; + + public ExceptionHandler() { + } + + public ExceptionHandler(int from_raw, int to_raw, int handler_raw, String exceptionClass) { + this.from = from_raw; + this.to = to_raw; + this.handler = handler_raw; + this.exceptionClass = exceptionClass; + } + + public void writeToStream(DataOutputStream out) throws IOException { + out.writeShort(from); + out.writeShort(to); + out.writeShort(handler); + out.writeShort(class_index); + } + + public String toString() { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + buf.append("from: " + from + " to: " + to + " handler: " + handler + new_line_separator); + buf.append("from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator); + buf.append("exceptionClass: " + exceptionClass + new_line_separator); + return buf.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/code/ExceptionTable.java b/src/org/jetbrains/java/decompiler/code/ExceptionTable.java index da8ec22..e828a7f 100644 --- a/src/org/jetbrains/java/decompiler/code/ExceptionTable.java +++ b/src/org/jetbrains/java/decompiler/code/ExceptionTable.java @@ -1,57 +1,58 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.interpreter.Util; import org.jetbrains.java.decompiler.struct.StructContext; +import java.util.ArrayList; +import java.util.List; + public class ExceptionTable { - private List<ExceptionHandler> handlers = new ArrayList<ExceptionHandler>(); - - public ExceptionTable() {} - - public ExceptionTable(List<ExceptionHandler> handlers) { - this.handlers = handlers; - } - - - public ExceptionHandler getHandlerByClass(StructContext context, int line, String valclass, boolean withany) { - - ExceptionHandler res = null; // no handler found - - for(ExceptionHandler handler : handlers) { - if(handler.from<=line && handler.to>line) { - String name = handler.exceptionClass; - - if((withany && name==null) || // any -> finally or synchronized handler - (name!=null && Util.instanceOf(context, valclass, name))) { - res = handler; - break; - } - } - } - - return res; - } - - public List<ExceptionHandler> getHandlers() { - return handlers; - } - + private List<ExceptionHandler> handlers = new ArrayList<ExceptionHandler>(); + + public ExceptionTable() { + } + + public ExceptionTable(List<ExceptionHandler> handlers) { + this.handlers = handlers; + } + + + public ExceptionHandler getHandlerByClass(StructContext context, int line, String valclass, boolean withany) { + + ExceptionHandler res = null; // no handler found + + for (ExceptionHandler handler : handlers) { + if (handler.from <= line && handler.to > line) { + String name = handler.exceptionClass; + + if ((withany && name == null) || // any -> finally or synchronized handler + (name != null && Util.instanceOf(context, valclass, name))) { + res = handler; + break; + } + } + } + + return res; + } + + public List<ExceptionHandler> getHandlers() { + return handlers; + } } diff --git a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java index f879fd0..eb8f819 100644 --- a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -19,20 +20,19 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; public class FullInstructionSequence extends InstructionSequence { - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) { - this.collinstr = collinstr; - this.exceptionTable = extable; - - // translate raw exception handlers to instr - for(ExceptionHandler handler : extable.getHandlers()) { - handler.from_instr = this.getPointerByAbsOffset(handler.from); - handler.to_instr = this.getPointerByAbsOffset(handler.to); - handler.handler_instr = this.getPointerByAbsOffset(handler.handler); - } - } + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) { + this.collinstr = collinstr; + this.exceptionTable = extable; + // translate raw exception handlers to instr + for (ExceptionHandler handler : extable.getHandlers()) { + handler.from_instr = this.getPointerByAbsOffset(handler.from); + handler.to_instr = this.getPointerByAbsOffset(handler.to); + handler.handler_instr = this.getPointerByAbsOffset(handler.handler); + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/IfInstruction.java b/src/org/jetbrains/java/decompiler/code/IfInstruction.java index befae18..1d4bf6c 100644 --- a/src/org/jetbrains/java/decompiler/code/IfInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/IfInstruction.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; import java.io.DataOutputStream; @@ -24,13 +25,12 @@ import java.io.IOException; public class IfInstruction extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opcode); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opcode); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/Instruction.java b/src/org/jetbrains/java/decompiler/code/Instruction.java index faa8926..2c0be6b 100644 --- a/src/org/jetbrains/java/decompiler/code/Instruction.java +++ b/src/org/jetbrains/java/decompiler/code/Instruction.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; import java.io.DataOutputStream; @@ -19,106 +20,107 @@ import java.io.IOException; public class Instruction implements CodeConstants { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int opcode; - - public int group = CodeConstants.GROUP_GENERAL; - - public boolean wide = false; - - public int bytecode_version = BYTECODE_JAVA_LE_4; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private int[] operands = null; - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public Instruction() {} - - public int length() { - return 1; - } - - public int operandsCount() { - return (operands==null)?0:operands.length; - } - - public int getOperand(int index) { - return operands[index]; - } - - public Instruction clone() { - return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands==null?null:(int[])operands.clone()); - } - - public String toString() { - - String res = wide?"@wide ":""; - res+="@"+ConstantsUtil.getName(opcode); - - int len = operandsCount(); - for(int i=0;i<len;i++) { - int op = operands[i]; - if(op<0) { - res+=" -"+Integer.toHexString(-op); - } else { - res+=" "+Integer.toHexString(op); - } - } - - return res; - - } - - public boolean canFallthrough() { - return opcode!=opc_goto && opcode!=opc_goto_w && opcode!=opc_ret && - !(opcode>=opc_ireturn && opcode<=opc_return) && opcode!=opc_athrow - && opcode!=opc_jsr && opcode!=opc_tableswitch && opcode!=opc_lookupswitch; - } - - public boolean equalsInstruction(Instruction instr) { - if(opcode != instr.opcode || wide != instr.wide - || operandsCount() != instr.operandsCount()) { - return false; - } - - if(operands != null) { - for(int i=0;i<operands.length;i++) { - if(operands[i] != instr.getOperand(i)) { - return false; - } - } - } - - return true; - } - - // should be overwritten by subclasses - public void initInstruction(InstructionSequence seq) {} - - // should be overwritten by subclasses - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opcode); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public int[] getOperands() { - return operands; - } - - public void setOperands(int[] operands) { - this.operands = operands; - } - + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int opcode; + + public int group = CodeConstants.GROUP_GENERAL; + + public boolean wide = false; + + public int bytecode_version = BYTECODE_JAVA_LE_4; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private int[] operands = null; + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public Instruction() { + } + + public int length() { + return 1; + } + + public int operandsCount() { + return (operands == null) ? 0 : operands.length; + } + + public int getOperand(int index) { + return operands[index]; + } + + public Instruction clone() { + return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands == null ? null : (int[])operands.clone()); + } + + public String toString() { + + String res = wide ? "@wide " : ""; + res += "@" + ConstantsUtil.getName(opcode); + + int len = operandsCount(); + for (int i = 0; i < len; i++) { + int op = operands[i]; + if (op < 0) { + res += " -" + Integer.toHexString(-op); + } + else { + res += " " + Integer.toHexString(op); + } + } + + return res; + } + + public boolean canFallthrough() { + return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret && + !(opcode >= opc_ireturn && opcode <= opc_return) && opcode != opc_athrow + && opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch; + } + + public boolean equalsInstruction(Instruction instr) { + if (opcode != instr.opcode || wide != instr.wide + || operandsCount() != instr.operandsCount()) { + return false; + } + + if (operands != null) { + for (int i = 0; i < operands.length; i++) { + if (operands[i] != instr.getOperand(i)) { + return false; + } + } + } + + return true; + } + + // should be overwritten by subclasses + public void initInstruction(InstructionSequence seq) { + } + + // should be overwritten by subclasses + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opcode); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public int[] getOperands() { + return operands; + } + + public void setOperands(int[] operands) { + this.operands = operands; + } } diff --git a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java index 4c29820..696908c 100644 --- a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java @@ -1,219 +1,227 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - import org.jetbrains.java.decompiler.code.interpreter.Util; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + public abstract class InstructionSequence { - // ***************************************************************************** - // private fields - // ***************************************************************************** - - protected VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); - - protected int pointer = 0; - - protected ExceptionTable exceptionTable = new ExceptionTable(); - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - // to nbe overwritten - public InstructionSequence clone() {return null;} - - public void clear() { - collinstr.clear(); - pointer = 0; - exceptionTable = new ExceptionTable(); - } - - public void addInstruction(Instruction inst, int offset){ - collinstr.addWithKey(inst, offset); - } - - public void addInstruction(int index, Instruction inst, int offset){ - collinstr.addWithKeyAndIndex(index, inst, offset); - } - - public void addSequence(InstructionSequence seq){ - for(int i=0;i<seq.length();i++) { - addInstruction(seq.getInstr(i), -1); // TODO: any sensible value possible? - } - } - - public void removeInstruction(int index) { - collinstr.remove(index); - } - - public Instruction getCurrentInstr() { - return (Instruction)collinstr.get(pointer); - } - - public Instruction getInstr(int index) { - return (Instruction)collinstr.get(index); - } - - public Instruction getLastInstr() { - return (Instruction)collinstr.getLast(); - } - - public int getCurrentOffset() { - return ((Integer)collinstr.getKey(pointer)).intValue(); - } - - public int getOffset(int index) { - return ((Integer)collinstr.getKey(index)).intValue(); - } - - public int getPointerByAbsOffset(int offset) { - Integer absoffset = new Integer(offset); - if(collinstr.containsKey(absoffset)) { - return collinstr.getIndexByKey(absoffset); - } else { - return -1; - } - } - - public int getPointerByRelOffset(int offset) { - Integer absoffset = new Integer(((Integer)collinstr.getKey(pointer)).intValue()+offset); - if(collinstr.containsKey(absoffset)) { - return collinstr.getIndexByKey(absoffset); - } else { - return -1; - } - } - - public void setPointerByAbsOffset(int offset) { - Integer absoffset = new Integer(((Integer)collinstr.getKey(pointer)).intValue()+offset); - if(collinstr.containsKey(absoffset)) { - pointer = collinstr.getIndexByKey(absoffset); - } - } - - public int length() { - return collinstr.size(); - } - - public boolean isEmpty() { - return collinstr.isEmpty(); - } - - public void addToPointer(int diff) { - this.pointer += diff; - } - - public String toString() { - return toString(0); - } - - public String toString(int indent) { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - - for(int i=0;i<collinstr.size();i++) { - buf.append(InterpreterUtil.getIndentString(indent)); - buf.append(((Integer)collinstr.getKey(i)).intValue()); - buf.append(": "); - buf.append(((Instruction)collinstr.get(i)).toString()); - buf.append(new_line_separator); - } - - return buf.toString(); - } - - public void writeCodeToStream(DataOutputStream out) throws IOException { - - for(int i=0;i<collinstr.size();i++) { - ((Instruction)collinstr.get(i)).writeToStream(out, ((Integer)collinstr.getKey(i)).intValue()); - } - } - - public void writeExceptionsToStream(DataOutputStream out) throws IOException { - - List<ExceptionHandler> handlers = exceptionTable.getHandlers(); - - out.writeShort(handlers.size()); - for(int i=0;i<handlers.size();i++) { - ((ExceptionHandler)handlers.get(i)).writeToStream(out); - } - } - - public void sortHandlers(final StructContext context) { - - Collections.sort(exceptionTable.getHandlers(), new Comparator<ExceptionHandler>() { - - public int compare(ExceptionHandler handler0, ExceptionHandler handler1) { - - if(handler0.to == handler1.to) { - if(handler0.exceptionClass == null) { - return 1; - } else { - if(handler1.exceptionClass == null) { - return -1; - } else if(handler0.exceptionClass.equals(handler1.exceptionClass)){ - return (handler0.from > handler1.from)?-1:1; // invalid code - } else { - if(Util.instanceOf(context, handler0.exceptionClass, handler1.exceptionClass)) { - return -1; - } else { - return 1; - } - } - } - } else { - return (handler0.to > handler1.to)?1:-1; - } - } - }); - - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public int getPointer() { - return pointer; - } - - public void setPointer(int pointer) { - this.pointer = pointer; - } - - public ExceptionTable getExceptionTable() { - return exceptionTable; - } - - public void setExceptionTable(ExceptionTable exceptionTable) { - this.exceptionTable = exceptionTable; - } - + // ***************************************************************************** + // private fields + // ***************************************************************************** + + protected VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); + + protected int pointer = 0; + + protected ExceptionTable exceptionTable = new ExceptionTable(); + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + // to nbe overwritten + public InstructionSequence clone() { + return null; + } + + public void clear() { + collinstr.clear(); + pointer = 0; + exceptionTable = new ExceptionTable(); + } + + public void addInstruction(Instruction inst, int offset) { + collinstr.addWithKey(inst, offset); + } + + public void addInstruction(int index, Instruction inst, int offset) { + collinstr.addWithKeyAndIndex(index, inst, offset); + } + + public void addSequence(InstructionSequence seq) { + for (int i = 0; i < seq.length(); i++) { + addInstruction(seq.getInstr(i), -1); // TODO: any sensible value possible? + } + } + + public void removeInstruction(int index) { + collinstr.remove(index); + } + + public Instruction getCurrentInstr() { + return (Instruction)collinstr.get(pointer); + } + + public Instruction getInstr(int index) { + return (Instruction)collinstr.get(index); + } + + public Instruction getLastInstr() { + return (Instruction)collinstr.getLast(); + } + + public int getCurrentOffset() { + return ((Integer)collinstr.getKey(pointer)).intValue(); + } + + public int getOffset(int index) { + return ((Integer)collinstr.getKey(index)).intValue(); + } + + public int getPointerByAbsOffset(int offset) { + Integer absoffset = new Integer(offset); + if (collinstr.containsKey(absoffset)) { + return collinstr.getIndexByKey(absoffset); + } + else { + return -1; + } + } + + public int getPointerByRelOffset(int offset) { + Integer absoffset = new Integer(((Integer)collinstr.getKey(pointer)).intValue() + offset); + if (collinstr.containsKey(absoffset)) { + return collinstr.getIndexByKey(absoffset); + } + else { + return -1; + } + } + + public void setPointerByAbsOffset(int offset) { + Integer absoffset = new Integer(((Integer)collinstr.getKey(pointer)).intValue() + offset); + if (collinstr.containsKey(absoffset)) { + pointer = collinstr.getIndexByKey(absoffset); + } + } + + public int length() { + return collinstr.size(); + } + + public boolean isEmpty() { + return collinstr.isEmpty(); + } + + public void addToPointer(int diff) { + this.pointer += diff; + } + + public String toString() { + return toString(0); + } + + public String toString(int indent) { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < collinstr.size(); i++) { + buf.append(InterpreterUtil.getIndentString(indent)); + buf.append(((Integer)collinstr.getKey(i)).intValue()); + buf.append(": "); + buf.append(((Instruction)collinstr.get(i)).toString()); + buf.append(new_line_separator); + } + + return buf.toString(); + } + + public void writeCodeToStream(DataOutputStream out) throws IOException { + + for (int i = 0; i < collinstr.size(); i++) { + ((Instruction)collinstr.get(i)).writeToStream(out, ((Integer)collinstr.getKey(i)).intValue()); + } + } + + public void writeExceptionsToStream(DataOutputStream out) throws IOException { + + List<ExceptionHandler> handlers = exceptionTable.getHandlers(); + + out.writeShort(handlers.size()); + for (int i = 0; i < handlers.size(); i++) { + ((ExceptionHandler)handlers.get(i)).writeToStream(out); + } + } + + public void sortHandlers(final StructContext context) { + + Collections.sort(exceptionTable.getHandlers(), new Comparator<ExceptionHandler>() { + + public int compare(ExceptionHandler handler0, ExceptionHandler handler1) { + + if (handler0.to == handler1.to) { + if (handler0.exceptionClass == null) { + return 1; + } + else { + if (handler1.exceptionClass == null) { + return -1; + } + else if (handler0.exceptionClass.equals(handler1.exceptionClass)) { + return (handler0.from > handler1.from) ? -1 : 1; // invalid code + } + else { + if (Util.instanceOf(context, handler0.exceptionClass, handler1.exceptionClass)) { + return -1; + } + else { + return 1; + } + } + } + } + else { + return (handler0.to > handler1.to) ? 1 : -1; + } + } + }); + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public int getPointer() { + return pointer; + } + + public void setPointer(int pointer) { + this.pointer = pointer; + } + + public ExceptionTable getExceptionTable() { + return exceptionTable; + } + + public void setExceptionTable(ExceptionTable exceptionTable) { + this.exceptionTable = exceptionTable; + } } diff --git a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java index 2daeeeb..44f4ff9 100644 --- a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; /* @@ -23,19 +24,19 @@ package org.jetbrains.java.decompiler.code; public class JumpInstruction extends Instruction { - public int destination; - - public JumpInstruction() {} - - public void initInstruction(InstructionSequence seq) { - destination = seq.getPointerByRelOffset(this.getOperand(0)); - } - - public JumpInstruction clone() { - JumpInstruction newinstr = (JumpInstruction)super.clone(); - - newinstr.destination = destination; - return newinstr; - } - + public int destination; + + public JumpInstruction() { + } + + public void initInstruction(InstructionSequence seq) { + destination = seq.getPointerByRelOffset(this.getOperand(0)); + } + + public JumpInstruction clone() { + JumpInstruction newinstr = (JumpInstruction)super.clone(); + + newinstr.destination = destination; + return newinstr; + } } diff --git a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java index c082092..bcc3c4a 100644 --- a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java @@ -1,39 +1,39 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; import org.jetbrains.java.decompiler.util.VBStyleCollection; public class SimpleInstructionSequence extends InstructionSequence { - public SimpleInstructionSequence() {} + public SimpleInstructionSequence() { + } + + public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) { + this.collinstr = collinstr; + } + + public SimpleInstructionSequence clone() { + SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); + newseq.setPointer(this.getPointer()); - public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) { - this.collinstr = collinstr; - } - - public SimpleInstructionSequence clone() { - SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); - newseq.setPointer(this.getPointer()); - - return newseq; - } + return newseq; + } - public void removeInstruction(int index) { - collinstr.remove(index); - } - - + public void removeInstruction(int index) { + collinstr.remove(index); + } } diff --git a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java index 63d0f4c..6aa6a7f 100644 --- a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; /* @@ -20,74 +21,77 @@ package org.jetbrains.java.decompiler.code; public class SwitchInstruction extends Instruction { - private int[] destinations; - - private int[] values; - - private int defaultdest; - - public SwitchInstruction() {} - - - public void initInstruction(InstructionSequence seq) { - - int pref = (opcode==CodeConstants.opc_tableswitch?3:2); - int len = this.getOperands().length - pref; - defaultdest = seq.getPointerByRelOffset(this.getOperand(0)); - - int low = 0; - - if(opcode==CodeConstants.opc_lookupswitch) { - len/=2; - } else { - low = this.getOperand(1); - } - - destinations = new int[len]; - values = new int[len]; - - for(int i=0,k=0;i<len;i++,k++) { - if(opcode==CodeConstants.opc_lookupswitch){ - values[i] = this.getOperand(pref+k); - k++; - } else { - values[i] = low+k; - } - destinations[i] = seq.getPointerByRelOffset(this.getOperand(pref+k)); - } - } - - public SwitchInstruction clone() { - SwitchInstruction newinstr = (SwitchInstruction)super.clone(); - - newinstr.defaultdest = defaultdest; - newinstr.destinations = destinations.clone(); - newinstr.values = values.clone(); - - return newinstr; - } - - public int[] getDestinations() { - return destinations; - } - - public void setDestinations(int[] destinations) { - this.destinations = destinations; - } - - public int getDefaultdest() { - return defaultdest; - } - - public void setDefaultdest(int defaultdest) { - this.defaultdest = defaultdest; - } - - public int[] getValues() { - return values; - } - - public void setValues(int[] values) { - this.values = values; - } + private int[] destinations; + + private int[] values; + + private int defaultdest; + + public SwitchInstruction() { + } + + + public void initInstruction(InstructionSequence seq) { + + int pref = (opcode == CodeConstants.opc_tableswitch ? 3 : 2); + int len = this.getOperands().length - pref; + defaultdest = seq.getPointerByRelOffset(this.getOperand(0)); + + int low = 0; + + if (opcode == CodeConstants.opc_lookupswitch) { + len /= 2; + } + else { + low = this.getOperand(1); + } + + destinations = new int[len]; + values = new int[len]; + + for (int i = 0, k = 0; i < len; i++, k++) { + if (opcode == CodeConstants.opc_lookupswitch) { + values[i] = this.getOperand(pref + k); + k++; + } + else { + values[i] = low + k; + } + destinations[i] = seq.getPointerByRelOffset(this.getOperand(pref + k)); + } + } + + public SwitchInstruction clone() { + SwitchInstruction newinstr = (SwitchInstruction)super.clone(); + + newinstr.defaultdest = defaultdest; + newinstr.destinations = destinations.clone(); + newinstr.values = values.clone(); + + return newinstr; + } + + public int[] getDestinations() { + return destinations; + } + + public void setDestinations(int[] destinations) { + this.destinations = destinations; + } + + public int getDefaultdest() { + return defaultdest; + } + + public void setDefaultdest(int defaultdest) { + this.defaultdest = defaultdest; + } + + public int[] getValues() { + return values; + } + + public void setValues(int[] values) { + this.values = values; + } } diff --git a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java index 917da08..f8bf793 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java @@ -1,265 +1,266 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.cfg; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; +import java.util.ArrayList; +import java.util.List; + public class BasicBlock implements IGraphNode { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int id = 0; - - public int mark = 0; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private InstructionSequence seq = new SimpleInstructionSequence(); - - private List<BasicBlock> preds = new ArrayList<BasicBlock>(); - - private List<BasicBlock> succs = new ArrayList<BasicBlock>(); - - private List<Integer> instrOldOffsets = new ArrayList<Integer>(); - - private List<BasicBlock> predExceptions = new ArrayList<BasicBlock>(); - - private List<BasicBlock> succExceptions = new ArrayList<BasicBlock>(); - - - - public BasicBlock() {} - - public BasicBlock(int id) { - this.id = id; - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public Object clone() { - - BasicBlock block = new BasicBlock(); - block.id = id; - block.setSeq(seq.clone()); - block.setInstrOldOffsets(new ArrayList<Integer>(instrOldOffsets)); - - return block; - } - - public void free() { - preds.clear(); - succs.clear(); - instrOldOffsets.clear(); - succExceptions.clear(); - seq = new SimpleInstructionSequence(); - } - - public Instruction getInstruction(int index) { - return seq.getInstr(index); - } - - public Instruction getLastInstruction() { - if(seq.isEmpty()) { - return null; - } else { - return seq.getLastInstr(); - } - } - - public int size() { - return seq.length(); - } - - public void addPredecessor(BasicBlock block) { - preds.add(block); - } - - public void removePredecessor(BasicBlock block) { - while(preds.remove(block)); - } - - public void addSuccessor(BasicBlock block) { - succs.add(block); - block.addPredecessor(this); - } - - public void removeSuccessor(BasicBlock block) { - while(succs.remove(block)); - block.removePredecessor(this); - } - - // FIXME: unify block comparisons: id or direkt equality - public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) { - for(int i=0;i<succs.size();i++) { - if(succs.get(i).id == oldBlock.id) { - succs.set(i, newBlock); - oldBlock.removePredecessor(this); - newBlock.addPredecessor(this); - } - } - - for(int i=0;i<succExceptions.size();i++) { - if(succExceptions.get(i).id == oldBlock.id) { - succExceptions.set(i, newBlock); - oldBlock.removePredecessorException(this); - newBlock.addPredecessorException(this); - } - } - } - - public void addPredecessorException(BasicBlock block) { - predExceptions.add(block); - } - - public void removePredecessorException(BasicBlock block) { - while(predExceptions.remove(block)); - } - - public void addSuccessorException(BasicBlock block) { - if(!succExceptions.contains(block)) { - succExceptions.add(block); - block.addPredecessorException(this); - } - } - - public void removeSuccessorException(BasicBlock block) { - while(succExceptions.remove(block)); - block.removePredecessorException(this); - } - - public String toString() { - return toString(0); - } - - public String toString(int indent) { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - return id+":" + new_line_separator +seq.toString(indent); - } - - public String toStringOldIndices() { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - - for(int i=0;i<seq.length();i++) { - if(i<instrOldOffsets.size()) { - buf.append(instrOldOffsets.get(i)); - } else { - buf.append("-1"); - } - buf.append(": "); - buf.append(seq.getInstr(i).toString()); - buf.append(new_line_separator); - } - - return buf.toString(); - } - - public boolean isSuccessor(BasicBlock block) { - for(BasicBlock succ : succs) { - if(succ.id == block.id) { - return true; - } - } - return false; - } - - public boolean isPredecessor(BasicBlock block) { - for(int i=0;i<preds.size();i++) { - if(preds.get(i).id == block.id) { - return true; - } - } - return false; - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public List<Integer> getInstrOldOffsets() { - return instrOldOffsets; - } - - public void setInstrOldOffsets(List<Integer> instrInds) { - this.instrOldOffsets = instrInds; - } - - public List<? extends IGraphNode> getPredecessors() { - List<BasicBlock> lst = new ArrayList<BasicBlock>(preds); - lst.addAll(predExceptions); - return lst; - } - - public List<BasicBlock> getPreds() { - return preds; - } - - public void setPreds(List<BasicBlock> preds) { - this.preds = preds; - } - - public InstructionSequence getSeq() { - return seq; - } - - public void setSeq(InstructionSequence seq) { - this.seq = seq; - } - - public List<BasicBlock> getSuccs() { - return succs; - } - - public void setSuccs(List<BasicBlock> succs) { - this.succs = succs; - } - - - public List<BasicBlock> getSuccExceptions() { - return succExceptions; - } - - - public void setSuccExceptions(List<BasicBlock> succExceptions) { - this.succExceptions = succExceptions; - } - - public List<BasicBlock> getPredExceptions() { - return predExceptions; - } - - public void setPredExceptions(List<BasicBlock> predExceptions) { - this.predExceptions = predExceptions; - } - - + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int id = 0; + + public int mark = 0; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private InstructionSequence seq = new SimpleInstructionSequence(); + + private List<BasicBlock> preds = new ArrayList<BasicBlock>(); + + private List<BasicBlock> succs = new ArrayList<BasicBlock>(); + + private List<Integer> instrOldOffsets = new ArrayList<Integer>(); + + private List<BasicBlock> predExceptions = new ArrayList<BasicBlock>(); + + private List<BasicBlock> succExceptions = new ArrayList<BasicBlock>(); + + + public BasicBlock() { + } + + public BasicBlock(int id) { + this.id = id; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public Object clone() { + + BasicBlock block = new BasicBlock(); + block.id = id; + block.setSeq(seq.clone()); + block.setInstrOldOffsets(new ArrayList<Integer>(instrOldOffsets)); + + return block; + } + + public void free() { + preds.clear(); + succs.clear(); + instrOldOffsets.clear(); + succExceptions.clear(); + seq = new SimpleInstructionSequence(); + } + + public Instruction getInstruction(int index) { + return seq.getInstr(index); + } + + public Instruction getLastInstruction() { + if (seq.isEmpty()) { + return null; + } + else { + return seq.getLastInstr(); + } + } + + public int size() { + return seq.length(); + } + + public void addPredecessor(BasicBlock block) { + preds.add(block); + } + + public void removePredecessor(BasicBlock block) { + while (preds.remove(block)) ; + } + + public void addSuccessor(BasicBlock block) { + succs.add(block); + block.addPredecessor(this); + } + + public void removeSuccessor(BasicBlock block) { + while (succs.remove(block)) ; + block.removePredecessor(this); + } + + // FIXME: unify block comparisons: id or direkt equality + public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) { + for (int i = 0; i < succs.size(); i++) { + if (succs.get(i).id == oldBlock.id) { + succs.set(i, newBlock); + oldBlock.removePredecessor(this); + newBlock.addPredecessor(this); + } + } + + for (int i = 0; i < succExceptions.size(); i++) { + if (succExceptions.get(i).id == oldBlock.id) { + succExceptions.set(i, newBlock); + oldBlock.removePredecessorException(this); + newBlock.addPredecessorException(this); + } + } + } + + public void addPredecessorException(BasicBlock block) { + predExceptions.add(block); + } + + public void removePredecessorException(BasicBlock block) { + while (predExceptions.remove(block)) ; + } + + public void addSuccessorException(BasicBlock block) { + if (!succExceptions.contains(block)) { + succExceptions.add(block); + block.addPredecessorException(this); + } + } + + public void removeSuccessorException(BasicBlock block) { + while (succExceptions.remove(block)) ; + block.removePredecessorException(this); + } + + public String toString() { + return toString(0); + } + + public String toString(int indent) { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + return id + ":" + new_line_separator + seq.toString(indent); + } + + public String toStringOldIndices() { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < seq.length(); i++) { + if (i < instrOldOffsets.size()) { + buf.append(instrOldOffsets.get(i)); + } + else { + buf.append("-1"); + } + buf.append(": "); + buf.append(seq.getInstr(i).toString()); + buf.append(new_line_separator); + } + + return buf.toString(); + } + + public boolean isSuccessor(BasicBlock block) { + for (BasicBlock succ : succs) { + if (succ.id == block.id) { + return true; + } + } + return false; + } + + public boolean isPredecessor(BasicBlock block) { + for (int i = 0; i < preds.size(); i++) { + if (preds.get(i).id == block.id) { + return true; + } + } + return false; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<Integer> getInstrOldOffsets() { + return instrOldOffsets; + } + + public void setInstrOldOffsets(List<Integer> instrInds) { + this.instrOldOffsets = instrInds; + } + + public List<? extends IGraphNode> getPredecessors() { + List<BasicBlock> lst = new ArrayList<BasicBlock>(preds); + lst.addAll(predExceptions); + return lst; + } + + public List<BasicBlock> getPreds() { + return preds; + } + + public void setPreds(List<BasicBlock> preds) { + this.preds = preds; + } + + public InstructionSequence getSeq() { + return seq; + } + + public void setSeq(InstructionSequence seq) { + this.seq = seq; + } + + public List<BasicBlock> getSuccs() { + return succs; + } + + public void setSuccs(List<BasicBlock> succs) { + this.succs = succs; + } + + + public List<BasicBlock> getSuccExceptions() { + return succExceptions; + } + + + public void setSuccExceptions(List<BasicBlock> succExceptions) { + this.succExceptions = succExceptions; + } + + public List<BasicBlock> getPredExceptions() { + return predExceptions; + } + + public void setPredExceptions(List<BasicBlock> predExceptions) { + this.predExceptions = predExceptions; + } } diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 824e8af..6b0624f 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -1,37 +1,21 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.cfg; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.ExceptionHandler; -import org.jetbrains.java.decompiler.code.Instruction; -import org.jetbrains.java.decompiler.code.InstructionSequence; -import org.jetbrains.java.decompiler.code.JumpInstruction; -import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; -import org.jetbrains.java.decompiler.code.SwitchInstruction; +import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; @@ -42,846 +26,852 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.ListStack; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.*; +import java.util.Map.Entry; + public class ControlFlowGraph implements CodeConstants { - public int last_id = 0; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private VBStyleCollection<BasicBlock, Integer> blocks; - - private BasicBlock first; - - private BasicBlock last; - - private List<ExceptionRangeCFG> exceptions; - - private HashMap<BasicBlock, BasicBlock> subroutines; - - private HashSet<BasicBlock> finallyExits = new HashSet<BasicBlock>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public ControlFlowGraph(InstructionSequence seq) { - buildBlocks(seq); - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void free() { - - for(BasicBlock block: blocks) { - block.free(); - } - - blocks.clear(); - first = null; - last = null; - exceptions.clear(); - finallyExits.clear(); - } - - public void removeMarkers() { - for(BasicBlock block: blocks) { - block.mark = 0; - } - } - - public String toString() { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - - for(BasicBlock block: blocks) { - buf.append("----- Block "+block.id+" -----" + new_line_separator); - buf.append(block.toString()); - buf.append("----- Edges -----" + new_line_separator); - - List<BasicBlock> suc = block.getSuccs(); - for(int j=0;j<suc.size();j++) { - buf.append(">>>>>>>>(regular) Block "+((BasicBlock)suc.get(j)).id+new_line_separator); - } - suc = block.getSuccExceptions(); - for(int j=0;j<suc.size();j++) { - BasicBlock handler = (BasicBlock)suc.get(j); - ExceptionRangeCFG range = getExceptionRange(handler, block); - - if(range == null) { - buf.append(">>>>>>>>(exception) Block "+handler.id+"\t"+"ERROR: range not found!"+new_line_separator); - } else { - List<String> exceptionTypes = range.getExceptionTypes(); - if(exceptionTypes == null) { - buf.append(">>>>>>>>(exception) Block "+handler.id+"\t"+"NULL"+new_line_separator); - } else { - for(String exceptionType : exceptionTypes) { - buf.append(">>>>>>>>(exception) Block "+handler.id+"\t"+exceptionType+new_line_separator); - } - } - } - } - buf.append("----- ----- -----" + new_line_separator); - - } - - return buf.toString(); - - } - - public void inlineJsr(StructMethod mt) { - processJsr(); - removeJsr(mt); - - removeMarkers(); - - DeadCodeHelper.removeEmptyBlocks(this); - } - - public void removeBlock(BasicBlock block) { - - while(block.getSuccs().size()>0) { - block.removeSuccessor((BasicBlock)block.getSuccs().get(0)); - } - - while(block.getSuccExceptions().size()>0) { - block.removeSuccessorException((BasicBlock)block.getSuccExceptions().get(0)); - } - - while(block.getPreds().size()>0) { - ((BasicBlock)block.getPreds().get(0)).removeSuccessor(block); - } - - while(block.getPredExceptions().size()>0) { - ((BasicBlock)block.getPredExceptions().get(0)).removeSuccessorException(block); - } - - last.removePredecessor(block); - - blocks.removeWithKey(block.id); - - for(int i=exceptions.size()-1;i>=0;i--) { - ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); - if(range.getHandler() == block) { - exceptions.remove(i); - } else { - List<BasicBlock> lstRange = range.getProtectedRange(); - lstRange.remove(block); - - if(lstRange.isEmpty()) { - exceptions.remove(i); - } - } - } - - Iterator<Entry<BasicBlock, BasicBlock>> it = subroutines.entrySet().iterator(); - while(it.hasNext()) { - Entry<BasicBlock, BasicBlock> ent = it.next(); - if(ent.getKey() == block || ent.getValue() == block) { - it.remove(); - } - } - - } - - public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) { - - //List<ExceptionRangeCFG> ranges = new ArrayList<ExceptionRangeCFG>(); - - for(int i=exceptions.size()-1;i>=0;i--) { - ExceptionRangeCFG range = exceptions.get(i); - if(range.getHandler() == handler && range.getProtectedRange().contains(block)) { - return range; - //ranges.add(range); - } - } - - return null; - //return ranges.isEmpty() ? null : ranges; - } - -// public String getExceptionsUniqueString(BasicBlock handler, BasicBlock block) { -// -// List<ExceptionRangeCFG> ranges = getExceptionRange(handler, block); -// -// if(ranges == null) { -// return null; -// } else { -// Set<String> setExceptionStrings = new HashSet<String>(); -// for(ExceptionRangeCFG range : ranges) { -// setExceptionStrings.add(range.getExceptionType()); -// } -// -// String ret = ""; -// for(String exception : setExceptionStrings) { -// ret += exception; -// } -// -// return ret; -// } -// } - - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void buildBlocks(InstructionSequence instrseq) { - - short[] states = findStartInstructions(instrseq); - - HashMap<Integer, BasicBlock> mapInstrBlocks = new HashMap<Integer, BasicBlock>(); - VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks); - - blocks = colBlocks; - - connectBlocks(colBlocks, mapInstrBlocks); - - setExceptionEdges(instrseq, mapInstrBlocks); - - setSubroutineEdges(); - - setFirstAndLastBlocks(); - } - - private short[] findStartInstructions(InstructionSequence seq) { - - int len = seq.length(); - short[] inststates = new short[len]; - - HashSet<Integer> excSet = new HashSet<Integer>(); - - for(ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { - excSet.add(handler.from_instr); - excSet.add(handler.to_instr); - excSet.add(handler.handler_instr); - } - - - for(int i=0;i<len;i++) { - - // exception blocks - if(excSet.contains(new Integer(i))) { - inststates[i] = 1; - } - - Instruction instr = seq.getInstr(i); - switch(instr.group){ - case GROUP_JUMP: - inststates[((JumpInstruction)instr).destination] = 1; - case GROUP_RETURN: - if(i+1 < len) { - inststates[i+1] = 1; - } - break; - case GROUP_SWITCH: - SwitchInstruction swinstr = (SwitchInstruction)instr; - int[] dests = swinstr.getDestinations(); - for(int j=dests.length-1;j>=0;j--) { - inststates[dests[j]] = 1; - } - inststates[swinstr.getDefaultdest()] = 1; - if(i+1 < len) { - inststates[i+1] = 1; - } - } - } - - // first instruction - inststates[0] = 1; - - return inststates; - } - - - private VBStyleCollection<BasicBlock, Integer> createBasicBlocks(short[] startblock, InstructionSequence instrseq, - HashMap<Integer, BasicBlock> mapInstrBlocks) { - - VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<BasicBlock, Integer>(); - - InstructionSequence currseq = null; - ArrayList<Integer> lstOffs = null; - - int len = startblock.length; - short counter = 0; - int blockoffset = 0; - - BasicBlock currentBlock = null; - for(int i=0;i<len;i++) { - - if (startblock[i] == 1) { - currentBlock = new BasicBlock(); - currentBlock.id = ++counter; - - currseq = new SimpleInstructionSequence(); - lstOffs = new ArrayList<Integer>(); - - currentBlock.setSeq(currseq); - currentBlock.setInstrOldOffsets(lstOffs); - col.addWithKey(currentBlock, currentBlock.id); - - blockoffset = instrseq.getOffset(i); - } - - startblock[i] = counter; - mapInstrBlocks.put(i, currentBlock); - - currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i)-blockoffset); - lstOffs.add(instrseq.getOffset(i)); - } - - last_id = counter; - - return col; - } - - - private void connectBlocks(List<BasicBlock> lstbb, HashMap<Integer, BasicBlock> mapInstrBlocks) { - - for(int i=0;i<lstbb.size();i++) { - - BasicBlock block = lstbb.get(i); - Instruction instr = block.getLastInstruction(); - - boolean fallthrough = instr.canFallthrough(); - BasicBlock bTemp; - - switch(instr.group) { - case GROUP_JUMP: - int dest = ((JumpInstruction)instr).destination; - bTemp = mapInstrBlocks.get(dest); - block.addSuccessor(bTemp); - - break; - case GROUP_SWITCH: - SwitchInstruction sinstr = (SwitchInstruction)instr; - int[] dests = sinstr.getDestinations(); - - bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest()); - block.addSuccessor(bTemp); - for(int j=0;j<dests.length;j++) { - bTemp = mapInstrBlocks.get(dests[j]); - block.addSuccessor(bTemp); - } - } - - if(fallthrough && i<lstbb.size()-1) { - BasicBlock defaultBlock = lstbb.get(i+1); - block.addSuccessor(defaultBlock); - } - } - - } - - private void setExceptionEdges(InstructionSequence instrseq, HashMap<Integer, BasicBlock> instrBlocks) { - - exceptions = new ArrayList<ExceptionRangeCFG>(); - - Map<String, ExceptionRangeCFG> mapRanges = new HashMap<String, ExceptionRangeCFG>(); - - for(ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) { - - BasicBlock from = instrBlocks.get(handler.from_instr); - BasicBlock to = instrBlocks.get(handler.to_instr); - BasicBlock handle = instrBlocks.get(handler.handler_instr); - - String key = from.id + ":" + to.id + ":" + handle.id; - - if(mapRanges.containsKey(key)) { - ExceptionRangeCFG range = mapRanges.get(key); - range.addExceptionType(handler.exceptionClass); - } else { - - List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); - for(int j=from.id;j<to.id;j++) { - BasicBlock block = blocks.getWithKey(j); - protectedRange.add(block); - block.addSuccessorException(handle); - } - - ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null ? null : Arrays.asList(new String[]{handler.exceptionClass})); - mapRanges.put(key, range); - - exceptions.add(range); - } - } - } - - private void setSubroutineEdges() { - - final HashMap<BasicBlock, BasicBlock> subroutines = new HashMap<BasicBlock, BasicBlock>(); - - for(BasicBlock block : blocks) { - - if(block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) { - - LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); - LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<LinkedList<BasicBlock>>(); - - HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); - - stack.add(block); - stackJsrStacks.add(new LinkedList<BasicBlock>()); - - while(!stack.isEmpty()) { - - BasicBlock node = stack.removeFirst(); - LinkedList<BasicBlock> jsrstack = stackJsrStacks.removeFirst(); - - setVisited.add(node); - - switch(node.getSeq().getLastInstr().opcode) { - case CodeConstants.opc_jsr: - jsrstack.add(node); - break; - case CodeConstants.opc_ret: - BasicBlock enter = jsrstack.getLast(); - BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way - - if(exit!=null) { - if(!node.isSuccessor(exit)) { - node.addSuccessor(exit); - } - jsrstack.removeLast(); - subroutines.put(enter, exit); - } else { - throw new RuntimeException("ERROR: last instruction jsr"); - } - } - - if(!jsrstack.isEmpty()) { - for(BasicBlock succ : node.getSuccs()) { - if(!setVisited.contains(succ)) { - stack.add(succ); - stackJsrStacks.add(new LinkedList<BasicBlock>(jsrstack)); - } - } - } - } - } - } - - this.subroutines = subroutines; - } - - private void processJsr() { - - while(processJsrRanges()!=0); - } - - private int processJsrRanges() { - - List<Object[]> lstJsrAll = new ArrayList<Object[]>(); - - // get all jsr ranges - for(Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()){ - BasicBlock jsr = ent.getKey(); - BasicBlock ret = ent.getValue(); - - lstJsrAll.add(new Object[]{jsr, getJsrRange(jsr, ret), ret}); - } - - // sort ranges - // FIXME: better sort order - List<Object[]> lstJsr = new ArrayList<Object[]>(); - for(Object[] arr : lstJsrAll) { - int i=0; - for(;i<lstJsr.size();i++) { - Object[] arrJsr = lstJsr.get(i); - - if(((HashSet<BasicBlock>)arrJsr[1]).contains(arr[0])) { - break; - } - } - - lstJsr.add(i, arr); - } - - // find the first intersection - for(int i=0;i<lstJsr.size();i++) { - Object[] arr = (Object[])lstJsr.get(i); - HashSet<BasicBlock> set = (HashSet<BasicBlock>)arr[1]; - - for(int j=i+1;j<lstJsr.size();j++) { - Object[] arr1 = (Object[])lstJsr.get(j); - HashSet<BasicBlock> set1 = (HashSet<BasicBlock>)arr1[1]; - - if(!set.contains(arr1[0]) && !set1.contains(arr[0])) { // rang 0 doesn't contain entry 1 and vice versa - HashSet<BasicBlock> setc = new HashSet<BasicBlock>(set); - setc.retainAll(set1); - - if(!setc.isEmpty()) { - splitJsrRange((BasicBlock)arr[0], (BasicBlock)arr[2], setc); - return 1; - } - } - } - } - - return 0; - } - - private HashSet<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) { - - HashSet<BasicBlock> blocks = new HashSet<BasicBlock>(); - - LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); - lstNodes.add(jsr); - - BasicBlock dom = jsr.getSuccs().get(0); - - while(!lstNodes.isEmpty()) { - - BasicBlock node = lstNodes.remove(0); - - for(int j=0;j<2;j++) { - List<BasicBlock> lst; - if(j==0) { - if(node.getLastInstruction().opcode == CodeConstants.opc_ret) { - if(node.getSuccs().contains(ret)) { - continue; - } - } - lst = node.getSuccs(); - } else { - if(node == jsr) { - continue; - } - lst = node.getSuccExceptions(); - } - - CHILD: - for(int i=lst.size()-1;i>=0;i--) { - - BasicBlock child = lst.get(i); - if(!blocks.contains(child)) { - - if(node != jsr) { - for(int k=0;k<child.getPreds().size();k++) { - if(!DeadCodeHelper.isDominator(this, child.getPreds().get(k), dom)) { - continue CHILD; - } - } - - for(int k=0;k<child.getPredExceptions().size();k++) { - if(!DeadCodeHelper.isDominator(this, child.getPredExceptions().get(k), dom)) { - continue CHILD; - } - } - } - - // last block is a dummy one - if(child!=last) { - blocks.add(child); - } - - lstNodes.add(child); - } - } - } - } - - return blocks; - } - - private void splitJsrRange(BasicBlock jsr, BasicBlock ret, HashSet<BasicBlock> common_blocks) { - - LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); - HashMap<Integer, BasicBlock> mapNewNodes = new HashMap<Integer, BasicBlock>(); - - lstNodes.add(jsr); - mapNewNodes.put(jsr.id, jsr); - - while(!lstNodes.isEmpty()) { - - BasicBlock node = lstNodes.remove(0); - - for(int j=0;j<2;j++) { - List<BasicBlock> lst; - if(j==0) { - if(node.getLastInstruction().opcode == CodeConstants.opc_ret) { - if(node.getSuccs().contains(ret)) { - continue; - } - } - lst = node.getSuccs(); - } else { - if(node == jsr) { - continue; - } - lst = node.getSuccExceptions(); - } - - - for(int i=lst.size()-1;i>=0;i--) { - - BasicBlock child = (BasicBlock)lst.get(i); - Integer childid = child.id; - - if(mapNewNodes.containsKey(childid)) { - node.replaceSuccessor(child, (BasicBlock)mapNewNodes.get(childid)); - } else if(common_blocks.contains(child)) { - - // make a copy of the current block - BasicBlock copy = (BasicBlock)child.clone(); - copy.id = ++last_id; - // copy all successors - if(copy.getLastInstruction().opcode == CodeConstants.opc_ret && - child.getSuccs().contains(ret)) { - copy.addSuccessor(ret); - child.removeSuccessor(ret); - } else { - for(int k=0;k<child.getSuccs().size();k++) { - copy.addSuccessor((BasicBlock)child.getSuccs().get(k)); - } - } - for(int k=0;k<child.getSuccExceptions().size();k++) { - copy.addSuccessorException((BasicBlock)child.getSuccExceptions().get(k)); - } - - lstNodes.add(copy); - mapNewNodes.put(childid, copy); - - if(last.getPreds().contains(child)) { - last.addPredecessor(copy); - } - - node.replaceSuccessor(child, copy); - blocks.addWithKey(copy, copy.id); - } else { - // stop at the first fixed node - //lstNodes.add(child); - mapNewNodes.put(childid, child); - } - } - - } - } - - // note: subroutines won't be copied! - splitJsrExceptionRanges(common_blocks, mapNewNodes); - } - - private void splitJsrExceptionRanges(HashSet<BasicBlock> common_blocks, HashMap<Integer, BasicBlock> mapNewNodes) { - - for(int i=exceptions.size()-1;i>=0;i--) { - - ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); - List<BasicBlock> lstRange = range.getProtectedRange(); - - HashSet<BasicBlock> setBoth = new HashSet<BasicBlock>(common_blocks); - setBoth.retainAll(lstRange); - - if(setBoth.size()>0) { - List<BasicBlock> lstNewRange; - - if(setBoth.size()==lstRange.size()) { - lstNewRange = new ArrayList<BasicBlock>(); - ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange, - (BasicBlock)mapNewNodes.get(range.getHandler().id),range.getExceptionTypes()); - exceptions.add(newRange); - } else { - lstNewRange = lstRange; - } - - for(BasicBlock block : setBoth) { - lstNewRange.add(mapNewNodes.get(block.id)); - } - } - } - - } - - private void removeJsr(StructMethod mt) { - removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); - } - - private void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { - - ListStack<VarType> stack = data.getStack(); - - InstructionSequence seq = block.getSeq(); - for(int i=0;i<seq.length();i++) { - Instruction instr = seq.getInstr(i); - - VarType var = null; - if(instr.opcode == CodeConstants.opc_astore || instr.opcode == CodeConstants.opc_pop) { - var = stack.getByOffset(-1); - } - - InstructionImpact.stepTypes(data, instr, pool); - - switch(instr.opcode) { - case CodeConstants.opc_jsr: - case CodeConstants.opc_ret: - seq.removeInstruction(i); - i--; - break; - case CodeConstants.opc_astore: - case CodeConstants.opc_pop: - if(var.type == CodeConstants.TYPE_ADDRESS) { - seq.removeInstruction(i); - i--; - } - } - - } - - block.mark = 1; - - for(int i=0;i<block.getSuccs().size();i++) { - BasicBlock suc = (BasicBlock)block.getSuccs().get(i); - if(suc.mark != 1) { - removeJsrInstructions(pool, suc, data.copy()); - } - } - - for(int i=0;i<block.getSuccExceptions().size();i++) { - BasicBlock suc = (BasicBlock)block.getSuccExceptions().get(i); - if(suc.mark != 1) { - - DataPoint point = new DataPoint(); - point.setLocalVariables(new ArrayList<VarType>(data.getLocalVariables())); - point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); - - removeJsrInstructions(pool, suc, point); - } - } - - } - - private void setFirstAndLastBlocks() { - - first = blocks.get(0); - - last = new BasicBlock(); - last.id = ++last_id; - last.setSeq(new SimpleInstructionSequence()); - - for(BasicBlock block: blocks) { - if(block.getSuccs().isEmpty()) { - last.addPredecessor(block); - } - } - } - - public List<BasicBlock> getReversePostOrder() { - - LinkedList<BasicBlock> res = new LinkedList<BasicBlock>(); - addToReversePostOrderListIterative(first, res); - - return res; - } - - private void addToReversePostOrderListIterative(BasicBlock root, List<BasicBlock> lst) { - - LinkedList<BasicBlock> stackNode = new LinkedList<BasicBlock>(); - LinkedList<Integer> stackIndex = new LinkedList<Integer>(); - - HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); - - stackNode.add(root); - stackIndex.add(0); - - while(!stackNode.isEmpty()) { - - BasicBlock node = stackNode.getLast(); - int index = stackIndex.removeLast(); - - setVisited.add(node); - - List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(node.getSuccs()); - lstSuccs.addAll(node.getSuccExceptions()); - - for(;index<lstSuccs.size();index++) { - BasicBlock succ = lstSuccs.get(index); - - if(!setVisited.contains(succ)) { - stackIndex.add(index+1); - - stackNode.add(succ); - stackIndex.add(0); - - break; - } - } - - if(index == lstSuccs.size()) { - lst.add(0, node); - - stackNode.removeLast(); - } - } - - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public VBStyleCollection<BasicBlock, Integer> getBlocks() { - return blocks; - } - - public void setBlocks(VBStyleCollection<BasicBlock, Integer> blocks) { - this.blocks = blocks; - } - - public BasicBlock getFirst() { - return first; - } - - public void setFirst(BasicBlock first) { - this.first = first; - } - - public List<BasicBlock> getEndBlocks() { - return last.getPreds(); - } - - public List<ExceptionRangeCFG> getExceptions() { - return exceptions; - } - - public void setExceptions(List<ExceptionRangeCFG> exceptions) { - this.exceptions = exceptions; - } - - - public BasicBlock getLast() { - return last; - } - - - public void setLast(BasicBlock last) { - this.last = last; - } - - - public HashMap<BasicBlock, BasicBlock> getSubroutines() { - return subroutines; - } - - - public void setSubroutines(HashMap<BasicBlock, BasicBlock> subroutines) { - this.subroutines = subroutines; - } - - - public HashSet<BasicBlock> getFinallyExits() { - return finallyExits; - } - - - public void setFinallyExits(HashSet<BasicBlock> finallyExits) { - this.finallyExits = finallyExits; - } + public int last_id = 0; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private VBStyleCollection<BasicBlock, Integer> blocks; + + private BasicBlock first; + + private BasicBlock last; + + private List<ExceptionRangeCFG> exceptions; + + private HashMap<BasicBlock, BasicBlock> subroutines; + + private HashSet<BasicBlock> finallyExits = new HashSet<BasicBlock>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public ControlFlowGraph(InstructionSequence seq) { + buildBlocks(seq); + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void free() { + + for (BasicBlock block : blocks) { + block.free(); + } + + blocks.clear(); + first = null; + last = null; + exceptions.clear(); + finallyExits.clear(); + } + + public void removeMarkers() { + for (BasicBlock block : blocks) { + block.mark = 0; + } + } + + public String toString() { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + for (BasicBlock block : blocks) { + buf.append("----- Block " + block.id + " -----" + new_line_separator); + buf.append(block.toString()); + buf.append("----- Edges -----" + new_line_separator); + + List<BasicBlock> suc = block.getSuccs(); + for (int j = 0; j < suc.size(); j++) { + buf.append(">>>>>>>>(regular) Block " + ((BasicBlock)suc.get(j)).id + new_line_separator); + } + suc = block.getSuccExceptions(); + for (int j = 0; j < suc.size(); j++) { + BasicBlock handler = (BasicBlock)suc.get(j); + ExceptionRangeCFG range = getExceptionRange(handler, block); + + if (range == null) { + buf.append(">>>>>>>>(exception) Block " + handler.id + "\t" + "ERROR: range not found!" + new_line_separator); + } + else { + List<String> exceptionTypes = range.getExceptionTypes(); + if (exceptionTypes == null) { + buf.append(">>>>>>>>(exception) Block " + handler.id + "\t" + "NULL" + new_line_separator); + } + else { + for (String exceptionType : exceptionTypes) { + buf.append(">>>>>>>>(exception) Block " + handler.id + "\t" + exceptionType + new_line_separator); + } + } + } + } + buf.append("----- ----- -----" + new_line_separator); + } + + return buf.toString(); + } + + public void inlineJsr(StructMethod mt) { + processJsr(); + removeJsr(mt); + + removeMarkers(); + + DeadCodeHelper.removeEmptyBlocks(this); + } + + public void removeBlock(BasicBlock block) { + + while (block.getSuccs().size() > 0) { + block.removeSuccessor((BasicBlock)block.getSuccs().get(0)); + } + + while (block.getSuccExceptions().size() > 0) { + block.removeSuccessorException((BasicBlock)block.getSuccExceptions().get(0)); + } + + while (block.getPreds().size() > 0) { + ((BasicBlock)block.getPreds().get(0)).removeSuccessor(block); + } + + while (block.getPredExceptions().size() > 0) { + ((BasicBlock)block.getPredExceptions().get(0)).removeSuccessorException(block); + } + + last.removePredecessor(block); + + blocks.removeWithKey(block.id); + + for (int i = exceptions.size() - 1; i >= 0; i--) { + ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); + if (range.getHandler() == block) { + exceptions.remove(i); + } + else { + List<BasicBlock> lstRange = range.getProtectedRange(); + lstRange.remove(block); + + if (lstRange.isEmpty()) { + exceptions.remove(i); + } + } + } + + Iterator<Entry<BasicBlock, BasicBlock>> it = subroutines.entrySet().iterator(); + while (it.hasNext()) { + Entry<BasicBlock, BasicBlock> ent = it.next(); + if (ent.getKey() == block || ent.getValue() == block) { + it.remove(); + } + } + } + + public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) { + + //List<ExceptionRangeCFG> ranges = new ArrayList<ExceptionRangeCFG>(); + + for (int i = exceptions.size() - 1; i >= 0; i--) { + ExceptionRangeCFG range = exceptions.get(i); + if (range.getHandler() == handler && range.getProtectedRange().contains(block)) { + return range; + //ranges.add(range); + } + } + + return null; + //return ranges.isEmpty() ? null : ranges; + } + + // public String getExceptionsUniqueString(BasicBlock handler, BasicBlock block) { + // + // List<ExceptionRangeCFG> ranges = getExceptionRange(handler, block); + // + // if(ranges == null) { + // return null; + // } else { + // Set<String> setExceptionStrings = new HashSet<String>(); + // for(ExceptionRangeCFG range : ranges) { + // setExceptionStrings.add(range.getExceptionType()); + // } + // + // String ret = ""; + // for(String exception : setExceptionStrings) { + // ret += exception; + // } + // + // return ret; + // } + // } + + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void buildBlocks(InstructionSequence instrseq) { + + short[] states = findStartInstructions(instrseq); + + HashMap<Integer, BasicBlock> mapInstrBlocks = new HashMap<Integer, BasicBlock>(); + VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks); + + blocks = colBlocks; + + connectBlocks(colBlocks, mapInstrBlocks); + + setExceptionEdges(instrseq, mapInstrBlocks); + + setSubroutineEdges(); + + setFirstAndLastBlocks(); + } + + private short[] findStartInstructions(InstructionSequence seq) { + + int len = seq.length(); + short[] inststates = new short[len]; + + HashSet<Integer> excSet = new HashSet<Integer>(); + + for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { + excSet.add(handler.from_instr); + excSet.add(handler.to_instr); + excSet.add(handler.handler_instr); + } + + + for (int i = 0; i < len; i++) { + + // exception blocks + if (excSet.contains(new Integer(i))) { + inststates[i] = 1; + } + + Instruction instr = seq.getInstr(i); + switch (instr.group) { + case GROUP_JUMP: + inststates[((JumpInstruction)instr).destination] = 1; + case GROUP_RETURN: + if (i + 1 < len) { + inststates[i + 1] = 1; + } + break; + case GROUP_SWITCH: + SwitchInstruction swinstr = (SwitchInstruction)instr; + int[] dests = swinstr.getDestinations(); + for (int j = dests.length - 1; j >= 0; j--) { + inststates[dests[j]] = 1; + } + inststates[swinstr.getDefaultdest()] = 1; + if (i + 1 < len) { + inststates[i + 1] = 1; + } + } + } + + // first instruction + inststates[0] = 1; + + return inststates; + } + + + private VBStyleCollection<BasicBlock, Integer> createBasicBlocks(short[] startblock, InstructionSequence instrseq, + HashMap<Integer, BasicBlock> mapInstrBlocks) { + + VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<BasicBlock, Integer>(); + + InstructionSequence currseq = null; + ArrayList<Integer> lstOffs = null; + + int len = startblock.length; + short counter = 0; + int blockoffset = 0; + + BasicBlock currentBlock = null; + for (int i = 0; i < len; i++) { + + if (startblock[i] == 1) { + currentBlock = new BasicBlock(); + currentBlock.id = ++counter; + + currseq = new SimpleInstructionSequence(); + lstOffs = new ArrayList<Integer>(); + + currentBlock.setSeq(currseq); + currentBlock.setInstrOldOffsets(lstOffs); + col.addWithKey(currentBlock, currentBlock.id); + + blockoffset = instrseq.getOffset(i); + } + + startblock[i] = counter; + mapInstrBlocks.put(i, currentBlock); + + currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i) - blockoffset); + lstOffs.add(instrseq.getOffset(i)); + } + + last_id = counter; + + return col; + } + + + private void connectBlocks(List<BasicBlock> lstbb, HashMap<Integer, BasicBlock> mapInstrBlocks) { + + for (int i = 0; i < lstbb.size(); i++) { + + BasicBlock block = lstbb.get(i); + Instruction instr = block.getLastInstruction(); + + boolean fallthrough = instr.canFallthrough(); + BasicBlock bTemp; + + switch (instr.group) { + case GROUP_JUMP: + int dest = ((JumpInstruction)instr).destination; + bTemp = mapInstrBlocks.get(dest); + block.addSuccessor(bTemp); + + break; + case GROUP_SWITCH: + SwitchInstruction sinstr = (SwitchInstruction)instr; + int[] dests = sinstr.getDestinations(); + + bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest()); + block.addSuccessor(bTemp); + for (int j = 0; j < dests.length; j++) { + bTemp = mapInstrBlocks.get(dests[j]); + block.addSuccessor(bTemp); + } + } + + if (fallthrough && i < lstbb.size() - 1) { + BasicBlock defaultBlock = lstbb.get(i + 1); + block.addSuccessor(defaultBlock); + } + } + } + + private void setExceptionEdges(InstructionSequence instrseq, HashMap<Integer, BasicBlock> instrBlocks) { + + exceptions = new ArrayList<ExceptionRangeCFG>(); + + Map<String, ExceptionRangeCFG> mapRanges = new HashMap<String, ExceptionRangeCFG>(); + + for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) { + + BasicBlock from = instrBlocks.get(handler.from_instr); + BasicBlock to = instrBlocks.get(handler.to_instr); + BasicBlock handle = instrBlocks.get(handler.handler_instr); + + String key = from.id + ":" + to.id + ":" + handle.id; + + if (mapRanges.containsKey(key)) { + ExceptionRangeCFG range = mapRanges.get(key); + range.addExceptionType(handler.exceptionClass); + } + else { + + List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); + for (int j = from.id; j < to.id; j++) { + BasicBlock block = blocks.getWithKey(j); + protectedRange.add(block); + block.addSuccessorException(handle); + } + + ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null + ? null + : Arrays.asList(new String[]{handler.exceptionClass})); + mapRanges.put(key, range); + + exceptions.add(range); + } + } + } + + private void setSubroutineEdges() { + + final HashMap<BasicBlock, BasicBlock> subroutines = new HashMap<BasicBlock, BasicBlock>(); + + for (BasicBlock block : blocks) { + + if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) { + + LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); + LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<LinkedList<BasicBlock>>(); + + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + stack.add(block); + stackJsrStacks.add(new LinkedList<BasicBlock>()); + + while (!stack.isEmpty()) { + + BasicBlock node = stack.removeFirst(); + LinkedList<BasicBlock> jsrstack = stackJsrStacks.removeFirst(); + + setVisited.add(node); + + switch (node.getSeq().getLastInstr().opcode) { + case CodeConstants.opc_jsr: + jsrstack.add(node); + break; + case CodeConstants.opc_ret: + BasicBlock enter = jsrstack.getLast(); + BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way + + if (exit != null) { + if (!node.isSuccessor(exit)) { + node.addSuccessor(exit); + } + jsrstack.removeLast(); + subroutines.put(enter, exit); + } + else { + throw new RuntimeException("ERROR: last instruction jsr"); + } + } + + if (!jsrstack.isEmpty()) { + for (BasicBlock succ : node.getSuccs()) { + if (!setVisited.contains(succ)) { + stack.add(succ); + stackJsrStacks.add(new LinkedList<BasicBlock>(jsrstack)); + } + } + } + } + } + } + + this.subroutines = subroutines; + } + + private void processJsr() { + + while (processJsrRanges() != 0) ; + } + + private int processJsrRanges() { + + List<Object[]> lstJsrAll = new ArrayList<Object[]>(); + + // get all jsr ranges + for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) { + BasicBlock jsr = ent.getKey(); + BasicBlock ret = ent.getValue(); + + lstJsrAll.add(new Object[]{jsr, getJsrRange(jsr, ret), ret}); + } + + // sort ranges + // FIXME: better sort order + List<Object[]> lstJsr = new ArrayList<Object[]>(); + for (Object[] arr : lstJsrAll) { + int i = 0; + for (; i < lstJsr.size(); i++) { + Object[] arrJsr = lstJsr.get(i); + + if (((HashSet<BasicBlock>)arrJsr[1]).contains(arr[0])) { + break; + } + } + + lstJsr.add(i, arr); + } + + // find the first intersection + for (int i = 0; i < lstJsr.size(); i++) { + Object[] arr = (Object[])lstJsr.get(i); + HashSet<BasicBlock> set = (HashSet<BasicBlock>)arr[1]; + + for (int j = i + 1; j < lstJsr.size(); j++) { + Object[] arr1 = (Object[])lstJsr.get(j); + HashSet<BasicBlock> set1 = (HashSet<BasicBlock>)arr1[1]; + + if (!set.contains(arr1[0]) && !set1.contains(arr[0])) { // rang 0 doesn't contain entry 1 and vice versa + HashSet<BasicBlock> setc = new HashSet<BasicBlock>(set); + setc.retainAll(set1); + + if (!setc.isEmpty()) { + splitJsrRange((BasicBlock)arr[0], (BasicBlock)arr[2], setc); + return 1; + } + } + } + } + + return 0; + } + + private HashSet<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) { + + HashSet<BasicBlock> blocks = new HashSet<BasicBlock>(); + + LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); + lstNodes.add(jsr); + + BasicBlock dom = jsr.getSuccs().get(0); + + while (!lstNodes.isEmpty()) { + + BasicBlock node = lstNodes.remove(0); + + for (int j = 0; j < 2; j++) { + List<BasicBlock> lst; + if (j == 0) { + if (node.getLastInstruction().opcode == CodeConstants.opc_ret) { + if (node.getSuccs().contains(ret)) { + continue; + } + } + lst = node.getSuccs(); + } + else { + if (node == jsr) { + continue; + } + lst = node.getSuccExceptions(); + } + + CHILD: + for (int i = lst.size() - 1; i >= 0; i--) { + + BasicBlock child = lst.get(i); + if (!blocks.contains(child)) { + + if (node != jsr) { + for (int k = 0; k < child.getPreds().size(); k++) { + if (!DeadCodeHelper.isDominator(this, child.getPreds().get(k), dom)) { + continue CHILD; + } + } + + for (int k = 0; k < child.getPredExceptions().size(); k++) { + if (!DeadCodeHelper.isDominator(this, child.getPredExceptions().get(k), dom)) { + continue CHILD; + } + } + } + + // last block is a dummy one + if (child != last) { + blocks.add(child); + } + + lstNodes.add(child); + } + } + } + } + + return blocks; + } + + private void splitJsrRange(BasicBlock jsr, BasicBlock ret, HashSet<BasicBlock> common_blocks) { + + LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); + HashMap<Integer, BasicBlock> mapNewNodes = new HashMap<Integer, BasicBlock>(); + + lstNodes.add(jsr); + mapNewNodes.put(jsr.id, jsr); + + while (!lstNodes.isEmpty()) { + + BasicBlock node = lstNodes.remove(0); + + for (int j = 0; j < 2; j++) { + List<BasicBlock> lst; + if (j == 0) { + if (node.getLastInstruction().opcode == CodeConstants.opc_ret) { + if (node.getSuccs().contains(ret)) { + continue; + } + } + lst = node.getSuccs(); + } + else { + if (node == jsr) { + continue; + } + lst = node.getSuccExceptions(); + } + + + for (int i = lst.size() - 1; i >= 0; i--) { + + BasicBlock child = (BasicBlock)lst.get(i); + Integer childid = child.id; + + if (mapNewNodes.containsKey(childid)) { + node.replaceSuccessor(child, (BasicBlock)mapNewNodes.get(childid)); + } + else if (common_blocks.contains(child)) { + + // make a copy of the current block + BasicBlock copy = (BasicBlock)child.clone(); + copy.id = ++last_id; + // copy all successors + if (copy.getLastInstruction().opcode == CodeConstants.opc_ret && + child.getSuccs().contains(ret)) { + copy.addSuccessor(ret); + child.removeSuccessor(ret); + } + else { + for (int k = 0; k < child.getSuccs().size(); k++) { + copy.addSuccessor((BasicBlock)child.getSuccs().get(k)); + } + } + for (int k = 0; k < child.getSuccExceptions().size(); k++) { + copy.addSuccessorException((BasicBlock)child.getSuccExceptions().get(k)); + } + + lstNodes.add(copy); + mapNewNodes.put(childid, copy); + + if (last.getPreds().contains(child)) { + last.addPredecessor(copy); + } + + node.replaceSuccessor(child, copy); + blocks.addWithKey(copy, copy.id); + } + else { + // stop at the first fixed node + //lstNodes.add(child); + mapNewNodes.put(childid, child); + } + } + } + } + + // note: subroutines won't be copied! + splitJsrExceptionRanges(common_blocks, mapNewNodes); + } + + private void splitJsrExceptionRanges(HashSet<BasicBlock> common_blocks, HashMap<Integer, BasicBlock> mapNewNodes) { + + for (int i = exceptions.size() - 1; i >= 0; i--) { + + ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); + List<BasicBlock> lstRange = range.getProtectedRange(); + + HashSet<BasicBlock> setBoth = new HashSet<BasicBlock>(common_blocks); + setBoth.retainAll(lstRange); + + if (setBoth.size() > 0) { + List<BasicBlock> lstNewRange; + + if (setBoth.size() == lstRange.size()) { + lstNewRange = new ArrayList<BasicBlock>(); + ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange, + (BasicBlock)mapNewNodes.get(range.getHandler().id), range.getExceptionTypes()); + exceptions.add(newRange); + } + else { + lstNewRange = lstRange; + } + + for (BasicBlock block : setBoth) { + lstNewRange.add(mapNewNodes.get(block.id)); + } + } + } + } + + private void removeJsr(StructMethod mt) { + removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); + } + + private void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { + + ListStack<VarType> stack = data.getStack(); + + InstructionSequence seq = block.getSeq(); + for (int i = 0; i < seq.length(); i++) { + Instruction instr = seq.getInstr(i); + + VarType var = null; + if (instr.opcode == CodeConstants.opc_astore || instr.opcode == CodeConstants.opc_pop) { + var = stack.getByOffset(-1); + } + + InstructionImpact.stepTypes(data, instr, pool); + + switch (instr.opcode) { + case CodeConstants.opc_jsr: + case CodeConstants.opc_ret: + seq.removeInstruction(i); + i--; + break; + case CodeConstants.opc_astore: + case CodeConstants.opc_pop: + if (var.type == CodeConstants.TYPE_ADDRESS) { + seq.removeInstruction(i); + i--; + } + } + } + + block.mark = 1; + + for (int i = 0; i < block.getSuccs().size(); i++) { + BasicBlock suc = (BasicBlock)block.getSuccs().get(i); + if (suc.mark != 1) { + removeJsrInstructions(pool, suc, data.copy()); + } + } + + for (int i = 0; i < block.getSuccExceptions().size(); i++) { + BasicBlock suc = (BasicBlock)block.getSuccExceptions().get(i); + if (suc.mark != 1) { + + DataPoint point = new DataPoint(); + point.setLocalVariables(new ArrayList<VarType>(data.getLocalVariables())); + point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); + + removeJsrInstructions(pool, suc, point); + } + } + } + + private void setFirstAndLastBlocks() { + + first = blocks.get(0); + + last = new BasicBlock(); + last.id = ++last_id; + last.setSeq(new SimpleInstructionSequence()); + + for (BasicBlock block : blocks) { + if (block.getSuccs().isEmpty()) { + last.addPredecessor(block); + } + } + } + + public List<BasicBlock> getReversePostOrder() { + + LinkedList<BasicBlock> res = new LinkedList<BasicBlock>(); + addToReversePostOrderListIterative(first, res); + + return res; + } + + private void addToReversePostOrderListIterative(BasicBlock root, List<BasicBlock> lst) { + + LinkedList<BasicBlock> stackNode = new LinkedList<BasicBlock>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + stackNode.add(root); + stackIndex.add(0); + + while (!stackNode.isEmpty()) { + + BasicBlock node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(node.getSuccs()); + lstSuccs.addAll(node.getSuccExceptions()); + + for (; index < lstSuccs.size(); index++) { + BasicBlock succ = lstSuccs.get(index); + + if (!setVisited.contains(succ)) { + stackIndex.add(index + 1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if (index == lstSuccs.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public VBStyleCollection<BasicBlock, Integer> getBlocks() { + return blocks; + } + + public void setBlocks(VBStyleCollection<BasicBlock, Integer> blocks) { + this.blocks = blocks; + } + + public BasicBlock getFirst() { + return first; + } + + public void setFirst(BasicBlock first) { + this.first = first; + } + + public List<BasicBlock> getEndBlocks() { + return last.getPreds(); + } + + public List<ExceptionRangeCFG> getExceptions() { + return exceptions; + } + + public void setExceptions(List<ExceptionRangeCFG> exceptions) { + this.exceptions = exceptions; + } + + + public BasicBlock getLast() { + return last; + } + + + public void setLast(BasicBlock last) { + this.last = last; + } + + + public HashMap<BasicBlock, BasicBlock> getSubroutines() { + return subroutines; + } + + + public void setSubroutines(HashMap<BasicBlock, BasicBlock> subroutines) { + this.subroutines = subroutines; + } + + + public HashSet<BasicBlock> getFinallyExits() { + return finallyExits; + } + + public void setFinallyExits(HashSet<BasicBlock> finallyExits) { + this.finallyExits = finallyExits; + } } diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java index 53f4a77..7d83ef6 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java @@ -1,128 +1,129 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.cfg; +import org.jetbrains.java.decompiler.main.DecompilerContext; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.jetbrains.java.decompiler.main.DecompilerContext; - public class ExceptionRangeCFG { - - private List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); // FIXME: replace with set - - private BasicBlock handler; - - private List<String> exceptionTypes; - - public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) { - this.protectedRange = protectedRange; - this.handler = handler; - - if(exceptionType != null) { - this.exceptionTypes = new ArrayList<String>(exceptionType); - } - } - - public boolean isCircular() { - return protectedRange.contains(handler); - } - - public String toString() { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - - buf.append("exceptionType:"); - for(String exception_type : exceptionTypes) { - buf.append(" "+exception_type); - } - buf.append(new_line_separator); - - buf.append("handler: "+handler.id+new_line_separator); - buf.append("range: "); - for(int i=0;i<protectedRange.size();i++) { - buf.append(protectedRange.get(i).id+" "); - } - buf.append(new_line_separator); - - return buf.toString(); - } - - public BasicBlock getHandler() { - return handler; - } - - public void setHandler(BasicBlock handler) { - this.handler = handler; - } - - public List<BasicBlock> getProtectedRange() { - return protectedRange; - } - - public void setProtectedRange(List<BasicBlock> protectedRange) { - this.protectedRange = protectedRange; - } - - public List<String> getExceptionTypes() { - return this.exceptionTypes; - } - - public void addExceptionType(String exceptionType) { - - if(this.exceptionTypes == null) { - return; - } - - if(exceptionType == null) { - this.exceptionTypes = null; - } else { - this.exceptionTypes.add(exceptionType); - } - } - - public String getUniqueExceptionsString() { - - if(exceptionTypes == null) { - return null; - } - - Set<String> setExceptionStrings = new HashSet<String>(); - - for(String exceptionType : exceptionTypes) { // normalize order - setExceptionStrings.add(exceptionType); - } - - String ret = ""; - for(String exception : setExceptionStrings) { - if(!ret.isEmpty()) { - ret += ":"; - } - ret += exception; - } - - return ret; - } - - -// public void setExceptionType(String exceptionType) { -// this.exceptionType = exceptionType; -// } - + + private List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); // FIXME: replace with set + + private BasicBlock handler; + + private List<String> exceptionTypes; + + public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) { + this.protectedRange = protectedRange; + this.handler = handler; + + if (exceptionType != null) { + this.exceptionTypes = new ArrayList<String>(exceptionType); + } + } + + public boolean isCircular() { + return protectedRange.contains(handler); + } + + public String toString() { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + buf.append("exceptionType:"); + for (String exception_type : exceptionTypes) { + buf.append(" " + exception_type); + } + buf.append(new_line_separator); + + buf.append("handler: " + handler.id + new_line_separator); + buf.append("range: "); + for (int i = 0; i < protectedRange.size(); i++) { + buf.append(protectedRange.get(i).id + " "); + } + buf.append(new_line_separator); + + return buf.toString(); + } + + public BasicBlock getHandler() { + return handler; + } + + public void setHandler(BasicBlock handler) { + this.handler = handler; + } + + public List<BasicBlock> getProtectedRange() { + return protectedRange; + } + + public void setProtectedRange(List<BasicBlock> protectedRange) { + this.protectedRange = protectedRange; + } + + public List<String> getExceptionTypes() { + return this.exceptionTypes; + } + + public void addExceptionType(String exceptionType) { + + if (this.exceptionTypes == null) { + return; + } + + if (exceptionType == null) { + this.exceptionTypes = null; + } + else { + this.exceptionTypes.add(exceptionType); + } + } + + public String getUniqueExceptionsString() { + + if (exceptionTypes == null) { + return null; + } + + Set<String> setExceptionStrings = new HashSet<String>(); + + for (String exceptionType : exceptionTypes) { // normalize order + setExceptionStrings.add(exceptionType); + } + + String ret = ""; + for (String exception : setExceptionStrings) { + if (!ret.isEmpty()) { + ret += ":"; + } + ret += exception; + } + + return ret; + } + + + // public void setExceptionType(String exceptionType) { + // this.exceptionType = exceptionType; + // } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/AALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/AALOAD.java index 6e92fe6..a460184 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/AALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/AALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/AASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/AASTORE.java index 09f5c4b..9e0805d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/AASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/AASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ACONST_NULL.java b/src/org/jetbrains/java/decompiler/code/instructions/ACONST_NULL.java index 5733d40..2f78e54 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ACONST_NULL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ACONST_NULL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/ALOAD.java index a9c9e05..604585c 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ALOAD.java @@ -1,43 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ALOAD extends Instruction { - - private static int[] opcodes = new int[] {opc_aload_0,opc_aload_1,opc_aload_2,opc_aload_3}; - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_aload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } + private static int[] opcodes = new int[]{opc_aload_0, opc_aload_1, opc_aload_2, opc_aload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_aload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } - + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/instructions/ANEWARRAY.java index aa9a6c9..09afe91 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ANEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ANEWARRAY.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ANEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_anewarray); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_anewarray); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ARETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/ARETURN.java index 915a50f..bf4bfae 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ARETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ARETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ARRAYLENGTH.java b/src/org/jetbrains/java/decompiler/code/instructions/ARRAYLENGTH.java index a54aa41..6ddee94 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ARRAYLENGTH.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ARRAYLENGTH.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/ASTORE.java index 701f082..67ef5dd 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ASTORE.java @@ -1,42 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ASTORE extends Instruction { - private static int[] opcodes = new int[] {opc_astore_0,opc_astore_1,opc_astore_2,opc_astore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_astore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_astore_0, opc_astore_1, opc_astore_2, opc_astore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_astore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ATHROW.java b/src/org/jetbrains/java/decompiler/code/instructions/ATHROW.java index 1ca07c5..66d22a0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ATHROW.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ATHROW.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/BALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/BALOAD.java index c9d9e79..4a2e5d9 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/BALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/BALOAD.java @@ -1,7 +1,22 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; public class BALOAD extends Instruction { - + } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/BASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/BASTORE.java index 478a887..70e8f0d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/BASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/BASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/BIPUSH.java b/src/org/jetbrains/java/decompiler/code/instructions/BIPUSH.java index 90874c5..836ee10 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/BIPUSH.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/BIPUSH.java @@ -1,30 +1,48 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class BIPUSH extends Instruction { - private static int[] opcodes = new int[] {opc_iconst_m1,opc_iconst_0,opc_iconst_1,opc_iconst_2,opc_iconst_3,opc_iconst_4,opc_iconst_5}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int value = getOperand(0); - if(value<-1 || value > 5) { - out.writeByte(opc_bipush); - out.writeByte(value); - } else { - out.writeByte(opcodes[value+1]); - } - } - - public int length() { - int value = getOperand(0); - if(value<-1 || value > 5) { - return 2; - } else { - return 1; - } - } + private static int[] opcodes = + new int[]{opc_iconst_m1, opc_iconst_0, opc_iconst_1, opc_iconst_2, opc_iconst_3, opc_iconst_4, opc_iconst_5}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int value = getOperand(0); + if (value < -1 || value > 5) { + out.writeByte(opc_bipush); + out.writeByte(value); + } + else { + out.writeByte(opcodes[value + 1]); + } + } + + public int length() { + int value = getOperand(0); + if (value < -1 || value > 5) { + return 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/CALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/CALOAD.java index cf3fb87..9646179 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/CALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/CALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/CASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/CASTORE.java index 2ed21e5..6222e2f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/CASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/CASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/CHECKCAST.java b/src/org/jetbrains/java/decompiler/code/instructions/CHECKCAST.java index a3bb57e..bfff1cf 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/CHECKCAST.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/CHECKCAST.java @@ -1,20 +1,34 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class CHECKCAST extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_checkcast); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_checkcast); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/D2F.java b/src/org/jetbrains/java/decompiler/code/instructions/D2F.java index fd42a2e..e40a093 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/D2F.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/D2F.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/D2I.java b/src/org/jetbrains/java/decompiler/code/instructions/D2I.java index 4fc7801..97e1675 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/D2I.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/D2I.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/D2L.java b/src/org/jetbrains/java/decompiler/code/instructions/D2L.java index cc90f1c..0d6a304 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/D2L.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/D2L.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DADD.java b/src/org/jetbrains/java/decompiler/code/instructions/DADD.java index ac84423..e65a669 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DADD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DADD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/DALOAD.java index 00a0d17..a356c5b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/DASTORE.java index e33959f..f1c6cda 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DCMPG.java b/src/org/jetbrains/java/decompiler/code/instructions/DCMPG.java index 2b853ac..8c1ffc0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DCMPG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DCMPG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DCMPL.java b/src/org/jetbrains/java/decompiler/code/instructions/DCMPL.java index 7fdba33..2b146a5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DCMPL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DCMPL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DCONST_0.java b/src/org/jetbrains/java/decompiler/code/instructions/DCONST_0.java index d0023f2..78c5dde 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DCONST_0.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DCONST_0.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DCONST_1.java b/src/org/jetbrains/java/decompiler/code/instructions/DCONST_1.java index bcd7c67..8127cfb 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DCONST_1.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DCONST_1.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DDIV.java b/src/org/jetbrains/java/decompiler/code/instructions/DDIV.java index 40d86a8..d15e7af 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DDIV.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DDIV.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DLOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/DLOAD.java index ee098d6..1d7fea4 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DLOAD.java @@ -1,42 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class DLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_dload_0,opc_dload_1,opc_dload_2,opc_dload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_dload_0, opc_dload_1, opc_dload_2, opc_dload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_dload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DMUL.java b/src/org/jetbrains/java/decompiler/code/instructions/DMUL.java index 65bedca..3fdc17a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DMUL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DMUL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DNEG.java b/src/org/jetbrains/java/decompiler/code/instructions/DNEG.java index 15bd9f3..83f82c5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DNEG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DNEG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DREM.java b/src/org/jetbrains/java/decompiler/code/instructions/DREM.java index ab57911..55453fc 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DREM.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DREM.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DRETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/DRETURN.java index 0a6e376..b758dcb 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DRETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DRETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DSTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/DSTORE.java index fe1feb8..e1e37e5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DSTORE.java @@ -1,41 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class DSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_dstore_0,opc_dstore_1,opc_dstore_2,opc_dstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } + private static int[] opcodes = new int[]{opc_dstore_0, opc_dstore_1, opc_dstore_2, opc_dstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_dstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DSUB.java b/src/org/jetbrains/java/decompiler/code/instructions/DSUB.java index d683a63..33026ca 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DSUB.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DSUB.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP.java index 50119da..07a7c4f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP2.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP2.java index 3929ee0..6451cf2 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP2.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP2.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X1.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X1.java index ad59230..deddd1b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X1.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X1.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X2.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X2.java index d27ca93..deafca6 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X2.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP2_X2.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP_X1.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP_X1.java index 9b6b271..a1268e9 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP_X1.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP_X1.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/DUP_X2.java b/src/org/jetbrains/java/decompiler/code/instructions/DUP_X2.java index 8c4b03e..bf5f71d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/DUP_X2.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/DUP_X2.java @@ -1,7 +1,22 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; public class DUP_X2 extends Instruction { - + } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/F2D.java b/src/org/jetbrains/java/decompiler/code/instructions/F2D.java index debb8de..dd5b4a6 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/F2D.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/F2D.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/F2I.java b/src/org/jetbrains/java/decompiler/code/instructions/F2I.java index bbda5c8..0811e2b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/F2I.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/F2I.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/F2L.java b/src/org/jetbrains/java/decompiler/code/instructions/F2L.java index a58a398..a470414 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/F2L.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/F2L.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FADD.java b/src/org/jetbrains/java/decompiler/code/instructions/FADD.java index ab7a357..bdd9b89 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FADD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FADD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/FALOAD.java index cc56e21..e9befaf 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/FASTORE.java index d18a2ef..6e280fc 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FCMPG.java b/src/org/jetbrains/java/decompiler/code/instructions/FCMPG.java index a86b2a9..4c3c9c5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FCMPG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FCMPG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FCMPL.java b/src/org/jetbrains/java/decompiler/code/instructions/FCMPL.java index 450546c..42dcb4b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FCMPL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FCMPL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_0.java b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_0.java index 77f4e84..957fe29 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_0.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_0.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_1.java b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_1.java index e90fd44..ab014c4 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_1.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_1.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_2.java b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_2.java index 7736565..fa6be0f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FCONST_2.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FCONST_2.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FDIV.java b/src/org/jetbrains/java/decompiler/code/instructions/FDIV.java index 0524b01..eb6dce4 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FDIV.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FDIV.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FLOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/FLOAD.java index 03ba1d2..6640d5d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FLOAD.java @@ -1,42 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class FLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_fload_0,opc_fload_1,opc_fload_2,opc_fload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_fload_0, opc_fload_1, opc_fload_2, opc_fload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_fload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FMUL.java b/src/org/jetbrains/java/decompiler/code/instructions/FMUL.java index 598145b..5a5cee3 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FMUL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FMUL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FNEG.java b/src/org/jetbrains/java/decompiler/code/instructions/FNEG.java index b1d96ea..ab68c82 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FNEG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FNEG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FREM.java b/src/org/jetbrains/java/decompiler/code/instructions/FREM.java index b8dc0fe..91900a8 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FREM.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FREM.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FRETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/FRETURN.java index 3781401..a310022 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FRETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FRETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FSTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/FSTORE.java index 203588b..15b5d42 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FSTORE.java @@ -1,41 +1,60 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class FSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_fstore_0,opc_fstore_1,opc_fstore_2,opc_fstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } + private static int[] opcodes = new int[]{opc_fstore_0, opc_fstore_1, opc_fstore_2, opc_fstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_fstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/FSUB.java b/src/org/jetbrains/java/decompiler/code/instructions/FSUB.java index 841ee0d..09a07b8 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/FSUB.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/FSUB.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/GETFIELD.java b/src/org/jetbrains/java/decompiler/code/instructions/GETFIELD.java index 8500f46..93b46ae 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/GETFIELD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/GETFIELD.java @@ -1,18 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class GETFIELD extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_getfield); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/GETSTATIC.java b/src/org/jetbrains/java/decompiler/code/instructions/GETSTATIC.java index 761624f..86f1b75 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/GETSTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/GETSTATIC.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class GETSTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_getstatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/GOTO.java b/src/org/jetbrains/java/decompiler/code/instructions/GOTO.java index 91f5081..515275f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/GOTO.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/GOTO.java @@ -1,30 +1,46 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class GOTO extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - out.writeByte(opc_goto_w); - out.writeInt(operand); - } else { - out.writeByte(opc_goto); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - return 5; - } else { - return 3; - } - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + out.writeByte(opc_goto_w); + out.writeInt(operand); + } + else { + out.writeByte(opc_goto); + out.writeShort(operand); + } + } + + public int length() { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + return 5; + } + else { + return 3; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/GOTO_W.java b/src/org/jetbrains/java/decompiler/code/instructions/GOTO_W.java index 830b766..ec427f8 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/GOTO_W.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/GOTO_W.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class GOTO_W extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_goto_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_goto_w); + out.writeInt(getOperand(0)); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2B.java b/src/org/jetbrains/java/decompiler/code/instructions/I2B.java index f5a3fbd..b6fc681 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2B.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2B.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2C.java b/src/org/jetbrains/java/decompiler/code/instructions/I2C.java index a180a64..a1dca6b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2C.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2C.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2D.java b/src/org/jetbrains/java/decompiler/code/instructions/I2D.java index b0bad9a..6478d78 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2D.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2D.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2F.java b/src/org/jetbrains/java/decompiler/code/instructions/I2F.java index ca1a0fb..1247eff 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2F.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2F.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2L.java b/src/org/jetbrains/java/decompiler/code/instructions/I2L.java index 24bca4e..6e27d5a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2L.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2L.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/I2S.java b/src/org/jetbrains/java/decompiler/code/instructions/I2S.java index 370ae2f..e66b75b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/I2S.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/I2S.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IADD.java b/src/org/jetbrains/java/decompiler/code/instructions/IADD.java index f427c40..5922136 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IADD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IADD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/IALOAD.java index d70fae6..645d6df 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IAND.java b/src/org/jetbrains/java/decompiler/code/instructions/IAND.java index 00ae7a6..207ed34 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IAND.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IAND.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/IASTORE.java index 78a1d28..56d3d24 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IDIV.java b/src/org/jetbrains/java/decompiler/code/instructions/IDIV.java index bec8cc9..60b4690 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IDIV.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IDIV.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFEQ.java b/src/org/jetbrains/java/decompiler/code/instructions/IFEQ.java index 47711d8..28cfd81 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFEQ.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFEQ.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFEQ extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifeq); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifeq); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFGE.java b/src/org/jetbrains/java/decompiler/code/instructions/IFGE.java index 9cfaba4..d805789 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFGE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFGE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFGE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifge); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifge); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFGT.java b/src/org/jetbrains/java/decompiler/code/instructions/IFGT.java index 99b171e..13f650a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFGT.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFGT.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFGT extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifgt); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifgt); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFLE.java b/src/org/jetbrains/java/decompiler/code/instructions/IFLE.java index b21b2cf..e03d915 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFLE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFLE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFLE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifle); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifle); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFLT.java b/src/org/jetbrains/java/decompiler/code/instructions/IFLT.java index 66ba02b..0299a91 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFLT.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFLT.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFLT extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_iflt); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_iflt); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFNE.java b/src/org/jetbrains/java/decompiler/code/instructions/IFNE.java index 983fea2..3402362 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFNE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFNE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFNE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifne); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifne); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFNONNULL.java b/src/org/jetbrains/java/decompiler/code/instructions/IFNONNULL.java index 2555a80..64a12d0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFNONNULL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFNONNULL.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFNONNULL extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifnonnull); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifnonnull); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IFNULL.java b/src/org/jetbrains/java/decompiler/code/instructions/IFNULL.java index b695787..71265b6 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IFNULL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IFNULL.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IFNULL extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ifnull); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ifnull); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPEQ.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPEQ.java index 30e530b..5b23cbf 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPEQ.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPEQ.java @@ -1,20 +1,34 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ACMPEQ extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_acmpeq); - out.writeShort(getOperand(0)); - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_acmpeq); + out.writeShort(getOperand(0)); + } - public int length() { - return 3; - } - + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPNE.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPNE.java index ccb513f..e41ea9f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPNE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ACMPNE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ACMPNE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_acmpne); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_acmpne); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPEQ.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPEQ.java index 1343aca..dad633d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPEQ.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPEQ.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPEQ extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmpeq); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmpeq); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGE.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGE.java index beeabec..54f6295 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPGE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmpge); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmpge); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGT.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGT.java index e41d261..e2e1972 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGT.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPGT.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPGT extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmpgt); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmpgt); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLE.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLE.java index c9288da..4f8c76c 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPLE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmple); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmple); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLT.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLT.java index 107519e..d6a9a48 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLT.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPLT.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPLT extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmplt); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmplt); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPNE.java b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPNE.java index 703c10c..6484344 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPNE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IF_ICMPNE.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class IF_ICMPNE extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_if_icmpne); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_if_icmpne); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IINC.java b/src/org/jetbrains/java/decompiler/code/instructions/IINC.java index 763a192..fc2506c 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IINC.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IINC.java @@ -1,28 +1,43 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class IINC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iinc); - if(wide) { - out.writeShort(getOperand(0)); - out.writeShort(getOperand(1)); - } else { - out.writeByte(getOperand(0)); - out.writeByte(getOperand(1)); - } - } - - public int length() { - return wide?6:3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_iinc); + if (wide) { + out.writeShort(getOperand(0)); + out.writeShort(getOperand(1)); + } + else { + out.writeByte(getOperand(0)); + out.writeByte(getOperand(1)); + } + } + + public int length() { + return wide ? 6 : 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ILOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/ILOAD.java index 4b9bd59..3be609d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ILOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ILOAD.java @@ -1,38 +1,55 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ILOAD extends Instruction { - private static int[] opcodes = new int[] {opc_iload_0,opc_iload_1,opc_iload_2,opc_iload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_iload_0, opc_iload_1, opc_iload_2, opc_iload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_iload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IMUL.java b/src/org/jetbrains/java/decompiler/code/instructions/IMUL.java index 64dd4cc..8cec91b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IMUL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IMUL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INEG.java b/src/org/jetbrains/java/decompiler/code/instructions/INEG.java index cf341e7..903a22d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INEG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INEG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INSTANCEOF.java b/src/org/jetbrains/java/decompiler/code/instructions/INSTANCEOF.java index a4214ec..e5acd87 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INSTANCEOF.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INSTANCEOF.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INSTANCEOF extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_instanceof); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_instanceof); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INVOKEINTERFACE.java b/src/org/jetbrains/java/decompiler/code/instructions/INVOKEINTERFACE.java index bd1ce37..ecf15b1 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INVOKEINTERFACE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INVOKEINTERFACE.java @@ -1,21 +1,35 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKEINTERFACE extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokeinterface); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - out.writeByte(0); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokeinterface); + out.writeShort(getOperand(0)); + out.writeByte(getOperand(1)); + out.writeByte(0); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INVOKESPECIAL.java b/src/org/jetbrains/java/decompiler/code/instructions/INVOKESPECIAL.java index d14a8ee..0f3ad00 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INVOKESPECIAL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INVOKESPECIAL.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKESPECIAL extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokespecial); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokespecial); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INVOKESTATIC.java b/src/org/jetbrains/java/decompiler/code/instructions/INVOKESTATIC.java index 8ef9f26..1d21cd2 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INVOKESTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INVOKESTATIC.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKESTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokestatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokestatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/INVOKEVIRTUAL.java b/src/org/jetbrains/java/decompiler/code/instructions/INVOKEVIRTUAL.java index 687c6e4..2ce543d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/INVOKEVIRTUAL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/INVOKEVIRTUAL.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKEVIRTUAL extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokevirtual); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokevirtual); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IOR.java b/src/org/jetbrains/java/decompiler/code/instructions/IOR.java index b3e6bae..b8bc4b1 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IOR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IOR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IREM.java b/src/org/jetbrains/java/decompiler/code/instructions/IREM.java index db1fbb8..4d299b7 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IREM.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IREM.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IRETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/IRETURN.java index 2a2e48c..7f95241 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IRETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IRETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ISHL.java b/src/org/jetbrains/java/decompiler/code/instructions/ISHL.java index 5e0b1e0..f440380 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ISHL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ISHL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ISHR.java b/src/org/jetbrains/java/decompiler/code/instructions/ISHR.java index 9a8c182..6fa2b71 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ISHR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ISHR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ISTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/ISTORE.java index 7bc1647..5d6024a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ISTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ISTORE.java @@ -1,38 +1,55 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ISTORE extends Instruction { - private static int[] opcodes = new int[] {opc_istore_0,opc_istore_1,opc_istore_2,opc_istore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_istore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_istore_0, opc_istore_1, opc_istore_2, opc_istore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_istore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/ISUB.java b/src/org/jetbrains/java/decompiler/code/instructions/ISUB.java index 3007c8a..7e3caf0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/ISUB.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/ISUB.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IUSHR.java b/src/org/jetbrains/java/decompiler/code/instructions/IUSHR.java index 3171c1a..b548209 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IUSHR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IUSHR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/IXOR.java b/src/org/jetbrains/java/decompiler/code/instructions/IXOR.java index f6568d2..261146b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/IXOR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/IXOR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/JSR.java b/src/org/jetbrains/java/decompiler/code/instructions/JSR.java index bb23f66..1c80177 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/JSR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/JSR.java @@ -1,30 +1,46 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class JSR extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - out.writeByte(opc_jsr_w); - out.writeInt(operand); - } else { - out.writeByte(opc_jsr); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - return 5; - } else { - return 3; - } - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + out.writeByte(opc_jsr_w); + out.writeInt(operand); + } + else { + out.writeByte(opc_jsr); + out.writeShort(operand); + } + } + + public int length() { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + return 5; + } + else { + return 3; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/JSR_W.java b/src/org/jetbrains/java/decompiler/code/instructions/JSR_W.java index 47af3e6..67cf91d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/JSR_W.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/JSR_W.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class JSR_W extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_jsr_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_jsr_w); + out.writeInt(getOperand(0)); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/L2D.java b/src/org/jetbrains/java/decompiler/code/instructions/L2D.java index 59178d2..5569d3f 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/L2D.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/L2D.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/L2F.java b/src/org/jetbrains/java/decompiler/code/instructions/L2F.java index 37932b6..fcab0ba 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/L2F.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/L2F.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/L2I.java b/src/org/jetbrains/java/decompiler/code/instructions/L2I.java index c197282..558e5f5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/L2I.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/L2I.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LADD.java b/src/org/jetbrains/java/decompiler/code/instructions/LADD.java index 8c5625c..358a788 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LADD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LADD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/LALOAD.java index 3590f94..9e6b0b0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LAND.java b/src/org/jetbrains/java/decompiler/code/instructions/LAND.java index 46a396c..37da9bd 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LAND.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LAND.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/LASTORE.java index 107af51..e89aeff 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LCMP.java b/src/org/jetbrains/java/decompiler/code/instructions/LCMP.java index 8a9dac5..399d231 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LCMP.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LCMP.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LCONST_0.java b/src/org/jetbrains/java/decompiler/code/instructions/LCONST_0.java index 5617fbe..04bc841 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LCONST_0.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LCONST_0.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LCONST_1.java b/src/org/jetbrains/java/decompiler/code/instructions/LCONST_1.java index 8244407..b58fadf 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LCONST_1.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LCONST_1.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LDC.java b/src/org/jetbrains/java/decompiler/code/instructions/LDC.java index 088e5a5..aa1e9ac 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LDC.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LDC.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc); + out.writeByte(getOperand(0)); + } + + public int length() { + return 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LDC2_W.java b/src/org/jetbrains/java/decompiler/code/instructions/LDC2_W.java index 9b67a7e..67ba631 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LDC2_W.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LDC2_W.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC2_W extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc2_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc2_w); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LDC_W.java b/src/org/jetbrains/java/decompiler/code/instructions/LDC_W.java index 290462e..1e5030a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LDC_W.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LDC_W.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC_W extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc_w); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LDIV.java b/src/org/jetbrains/java/decompiler/code/instructions/LDIV.java index 36b09ca..b90015b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LDIV.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LDIV.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LLOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/LLOAD.java index 0cf9695..44afa3b 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LLOAD.java @@ -1,38 +1,55 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_lload_0,opc_lload_1,opc_lload_2,opc_lload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } + private static int[] opcodes = new int[]{opc_lload_0, opc_lload_1, opc_lload_2, opc_lload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_lload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LMUL.java b/src/org/jetbrains/java/decompiler/code/instructions/LMUL.java index 075c50b..475030d 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LMUL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LMUL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LNEG.java b/src/org/jetbrains/java/decompiler/code/instructions/LNEG.java index b2c9f10..5cacf17 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LNEG.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LNEG.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LOOKUPSWITCH.java b/src/org/jetbrains/java/decompiler/code/instructions/LOOKUPSWITCH.java index dbe5f03..04ec7ba 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LOOKUPSWITCH.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LOOKUPSWITCH.java @@ -1,28 +1,42 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.SwitchInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.SwitchInstruction; - public class LOOKUPSWITCH extends SwitchInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_lookupswitch); - - int padding = 3 - (offset%4); - for(int i=0;i<padding;i++){ - out.writeByte(0); - } - - for(int i=0;i<operandsCount();i++) { - out.writeInt(getOperand(i)); - } - } - - public int length() { - return 1+operandsCount()*4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + + out.writeByte(opc_lookupswitch); + + int padding = 3 - (offset % 4); + for (int i = 0; i < padding; i++) { + out.writeByte(0); + } + + for (int i = 0; i < operandsCount(); i++) { + out.writeInt(getOperand(i)); + } + } + + public int length() { + return 1 + operandsCount() * 4; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LOR.java b/src/org/jetbrains/java/decompiler/code/instructions/LOR.java index 55921d4..0c65e5c 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LOR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LOR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LREM.java b/src/org/jetbrains/java/decompiler/code/instructions/LREM.java index cd5073b..b75ebc5 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LREM.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LREM.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LRETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/LRETURN.java index e7dea00..b2f03de 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LRETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LRETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LSHL.java b/src/org/jetbrains/java/decompiler/code/instructions/LSHL.java index 206f91a..ae5e9f2 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LSHL.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LSHL.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LSHR.java b/src/org/jetbrains/java/decompiler/code/instructions/LSHR.java index b4276ad..7402178 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LSHR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LSHR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LSTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/LSTORE.java index 1f5dfea..230c3e4 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LSTORE.java @@ -1,38 +1,55 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_lstore_0,opc_lstore_1,opc_lstore_2,opc_lstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_lstore_0, opc_lstore_1, opc_lstore_2, opc_lstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_lstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LSUB.java b/src/org/jetbrains/java/decompiler/code/instructions/LSUB.java index 14b019f..cce794a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LSUB.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LSUB.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LUSHR.java b/src/org/jetbrains/java/decompiler/code/instructions/LUSHR.java index 63d5ca4..ecdc444 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LUSHR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LUSHR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/LXOR.java b/src/org/jetbrains/java/decompiler/code/instructions/LXOR.java index 9812a1a..d9f2644 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/LXOR.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/LXOR.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/MONITORENTER.java b/src/org/jetbrains/java/decompiler/code/instructions/MONITORENTER.java index d2ed979..c92bbf0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/MONITORENTER.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/MONITORENTER.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/MONITOREXIT.java b/src/org/jetbrains/java/decompiler/code/instructions/MONITOREXIT.java index f1324be..1a645b9 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/MONITOREXIT.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/MONITOREXIT.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/MULTIANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/instructions/MULTIANEWARRAY.java index 313b708..9a1b16a 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/MULTIANEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/MULTIANEWARRAY.java @@ -1,20 +1,34 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class MULTIANEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_multianewarray); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - } - - public int length() { - return 4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_multianewarray); + out.writeShort(getOperand(0)); + out.writeByte(getOperand(1)); + } + + public int length() { + return 4; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/NEW.java b/src/org/jetbrains/java/decompiler/code/instructions/NEW.java index 8c5ab34..7ffbdd3 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/NEW.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/NEW.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class NEW extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_new); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_new); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/NEWARRAY.java b/src/org/jetbrains/java/decompiler/code/instructions/NEWARRAY.java index eb25456..ec4ada8 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/NEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/NEWARRAY.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class NEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_newarray); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_newarray); + out.writeByte(getOperand(0)); + } + + public int length() { + return 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/NOP.java b/src/org/jetbrains/java/decompiler/code/instructions/NOP.java index ef11da8..4c47938 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/NOP.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/NOP.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/POP.java b/src/org/jetbrains/java/decompiler/code/instructions/POP.java index 8848163..99fa873 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/POP.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/POP.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/POP2.java b/src/org/jetbrains/java/decompiler/code/instructions/POP2.java index f07d5d5..1ff12f3 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/POP2.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/POP2.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/PUTFIELD.java b/src/org/jetbrains/java/decompiler/code/instructions/PUTFIELD.java index b37efb0..1ab8773 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/PUTFIELD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/PUTFIELD.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class PUTFIELD extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_putfield); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/PUTSTATIC.java b/src/org/jetbrains/java/decompiler/code/instructions/PUTSTATIC.java index 260e57c..92e2271 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/PUTSTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/PUTSTATIC.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class PUTSTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_putstatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/RET.java b/src/org/jetbrains/java/decompiler/code/instructions/RET.java index 490ad8c..c317be0 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/RET.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/RET.java @@ -1,26 +1,41 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class RET extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_ret); - if(wide) { - out.writeShort(getOperand(0)); - } else { - out.writeByte(getOperand(0)); - } - } - - public int length() { - return wide?4:2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_ret); + if (wide) { + out.writeShort(getOperand(0)); + } + else { + out.writeByte(getOperand(0)); + } + } + + public int length() { + return wide ? 4 : 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/RETURN.java b/src/org/jetbrains/java/decompiler/code/instructions/RETURN.java index cbdcbfc..83bf8c7 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/RETURN.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/RETURN.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/SALOAD.java b/src/org/jetbrains/java/decompiler/code/instructions/SALOAD.java index c9da26e..deb82e3 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/SALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/SALOAD.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/SASTORE.java b/src/org/jetbrains/java/decompiler/code/instructions/SASTORE.java index 4a90aac..e1ff283 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/SASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/SASTORE.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/SIPUSH.java b/src/org/jetbrains/java/decompiler/code/instructions/SIPUSH.java index a5f7025..7c5c55e 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/SIPUSH.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/SIPUSH.java @@ -1,19 +1,33 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class SIPUSH extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_sipush); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_sipush); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/SWAP.java b/src/org/jetbrains/java/decompiler/code/instructions/SWAP.java index 9fd99b3..64d67f4 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/SWAP.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/SWAP.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/instructions/TABLESWITCH.java b/src/org/jetbrains/java/decompiler/code/instructions/TABLESWITCH.java index 4a4be0a..ac5e6bd 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/TABLESWITCH.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/TABLESWITCH.java @@ -1,29 +1,42 @@ +/* + * 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.code.instructions; +import org.jetbrains.java.decompiler.code.SwitchInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.SwitchInstruction; - public class TABLESWITCH extends SwitchInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_tableswitch); - - int padding = 3 - (offset%4); - for(int i=0;i<padding;i++){ - out.writeByte(0); - } - - for(int i=0;i<operandsCount();i++) { - out.writeInt(getOperand(i)); - } - - } - - public int length() { - return 1+operandsCount()*4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + + out.writeByte(opc_tableswitch); + + int padding = 3 - (offset % 4); + for (int i = 0; i < padding; i++) { + out.writeByte(0); + } + + for (int i = 0; i < operandsCount(); i++) { + out.writeInt(getOperand(i)); + } + } + + public int length() { + return 1 + operandsCount() * 4; + } } diff --git a/src/org/jetbrains/java/decompiler/code/instructions/XXXUNUSEDXXX.java b/src/org/jetbrains/java/decompiler/code/instructions/XXXUNUSEDXXX.java index 2907258..739aea9 100644 --- a/src/org/jetbrains/java/decompiler/code/instructions/XXXUNUSEDXXX.java +++ b/src/org/jetbrains/java/decompiler/code/instructions/XXXUNUSEDXXX.java @@ -1,3 +1,18 @@ +/* + * 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.code.instructions; import org.jetbrains.java.decompiler.code.Instruction; diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java index b155f05..a3da8a6 100644 --- a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java +++ b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.interpreter; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -25,444 +26,502 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.ListStack; public class InstructionImpact { - - // {read, write} - private static final int[][][] stack_impact = { - - {null,null}, // public final static int opc_nop = 0; - null, // public final static int opc_aconst_null = 1; - null, // public final static int opc_iconst_m1 = 2; - null, // public final static int opc_iconst_0 = 3; - null, // public final static int opc_iconst_1 = 4; - null, // public final static int opc_iconst_2 = 5; - null, // public final static int opc_iconst_3 = 6; - null, // public final static int opc_iconst_4 = 7; - null, // public final static int opc_iconst_5 = 8; - {null,{CodeConstants.TYPE_LONG}}, // public final static int opc_lconst_0 = 9; - {null,{CodeConstants.TYPE_LONG}}, // public final static int opc_lconst_1 = 10; - {null,{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_0 = 11; - {null,{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_1 = 12; - {null,{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_2 = 13; - {null,{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dconst_0 = 14; - {null,{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dconst_1 = 15; - {null,{CodeConstants.TYPE_INT}}, // public final static int opc_bipush = 16; - {null,{CodeConstants.TYPE_INT}}, // public final static int opc_sipush = 17; - null, // public final static int opc_ldc = 18; - null, // public final static int opc_ldc_w = 19; - null, // public final static int opc_ldc2_w = 20; - {null,{CodeConstants.TYPE_INT}}, // public final static int opc_iload = 21; - {null,{CodeConstants.TYPE_LONG}}, // public final static int opc_lload = 22; - {null,{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fload = 23; - {null,{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dload = 24; - null, // public final static int opc_aload = 25; - null, // public final static int opc_iload_0 = 26; - null, // public final static int opc_iload_1 = 27; - null, // public final static int opc_iload_2 = 28; - null, // public final static int opc_iload_3 = 29; - null, // public final static int opc_lload_0 = 30; - null, // public final static int opc_lload_1 = 31; - null, // public final static int opc_lload_2 = 32; - null, // public final static int opc_lload_3 = 33; - null, // public final static int opc_fload_0 = 34; - null, // public final static int opc_fload_1 = 35; - null, // public final static int opc_fload_2 = 36; - null, // public final static int opc_fload_3 = 37; - null, // public final static int opc_dload_0 = 38; - null, // public final static int opc_dload_1 = 39; - null, // public final static int opc_dload_2 = 40; - null, // public final static int opc_dload_3 = 41; - null, // public final static int opc_aload_0 = 42; - null, // public final static int opc_aload_1 = 43; - null, // public final static int opc_aload_2 = 44; - null, // public final static int opc_aload_3 = 45; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_iaload = 46; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_LONG}}, // public final static int opc_laload = 47; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_faload = 48; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_daload = 49; - null, // public final static int opc_aaload = 50; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_baload = 51; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_caload = 52; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_saload = 53; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_istore = 54; - {{CodeConstants.TYPE_LONG},null}, // public final static int opc_lstore = 55; - {{CodeConstants.TYPE_FLOAT},null}, // public final static int opc_fstore = 56; - {{CodeConstants.TYPE_DOUBLE},null}, // public final static int opc_dstore = 57; - null, // public final static int opc_astore = 58; - null, // public final static int opc_istore_0 = 59; - null, // public final static int opc_istore_1 = 60; - null, // public final static int opc_istore_2 = 61; - null, // public final static int opc_istore_3 = 62; - null, // public final static int opc_lstore_0 = 63; - null, // public final static int opc_lstore_1 = 64; - null, // public final static int opc_lstore_2 = 65; - null, // public final static int opc_lstore_3 = 66; - null, // public final static int opc_fstore_0 = 67; - null, // public final static int opc_fstore_1 = 68; - null, // public final static int opc_fstore_2 = 69; - null, // public final static int opc_fstore_3 = 70; - null, // public final static int opc_dstore_0 = 71; - null, // public final static int opc_dstore_1 = 72; - null, // public final static int opc_dstore_2 = 73; - null, // public final static int opc_dstore_3 = 74; - null, // public final static int opc_astore_0 = 75; - null, // public final static int opc_astore_1 = 76; - null, // public final static int opc_astore_2 = 77; - null, // public final static int opc_astore_3 = 78; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_INT},null}, // public final static int opc_iastore = 79; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_LONG},null}, // public final static int opc_lastore = 80; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_FLOAT},null}, // public final static int opc_fastore = 81; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_DOUBLE},null}, // public final static int opc_dastore = 82; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_OBJECT},null}, // public final static int opc_aastore = 83; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_INT},null}, // public final static int opc_bastore = 84; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_INT},null}, // public final static int opc_castore = 85; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT,CodeConstants.TYPE_INT},null}, // public final static int opc_sastore = 86; - {{CodeConstants.TYPE_ANY},null}, // public final static int opc_pop = 87; - {{CodeConstants.TYPE_ANY, CodeConstants.TYPE_ANY},null}, // public final static int opc_pop2 = 88; - null, // public final static int opc_dup = 89; - null, // public final static int opc_dup_x1 = 90; - null, // public final static int opc_dup_x2 = 91; - null, // public final static int opc_dup2 = 92; - null, // public final static int opc_dup2_x1 = 93; - null, // public final static int opc_dup2_x2 = 94; - null, // public final static int opc_swap = 95; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_iadd = 96; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_ladd = 97; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fadd = 98; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dadd = 99; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_isub = 100; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lsub = 101; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fsub = 102; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dsub = 103; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_imul = 104; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lmul = 105; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fmul = 106; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dmul = 107; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_idiv = 108; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_ldiv = 109; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fdiv = 110; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_ddiv = 111; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_irem = 112; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lrem = 113; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_frem = 114; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_drem = 115; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_ineg = 116; - {{CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lneg = 117; - {{CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_fneg = 118; - {{CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dneg = 119; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_ishl = 120; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT},{CodeConstants.TYPE_LONG}}, // public final static int opc_lshl = 121; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_ishr = 122; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT},{CodeConstants.TYPE_LONG}}, // public final static int opc_lshr = 123; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_iushr = 124; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT},{CodeConstants.TYPE_LONG}}, // public final static int opc_lushr = 125; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_iand = 126; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_land = 127; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_ior = 128; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lor = 129; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_ixor = 130; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_LONG}}, // public final static int opc_lxor = 131; - {null,null}, // public final static int opc_iinc = 132; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_LONG}}, // public final static int opc_i2l = 133; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_i2f = 134; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_i2d = 135; - {{CodeConstants.TYPE_LONG},{CodeConstants.TYPE_INT}}, // public final static int opc_l2i = 136; - {{CodeConstants.TYPE_LONG},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_l2f = 137; - {{CodeConstants.TYPE_LONG},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_l2d = 138; - {{CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_INT}}, // public final static int opc_f2i = 139; - {{CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_LONG}}, // public final static int opc_f2l = 140; - {{CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_DOUBLE}}, // public final static int opc_f2d = 141; - {{CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_INT}}, // public final static int opc_d2i = 142; - {{CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_LONG}}, // public final static int opc_d2l = 143; - {{CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_FLOAT}}, // public final static int opc_d2f = 144; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_i2b = 145; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_i2c = 146; - {{CodeConstants.TYPE_INT},{CodeConstants.TYPE_INT}}, // public final static int opc_i2s = 147; - {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG},{CodeConstants.TYPE_INT}}, // public final static int opc_lcmp = 148; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_INT}}, // public final static int opc_fcmpl = 149; - {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT},{CodeConstants.TYPE_INT}}, // public final static int opc_fcmpg = 150; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_INT}}, // public final static int opc_dcmpl = 151; - {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE},{CodeConstants.TYPE_INT}}, // public final static int opc_dcmpg = 152; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ifeq = 153; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ifne = 154; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_iflt = 155; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ifge = 156; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ifgt = 157; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ifle = 158; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmpeq = 159; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmpne = 160; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmplt = 161; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmpge = 162; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmpgt = 163; - {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT},null}, // public final static int opc_if_icmple = 164; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT},null}, // public final static int opc_if_acmpeq = 165; - {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT},null}, // public final static int opc_if_acmpne = 166; - {null,null}, // public final static int opc_goto = 167; - {null,{CodeConstants.TYPE_ADDRESS}}, // public final static int opc_jsr = 168; - {null,null}, // public final static int opc_ret = 169; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_tableswitch = 170; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_lookupswitch = 171; - {{CodeConstants.TYPE_INT},null}, // public final static int opc_ireturn = 172; - {{CodeConstants.TYPE_LONG},null}, // public final static int opc_lreturn = 173; - {{CodeConstants.TYPE_FLOAT},null}, // public final static int opc_freturn = 174; - {{CodeConstants.TYPE_DOUBLE},null}, // public final static int opc_dreturn = 175; - {{CodeConstants.TYPE_OBJECT},null}, // public final static int opc_areturn = 176; - {null,null}, // public final static int opc_return = 177; - null, // public final static int opc_getstatic = 178; - null, // public final static int opc_putstatic = 179; - null, // public final static int opc_getfield = 180; - null, // public final static int opc_putfield = 181; - null, // public final static int opc_invokevirtual = 182; - null, // public final static int opc_invokespecial = 183; - null, // public final static int opc_invokestatic = 184; - null, // public final static int opc_invokeinterface = 185; - null, // public final static int opc_xxxunusedxxx = 186; - null, // public final static int opc_new = 187; - null, // public final static int opc_newarray = 188; - null, // public final static int opc_anewarray = 189; - {{CodeConstants.TYPE_OBJECT},{CodeConstants.TYPE_INT}}, // public final static int opc_arraylength = 190; - null, // public final static int opc_athrow = 191; - null, // public final static int opc_checkcast = 192; - null, // public final static int opc_instanceof = 193; - {{CodeConstants.TYPE_OBJECT},null}, // public final static int opc_monitorenter = 194; - {{CodeConstants.TYPE_OBJECT},null}, // public final static int opc_monitorexit = 195; - null, // public final static int opc_wide = 196; - null, // public final static int opc_multianewarray = 197; - {{CodeConstants.TYPE_OBJECT},null}, // public final static int opc_ifnull = 198; - {{CodeConstants.TYPE_OBJECT},null}, // public final static int opc_ifnonnull = 199; - {null,null}, // public final static int opc_goto_w = 200; - {null,{CodeConstants.TYPE_ADDRESS}}, // public final static int opc_jsr_w = 201; - }; - 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 - }; - - - // Sonderbehandlung -// null, // public final static int opc_aconst_null = 1; -// null, // public final static int opc_ldc = 18; -// null, // public final static int opc_ldc_w = 19; -// null, // public final static int opc_ldc2_w = 20; -// null, // public final static int opc_aload = 25; -// null, // public final static int opc_aaload = 50; -// null, // public final static int opc_astore = 58; -// null, // public final static int opc_dup = 89; -// null, // public final static int opc_dup_x1 = 90; -// null, // public final static int opc_dup_x2 = 91; -// null, // public final static int opc_dup2 = 92; -// null, // public final static int opc_dup2_x1 = 93; -// null, // public final static int opc_dup2_x2 = 94; -// null, // public final static int opc_swap = 95; -// null, // public final static int opc_getstatic = 178; -// null, // public final static int opc_putstatic = 179; -// null, // public final static int opc_getfield = 180; -// null, // public final static int opc_putfield = 181; -// null, // public final static int opc_invokevirtual = 182; -// null, // public final static int opc_invokespecial = 183; -// null, // public final static int opc_invokestatic = 184; -// null, // public final static int opc_invokeinterface = 185; -// null, // public final static int opc_new = 187; -// null, // public final static int opc_newarray = 188; -// null, // public final static int opc_anewarray = 189; -// null, // public final static int opc_athrow = 191; -// null, // public final static int opc_checkcast = 192; -// null, // public final static int opc_instanceof = 193; -// null, // public final static int opc_multianewarray = 197; - - - public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) { - - ListStack<VarType> stack = data.getStack(); - int[][] arr = stack_impact[instr.opcode]; - - if(arr != null) { - // simple types only - - int[] read = arr[0]; - int[] write = arr[1]; - - if(read!=null) { - int depth = 0; - for(int i=0;i<read.length;i++) { - int type = read[i]; - depth++; - if(type == CodeConstants.TYPE_LONG || - type == CodeConstants.TYPE_DOUBLE) { - depth++; - } - } - - stack.removeMultiple(depth); - } - - if(write!=null) { - for(int i=0;i<write.length;i++) { - int type = write[i]; - stack.push(new VarType(type)); - if(type == CodeConstants.TYPE_LONG || - type == CodeConstants.TYPE_DOUBLE) { - stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - } - } - } - - } else { - // Sonderbehandlung - processSpecialInstructions(data, instr, pool); - } - - } - - private static void processSpecialInstructions(DataPoint data, Instruction instr, ConstantPool pool) { - - VarType var1; - PrimitiveConstant cn; - LinkConstant ck; - - ListStack<VarType> stack = data.getStack(); - - switch(instr.opcode) { - case CodeConstants.opc_aconst_null: - stack.push(new VarType(CodeConstants.TYPE_NULL,0,null)); - break; - case CodeConstants.opc_ldc: - case CodeConstants.opc_ldc_w: - case CodeConstants.opc_ldc2_w: - cn = pool.getPrimitiveConstant(instr.getOperand(0)); - switch(cn.type) { - case CodeConstants.CONSTANT_Integer: - stack.push(new VarType(CodeConstants.TYPE_INT)); - break; - case CodeConstants.CONSTANT_Float: - stack.push(new VarType(CodeConstants.TYPE_FLOAT)); - break; - case CodeConstants.CONSTANT_Long: - stack.push(new VarType(CodeConstants.TYPE_LONG)); - stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - break; - case CodeConstants.CONSTANT_Double: - stack.push(new VarType(CodeConstants.TYPE_DOUBLE)); - stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - break; - case CodeConstants.CONSTANT_String: - stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String")); - break; - case CodeConstants.CONSTANT_Class: - stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class")); - break; - } - break; - case CodeConstants.opc_aload: - var1 = data.getVariable(instr.getOperand(0)); - if(var1!=null) { - stack.push(var1); - } else { - stack.push(new VarType(CodeConstants.TYPE_OBJECT,0,null)); - } - break; - case CodeConstants.opc_aaload: - var1 = stack.pop(2); - stack.push(new VarType(var1.type, var1.arraydim-1, var1.value)); - break; - case CodeConstants.opc_astore: - data.setVariable(instr.getOperand(0), (VarType)stack.pop()); - break; - case CodeConstants.opc_dup: - case CodeConstants.opc_dup_x1: - case CodeConstants.opc_dup_x2: - int depth1 = 88 - instr.opcode; - stack.insertByOffset(depth1, stack.getByOffset(-1).copy()); - break; - case CodeConstants.opc_dup2: - case CodeConstants.opc_dup2_x1: - case CodeConstants.opc_dup2_x2: - int depth2 = 90 - instr.opcode; - stack.insertByOffset(depth2, stack.getByOffset(-2).copy()); - stack.insertByOffset(depth2, stack.getByOffset(-1).copy()); - break; - case CodeConstants.opc_swap: - var1 = stack.pop(); - stack.insertByOffset(-1, var1); - break; - case CodeConstants.opc_getfield: - stack.pop(); - case CodeConstants.opc_getstatic: - ck = pool.getLinkConstant(instr.getOperand(0)); - var1 = new VarType(ck.descriptor); - stack.push(var1); - if(var1.stack_size==2) { - stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - } - break; - case CodeConstants.opc_putfield: - stack.pop(); - case CodeConstants.opc_putstatic: - ck = pool.getLinkConstant(instr.getOperand(0)); - var1 = new VarType(ck.descriptor); - stack.pop(var1.stack_size); - break; - case CodeConstants.opc_invokevirtual: - case CodeConstants.opc_invokespecial: - case CodeConstants.opc_invokeinterface: - stack.pop(); - case CodeConstants.opc_invokestatic: - case CodeConstants.opc_invokedynamic: - if(instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { - ck = pool.getLinkConstant(instr.getOperand(0)); - MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); - for(int i=0;i<md.params.length;i++) { - stack.pop(md.params[i].stack_size); - } - if(md.ret.type != CodeConstants.TYPE_VOID) { - stack.push(md.ret); - if(md.ret.stack_size==2) { - stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - } - } - } - break; - case CodeConstants.opc_new: - cn = pool.getPrimitiveConstant(instr.getOperand(0)); - stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); - break; - case CodeConstants.opc_newarray: - stack.pop(); - var1 = new VarType(arr_type[instr.getOperand(0)-4]); - var1.arraydim = 1; - stack.push(var1); - break; - case CodeConstants.opc_athrow: - var1 = stack.pop(); - stack.clear(); - stack.push(var1); - break; - case CodeConstants.opc_checkcast: - case CodeConstants.opc_instanceof: - stack.pop(); - cn = pool.getPrimitiveConstant(instr.getOperand(0)); - stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); - break; - case CodeConstants.opc_anewarray: - case CodeConstants.opc_multianewarray: - int dimensions = (instr.opcode==CodeConstants.opc_anewarray)?1:instr.getOperand(1); - stack.pop(dimensions); - cn = pool.getPrimitiveConstant(instr.getOperand(0)); - if(cn.isArray) { - var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()); - var1.arraydim+=dimensions; - stack.push(var1); - } else { - stack.push(new VarType(CodeConstants.TYPE_OBJECT, dimensions, cn.getString())); - } - } - - } - + // {read, write} + private static final int[][][] stack_impact = { + + {null, null}, // public final static int opc_nop = 0; + null, // public final static int opc_aconst_null = 1; + null, // public final static int opc_iconst_m1 = 2; + null, // public final static int opc_iconst_0 = 3; + null, // public final static int opc_iconst_1 = 4; + null, // public final static int opc_iconst_2 = 5; + null, // public final static int opc_iconst_3 = 6; + null, // public final static int opc_iconst_4 = 7; + null, // public final static int opc_iconst_5 = 8; + {null, {CodeConstants.TYPE_LONG}}, // public final static int opc_lconst_0 = 9; + {null, {CodeConstants.TYPE_LONG}}, // public final static int opc_lconst_1 = 10; + {null, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_0 = 11; + {null, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_1 = 12; + {null, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_fconst_2 = 13; + {null, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dconst_0 = 14; + {null, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dconst_1 = 15; + {null, {CodeConstants.TYPE_INT}}, // public final static int opc_bipush = 16; + {null, {CodeConstants.TYPE_INT}}, // public final static int opc_sipush = 17; + null, // public final static int opc_ldc = 18; + null, // public final static int opc_ldc_w = 19; + null, // public final static int opc_ldc2_w = 20; + {null, {CodeConstants.TYPE_INT}}, // public final static int opc_iload = 21; + {null, {CodeConstants.TYPE_LONG}}, // public final static int opc_lload = 22; + {null, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_fload = 23; + {null, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dload = 24; + null, // public final static int opc_aload = 25; + null, // public final static int opc_iload_0 = 26; + null, // public final static int opc_iload_1 = 27; + null, // public final static int opc_iload_2 = 28; + null, // public final static int opc_iload_3 = 29; + null, // public final static int opc_lload_0 = 30; + null, // public final static int opc_lload_1 = 31; + null, // public final static int opc_lload_2 = 32; + null, // public final static int opc_lload_3 = 33; + null, // public final static int opc_fload_0 = 34; + null, // public final static int opc_fload_1 = 35; + null, // public final static int opc_fload_2 = 36; + null, // public final static int opc_fload_3 = 37; + null, // public final static int opc_dload_0 = 38; + null, // public final static int opc_dload_1 = 39; + null, // public final static int opc_dload_2 = 40; + null, // public final static int opc_dload_3 = 41; + null, // public final static int opc_aload_0 = 42; + null, // public final static int opc_aload_1 = 43; + null, // public final static int opc_aload_2 = 44; + null, // public final static int opc_aload_3 = 45; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_iaload = 46; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_laload = 47; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_faload = 48; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_daload = 49; + null, // public final static int opc_aaload = 50; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_baload = 51; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_caload = 52; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_saload = 53; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_istore = 54; + {{CodeConstants.TYPE_LONG}, null}, // public final static int opc_lstore = 55; + {{CodeConstants.TYPE_FLOAT}, null}, // public final static int opc_fstore = 56; + {{CodeConstants.TYPE_DOUBLE}, null}, // public final static int opc_dstore = 57; + null, // public final static int opc_astore = 58; + null, // public final static int opc_istore_0 = 59; + null, // public final static int opc_istore_1 = 60; + null, // public final static int opc_istore_2 = 61; + null, // public final static int opc_istore_3 = 62; + null, // public final static int opc_lstore_0 = 63; + null, // public final static int opc_lstore_1 = 64; + null, // public final static int opc_lstore_2 = 65; + null, // public final static int opc_lstore_3 = 66; + null, // public final static int opc_fstore_0 = 67; + null, // public final static int opc_fstore_1 = 68; + null, // public final static int opc_fstore_2 = 69; + null, // public final static int opc_fstore_3 = 70; + null, // public final static int opc_dstore_0 = 71; + null, // public final static int opc_dstore_1 = 72; + null, // public final static int opc_dstore_2 = 73; + null, // public final static int opc_dstore_3 = 74; + null, // public final static int opc_astore_0 = 75; + null, // public final static int opc_astore_1 = 76; + null, // public final static int opc_astore_2 = 77; + null, // public final static int opc_astore_3 = 78; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, + // public final static int opc_iastore = 79; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG}, null}, + // public final static int opc_lastore = 80; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_FLOAT}, null}, + // public final static int opc_fastore = 81; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_DOUBLE}, null}, + // public final static int opc_dastore = 82; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_OBJECT}, null}, + // public final static int opc_aastore = 83; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, + // public final static int opc_bastore = 84; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, + // public final static int opc_castore = 85; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, + // public final static int opc_sastore = 86; + {{CodeConstants.TYPE_ANY}, null}, // public final static int opc_pop = 87; + {{CodeConstants.TYPE_ANY, CodeConstants.TYPE_ANY}, null}, // public final static int opc_pop2 = 88; + null, // public final static int opc_dup = 89; + null, // public final static int opc_dup_x1 = 90; + null, // public final static int opc_dup_x2 = 91; + null, // public final static int opc_dup2 = 92; + null, // public final static int opc_dup2_x1 = 93; + null, // public final static int opc_dup2_x2 = 94; + null, // public final static int opc_swap = 95; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_iadd = 96; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_ladd = 97; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_fadd = 98; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_dadd = 99; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_isub = 100; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lsub = 101; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_fsub = 102; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_dsub = 103; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_imul = 104; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lmul = 105; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_fmul = 106; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_dmul = 107; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_idiv = 108; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_ldiv = 109; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_fdiv = 110; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_ddiv = 111; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_irem = 112; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lrem = 113; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, + // public final static int opc_frem = 114; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, + // public final static int opc_drem = 115; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, // public final static int opc_ineg = 116; + {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, // public final static int opc_lneg = 117; + {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_fneg = 118; + {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_dneg = 119; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_ishl = 120; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lshl = 121; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_ishr = 122; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lshr = 123; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_iushr = 124; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lushr = 125; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_iand = 126; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_land = 127; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_ior = 128; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lor = 129; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_ixor = 130; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}}, + // public final static int opc_lxor = 131; + {null, null}, // public final static int opc_iinc = 132; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}}, // public final static int opc_i2l = 133; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_i2f = 134; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_i2d = 135; + {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_INT}}, // public final static int opc_l2i = 136; + {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_l2f = 137; + {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_l2d = 138; + {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}}, // public final static int opc_f2i = 139; + {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_LONG}}, // public final static int opc_f2l = 140; + {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_DOUBLE}}, // public final static int opc_f2d = 141; + {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}}, // public final static int opc_d2i = 142; + {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_LONG}}, // public final static int opc_d2l = 143; + {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_FLOAT}}, // public final static int opc_d2f = 144; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, // public final static int opc_i2b = 145; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, // public final static int opc_i2c = 146; + {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}}, // public final static int opc_i2s = 147; + {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_INT}}, + // public final static int opc_lcmp = 148; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_fcmpl = 149; + {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}}, + // public final static int opc_fcmpg = 150; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}}, + // public final static int opc_dcmpl = 151; + {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}}, + // public final static int opc_dcmpg = 152; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ifeq = 153; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ifne = 154; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_iflt = 155; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ifge = 156; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ifgt = 157; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ifle = 158; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmpeq = 159; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmpne = 160; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmplt = 161; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmpge = 162; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmpgt = 163; + {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null}, // public final static int opc_if_icmple = 164; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT}, null}, + // public final static int opc_if_acmpeq = 165; + {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT}, null}, + // public final static int opc_if_acmpne = 166; + {null, null}, // public final static int opc_goto = 167; + {null, {CodeConstants.TYPE_ADDRESS}}, // public final static int opc_jsr = 168; + {null, null}, // public final static int opc_ret = 169; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_tableswitch = 170; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_lookupswitch = 171; + {{CodeConstants.TYPE_INT}, null}, // public final static int opc_ireturn = 172; + {{CodeConstants.TYPE_LONG}, null}, // public final static int opc_lreturn = 173; + {{CodeConstants.TYPE_FLOAT}, null}, // public final static int opc_freturn = 174; + {{CodeConstants.TYPE_DOUBLE}, null}, // public final static int opc_dreturn = 175; + {{CodeConstants.TYPE_OBJECT}, null}, // public final static int opc_areturn = 176; + {null, null}, // public final static int opc_return = 177; + null, // public final static int opc_getstatic = 178; + null, // public final static int opc_putstatic = 179; + null, // public final static int opc_getfield = 180; + null, // public final static int opc_putfield = 181; + null, // public final static int opc_invokevirtual = 182; + null, // public final static int opc_invokespecial = 183; + null, // public final static int opc_invokestatic = 184; + null, // public final static int opc_invokeinterface = 185; + null, // public final static int opc_xxxunusedxxx = 186; + null, // public final static int opc_new = 187; + null, // public final static int opc_newarray = 188; + null, // public final static int opc_anewarray = 189; + {{CodeConstants.TYPE_OBJECT}, {CodeConstants.TYPE_INT}}, // public final static int opc_arraylength = 190; + null, + // public final static int opc_athrow = 191; + null, + // public final static int opc_checkcast = 192; + null, + // public final static int opc_instanceof = 193; + {{CodeConstants.TYPE_OBJECT}, null}, // public final static int opc_monitorenter = 194; + {{CodeConstants.TYPE_OBJECT}, null}, // public final static int opc_monitorexit = 195; + null, + // public final static int opc_wide = 196; + null, + // public final static int opc_multianewarray = 197; + {{CodeConstants.TYPE_OBJECT}, null}, // public final static int opc_ifnull = 198; + {{CodeConstants.TYPE_OBJECT}, null}, // public final static int opc_ifnonnull = 199; + {null, null}, // public final static int opc_goto_w = 200; + {null, {CodeConstants.TYPE_ADDRESS}}, // public final static int opc_jsr_w = 201; + }; + + 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 + }; + + + // Sonderbehandlung + // null, // public final static int opc_aconst_null = 1; + // null, // public final static int opc_ldc = 18; + // null, // public final static int opc_ldc_w = 19; + // null, // public final static int opc_ldc2_w = 20; + // null, // public final static int opc_aload = 25; + // null, // public final static int opc_aaload = 50; + // null, // public final static int opc_astore = 58; + // null, // public final static int opc_dup = 89; + // null, // public final static int opc_dup_x1 = 90; + // null, // public final static int opc_dup_x2 = 91; + // null, // public final static int opc_dup2 = 92; + // null, // public final static int opc_dup2_x1 = 93; + // null, // public final static int opc_dup2_x2 = 94; + // null, // public final static int opc_swap = 95; + // null, // public final static int opc_getstatic = 178; + // null, // public final static int opc_putstatic = 179; + // null, // public final static int opc_getfield = 180; + // null, // public final static int opc_putfield = 181; + // null, // public final static int opc_invokevirtual = 182; + // null, // public final static int opc_invokespecial = 183; + // null, // public final static int opc_invokestatic = 184; + // null, // public final static int opc_invokeinterface = 185; + // null, // public final static int opc_new = 187; + // null, // public final static int opc_newarray = 188; + // null, // public final static int opc_anewarray = 189; + // null, // public final static int opc_athrow = 191; + // null, // public final static int opc_checkcast = 192; + // null, // public final static int opc_instanceof = 193; + // null, // public final static int opc_multianewarray = 197; + + + public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) { + + ListStack<VarType> stack = data.getStack(); + int[][] arr = stack_impact[instr.opcode]; + + if (arr != null) { + // simple types only + + int[] read = arr[0]; + int[] write = arr[1]; + + if (read != null) { + int depth = 0; + for (int i = 0; i < read.length; i++) { + int type = read[i]; + depth++; + if (type == CodeConstants.TYPE_LONG || + type == CodeConstants.TYPE_DOUBLE) { + depth++; + } + } + + stack.removeMultiple(depth); + } + + if (write != null) { + for (int i = 0; i < write.length; i++) { + int type = write[i]; + stack.push(new VarType(type)); + if (type == CodeConstants.TYPE_LONG || + type == CodeConstants.TYPE_DOUBLE) { + stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + } + } + } + } + else { + // Sonderbehandlung + processSpecialInstructions(data, instr, pool); + } + } + + private static void processSpecialInstructions(DataPoint data, Instruction instr, ConstantPool pool) { + + VarType var1; + PrimitiveConstant cn; + LinkConstant ck; + + ListStack<VarType> stack = data.getStack(); + + switch (instr.opcode) { + case CodeConstants.opc_aconst_null: + stack.push(new VarType(CodeConstants.TYPE_NULL, 0, null)); + break; + case CodeConstants.opc_ldc: + case CodeConstants.opc_ldc_w: + case CodeConstants.opc_ldc2_w: + cn = pool.getPrimitiveConstant(instr.getOperand(0)); + switch (cn.type) { + case CodeConstants.CONSTANT_Integer: + stack.push(new VarType(CodeConstants.TYPE_INT)); + break; + case CodeConstants.CONSTANT_Float: + stack.push(new VarType(CodeConstants.TYPE_FLOAT)); + break; + case CodeConstants.CONSTANT_Long: + stack.push(new VarType(CodeConstants.TYPE_LONG)); + stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + break; + case CodeConstants.CONSTANT_Double: + stack.push(new VarType(CodeConstants.TYPE_DOUBLE)); + stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + break; + case CodeConstants.CONSTANT_String: + stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String")); + break; + case CodeConstants.CONSTANT_Class: + stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class")); + break; + } + break; + case CodeConstants.opc_aload: + var1 = data.getVariable(instr.getOperand(0)); + if (var1 != null) { + stack.push(var1); + } + else { + stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); + } + break; + case CodeConstants.opc_aaload: + var1 = stack.pop(2); + stack.push(new VarType(var1.type, var1.arraydim - 1, var1.value)); + break; + case CodeConstants.opc_astore: + data.setVariable(instr.getOperand(0), (VarType)stack.pop()); + break; + case CodeConstants.opc_dup: + case CodeConstants.opc_dup_x1: + case CodeConstants.opc_dup_x2: + int depth1 = 88 - instr.opcode; + stack.insertByOffset(depth1, stack.getByOffset(-1).copy()); + break; + case CodeConstants.opc_dup2: + case CodeConstants.opc_dup2_x1: + case CodeConstants.opc_dup2_x2: + int depth2 = 90 - instr.opcode; + stack.insertByOffset(depth2, stack.getByOffset(-2).copy()); + stack.insertByOffset(depth2, stack.getByOffset(-1).copy()); + break; + case CodeConstants.opc_swap: + var1 = stack.pop(); + stack.insertByOffset(-1, var1); + break; + case CodeConstants.opc_getfield: + stack.pop(); + case CodeConstants.opc_getstatic: + ck = pool.getLinkConstant(instr.getOperand(0)); + var1 = new VarType(ck.descriptor); + stack.push(var1); + if (var1.stack_size == 2) { + stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + } + break; + case CodeConstants.opc_putfield: + stack.pop(); + case CodeConstants.opc_putstatic: + ck = pool.getLinkConstant(instr.getOperand(0)); + var1 = new VarType(ck.descriptor); + stack.pop(var1.stack_size); + break; + case CodeConstants.opc_invokevirtual: + case CodeConstants.opc_invokespecial: + case CodeConstants.opc_invokeinterface: + stack.pop(); + case CodeConstants.opc_invokestatic: + case CodeConstants.opc_invokedynamic: + if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { + ck = pool.getLinkConstant(instr.getOperand(0)); + MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); + for (int i = 0; i < md.params.length; i++) { + stack.pop(md.params[i].stack_size); + } + if (md.ret.type != CodeConstants.TYPE_VOID) { + stack.push(md.ret); + if (md.ret.stack_size == 2) { + stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + } + } + } + break; + case CodeConstants.opc_new: + cn = pool.getPrimitiveConstant(instr.getOperand(0)); + stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); + break; + case CodeConstants.opc_newarray: + stack.pop(); + var1 = new VarType(arr_type[instr.getOperand(0) - 4]); + var1.arraydim = 1; + stack.push(var1); + break; + case CodeConstants.opc_athrow: + var1 = stack.pop(); + stack.clear(); + stack.push(var1); + break; + case CodeConstants.opc_checkcast: + case CodeConstants.opc_instanceof: + stack.pop(); + cn = pool.getPrimitiveConstant(instr.getOperand(0)); + stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); + break; + case CodeConstants.opc_anewarray: + case CodeConstants.opc_multianewarray: + int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.getOperand(1); + stack.pop(dimensions); + cn = pool.getPrimitiveConstant(instr.getOperand(0)); + if (cn.isArray) { + var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()); + var1.arraydim += dimensions; + stack.push(var1); + } + else { + stack.push(new VarType(CodeConstants.TYPE_OBJECT, dimensions, cn.getString())); + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/Util.java b/src/org/jetbrains/java/decompiler/code/interpreter/Util.java index 30aaf2c..bf1e5eb 100644 --- a/src/org/jetbrains/java/decompiler/code/interpreter/Util.java +++ b/src/org/jetbrains/java/decompiler/code/interpreter/Util.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.interpreter; import org.jetbrains.java.decompiler.code.Instruction; @@ -21,245 +22,265 @@ import org.jetbrains.java.decompiler.struct.StructContext; // FIXME: move to StructContext public class Util { - - private static final String[][] runtime_exceptions = { - - null, // public final static int opc_nop = 0; - null, // public final static int opc_aconst_null = 1; - null, // public final static int opc_iconst_m1 = 2; - null, // public final static int opc_iconst_0 = 3; - null, // public final static int opc_iconst_1 = 4; - null, // public final static int opc_iconst_2 = 5; - null, // public final static int opc_iconst_3 = 6; - null, // public final static int opc_iconst_4 = 7; - null, // public final static int opc_iconst_5 = 8; - null, // public final static int opc_lconst_0 = 9; - null, // public final static int opc_lconst_1 = 10; - null, // public final static int opc_fconst_0 = 11; - null, // public final static int opc_fconst_1 = 12; - null, // public final static int opc_fconst_2 = 13; - null, // public final static int opc_dconst_0 = 14; - null, // public final static int opc_dconst_1 = 15; - null, // public final static int opc_bipush = 16; - null, // public final static int opc_sipush = 17; - null, // public final static int opc_ldc = 18; - null, // public final static int opc_ldc_w = 19; - null, // public final static int opc_ldc2_w = 20; - null, // public final static int opc_iload = 21; - null, // public final static int opc_lload = 22; - null, // public final static int opc_fload = 23; - null, // public final static int opc_dload = 24; - null, // public final static int opc_aload = 25; - null, // public final static int opc_iload_0 = 26; - null, // public final static int opc_iload_1 = 27; - null, // public final static int opc_iload_2 = 28; - null, // public final static int opc_iload_3 = 29; - null, // public final static int opc_lload_0 = 30; - null, // public final static int opc_lload_1 = 31; - null, // public final static int opc_lload_2 = 32; - null, // public final static int opc_lload_3 = 33; - null, // public final static int opc_fload_0 = 34; - null, // public final static int opc_fload_1 = 35; - null, // public final static int opc_fload_2 = 36; - null, // public final static int opc_fload_3 = 37; - null, // public final static int opc_dload_0 = 38; - null, // public final static int opc_dload_1 = 39; - null, // public final static int opc_dload_2 = 40; - null, // public final static int opc_dload_3 = 41; - null, // public final static int opc_aload_0 = 42; - null, // public final static int opc_aload_1 = 43; - null, // public final static int opc_aload_2 = 44; - null, // public final static int opc_aload_3 = 45; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_iaload = 46; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_laload = 47; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_faload = 48; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_daload = 49; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_aaload = 50; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_baload = 51; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_caload = 52; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_saload = 53; - null, // public final static int opc_istore = 54; - null, // public final static int opc_lstore = 55; - null, // public final static int opc_fstore = 56; - null, // public final static int opc_dstore = 57; - null, // public final static int opc_astore = 58; - null, // public final static int opc_istore_0 = 59; - null, // public final static int opc_istore_1 = 60; - null, // public final static int opc_istore_2 = 61; - null, // public final static int opc_istore_3 = 62; - null, // public final static int opc_lstore_0 = 63; - null, // public final static int opc_lstore_1 = 64; - null, // public final static int opc_lstore_2 = 65; - null, // public final static int opc_lstore_3 = 66; - null, // public final static int opc_fstore_0 = 67; - null, // public final static int opc_fstore_1 = 68; - null, // public final static int opc_fstore_2 = 69; - null, // public final static int opc_fstore_3 = 70; - null, // public final static int opc_dstore_0 = 71; - null, // public final static int opc_dstore_1 = 72; - null, // public final static int opc_dstore_2 = 73; - null, // public final static int opc_dstore_3 = 74; - null, // public final static int opc_astore_0 = 75; - null, // public final static int opc_astore_1 = 76; - null, // public final static int opc_astore_2 = 77; - null, // public final static int opc_astore_3 = 78; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_iastore = 79; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_lastore = 80; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_fastore = 81; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_dastore = 82; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException", "java/lang/ArrayStoreException"}, // public final static int opc_aastore = 83; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_bastore = 84; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_castore = 85; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, // public final static int opc_sastore = 86; - null, // public final static int opc_pop = 87; - null, // public final static int opc_pop2 = 88; - null, // public final static int opc_dup = 89; - null, // public final static int opc_dup_x1 = 90; - null, // public final static int opc_dup_x2 = 91; - null, // public final static int opc_dup2 = 92; - null, // public final static int opc_dup2_x1 = 93; - null, // public final static int opc_dup2_x2 = 94; - null, // public final static int opc_swap = 95; - null, // public final static int opc_iadd = 96; - null, // public final static int opc_ladd = 97; - null, // public final static int opc_fadd = 98; - null, // public final static int opc_dadd = 99; - null, // public final static int opc_isub = 100; - null, // public final static int opc_lsub = 101; - null, // public final static int opc_fsub = 102; - null, // public final static int opc_dsub = 103; - null, // public final static int opc_imul = 104; - null, // public final static int opc_lmul = 105; - null, // public final static int opc_fmul = 106; - null, // public final static int opc_dmul = 107; - {"java/lang/ArithmeticException"}, // public final static int opc_idiv = 108; - {"java/lang/ArithmeticException"}, // public final static int opc_ldiv = 109; - null, // public final static int opc_fdiv = 110; - null, // public final static int opc_ddiv = 111; - {"java/lang/ArithmeticException"}, // public final static int opc_irem = 112; - {"java/lang/ArithmeticException"}, // public final static int opc_lrem = 113; - null, // public final static int opc_frem = 114; - null, // public final static int opc_drem = 115; - null, // public final static int opc_ineg = 116; - null, // public final static int opc_lneg = 117; - null, // public final static int opc_fneg = 118; - null, // public final static int opc_dneg = 119; - null, // public final static int opc_ishl = 120; - null, // public final static int opc_lshl = 121; - null, // public final static int opc_ishr = 122; - null, // public final static int opc_lshr = 123; - null, // public final static int opc_iushr = 124; - null, // public final static int opc_lushr = 125; - null, // public final static int opc_iand = 126; - null, // public final static int opc_land = 127; - null, // public final static int opc_ior = 128; - null, // public final static int opc_lor = 129; - null, // public final static int opc_ixor = 130; - null, // public final static int opc_lxor = 131; - null, // public final static int opc_iinc = 132; - null, // public final static int opc_i2l = 133; - null, // public final static int opc_i2f = 134; - null, // public final static int opc_i2d = 135; - null, // public final static int opc_l2i = 136; - null, // public final static int opc_l2f = 137; - null, // public final static int opc_l2d = 138; - null, // public final static int opc_f2i = 139; - null, // public final static int opc_f2l = 140; - null, // public final static int opc_f2d = 141; - null, // public final static int opc_d2i = 142; - null, // public final static int opc_d2l = 143; - null, // public final static int opc_d2f = 144; - null, // public final static int opc_i2b = 145; - null, // public final static int opc_i2c = 146; - null, // public final static int opc_i2s = 147; - null, // public final static int opc_lcmp = 148; - null, // public final static int opc_fcmpl = 149; - null, // public final static int opc_fcmpg = 150; - null, // public final static int opc_dcmpl = 151; - null, // public final static int opc_dcmpg = 152; - null, // public final static int opc_ifeq = 153; - null, // public final static int opc_ifne = 154; - null, // public final static int opc_iflt = 155; - null, // public final static int opc_ifge = 156; - null, // public final static int opc_ifgt = 157; - null, // public final static int opc_ifle = 158; - null, // public final static int opc_if_icmpeq = 159; - null, // public final static int opc_if_icmpne = 160; - null, // public final static int opc_if_icmplt = 161; - null, // public final static int opc_if_icmpge = 162; - null, // public final static int opc_if_icmpgt = 163; - null, // public final static int opc_if_icmple = 164; - null, // public final static int opc_if_acmpeq = 165; - null, // public final static int opc_if_acmpne = 166; - null, // public final static int opc_goto = 167; - null, // public final static int opc_jsr = 168; - null, // public final static int opc_ret = 169; - null, // public final static int opc_tableswitch = 170; - null, // public final static int opc_lookupswitch = 171; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_ireturn = 172; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_lreturn = 173; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_freturn = 174; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_dreturn = 175; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_areturn = 176; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_return = 177; - null, // public final static int opc_getstatic = 178; - null, // public final static int opc_putstatic = 179; - {"java/lang/NullPointerException"}, // public final static int opc_getfield = 180; - {"java/lang/NullPointerException"}, // public final static int opc_putfield = 181; - {"java/lang/NullPointerException", "java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokevirtual = 182; - {"java/lang/NullPointerException", "java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokespecial = 183; - {"java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokestatic = 184; - {"java/lang/NullPointerException", "java/lang/IncompatibleClassChangeError", "java/lang/IllegalAccessError", "java/lang/java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokeinterface = 185; - null, // public final static int opc_xxxunusedxxx = 186; - null, // public final static int opc_new = 187; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_newarray = 188; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_anewarray = 189; - {"java/lang/NullPointerException"}, // public final static int opc_arraylength = 190; - {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, // public final static int opc_athrow = 191; - {"java/lang/ClassCastException"}, // public final static int opc_checkcast = 192; - null, // public final static int opc_instanceof = 193; - {"java/lang/NullPointerException"}, // public final static int opc_monitorenter = 194; - {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, // public final static int opc_monitorexit = 195; - null, // public final static int opc_wide = 196; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_multianewarray = 197; - null, // public final static int opc_ifnull = 198; - null, // public final static int opc_ifnonnull = 199; - null, // public final static int opc_goto_w = 200; - null, // public final static int opc_jsr_w = 201; - }; - - - public static boolean instanceOf(StructContext context, String valclass, String refclass) { - - if(valclass.equals(refclass)) { - return true; - } - - StructClass cl = context.getClass(valclass); - if(cl==null) { - return false; - } - - if(cl.superClass!=null && instanceOf(context, cl.superClass.getString(), refclass)) { - return true; - } - - int[] interfaces = cl.getInterfaces(); - for(int i=0;i<interfaces.length;i++) { - String intfc = cl.getPool().getPrimitiveConstant(interfaces[i]).getString(); - - if(instanceOf(context, intfc, refclass)) { - return true; - } - } - - return false; - } - - public static String[] getRuntimeExceptions(Instruction instr) { - return runtime_exceptions[instr.opcode]; - } - + private static final String[][] runtime_exceptions = { + + null, // public final static int opc_nop = 0; + null, // public final static int opc_aconst_null = 1; + null, // public final static int opc_iconst_m1 = 2; + null, // public final static int opc_iconst_0 = 3; + null, // public final static int opc_iconst_1 = 4; + null, // public final static int opc_iconst_2 = 5; + null, // public final static int opc_iconst_3 = 6; + null, // public final static int opc_iconst_4 = 7; + null, // public final static int opc_iconst_5 = 8; + null, // public final static int opc_lconst_0 = 9; + null, // public final static int opc_lconst_1 = 10; + null, // public final static int opc_fconst_0 = 11; + null, // public final static int opc_fconst_1 = 12; + null, // public final static int opc_fconst_2 = 13; + null, // public final static int opc_dconst_0 = 14; + null, // public final static int opc_dconst_1 = 15; + null, // public final static int opc_bipush = 16; + null, // public final static int opc_sipush = 17; + null, // public final static int opc_ldc = 18; + null, // public final static int opc_ldc_w = 19; + null, // public final static int opc_ldc2_w = 20; + null, // public final static int opc_iload = 21; + null, // public final static int opc_lload = 22; + null, // public final static int opc_fload = 23; + null, // public final static int opc_dload = 24; + null, // public final static int opc_aload = 25; + null, // public final static int opc_iload_0 = 26; + null, // public final static int opc_iload_1 = 27; + null, // public final static int opc_iload_2 = 28; + null, // public final static int opc_iload_3 = 29; + null, // public final static int opc_lload_0 = 30; + null, // public final static int opc_lload_1 = 31; + null, // public final static int opc_lload_2 = 32; + null, // public final static int opc_lload_3 = 33; + null, // public final static int opc_fload_0 = 34; + null, // public final static int opc_fload_1 = 35; + null, // public final static int opc_fload_2 = 36; + null, // public final static int opc_fload_3 = 37; + null, // public final static int opc_dload_0 = 38; + null, // public final static int opc_dload_1 = 39; + null, // public final static int opc_dload_2 = 40; + null, // public final static int opc_dload_3 = 41; + null, // public final static int opc_aload_0 = 42; + null, // public final static int opc_aload_1 = 43; + null, // public final static int opc_aload_2 = 44; + null, // public final static int opc_aload_3 = 45; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_iaload = 46; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_laload = 47; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_faload = 48; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_daload = 49; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_aaload = 50; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_baload = 51; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_caload = 52; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_saload = 53; + null, // public final static int opc_istore = 54; + null, // public final static int opc_lstore = 55; + null, // public final static int opc_fstore = 56; + null, // public final static int opc_dstore = 57; + null, // public final static int opc_astore = 58; + null, // public final static int opc_istore_0 = 59; + null, // public final static int opc_istore_1 = 60; + null, // public final static int opc_istore_2 = 61; + null, // public final static int opc_istore_3 = 62; + null, // public final static int opc_lstore_0 = 63; + null, // public final static int opc_lstore_1 = 64; + null, // public final static int opc_lstore_2 = 65; + null, // public final static int opc_lstore_3 = 66; + null, // public final static int opc_fstore_0 = 67; + null, // public final static int opc_fstore_1 = 68; + null, // public final static int opc_fstore_2 = 69; + null, // public final static int opc_fstore_3 = 70; + null, // public final static int opc_dstore_0 = 71; + null, // public final static int opc_dstore_1 = 72; + null, // public final static int opc_dstore_2 = 73; + null, // public final static int opc_dstore_3 = 74; + null, // public final static int opc_astore_0 = 75; + null, // public final static int opc_astore_1 = 76; + null, // public final static int opc_astore_2 = 77; + null, // public final static int opc_astore_3 = 78; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_iastore = 79; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_lastore = 80; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_fastore = 81; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_dastore = 82; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException", "java/lang/ArrayStoreException"}, + // public final static int opc_aastore = 83; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_bastore = 84; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_castore = 85; + {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, + // public final static int opc_sastore = 86; + null, // public final static int opc_pop = 87; + null, // public final static int opc_pop2 = 88; + null, // public final static int opc_dup = 89; + null, // public final static int opc_dup_x1 = 90; + null, // public final static int opc_dup_x2 = 91; + null, // public final static int opc_dup2 = 92; + null, // public final static int opc_dup2_x1 = 93; + null, // public final static int opc_dup2_x2 = 94; + null, // public final static int opc_swap = 95; + null, // public final static int opc_iadd = 96; + null, // public final static int opc_ladd = 97; + null, // public final static int opc_fadd = 98; + null, // public final static int opc_dadd = 99; + null, // public final static int opc_isub = 100; + null, // public final static int opc_lsub = 101; + null, // public final static int opc_fsub = 102; + null, // public final static int opc_dsub = 103; + null, // public final static int opc_imul = 104; + null, // public final static int opc_lmul = 105; + null, // public final static int opc_fmul = 106; + null, // public final static int opc_dmul = 107; + {"java/lang/ArithmeticException"}, // public final static int opc_idiv = 108; + {"java/lang/ArithmeticException"}, // public final static int opc_ldiv = 109; + null, // public final static int opc_fdiv = 110; + null, // public final static int opc_ddiv = 111; + {"java/lang/ArithmeticException"}, // public final static int opc_irem = 112; + {"java/lang/ArithmeticException"}, // public final static int opc_lrem = 113; + null, // public final static int opc_frem = 114; + null, // public final static int opc_drem = 115; + null, // public final static int opc_ineg = 116; + null, // public final static int opc_lneg = 117; + null, // public final static int opc_fneg = 118; + null, // public final static int opc_dneg = 119; + null, // public final static int opc_ishl = 120; + null, // public final static int opc_lshl = 121; + null, // public final static int opc_ishr = 122; + null, // public final static int opc_lshr = 123; + null, // public final static int opc_iushr = 124; + null, // public final static int opc_lushr = 125; + null, // public final static int opc_iand = 126; + null, // public final static int opc_land = 127; + null, // public final static int opc_ior = 128; + null, // public final static int opc_lor = 129; + null, // public final static int opc_ixor = 130; + null, // public final static int opc_lxor = 131; + null, // public final static int opc_iinc = 132; + null, // public final static int opc_i2l = 133; + null, // public final static int opc_i2f = 134; + null, // public final static int opc_i2d = 135; + null, // public final static int opc_l2i = 136; + null, // public final static int opc_l2f = 137; + null, // public final static int opc_l2d = 138; + null, // public final static int opc_f2i = 139; + null, // public final static int opc_f2l = 140; + null, // public final static int opc_f2d = 141; + null, // public final static int opc_d2i = 142; + null, // public final static int opc_d2l = 143; + null, // public final static int opc_d2f = 144; + null, // public final static int opc_i2b = 145; + null, // public final static int opc_i2c = 146; + null, // public final static int opc_i2s = 147; + null, // public final static int opc_lcmp = 148; + null, // public final static int opc_fcmpl = 149; + null, // public final static int opc_fcmpg = 150; + null, // public final static int opc_dcmpl = 151; + null, // public final static int opc_dcmpg = 152; + null, // public final static int opc_ifeq = 153; + null, // public final static int opc_ifne = 154; + null, // public final static int opc_iflt = 155; + null, // public final static int opc_ifge = 156; + null, // public final static int opc_ifgt = 157; + null, // public final static int opc_ifle = 158; + null, // public final static int opc_if_icmpeq = 159; + null, // public final static int opc_if_icmpne = 160; + null, // public final static int opc_if_icmplt = 161; + null, // public final static int opc_if_icmpge = 162; + null, // public final static int opc_if_icmpgt = 163; + null, // public final static int opc_if_icmple = 164; + null, // public final static int opc_if_acmpeq = 165; + null, // public final static int opc_if_acmpne = 166; + null, // public final static int opc_goto = 167; + null, // public final static int opc_jsr = 168; + null, // public final static int opc_ret = 169; + null, // public final static int opc_tableswitch = 170; + null, // public final static int opc_lookupswitch = 171; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_ireturn = 172; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_lreturn = 173; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_freturn = 174; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_dreturn = 175; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_areturn = 176; + {"java/lang/IllegalMonitorStateException"}, // public final static int opc_return = 177; + null, // public final static int opc_getstatic = 178; + null, // public final static int opc_putstatic = 179; + {"java/lang/NullPointerException"}, // public final static int opc_getfield = 180; + {"java/lang/NullPointerException"}, // public final static int opc_putfield = 181; + {"java/lang/NullPointerException", "java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, + // public final static int opc_invokevirtual = 182; + {"java/lang/NullPointerException", "java/lang/UnsatisfiedLinkError"}, + // public final static int opc_invokespecial = 183; + {"java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokestatic = 184; + {"java/lang/NullPointerException", "java/lang/IncompatibleClassChangeError", "java/lang/IllegalAccessError", + "java/lang/java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, + // public final static int opc_invokeinterface = 185; + null, // public final static int opc_xxxunusedxxx = 186; + null, // public final static int opc_new = 187; + {"java/lang/NegativeArraySizeException"}, // public final static int opc_newarray = 188; + {"java/lang/NegativeArraySizeException"}, // public final static int opc_anewarray = 189; + {"java/lang/NullPointerException"}, // public final static int opc_arraylength = 190; + {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, + // public final static int opc_athrow = 191; + {"java/lang/ClassCastException"}, // public final static int opc_checkcast = 192; + null, // public final static int opc_instanceof = 193; + {"java/lang/NullPointerException"}, // public final static int opc_monitorenter = 194; + {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, + // public final static int opc_monitorexit = 195; + null, // public final static int opc_wide = 196; + {"java/lang/NegativeArraySizeException"}, // public final static int opc_multianewarray = 197; + null, // public final static int opc_ifnull = 198; + null, // public final static int opc_ifnonnull = 199; + null, // public final static int opc_goto_w = 200; + null, // public final static int opc_jsr_w = 201; + }; + + + public static boolean instanceOf(StructContext context, String valclass, String refclass) { + + if (valclass.equals(refclass)) { + return true; + } + + StructClass cl = context.getClass(valclass); + if (cl == null) { + return false; + } + + if (cl.superClass != null && instanceOf(context, cl.superClass.getString(), refclass)) { + return true; + } + + int[] interfaces = cl.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + String intfc = cl.getPool().getPrimitiveConstant(interfaces[i]).getString(); + + if (instanceOf(context, intfc, refclass)) { + return true; + } + } + + return false; + } + + public static String[] getRuntimeExceptions(Instruction instr) { + return runtime_exceptions[instr.opcode]; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java index b2727cf..788795e 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java @@ -1,56 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ALOAD extends Instruction { - - private static int[] opcodes = new int[] {opc_aload_0,opc_aload_1,opc_aload_2,opc_aload_3}; - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_aload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_aload_0, opc_aload_1, opc_aload_2, opc_aload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_aload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java index 1e0d4ff..d514ffe 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ANEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_anewarray); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_anewarray); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java index 475df11..40336f4 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java @@ -1,56 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ASTORE extends Instruction { - private static int[] opcodes = new int[] {opc_astore_0,opc_astore_1,opc_astore_2,opc_astore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_astore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_astore_0, opc_astore_1, opc_astore_2, opc_astore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_astore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java index 2fb2db5..4686051 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java @@ -1,44 +1,48 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class BIPUSH extends Instruction { - private static int[] opcodes = new int[] {opc_iconst_m1,opc_iconst_0,opc_iconst_1,opc_iconst_2,opc_iconst_3,opc_iconst_4,opc_iconst_5}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int value = getOperand(0); - if(value<-1 || value > 5) { - out.writeByte(opc_bipush); - out.writeByte(value); - } else { - out.writeByte(opcodes[value+1]); - } - } - - public int length() { - int value = getOperand(0); - if(value<-1 || value > 5) { - return 2; - } else { - return 1; - } - } + private static int[] opcodes = + new int[]{opc_iconst_m1, opc_iconst_0, opc_iconst_1, opc_iconst_2, opc_iconst_3, opc_iconst_4, opc_iconst_5}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int value = getOperand(0); + if (value < -1 || value > 5) { + out.writeByte(opc_bipush); + out.writeByte(value); + } + else { + out.writeByte(opcodes[value + 1]); + } + } + + public int length() { + int value = getOperand(0); + if (value < -1 || value > 5) { + return 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java b/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java index 73417de..9a42f65 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java @@ -1,34 +1,34 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class CHECKCAST extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_checkcast); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_checkcast); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java index 96d5449..7630185 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java @@ -1,56 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class DLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_dload_0,opc_dload_1,opc_dload_2,opc_dload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_dload_0, opc_dload_1, opc_dload_2, opc_dload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_dload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java index 61a8776..2bdb749 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java @@ -1,55 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class DSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_dstore_0,opc_dstore_1,opc_dstore_2,opc_dstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } + private static int[] opcodes = new int[]{opc_dstore_0, opc_dstore_1, opc_dstore_2, opc_dstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_dstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java index 5c8fe26..8a89dff 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java @@ -1,56 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class FLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_fload_0,opc_fload_1,opc_fload_2,opc_fload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_fload_0, opc_fload_1, opc_fload_2, opc_fload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_fload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java index 3685254..9061913 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java @@ -1,55 +1,60 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class FSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_fstore_0,opc_fstore_1,opc_fstore_2,opc_fstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - if(wide) { - return 4; - } else { - return 2; - } - } else { - return 1; - } - } + private static int[] opcodes = new int[]{opc_fstore_0, opc_fstore_1, opc_fstore_2, opc_fstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_fstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + if (wide) { + return 4; + } + else { + return 2; + } + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java index b9a8ab5..6b95aaf 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java @@ -1,32 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class GETFIELD extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_getfield); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java index 1890bc0..c1401a6 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class GETSTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_getstatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java index 3a81194..97374c0 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java @@ -1,44 +1,46 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class GOTO extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - out.writeByte(opc_goto_w); - out.writeInt(operand); - } else { - out.writeByte(opc_goto); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - return 5; - } else { - return 3; - } - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + out.writeByte(opc_goto_w); + out.writeInt(operand); + } + else { + out.writeByte(opc_goto); + out.writeShort(operand); + } + } + + public int length() { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + return 5; + } + else { + return 3; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java index 6e1f6e7..56ba9e8 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class GOTO_W extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_goto_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_goto_w); + out.writeInt(getOperand(0)); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java index e945969..05b446c 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java @@ -1,42 +1,43 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class IINC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iinc); - if(wide) { - out.writeShort(getOperand(0)); - out.writeShort(getOperand(1)); - } else { - out.writeByte(getOperand(0)); - out.writeByte(getOperand(1)); - } - } - - public int length() { - return wide?6:3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_iinc); + if (wide) { + out.writeShort(getOperand(0)); + out.writeShort(getOperand(1)); + } + else { + out.writeByte(getOperand(0)); + out.writeByte(getOperand(1)); + } + } + + public int length() { + return wide ? 6 : 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java index 40bfa4d..24b95ff 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java @@ -1,52 +1,55 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ILOAD extends Instruction { - private static int[] opcodes = new int[] {opc_iload_0,opc_iload_1,opc_iload_2,opc_iload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_iload_0, opc_iload_1, opc_iload_2, opc_iload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_iload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java index f02dfc6..f93d5b8 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INSTANCEOF extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_instanceof); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_instanceof); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java index ee8c785..917c5bd 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java @@ -1,20 +1,35 @@ +/* + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKEDYNAMIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokedynamic); - out.writeShort(getOperand(0)); - out.writeByte(0); - out.writeByte(0); - } - - public int length() { - return 5; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokedynamic); + out.writeShort(getOperand(0)); + out.writeByte(0); + out.writeByte(0); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java index e4f06f2..51259ff 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java @@ -1,35 +1,35 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKEINTERFACE extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokeinterface); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - out.writeByte(0); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokeinterface); + out.writeShort(getOperand(0)); + out.writeByte(getOperand(1)); + out.writeByte(0); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java index 6b9b559..2436803 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKESPECIAL extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokespecial); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokespecial); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java index 0f1ad16..f036a1d 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKESTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokestatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokestatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java index f4537f3..ca3bcd3 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class INVOKEVIRTUAL extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokevirtual); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_invokevirtual); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java index 0ced128..5907538 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java @@ -1,52 +1,55 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class ISTORE extends Instruction { - private static int[] opcodes = new int[] {opc_istore_0,opc_istore_1,opc_istore_2,opc_istore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_istore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_istore_0, opc_istore_1, opc_istore_2, opc_istore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_istore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java index ef6c2ad..bb000a7 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java @@ -1,44 +1,46 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class JSR extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - out.writeByte(opc_jsr_w); - out.writeInt(operand); - } else { - out.writeByte(opc_jsr); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if(operand < -32768 || operand > 32767) { - return 5; - } else { - return 3; - } - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + out.writeByte(opc_jsr_w); + out.writeInt(operand); + } + else { + out.writeByte(opc_jsr); + out.writeShort(operand); + } + } + + public int length() { + int operand = getOperand(0); + if (operand < -32768 || operand > 32767) { + return 5; + } + else { + return 3; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java index bc7ae59..fb04ed9 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.JumpInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.JumpInstruction; - public class JSR_W extends JumpInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_jsr_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_jsr_w); + out.writeInt(getOperand(0)); + } + + public int length() { + return 5; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java index 4b1a715..ae7b3c6 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc); + out.writeByte(getOperand(0)); + } + + public int length() { + return 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java index 4b7ea0b..6ba1964 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC2_W extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc2_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc2_w); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java index 6a4e3a6..71e5fe0 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LDC_W extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_ldc_w); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java index 52f2b5c..6e50659 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java @@ -1,52 +1,55 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LLOAD extends Instruction { - private static int[] opcodes = new int[] {opc_lload_0,opc_lload_1,opc_lload_2,opc_lload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lload); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } + private static int[] opcodes = new int[]{opc_lload_0, opc_lload_1, opc_lload_2, opc_lload_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_lload); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java index 1654622..4183168 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java @@ -1,42 +1,42 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.SwitchInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.SwitchInstruction; - public class LOOKUPSWITCH extends SwitchInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_lookupswitch); - - int padding = 3 - (offset%4); - for(int i=0;i<padding;i++){ - out.writeByte(0); - } - - for(int i=0;i<operandsCount();i++) { - out.writeInt(getOperand(i)); - } - } - - public int length() { - return 1+operandsCount()*4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + + out.writeByte(opc_lookupswitch); + + int padding = 3 - (offset % 4); + for (int i = 0; i < padding; i++) { + out.writeByte(0); + } + + for (int i = 0; i < operandsCount(); i++) { + out.writeInt(getOperand(i)); + } + } + + public int length() { + return 1 + operandsCount() * 4; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java index 410d19a..9095f04 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java @@ -1,52 +1,55 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class LSTORE extends Instruction { - private static int[] opcodes = new int[] {opc_lstore_0,opc_lstore_1,opc_lstore_2,opc_lstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if(index>3) { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lstore); - if(wide) { - out.writeShort(index); - } else { - out.writeByte(index); - } - } else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if(index>3) { - return wide?4:2; - } else { - return 1; - } - } - + private static int[] opcodes = new int[]{opc_lstore_0, opc_lstore_1, opc_lstore_2, opc_lstore_3}; + + public void writeToStream(DataOutputStream out, int offset) throws IOException { + int index = getOperand(0); + if (index > 3) { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_lstore); + if (wide) { + out.writeShort(index); + } + else { + out.writeByte(index); + } + } + else { + out.writeByte(opcodes[index]); + } + } + + public int length() { + int index = getOperand(0); + if (index > 3) { + return wide ? 4 : 2; + } + else { + return 1; + } + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java index c7eb0b9..b6835e8 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java @@ -1,34 +1,34 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class MULTIANEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_multianewarray); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - } - - public int length() { - return 4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_multianewarray); + out.writeShort(getOperand(0)); + out.writeByte(getOperand(1)); + } + + public int length() { + return 4; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java b/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java index eafe17c..2e8854f 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class NEW extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_new); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_new); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java index 8764a6f..f2f8e4d 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class NEWARRAY extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_newarray); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_newarray); + out.writeByte(getOperand(0)); + } + + public int length() { + return 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java index 096da07..c1e3e92 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class PUTFIELD extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_putfield); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java index e543c88..0fe0240 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class PUTSTATIC extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_putstatic); + out.writeShort(getOperand(0)); + } + + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java b/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java index d52d2a2..93f62ff 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java @@ -1,40 +1,41 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class RET extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if(wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_ret); - if(wide) { - out.writeShort(getOperand(0)); - } else { - out.writeByte(getOperand(0)); - } - } - - public int length() { - return wide?4:2; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + if (wide) { + out.writeByte(opc_wide); + } + out.writeByte(opc_ret); + if (wide) { + out.writeShort(getOperand(0)); + } + else { + out.writeByte(getOperand(0)); + } + } + + public int length() { + return wide ? 4 : 2; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java index 5bac32f..08b10a5 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java @@ -1,33 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.Instruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.Instruction; - public class SIPUSH extends Instruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_sipush); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } + public void writeToStream(DataOutputStream out, int offset) throws IOException { + out.writeByte(opc_sipush); + out.writeShort(getOperand(0)); + } + public int length() { + return 3; + } } diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java index ca2eae5..0b41ecf 100644 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java +++ b/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java @@ -1,43 +1,42 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code.optinstructions; +import org.jetbrains.java.decompiler.code.SwitchInstruction; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.SwitchInstruction; - public class TABLESWITCH extends SwitchInstruction { - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_tableswitch); - - int padding = 3 - (offset%4); - for(int i=0;i<padding;i++){ - out.writeByte(0); - } - - for(int i=0;i<operandsCount();i++) { - out.writeInt(getOperand(i)); - } - - } - - public int length() { - return 1+operandsCount()*4; - } - + public void writeToStream(DataOutputStream out, int offset) throws IOException { + + out.writeByte(opc_tableswitch); + + int padding = 3 - (offset % 4); + for (int i = 0; i < padding; i++) { + out.writeByte(0); + } + + for (int i = 0; i < operandsCount(); i++) { + out.writeInt(getOperand(i)); + } + } + + public int length() { + return 1 + operandsCount() * 4; + } } diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java index 86e5c59..265ca5c 100644 --- a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -1,23 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; @@ -27,295 +24,292 @@ import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssertExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class AssertProcessor { - - private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError"); - - public static void buildAssertions(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - - StructField field = findAssertionField(node); - - if(field != null) { - - String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor()); - - boolean res = false; - - for(MethodWrapper meth : wrapper.getMethods()) { - RootStatement root = meth.root; - if(root != null) { - res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key); - } - } - - if(res) { - // hide the helper field - wrapper.getHideMembers().add(key); - } - } - - } - - private static StructField findAssertionField(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - - boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - - for(StructField fd: wrapper.getClassStruct().getFields()) { - - String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()); - - // initializer exists - if(wrapper.getStaticFieldInitializers().containsKey(keyField)) { - - int flags = fd.access_flags; - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); - - // access flags set - if((flags & CodeConstants.ACC_STATIC) != 0 && (flags & CodeConstants.ACC_FINAL) != 0 && - (isSynthetic || nosynthflag)) { - - // field type boolean - FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor()); - if(VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) { - - Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField); - if(initializer.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fexpr = (FunctionExprent)initializer; - - if(fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && - fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) { - - InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0); - - if(invexpr.getInstance() != null && invexpr.getInstance().type == Exprent.EXPRENT_CONST && "desiredAssertionStatus".equals(invexpr.getName()) - && "java/lang/Class".equals(invexpr.getClassname()) && invexpr.getLstParameters().isEmpty()) { - - ConstExprent cexpr = (ConstExprent)invexpr.getInstance(); - if(VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) { - - ClassNode nd = node; - while(nd != null) { - if(nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) { - break; - } - nd = nd.parent; - } - - if(nd != null) { // found enclosing class with the same name - return fd; - } - } - } - } - } - } - } - } - } - - - return null; - } - - - private static boolean replaceAssertions(Statement statement, String classname, String key) { - - boolean res = false; - - for(Statement st : statement.getStats()) { - res |= replaceAssertions(st, classname, key); - } - - boolean replaced = true; - while(replaced) { - replaced = false; - - for(Statement st : statement.getStats()) { - if(st.type == Statement.TYPE_IF) { - if(replaceAssertion(statement, (IfStatement)st, classname, key)) { - replaced = true; - break; - } - } - } - - res |= replaced; - } - - return res; - } - - private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) { - - Statement ifstat = stat.getIfstat(); - InvocationExprent throwError = isAssertionError(ifstat); - - if(throwError == null) { - return false; - } - - Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key); - if(!(Boolean)exprres[1]) { - return false; - } - - List<Exprent> lstParams = new ArrayList<Exprent>(); - - Exprent ascond = null, retcond = null; - if(exprres[0] != null) { - ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{(Exprent)exprres[0]})); - retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond); - } - - lstParams.add(retcond==null?ascond:retcond); - if(!throwError.getLstParameters().isEmpty()) { - lstParams.add(throwError.getLstParameters().get(0)); - } - - AssertExprent asexpr = new AssertExprent(lstParams); - - Statement newstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - newstat.setExprents(Arrays.asList(new Exprent[] {asexpr})); - - Statement first = stat.getFirst(); - - if(stat.iftype == IfStatement.IFTYPE_IFELSE || (first.getExprents() != null && - !first.getExprents().isEmpty())) { - - first.removeSuccessor(stat.getIfEdge()); - first.removeSuccessor(stat.getElseEdge()); - - List<Statement> lstStatements = new ArrayList<Statement>(); - if(first.getExprents() != null && !first.getExprents().isEmpty()) { - lstStatements.add(first); - } - lstStatements.add(newstat); - if(stat.iftype == IfStatement.IFTYPE_IFELSE) { - lstStatements.add(stat.getElsestat()); - } - - SequenceStatement sequence = new SequenceStatement(lstStatements); - sequence.setAllParent(); - - for(int i=0;i<sequence.getStats().size()-1;i++) { - sequence.getStats().get(i).addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, - sequence.getStats().get(i), sequence.getStats().get(i+1))); - } - - if(stat.iftype == IfStatement.IFTYPE_IFELSE) { - Statement ifelse = stat.getElsestat(); - - List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges(); - if(!lstSuccs.isEmpty()) { - StatEdge endedge = lstSuccs.get(0); - if(endedge.closure == stat) { - sequence.addLabeledEdge(endedge); - } - } - } - - newstat = sequence; - } - - newstat.getVarDefinitions().addAll(stat.getVarDefinitions()); - parent.replaceStatement(stat, newstat); - - return true; - } - - private static InvocationExprent isAssertionError(Statement stat) { - - if(stat == null || stat.getExprents() == null || stat.getExprents().size() !=1) { - return null; - } - - Exprent expr = stat.getExprents().get(0); - - if(expr.type == Exprent.EXPRENT_EXIT) { - ExitExprent exexpr = (ExitExprent)expr; - if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)exexpr.getValue(); - if(CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) { - return nexpr.getConstructor(); - } - } - } - - return null; - } - - private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) { - - if(exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fexpr = (FunctionExprent)exprent; - if(fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) { - - for(int i=0;i<2;i++) { - Exprent param = fexpr.getLstOperands().get(i); - - if(isAssertionField(param, classname, key)) { - return new Object[] {fexpr.getLstOperands().get(1-i), true}; - } - } - - for(int i=0;i<2;i++) { - Exprent param = fexpr.getLstOperands().get(i); - - Object[] res = getAssertionExprent(param, classname, key); - if((Boolean)res[1]) { - if(param != res[0]) { - fexpr.getLstOperands().set(i, (Exprent)res[0]); - } - return new Object[] {fexpr, true}; - } - } - } else if(isAssertionField(fexpr, classname, key)) { - // assert false; - return new Object[] {null, true}; - } - } - - return new Object[] {exprent, false}; - } - - private static boolean isAssertionField(Exprent exprent, String classname, String key) { - - if(exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fparam = (FunctionExprent)exprent; - if(fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && - fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { - FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); - if(classname.equals(fdparam.getClassname()) - && key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) { - return true; - } - } - } - - return false; - } + + private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError"); + + public static void buildAssertions(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + StructField field = findAssertionField(node); + + if (field != null) { + + String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor()); + + boolean res = false; + + for (MethodWrapper meth : wrapper.getMethods()) { + RootStatement root = meth.root; + if (root != null) { + res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key); + } + } + + if (res) { + // hide the helper field + wrapper.getHideMembers().add(key); + } + } + } + + private static StructField findAssertionField(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + for (StructField fd : wrapper.getClassStruct().getFields()) { + + String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()); + + // initializer exists + if (wrapper.getStaticFieldInitializers().containsKey(keyField)) { + + int flags = fd.access_flags; + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + + // access flags set + if ((flags & CodeConstants.ACC_STATIC) != 0 && (flags & CodeConstants.ACC_FINAL) != 0 && + (isSynthetic || nosynthflag)) { + + // field type boolean + FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor()); + if (VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) { + + Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField); + if (initializer.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)initializer; + + if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && + fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) { + + InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0); + + if (invexpr.getInstance() != null && + invexpr.getInstance().type == Exprent.EXPRENT_CONST && + "desiredAssertionStatus".equals(invexpr.getName()) + && + "java/lang/Class".equals(invexpr.getClassname()) && + invexpr.getLstParameters().isEmpty()) { + + ConstExprent cexpr = (ConstExprent)invexpr.getInstance(); + if (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) { + + ClassNode nd = node; + while (nd != null) { + if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) { + break; + } + nd = nd.parent; + } + + if (nd != null) { // found enclosing class with the same name + return fd; + } + } + } + } + } + } + } + } + } + + + return null; + } + + + private static boolean replaceAssertions(Statement statement, String classname, String key) { + + boolean res = false; + + for (Statement st : statement.getStats()) { + res |= replaceAssertions(st, classname, key); + } + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Statement st : statement.getStats()) { + if (st.type == Statement.TYPE_IF) { + if (replaceAssertion(statement, (IfStatement)st, classname, key)) { + replaced = true; + break; + } + } + } + + res |= replaced; + } + + return res; + } + + private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) { + + Statement ifstat = stat.getIfstat(); + InvocationExprent throwError = isAssertionError(ifstat); + + if (throwError == null) { + return false; + } + + Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key); + if (!(Boolean)exprres[1]) { + return false; + } + + List<Exprent> lstParams = new ArrayList<Exprent>(); + + Exprent ascond = null, retcond = null; + if (exprres[0] != null) { + ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{(Exprent)exprres[0]})); + retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond); + } + + lstParams.add(retcond == null ? ascond : retcond); + if (!throwError.getLstParameters().isEmpty()) { + lstParams.add(throwError.getLstParameters().get(0)); + } + + AssertExprent asexpr = new AssertExprent(lstParams); + + Statement newstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + newstat.setExprents(Arrays.asList(new Exprent[]{asexpr})); + + Statement first = stat.getFirst(); + + if (stat.iftype == IfStatement.IFTYPE_IFELSE || (first.getExprents() != null && + !first.getExprents().isEmpty())) { + + first.removeSuccessor(stat.getIfEdge()); + first.removeSuccessor(stat.getElseEdge()); + + List<Statement> lstStatements = new ArrayList<Statement>(); + if (first.getExprents() != null && !first.getExprents().isEmpty()) { + lstStatements.add(first); + } + lstStatements.add(newstat); + if (stat.iftype == IfStatement.IFTYPE_IFELSE) { + lstStatements.add(stat.getElsestat()); + } + + SequenceStatement sequence = new SequenceStatement(lstStatements); + sequence.setAllParent(); + + for (int i = 0; i < sequence.getStats().size() - 1; i++) { + sequence.getStats().get(i).addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, + sequence.getStats().get(i), sequence.getStats().get(i + 1))); + } + + if (stat.iftype == IfStatement.IFTYPE_IFELSE) { + Statement ifelse = stat.getElsestat(); + + List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges(); + if (!lstSuccs.isEmpty()) { + StatEdge endedge = lstSuccs.get(0); + if (endedge.closure == stat) { + sequence.addLabeledEdge(endedge); + } + } + } + + newstat = sequence; + } + + newstat.getVarDefinitions().addAll(stat.getVarDefinitions()); + parent.replaceStatement(stat, newstat); + + return true; + } + + private static InvocationExprent isAssertionError(Statement stat) { + + if (stat == null || stat.getExprents() == null || stat.getExprents().size() != 1) { + return null; + } + + Exprent expr = stat.getExprents().get(0); + + if (expr.type == Exprent.EXPRENT_EXIT) { + ExitExprent exexpr = (ExitExprent)expr; + if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)exexpr.getValue(); + if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) { + return nexpr.getConstructor(); + } + } + } + + return null; + } + + private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + if (fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) { + + for (int i = 0; i < 2; i++) { + Exprent param = fexpr.getLstOperands().get(i); + + if (isAssertionField(param, classname, key)) { + return new Object[]{fexpr.getLstOperands().get(1 - i), true}; + } + } + + for (int i = 0; i < 2; i++) { + Exprent param = fexpr.getLstOperands().get(i); + + Object[] res = getAssertionExprent(param, classname, key); + if ((Boolean)res[1]) { + if (param != res[0]) { + fexpr.getLstOperands().set(i, (Exprent)res[0]); + } + return new Object[]{fexpr, true}; + } + } + } + else if (isAssertionField(fexpr, classname, key)) { + // assert false; + return new Object[]{null, true}; + } + } + + return new Object[]{exprent, false}; + } + + private static boolean isAssertionField(Exprent exprent, String classname, String key) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)exprent; + if (fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && + fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); + if (classname.equals(fdparam.getClassname()) + && key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) { + return true; + } + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java index cdad6ca..411789c 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java @@ -1,39 +1,26 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; @@ -46,264 +33,272 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; + public class ClassReference14Processor { - - public ExitExprent bodyexprent; - - public ExitExprent handlerexprent; - - - public ClassReference14Processor() { - - InvocationExprent invfor = new InvocationExprent(); - invfor.setName("forName"); - invfor.setClassname("java/lang/Class"); - invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); - invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); - invfor.setStatic(true); - invfor.setLstParameters(Arrays.asList(new Exprent[] {new VarExprent(0, VarType.VARTYPE_STRING, null)})); - - bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, - invfor, - VarType.VARTYPE_CLASS); - - InvocationExprent constr = new InvocationExprent(); - constr.setName("<init>"); - constr.setClassname("java/lang/NoClassDefFoundError"); - constr.setStringDescriptor("()V"); - constr.setFunctype(InvocationExprent.TYP_INIT); - constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); - - NewExprent newexpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT,0,"java/lang/NoClassDefFoundError"), new ArrayList<Exprent>()); - newexpr.setConstructor(constr); - - InvocationExprent invcause = new InvocationExprent(); - invcause.setName("initCause"); - invcause.setClassname("java/lang/NoClassDefFoundError"); - invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); - invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); - invcause.setInstance(newexpr); - invcause.setLstParameters(Arrays.asList(new Exprent[] {new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)})); - - handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, - invcause, - null); - } - - - public void processClassReferences(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - -// int major_version = wrapper.getClassStruct().major_version; -// int minor_version = wrapper.getClassStruct().minor_version; -// -// if(major_version > 48 || (major_version == 48 && minor_version > 0)) { -// // version 1.5 or above -// return; -// } - - if(wrapper.getClassStruct().isVersionGE_1_5()) { - // version 1.5 or above - return; - } - - // find the synthetic method Class class$(String) if present - HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>(); - findClassMethod(node, mapClassMeths); - - if(mapClassMeths.isEmpty()) { - return; - } - - HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>(); - processClassRec(node, mapClassMeths, setFound); - - if(!setFound.isEmpty()) { - for(ClassWrapper wrp : setFound) { - StructMethod mt = mapClassMeths.get(wrp).methodStruct; - wrp.getHideMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); - } - } - - } - - private void processClassRec(ClassNode node, final HashMap<ClassWrapper, MethodWrapper> mapClassMeths, final HashSet<ClassWrapper> setFound) { - - final ClassWrapper wrapper = node.wrapper; - - // search code - for(MethodWrapper meth : wrapper.getMethods()) { - - RootStatement root = meth.root; - if(root != null) { - - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - for(Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { - if(replaceInvocations(exprent, ent.getKey(), ent.getValue())) { - setFound.add(ent.getKey()); - } - } - return 0; - } - }); - - } - } - - // search initializers - for(int j=0;j<2;j++) { - VBStyleCollection<Exprent, String> initializers = j==0?wrapper.getStaticFieldInitializers():wrapper.getDynamicFieldInitializers(); - - for(int i=0; i<initializers.size();i++) { - for(Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { - Exprent exprent = initializers.get(i); - if(replaceInvocations(exprent, ent.getKey(), ent.getValue())) { - setFound.add(ent.getKey()); - } - - String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue()); - if(cl != null) { - initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); - setFound.add(ent.getKey()); - } - } - } - } - - // iterate nested classes - for(ClassNode nd : node.nested) { - processClassRec(nd, mapClassMeths, setFound); - } - - } - - private void findClassMethod(ClassNode node, HashMap<ClassWrapper, MethodWrapper> mapClassMeths) { - - boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - - ClassWrapper wrapper = node.wrapper; - - for(MethodWrapper meth : wrapper.getMethods()) { - StructMethod mt = meth.methodStruct; - - if(((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic") - || nosynthflag) && - mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") && - (mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0) { - - RootStatement root = meth.root; - if(root != null) { - if(root.getFirst().type == Statement.TYPE_TRYCATCH) { - CatchStatement cst = (CatchStatement)root.getFirst(); - if(cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK && - cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK && - cst.getVars().get(0).getVartype().equals(new VarType(CodeConstants.TYPE_OBJECT,0,"java/lang/ClassNotFoundException"))) { - - BasicBlockStatement body = (BasicBlockStatement)cst.getFirst(); - BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1); - - if(body.getExprents().size() == 1 && handler.getExprents().size() == 1) { - if(bodyexprent.equals(body.getExprents().get(0)) && - handlerexprent.equals(handler.getExprents().get(0))) { - mapClassMeths.put(wrapper, meth); - break; - } - } - } - } - } - } - } - - // iterate nested classes - for(ClassNode nd : node.nested) { - findClassMethod(nd, mapClassMeths); - } - - } - - - private boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { - - boolean res = false; - - for(;;) { - - boolean found = false; - - for(Exprent expr : exprent.getAllExprents()) { - String cl = isClass14Invocation(expr, wrapper, meth); - if(cl != null) { - exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); - found = true; - res = true; - break; - } - - res |= replaceInvocations(expr, wrapper, meth); - } - - if(!found) { - break; - } - } - - return res; - } - - - - private String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { - - if(exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fexpr = (FunctionExprent)exprent; - if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) { - if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0); - if(headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) { - if(headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD && - headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST && - ((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) { - - FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0); - ClassNode fieldnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(field.getClassname()); - - if(fieldnode != null && fieldnode.classStruct.qualifiedName.equals(wrapper.getClassStruct().qualifiedName)) { // source class - StructField fd = wrapper.getClassStruct().getField(field.getName(), field.getDescriptor().descriptorString); // FIXME: can be null! why?? - - if(fd != null && (fd.access_flags & CodeConstants.ACC_STATIC) != 0 && - ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic") - || DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET))) { - - if(fexpr.getLstOperands().get(1).type == Exprent.EXPRENT_ASSIGNMENT && fexpr.getLstOperands().get(2).equals(field)) { - AssignmentExprent asexpr = (AssignmentExprent)fexpr.getLstOperands().get(1); - - if(asexpr.getLeft().equals(field) && asexpr.getRight().type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)asexpr.getRight(); - - if(invexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName) && - invexpr.getName().equals(meth.methodStruct.getName()) && - invexpr.getStringDescriptor().equals(meth.methodStruct.getDescriptor())) { - - if(invexpr.getLstParameters().get(0).type == Exprent.EXPRENT_CONST) { - wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field - return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString(); - } - } - } - } - } - } - } - } - } - } - } - - return null; - } + + public ExitExprent bodyexprent; + + public ExitExprent handlerexprent; + + + public ClassReference14Processor() { + + InvocationExprent invfor = new InvocationExprent(); + invfor.setName("forName"); + invfor.setClassname("java/lang/Class"); + invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); + invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); + invfor.setStatic(true); + invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)})); + + bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, + invfor, + VarType.VARTYPE_CLASS); + + InvocationExprent constr = new InvocationExprent(); + constr.setName("<init>"); + constr.setClassname("java/lang/NoClassDefFoundError"); + constr.setStringDescriptor("()V"); + constr.setFunctype(InvocationExprent.TYP_INIT); + constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); + + NewExprent newexpr = + new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>()); + newexpr.setConstructor(constr); + + InvocationExprent invcause = new InvocationExprent(); + invcause.setName("initCause"); + invcause.setClassname("java/lang/NoClassDefFoundError"); + invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); + invcause.setInstance(newexpr); + invcause.setLstParameters( + Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)})); + + handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, + invcause, + null); + } + + + public void processClassReferences(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + + // int major_version = wrapper.getClassStruct().major_version; + // int minor_version = wrapper.getClassStruct().minor_version; + // + // if(major_version > 48 || (major_version == 48 && minor_version > 0)) { + // // version 1.5 or above + // return; + // } + + if (wrapper.getClassStruct().isVersionGE_1_5()) { + // version 1.5 or above + return; + } + + // find the synthetic method Class class$(String) if present + HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>(); + findClassMethod(node, mapClassMeths); + + if (mapClassMeths.isEmpty()) { + return; + } + + HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>(); + processClassRec(node, mapClassMeths, setFound); + + if (!setFound.isEmpty()) { + for (ClassWrapper wrp : setFound) { + StructMethod mt = mapClassMeths.get(wrp).methodStruct; + wrp.getHideMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); + } + } + } + + private void processClassRec(ClassNode node, + final HashMap<ClassWrapper, MethodWrapper> mapClassMeths, + final HashSet<ClassWrapper> setFound) { + + final ClassWrapper wrapper = node.wrapper; + + // search code + for (MethodWrapper meth : wrapper.getMethods()) { + + RootStatement root = meth.root; + if (root != null) { + + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { + if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { + setFound.add(ent.getKey()); + } + } + return 0; + } + }); + } + } + + // search initializers + for (int j = 0; j < 2; j++) { + VBStyleCollection<Exprent, String> initializers = + j == 0 ? wrapper.getStaticFieldInitializers() : wrapper.getDynamicFieldInitializers(); + + for (int i = 0; i < initializers.size(); i++) { + for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { + Exprent exprent = initializers.get(i); + if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { + setFound.add(ent.getKey()); + } + + String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue()); + if (cl != null) { + initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); + setFound.add(ent.getKey()); + } + } + } + } + + // iterate nested classes + for (ClassNode nd : node.nested) { + processClassRec(nd, mapClassMeths, setFound); + } + } + + private void findClassMethod(ClassNode node, HashMap<ClassWrapper, MethodWrapper> mapClassMeths) { + + boolean nosynthflag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + ClassWrapper wrapper = node.wrapper; + + for (MethodWrapper meth : wrapper.getMethods()) { + StructMethod mt = meth.methodStruct; + + if (((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic") + || nosynthflag) && + mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") && + (mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0) { + + RootStatement root = meth.root; + if (root != null) { + if (root.getFirst().type == Statement.TYPE_TRYCATCH) { + CatchStatement cst = (CatchStatement)root.getFirst(); + if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK && + cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK && + cst.getVars().get(0).getVartype().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) { + + BasicBlockStatement body = (BasicBlockStatement)cst.getFirst(); + BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1); + + if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) { + if (bodyexprent.equals(body.getExprents().get(0)) && + handlerexprent.equals(handler.getExprents().get(0))) { + mapClassMeths.put(wrapper, meth); + break; + } + } + } + } + } + } + } + + // iterate nested classes + for (ClassNode nd : node.nested) { + findClassMethod(nd, mapClassMeths); + } + } + + + private boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { + + boolean res = false; + + for (; ; ) { + + boolean found = false; + + for (Exprent expr : exprent.getAllExprents()) { + String cl = isClass14Invocation(expr, wrapper, meth); + if (cl != null) { + exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); + found = true; + res = true; + break; + } + + res |= replaceInvocations(expr, wrapper, meth); + } + + if (!found) { + break; + } + } + + return res; + } + + + private String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + if (fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) { + if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0); + if (headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) { + if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD && + headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST && + ((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) { + + FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0); + ClassNode fieldnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(field.getClassname()); + + if (fieldnode != null && fieldnode.classStruct.qualifiedName.equals(wrapper.getClassStruct().qualifiedName)) { // source class + StructField fd = + wrapper.getClassStruct().getField(field.getName(), field.getDescriptor().descriptorString); // FIXME: can be null! why?? + + if (fd != null && (fd.access_flags & CodeConstants.ACC_STATIC) != 0 && + ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic") + || DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET))) { + + if (fexpr.getLstOperands().get(1).type == Exprent.EXPRENT_ASSIGNMENT && fexpr.getLstOperands().get(2).equals(field)) { + AssignmentExprent asexpr = (AssignmentExprent)fexpr.getLstOperands().get(1); + + if (asexpr.getLeft().equals(field) && asexpr.getRight().type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)asexpr.getRight(); + + if (invexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName) && + invexpr.getName().equals(meth.methodStruct.getName()) && + invexpr.getStringDescriptor().equals(meth.methodStruct.getDescriptor())) { + + if (invexpr.getLstParameters().get(0).type == Exprent.EXPRENT_CONST) { + wrapper.getHideMembers() + .add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field + return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString(); + } + } + } + } + } + } + } + } + } + } + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index c14fe79..3c8afab 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -1,27 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; @@ -40,1068 +33,1106 @@ import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; -import org.jetbrains.java.decompiler.struct.attr.StructAnnDefaultAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructConstantValueAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; +import org.jetbrains.java.decompiler.struct.attr.*; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; -import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; +import org.jetbrains.java.decompiler.struct.gen.generics.*; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + public class ClassWriter { - - private static final int[] modval_class = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_STRICT}; - - private static final String[] modstr_class = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "strictfp "}; - - private static final int[] modval_field = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_TRANSIENT, CodeConstants.ACC_VOLATILE}; - - private static final String[] modstr_field = new String[] {"public ", "protected ", "private ", "static ", "final ", "transient ", "volatile "}; - - private static final int[] modval_meth = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, - CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_SYNCHRONIZED, - CodeConstants.ACC_NATIVE, CodeConstants.ACC_STRICT}; - - private static final String[] modstr_meth = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "synchronized ", "native ", "strictfp "}; - - private static final HashSet<Integer> mod_notinterface = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC})); - private static final HashSet<Integer> mod_notinterface_fields = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL})); - private static final HashSet<Integer> mod_notinterface_meth = new HashSet<Integer>(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_ABSTRACT})); - - private ClassReference14Processor ref14processor; - - private PoolInterceptor interceptor; - - public ClassWriter() { - ref14processor = new ClassReference14Processor(); - interceptor = DecompilerContext.getPoolInterceptor(); - } - - - private void invokeProcessors(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - InitializerProcessor.extractInitializers(wrapper); - - if(node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { - ref14processor.processClassReferences(node); - } - - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (cl.access_flags & CodeConstants.ACC_ENUM) != 0) { - EnumProcessor.clearEnum(wrapper); - } - - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { - AssertProcessor.buildAssertions(node); - } - - } - - public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException { - - // get the class node with the content method - ClassNode node_content = node; - while(node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) { - node_content = node_content.parent; - } - - if(node_content == null) { - return; - } - - boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); - - ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); - - ClassWrapper wrapper = node_content.wrapper; - StructClass cl = wrapper.getClassStruct(); - - DecompilerContext.getLogger().startWriteClass(node.simpleName); - - if(node.lambda_information.is_method_reference) { - - if(!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method - writer.write(method_object.toJava(indent)); - } else { // reference to a static method - writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); - } - - writer.write("::"); - writer.write(node.lambda_information.content_method_name); - - writer.flush(); - - } else { - - // lambda method - StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); - - if(!lambda_to_anonymous) { // lambda parameters '() ->' - - StringBuilder buff = new StringBuilder("("); - - boolean firstpar = true; - int index = node.lambda_information.is_content_method_static ? 0 : 1;; - - int start_index = md_content.params.length - md_lambda.params.length; - - for(int i=0;i<md_content.params.length;i++) { - - if(i >= start_index) { - - if(!firstpar) { - buff.append(", "); - } - - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - buff.append(parname==null ? "param"+index : parname); // null iff decompiled with errors - - firstpar = false; - } - - index+=md_content.params[i].stack_size; - } - buff.append(") ->"); - - writer.write(buff.toString()); - } - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - if(lambda_to_anonymous) { - methodLambdaToJava(node, node_content, mt, bufstrwriter, indent+1, false); - } else { - methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true); - } - - bufstrwriter.flush(); - - // closing up class definition - writer.write(" {"); - writer.write(DecompilerContext.getNewLineSeparator()); - - writer.write(strwriter.toString()); - - writer.write(InterpreterUtil.getIndentString(indent)); - writer.write("}"); - writer.flush(); - } - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); - - DecompilerContext.getLogger().endWriteClass(); - } - - public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); - - // last minute processing - invokeProcessors(node); - - DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); - - writeClassDefinition(node, writer, indent); - - // methods - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - boolean firstmt = true; - boolean mthidden = false; - - for(StructMethod mt : cl.getMethods()) { - - int flags = mt.getAccessFlags(); - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); - boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; - - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && - (!isBridge || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE)) && - !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()))) { - if(!mthidden && (!firstmt || node.type != ClassNode.CLASS_ANONYMOUS)) { - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - firstmt = false; - } - - mthidden = !methodToJava(node, mt, bufstrwriter, indent+1); - } - } - bufstrwriter.flush(); - - StringWriter strwriter1 = new StringWriter(); - BufferedWriter bufstrwriter1 = new BufferedWriter(strwriter1); - - int fields_count = 0; - - boolean enumfields = false; - - // fields - for(StructField fd: cl.getFields()) { - int flags = fd.access_flags; - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) - && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { - - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - if(isEnum) { - if(enumfields) { - bufstrwriter1.write(","); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - } else { - enumfields = true; - } - } else { - if(enumfields) { - bufstrwriter1.write(";"); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - enumfields = false; - } - } - - fieldToJava(wrapper, cl, fd, bufstrwriter1, indent+1); - fields_count++; - } - } - - if(enumfields) { - bufstrwriter1.write(";"); - bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter1.flush(); - - if(fields_count > 0) { - writer.write(DecompilerContext.getNewLineSeparator()); - writer.write(strwriter1.toString()); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - - // methods - writer.write(strwriter.toString()); - - // member classes - for(ClassNode inner : node.nested) { - if(inner.type == ClassNode.CLASS_MEMBER) { - StructClass innercl = inner.classStruct; - - boolean isSynthetic = ((inner.access | innercl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || innercl.getAttributes().containsKey("Synthetic"); - if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) - && !wrapper.getHideMembers().contains(innercl.qualifiedName)) { - writer.write(DecompilerContext.getNewLineSeparator()); - classToJava(inner, writer, indent+1); - } - } - } - - writer.write(InterpreterUtil.getIndentString(indent)); - writer.write("}"); - if(node.type != ClassNode.CLASS_ANONYMOUS) { - writer.write(DecompilerContext.getNewLineSeparator()); - } - writer.flush(); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); - - DecompilerContext.getLogger().endWriteClass(); - } - - private void writeClassDefinition(ClassNode node, BufferedWriter writer, int indent) throws IOException { - - if(node.type == ClassNode.CLASS_ANONYMOUS) { - writer.write(" {"); - writer.write(DecompilerContext.getNewLineSeparator()); - } else { - - String indstr = InterpreterUtil.getIndentString(indent); - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - int flags = node.type == ClassNode.CLASS_ROOT?cl.access_flags:node.access; - boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; - boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - - boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName); - if(oldname != null) { - writer.write(indstr); - writer.write("// $FF: renamed from: "+getDescriptorPrintOut(oldname, 0)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - } - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // class annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); - - if(isSynthetic) { - writer.write(indstr); - writer.write("// $FF: synthetic class"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - writer.write(indstr); - - if(isEnum) { - // remove abstract and final flags (JLS 8.9 Enums) - flags &=~CodeConstants.ACC_ABSTRACT; - flags &=~CodeConstants.ACC_FINAL; - } - - for(int i=0;i<modval_class.length;i++) { - if(!isInterface || !mod_notinterface.contains(modval_class[i])) { - if((flags & modval_class[i]) != 0) { - writer.write(modstr_class[i]); - } - } - } - - if(isEnum) { - writer.write("enum "); - }else if(isInterface) { - if(isAnnotation) { - writer.write("@"); - } - writer.write("interface "); - } else { - writer.write("class "); - } - - GenericClassDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseClassSignature(attr.getSignature()); - } - } - - writer.write(node.simpleName); - if(descriptor != null && !descriptor.fparameters.isEmpty()) { - writer.write("<"); - for(int i=0;i<descriptor.fparameters.size();i++) { - if(i>0) { - writer.write(", "); - } - writer.write(descriptor.fparameters.get(i)); - - List<GenericType> lstBounds = descriptor.fbounds.get(i); + + private static final int[] modval_class = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_STRICT}; + + private static final String[] modstr_class = + new String[]{"public ", "protected ", "private ", "abstract ", "static ", "final ", "strictfp "}; + + private static final int[] modval_field = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_TRANSIENT, CodeConstants.ACC_VOLATILE}; + + private static final String[] modstr_field = + new String[]{"public ", "protected ", "private ", "static ", "final ", "transient ", "volatile "}; + + private static final int[] modval_meth = new int[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, + CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_SYNCHRONIZED, + CodeConstants.ACC_NATIVE, CodeConstants.ACC_STRICT}; + + private static final String[] modstr_meth = + new String[]{"public ", "protected ", "private ", "abstract ", "static ", "final ", "synchronized ", "native ", "strictfp "}; + + private static final HashSet<Integer> mod_notinterface = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC})); + private static final HashSet<Integer> mod_notinterface_fields = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL})); + private static final HashSet<Integer> mod_notinterface_meth = + new HashSet<Integer>(Arrays.asList(new Integer[]{CodeConstants.ACC_PUBLIC, CodeConstants.ACC_ABSTRACT})); + + private ClassReference14Processor ref14processor; + + private PoolInterceptor interceptor; + + public ClassWriter() { + ref14processor = new ClassReference14Processor(); + interceptor = DecompilerContext.getPoolInterceptor(); + } + + + private void invokeProcessors(ClassNode node) { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + InitializerProcessor.extractInitializers(wrapper); + + if (node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { + ref14processor.processClassReferences(node); + } + + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (cl.access_flags & CodeConstants.ACC_ENUM) != 0) { + EnumProcessor.clearEnum(wrapper); + } + + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { + AssertProcessor.buildAssertions(node); + } + } + + public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException { + + // get the class node with the content method + ClassNode node_content = node; + while (node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) { + node_content = node_content.parent; + } + + if (node_content == null) { + return; + } + + boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); + + ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); + + ClassWrapper wrapper = node_content.wrapper; + StructClass cl = wrapper.getClassStruct(); + + DecompilerContext.getLogger().startWriteClass(node.simpleName); + + if (node.lambda_information.is_method_reference) { + + if (!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method + writer.write(method_object.toJava(indent)); + } + else { // reference to a static method + writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); + } + + writer.write("::"); + writer.write(node.lambda_information.content_method_name); + + writer.flush(); + } + else { + + // lambda method + StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); + + if (!lambda_to_anonymous) { // lambda parameters '() ->' + + StringBuilder buff = new StringBuilder("("); + + boolean firstpar = true; + int index = node.lambda_information.is_content_method_static ? 0 : 1; + ; + + int start_index = md_content.params.length - md_lambda.params.length; + + for (int i = 0; i < md_content.params.length; i++) { + + if (i >= start_index) { + + if (!firstpar) { + buff.append(", "); + } + + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + buff.append(parname == null ? "param" + index : parname); // null iff decompiled with errors + + firstpar = false; + } + + index += md_content.params[i].stack_size; + } + buff.append(") ->"); + + writer.write(buff.toString()); + } + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + if (lambda_to_anonymous) { + methodLambdaToJava(node, node_content, mt, bufstrwriter, indent + 1, false); + } + else { + methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true); + } + + bufstrwriter.flush(); + + // closing up class definition + writer.write(" {"); + writer.write(DecompilerContext.getNewLineSeparator()); + + writer.write(strwriter.toString()); + + writer.write(InterpreterUtil.getIndentString(indent)); + writer.write("}"); + writer.flush(); + } + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); + + DecompilerContext.getLogger().endWriteClass(); + } + + public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); + + // last minute processing + invokeProcessors(node); + + DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); + + writeClassDefinition(node, writer, indent); + + // methods + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + boolean firstmt = true; + boolean mthidden = false; + + for (StructMethod mt : cl.getMethods()) { + + int flags = mt.getAccessFlags(); + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; + + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && + (!isBridge || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE)) && + !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()))) { + if (!mthidden && (!firstmt || node.type != ClassNode.CLASS_ANONYMOUS)) { + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + firstmt = false; + } + + mthidden = !methodToJava(node, mt, bufstrwriter, indent + 1); + } + } + bufstrwriter.flush(); + + StringWriter strwriter1 = new StringWriter(); + BufferedWriter bufstrwriter1 = new BufferedWriter(strwriter1); + + int fields_count = 0; + + boolean enumfields = false; + + // fields + for (StructField fd : cl.getFields()) { + int flags = fd.access_flags; + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) + && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { + + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + if (isEnum) { + if (enumfields) { + bufstrwriter1.write(","); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + } + else { + enumfields = true; + } + } + else { + if (enumfields) { + bufstrwriter1.write(";"); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + enumfields = false; + } + } + + fieldToJava(wrapper, cl, fd, bufstrwriter1, indent + 1); + fields_count++; + } + } + + if (enumfields) { + bufstrwriter1.write(";"); + bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter1.flush(); + + if (fields_count > 0) { + writer.write(DecompilerContext.getNewLineSeparator()); + writer.write(strwriter1.toString()); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + + // methods + writer.write(strwriter.toString()); + + // member classes + for (ClassNode inner : node.nested) { + if (inner.type == ClassNode.CLASS_MEMBER) { + StructClass innercl = inner.classStruct; + + boolean isSynthetic = + ((inner.access | innercl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || innercl.getAttributes().containsKey("Synthetic"); + if ((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) + && !wrapper.getHideMembers().contains(innercl.qualifiedName)) { + writer.write(DecompilerContext.getNewLineSeparator()); + classToJava(inner, writer, indent + 1); + } + } + } + + writer.write(InterpreterUtil.getIndentString(indent)); + writer.write("}"); + if (node.type != ClassNode.CLASS_ANONYMOUS) { + writer.write(DecompilerContext.getNewLineSeparator()); + } + writer.flush(); + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); + + DecompilerContext.getLogger().endWriteClass(); + } + + private void writeClassDefinition(ClassNode node, BufferedWriter writer, int indent) throws IOException { + + if (node.type == ClassNode.CLASS_ANONYMOUS) { + writer.write(" {"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + else { + + String indstr = InterpreterUtil.getIndentString(indent); + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + int flags = node.type == ClassNode.CLASS_ROOT ? cl.access_flags : node.access; + boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; + boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + + boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName); + if (oldname != null) { + writer.write(indstr); + writer.write("// $FF: renamed from: " + getDescriptorPrintOut(oldname, 0)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // class annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); + + if (isSynthetic) { + writer.write(indstr); + writer.write("// $FF: synthetic class"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + writer.write(indstr); + + if (isEnum) { + // remove abstract and final flags (JLS 8.9 Enums) + flags &= ~CodeConstants.ACC_ABSTRACT; + flags &= ~CodeConstants.ACC_FINAL; + } + + for (int i = 0; i < modval_class.length; i++) { + if (!isInterface || !mod_notinterface.contains(modval_class[i])) { + if ((flags & modval_class[i]) != 0) { + writer.write(modstr_class[i]); + } + } + } + + if (isEnum) { + writer.write("enum "); + } + else if (isInterface) { + if (isAnnotation) { + writer.write("@"); + } + writer.write("interface "); + } + else { + writer.write("class "); + } + + GenericClassDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseClassSignature(attr.getSignature()); + } + } + + writer.write(node.simpleName); + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + writer.write("<"); + for (int i = 0; i < descriptor.fparameters.size(); i++) { + if (i > 0) { + writer.write(", "); + } + writer.write(descriptor.fparameters.get(i)); + + List<GenericType> lstBounds = descriptor.fbounds.get(i); if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { writer.write(" extends "); writer.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); - for(int j=1;j<lstBounds.size();j++) { + for (int j = 1; j < lstBounds.size(); j++) { writer.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); } } - } - writer.write(">"); - } - writer.write(" "); - - if(!isEnum && !isInterface && cl.superClass != null) { - VarType supertype = new VarType(cl.superClass.getString(), true); - if(!VarType.VARTYPE_OBJECT.equals(supertype)) { - writer.write("extends "); - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.superclass)); - } else { - writer.write(ExprProcessor.getCastTypeName(supertype)); - } - writer.write(" "); - } - } - - if(!isAnnotation) { - int[] interfaces = cl.getInterfaces(); - if(interfaces.length > 0) { - writer.write(isInterface?"extends ":"implements "); - for(int i=0;i<interfaces.length;i++) { - if(i>0) { - writer.write(", "); - } - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); - } else { - writer.write(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); - } - } - writer.write(" "); - } - } - - writer.write("{"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - } - - private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, BufferedWriter writer, int indent) throws IOException { - - String indstr = InterpreterUtil.getIndentString(indent); - - boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; - int flags = fd.access_flags; - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName+" "+fd.getName()+" "+fd.getDescriptor()); - if(oldname != null) { - String[] element = oldname.split(" "); - - writer.write(indstr); - writer.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 1)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - } - - boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // field annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - - if(isSynthetic) { - writer.write(indstr); - writer.write("// $FF: synthetic field"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - writer.write(indstr); - - if(!isEnum) { - for(int i=0;i<modval_field.length;i++) { - if(!isInterface || !mod_notinterface_fields.contains(modval_field[i])) { - if((flags & modval_field[i]) != 0) { - writer.write(modstr_field[i]); - } - } - } - } - - VarType fieldType = new VarType(fd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } - - if(!isEnum) { - if(descriptor != null) { - writer.write(GenericMain.getGenericCastTypeName(descriptor.type)); - } else { - writer.write(ExprProcessor.getCastTypeName(fieldType)); - } - writer.write(" "); - } - - writer.write(fd.getName()); - - Exprent initializer; - if((flags & CodeConstants.ACC_STATIC) != 0) { - initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } else { - initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } - - if(initializer != null) { - if(isEnum && initializer.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)initializer; - nexpr.setEnumconst(true); - writer.write(nexpr.toJava(indent)); - } else { - writer.write(" = "); - writer.write(initializer.toJava(indent)); - } - } else if((flags & CodeConstants.ACC_FINAL) != 0 && (flags & CodeConstants.ACC_STATIC) != 0) { - StructConstantValueAttribute attr = (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); - if(attr != null) { - PrimitiveConstant cnst = cl.getPool().getPrimitiveConstant(attr.getIndex()); - writer.write(" = "); - writer.write(new ConstExprent(fieldType, cnst.value).toJava(indent)); - } - } - - if(!isEnum) { - writer.write(";"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - } - - public boolean methodLambdaToJava(ClassNode node_lambda, ClassNode node_content, StructMethod mt, BufferedWriter writer, int indent, boolean code_only) throws IOException { - - ClassWrapper wrapper = node_content.wrapper; - - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); - - String indstr = InterpreterUtil.getIndentString(indent); - - String method_name = node_lambda.lambda_information.method_name; - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.method_descriptor); - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - if(!code_only) { - bufstrwriter.write(indstr); - bufstrwriter.write("public "); - bufstrwriter.write(method_name); - bufstrwriter.write("("); - - boolean firstpar = true; - int index = node_lambda.lambda_information.is_content_method_static ? 0 : 1;; - - int start_index = md_content.params.length - md_lambda.params.length; - - for(int i=0;i<md_content.params.length;i++) { - - if(i >= start_index) { - - if(!firstpar) { - bufstrwriter.write(", "); - } - - VarType partype = md_content.params[i].copy(); - - String strpartype = ExprProcessor.getCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - bufstrwriter.write(" "); - - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors - - firstpar = false; - } - - index+=md_content.params[i].stack_size; - } - - bufstrwriter.write(")"); - bufstrwriter.write(" "); - bufstrwriter.write("{"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; - - if(root != null && !meth.decompiledWithErrors) { // check for existence - try { - String code = root.toJava(indent+1); - bufstrwriter.write(code); - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); - meth.decompiledWithErrors = true; - } - } - - if(meth.decompiledWithErrors) { - bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); - bufstrwriter.write("// $FF: Couldn't be decompiled"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - if(!code_only) { - bufstrwriter.write(indstr+"}"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.flush(); - - writer.write(strwriter.toString()); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); - - return true; - } - - public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException { - - ClassWrapper wrapper = node.wrapper; - StructClass cl = wrapper.getClassStruct(); - - MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - - MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); - - boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; - boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); - boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); - - String indstr = InterpreterUtil.getIndentString(indent); - boolean clinit = false, init = false, dinit = false; - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - int flags = mt.getAccessFlags(); - if((flags & CodeConstants.ACC_NATIVE) != 0) { - flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp - } - - if("<clinit>".equals(mt.getName())) { - flags &= CodeConstants.ACC_STATIC; // ingnore all modifiers except 'static' in a static initializer - } - - if(interceptor != null) { - String oldname = interceptor.getOldName(cl.qualifiedName+" "+mt.getName()+" "+mt.getDescriptor()); - if(oldname != null) { - String[] element = oldname.split(" "); - - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 2)); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - } - - if (isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - // method annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - bufstrwriter.write(annexpr.toJava(indent)); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); - boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; - - if(isSynthetic) { - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: synthetic method"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - if(isBridge) { - bufstrwriter.write(indstr); - bufstrwriter.write("// $FF: bridge method"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.write(indstr); - for(int i=0;i<modval_meth.length;i++) { - if(!isInterface || !mod_notinterface_meth.contains(modval_meth[i])) { - if((flags & modval_meth[i]) != 0) { - bufstrwriter.write(modstr_meth[i]); - } - } - } - - // 'default' modifier (Java 8) - if(isInterface && mt.containsCode()) { - bufstrwriter.write("default "); - } - - String name = mt.getName(); - if ("<init>".equals(name)) { - if (node.type == ClassNode.CLASS_ANONYMOUS) { - name = ""; - dinit = true; - } else { - name = node.simpleName; - init = true; - } - } else if ("<clinit>".equals(name)) { - name = ""; - clinit = true; - } - - GenericMethodDescriptor descriptor = null; - if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); - if(attr != null) { - descriptor = GenericMain.parseMethodSignature(attr.getSignature()); + } + writer.write(">"); + } + writer.write(" "); + + if (!isEnum && !isInterface && cl.superClass != null) { + VarType supertype = new VarType(cl.superClass.getString(), true); + if (!VarType.VARTYPE_OBJECT.equals(supertype)) { + writer.write("extends "); + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.superclass)); + } + else { + writer.write(ExprProcessor.getCastTypeName(supertype)); + } + writer.write(" "); + } + } + + if (!isAnnotation) { + int[] interfaces = cl.getInterfaces(); + if (interfaces.length > 0) { + writer.write(isInterface ? "extends " : "implements "); + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) { + writer.write(", "); + } + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); + } + else { + writer.write(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); + } + } + writer.write(" "); + } + } + + writer.write("{"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, BufferedWriter writer, int indent) throws IOException { + + String indstr = InterpreterUtil.getIndentString(indent); + + boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; + int flags = fd.access_flags; + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor()); + if (oldname != null) { + String[] element = oldname.split(" "); + + writer.write(indstr); + writer.write("// $FF: renamed from: " + element[1] + " " + getDescriptorPrintOut(element[2], 1)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // field annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + + if (isSynthetic) { + writer.write(indstr); + writer.write("// $FF: synthetic field"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + writer.write(indstr); + + if (!isEnum) { + for (int i = 0; i < modval_field.length; i++) { + if (!isInterface || !mod_notinterface_fields.contains(modval_field[i])) { + if ((flags & modval_field[i]) != 0) { + writer.write(modstr_field[i]); + } + } + } + } + + VarType fieldType = new VarType(fd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + if (!isEnum) { + if (descriptor != null) { + writer.write(GenericMain.getGenericCastTypeName(descriptor.type)); + } + else { + writer.write(ExprProcessor.getCastTypeName(fieldType)); + } + writer.write(" "); + } + + writer.write(fd.getName()); + + Exprent initializer; + if ((flags & CodeConstants.ACC_STATIC) != 0) { + initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + else { + initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + + if (initializer != null) { + if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)initializer; + nexpr.setEnumconst(true); + writer.write(nexpr.toJava(indent)); + } + else { + writer.write(" = "); + writer.write(initializer.toJava(indent)); + } + } + else if ((flags & CodeConstants.ACC_FINAL) != 0 && (flags & CodeConstants.ACC_STATIC) != 0) { + StructConstantValueAttribute attr = + (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); + if (attr != null) { + PrimitiveConstant cnst = cl.getPool().getPrimitiveConstant(attr.getIndex()); + writer.write(" = "); + writer.write(new ConstExprent(fieldType, cnst.value).toJava(indent)); + } + } + + if (!isEnum) { + writer.write(";"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + public boolean methodLambdaToJava(ClassNode node_lambda, + ClassNode node_content, + StructMethod mt, + BufferedWriter writer, + int indent, + boolean code_only) throws IOException { + + ClassWrapper wrapper = node_content.wrapper; + + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); + + String indstr = InterpreterUtil.getIndentString(indent); + + String method_name = node_lambda.lambda_information.method_name; + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.method_descriptor); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + if (!code_only) { + bufstrwriter.write(indstr); + bufstrwriter.write("public "); + bufstrwriter.write(method_name); + bufstrwriter.write("("); + + boolean firstpar = true; + int index = node_lambda.lambda_information.is_content_method_static ? 0 : 1; + ; + + int start_index = md_content.params.length - md_lambda.params.length; + + for (int i = 0; i < md_content.params.length; i++) { + + if (i >= start_index) { + + if (!firstpar) { + bufstrwriter.write(", "); + } + + VarType partype = md_content.params[i].copy(); + + String strpartype = ExprProcessor.getCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + bufstrwriter.write(" "); + + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + bufstrwriter.write(parname == null ? "param" + index : parname); // null iff decompiled with errors + + firstpar = false; + } + + index += md_content.params[i].stack_size; + } + + bufstrwriter.write(")"); + bufstrwriter.write(" "); + bufstrwriter.write("{"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + + if (root != null && !meth.decompiledWithErrors) { // check for existence + try { + String code = root.toJava(indent + 1); + bufstrwriter.write(code); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + meth.decompiledWithErrors = true; + } + } + + if (meth.decompiledWithErrors) { + bufstrwriter.write(InterpreterUtil.getIndentString(indent + 1)); + bufstrwriter.write("// $FF: Couldn't be decompiled"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + if (!code_only) { + bufstrwriter.write(indstr + "}"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.flush(); + + writer.write(strwriter.toString()); + + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); + + return true; + } + + public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException { + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); + + boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; + boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; + boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); + + String indstr = InterpreterUtil.getIndentString(indent); + boolean clinit = false, init = false, dinit = false; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + int flags = mt.getAccessFlags(); + if ((flags & CodeConstants.ACC_NATIVE) != 0) { + flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp + } + + if ("<clinit>".equals(mt.getName())) { + flags &= CodeConstants.ACC_STATIC; // ingnore all modifiers except 'static' in a static initializer + } + + if (interceptor != null) { + String oldname = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); + if (oldname != null) { + String[] element = oldname.split(" "); + + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: renamed from: " + element[1] + " " + getDescriptorPrintOut(element[2], 2)); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + } + + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + + // method annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); + for (AnnotationExprent annexpr : lstAnn) { + bufstrwriter.write(annexpr.toJava(indent)); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; + + if (isSynthetic) { + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: synthetic method"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + if (isBridge) { + bufstrwriter.write(indstr); + bufstrwriter.write("// $FF: bridge method"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.write(indstr); + for (int i = 0; i < modval_meth.length; i++) { + if (!isInterface || !mod_notinterface_meth.contains(modval_meth[i])) { + if ((flags & modval_meth[i]) != 0) { + bufstrwriter.write(modstr_meth[i]); + } + } + } + + // 'default' modifier (Java 8) + if (isInterface && mt.containsCode()) { + bufstrwriter.write("default "); + } + + String name = mt.getName(); + if ("<init>".equals(name)) { + if (node.type == ClassNode.CLASS_ANONYMOUS) { + name = ""; + dinit = true; + } + else { + name = node.simpleName; + init = true; + } + } + else if ("<clinit>".equals(name)) { + name = ""; + clinit = true; + } + + GenericMethodDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseMethodSignature(attr.getSignature()); int actualParams = md.params.length; - if(isEnum && init) actualParams -= 2; - if(actualParams != descriptor.params.size()) { - DecompilerContext.getLogger().writeMessage("Inconsistent generic signature in method "+mt.getName()+" "+mt.getDescriptor(), IFernflowerLogger.WARNING); - descriptor = null; - } - } - } - - boolean throwsExceptions = false; - - int param_count_explicit = 0; - - if(!clinit && !dinit) { - - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - // formal type parameters - if(descriptor != null && !descriptor.fparameters.isEmpty()) { - bufstrwriter.write("<"); - for(int i=0;i<descriptor.fparameters.size();i++) { - if(i>0) { - bufstrwriter.write(", "); - } - bufstrwriter.write(descriptor.fparameters.get(i)); - - List<GenericType> lstBounds = descriptor.fbounds.get(i); - if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { - bufstrwriter.write(" extends "); - bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); - - for(int j = 1; j < lstBounds.size(); j++) { - bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); - } - } - } - bufstrwriter.write("> "); - } - - if(!init) { - if(descriptor != null) { - bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.ret)); - } else { - bufstrwriter.write(ExprProcessor.getCastTypeName(md.ret)); - } - bufstrwriter.write(" "); - } - - bufstrwriter.write(name); - bufstrwriter.write("("); - - // parameter annotations - List<List<AnnotationExprent>> lstParAnn = getAllParameterAnnotations(mt.getAttributes()); - - List<VarVersionPaar> signFields = meth.signatureFields; - - // compute last visible parameter - int lastparam_index = -1; - for(int i=0;i<md.params.length;i++) { - if(signFields == null || signFields.get(i) == null) { - lastparam_index = i; - } - } - - boolean firstpar = true; - int index = isEnum && init ? 3 : thisvar ? 1 : 0; - int start = isEnum && init && descriptor == null ? 2 : 0; - int params = descriptor == null ? md.params.length : descriptor.params.size(); - for(int i = start; i < params; i++) { - if (signFields == null || signFields.get(i) == null) { - - if(!firstpar) { - bufstrwriter.write(", "); - } - - if(lstParAnn.size() > param_count_explicit) { - List<AnnotationExprent> annotations = lstParAnn.get(param_count_explicit); - for(int j=0;j<annotations.size();j++) { - AnnotationExprent annexpr = annotations.get(j); - if(annexpr.getAnnotationType() == AnnotationExprent.ANNOTATION_NORMAL) { - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - bufstrwriter.write(annexpr.toJava(indent+1)); - } else { - bufstrwriter.write(annexpr.toJava(0)); - } - bufstrwriter.write(" "); - } - } - - if(meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { - bufstrwriter.write("final "); - } - - - if(descriptor != null) { - GenericType partype = descriptor.params.get(i); - - boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 - && partype.arraydim > 0); - - if(isVarArgs) { - partype.arraydim--; - } - - String strpartype = GenericMain.getGenericCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - - if(isVarArgs) { - bufstrwriter.write(" ..."); - } - - } else { - VarType partype = md.params[i].copy(); - - boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 - && partype.arraydim > 0); - - if(isVarArgs) { - partype.decArrayDim(); - } - - String strpartype = ExprProcessor.getCastTypeName(partype); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - bufstrwriter.write(strpartype); - - if(isVarArgs) { - bufstrwriter.write(" ..."); - } - } - - bufstrwriter.write(" "); - String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); - bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors - firstpar = false; - param_count_explicit++; - } - - index+=md.params[i].stack_size; - } - - bufstrwriter.write(")"); - - StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); - if((descriptor!=null && !descriptor.exceptions.isEmpty()) || attr != null) { - throwsExceptions = true; - bufstrwriter.write(" throws "); - - for(int i=0;i<attr.getThrowsExceptions().size();i++) { - if(i>0) { - bufstrwriter.write(", "); - } - if(descriptor!=null && !descriptor.exceptions.isEmpty()) { - bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.exceptions.get(i))); - } else { - VarType exctype = new VarType(attr.getExcClassname(i, cl.getPool()), true); - bufstrwriter.write(ExprProcessor.getCastTypeName(exctype)); - } - } - } - } - - boolean hidemethod = false; - - if((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) - if(isAnnotation) { - StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); - if(attr != null) { - bufstrwriter.write(" default "); - bufstrwriter.write(attr.getDefaultValue().toJava(indent+1)); - } - } - - bufstrwriter.write(";"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } else { - if(!clinit && !dinit) { - bufstrwriter.write(" "); - } - bufstrwriter.write("{"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - - RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; - - if(root != null && !meth.decompiledWithErrors) { // check for existence - try { - String code = root.toJava(indent+1); - - boolean singleinit = false; - if(init && param_count_explicit == 0 && !throwsExceptions && DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { - int init_counter = 0; - for(MethodWrapper mth : wrapper.getMethods()) { - if("<init>".equals(mth.methodStruct.getName())) { - init_counter++; - } - } - singleinit = (init_counter == 1); - } - - hidemethod = (clinit || dinit || singleinit) && code.length() == 0; - - bufstrwriter.write(code); - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); - meth.decompiledWithErrors = true; - } - } - - if(meth.decompiledWithErrors) { - bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); - bufstrwriter.write("// $FF: Couldn't be decompiled"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.write(indstr+"}"); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } - - bufstrwriter.flush(); - - if(!hidemethod) { - writer.write(strwriter.toString()); - } - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); - - return !hidemethod; - } - - private List<AnnotationExprent> getAllAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { - - String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, - StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; - - List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); - - for(String attrname : annattrnames) { - StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(attrname); - if(attr != null) { - lst.addAll(attr.getAnnotations()); - } - } - - return lst; - } - - private List<List<AnnotationExprent>> getAllParameterAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { - - String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, - StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; - - List<List<AnnotationExprent>> ret = new ArrayList<List<AnnotationExprent>>(); - - for(String attrname : annattrnames) { - StructAnnotationParameterAttribute attr = (StructAnnotationParameterAttribute)attributes.getWithKey(attrname); - if(attr != null) { - for(int i=0;i<attr.getParamAnnotations().size();i++) { - List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); - boolean isnew = (ret.size()<=i); - - if(!isnew) { - lst = ret.get(i); - } - lst.addAll(attr.getParamAnnotations().get(i)); - - if(isnew) { - ret.add(lst); - } else { - ret.set(i, lst); - } - } - } - } - - return ret; - } - - private String getDescriptorPrintOut(String descriptor, int element) { - - switch(element) { - case 0: // class - return ExprProcessor.buildJavaClassName(descriptor); - case 1: // field - return getTypePrintOut(FieldDescriptor.parseDescriptor(descriptor).type); - case 2: // method - default: - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - - StringBuilder buffer = new StringBuilder("("); - - boolean first = true; - for(VarType partype : md.params) { - if(first) { - first = false; - } else { - buffer.append(", "); - } - buffer.append(getTypePrintOut(partype)); - } - buffer.append(") "); - buffer.append(getTypePrintOut(md.ret)); - - return buffer.toString(); - } - } - - private String getTypePrintOut(VarType type) { - String strtype = ExprProcessor.getCastTypeName(type, false); - if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strtype) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - strtype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); - } - return strtype; - } + if (isEnum && init) actualParams -= 2; + if (actualParams != descriptor.params.size()) { + DecompilerContext.getLogger() + .writeMessage("Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor(), IFernflowerLogger.WARNING); + descriptor = null; + } + } + } + + boolean throwsExceptions = false; + + int param_count_explicit = 0; + + if (!clinit && !dinit) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + // formal type parameters + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + bufstrwriter.write("<"); + for (int i = 0; i < descriptor.fparameters.size(); i++) { + if (i > 0) { + bufstrwriter.write(", "); + } + bufstrwriter.write(descriptor.fparameters.get(i)); + + List<GenericType> lstBounds = descriptor.fbounds.get(i); + if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { + bufstrwriter.write(" extends "); + bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); + + for (int j = 1; j < lstBounds.size(); j++) { + bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); + } + } + } + bufstrwriter.write("> "); + } + + if (!init) { + if (descriptor != null) { + bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.ret)); + } + else { + bufstrwriter.write(ExprProcessor.getCastTypeName(md.ret)); + } + bufstrwriter.write(" "); + } + + bufstrwriter.write(name); + bufstrwriter.write("("); + + // parameter annotations + List<List<AnnotationExprent>> lstParAnn = getAllParameterAnnotations(mt.getAttributes()); + + List<VarVersionPaar> signFields = meth.signatureFields; + + // compute last visible parameter + int lastparam_index = -1; + for (int i = 0; i < md.params.length; i++) { + if (signFields == null || signFields.get(i) == null) { + lastparam_index = i; + } + } + + boolean firstpar = true; + int index = isEnum && init ? 3 : thisvar ? 1 : 0; + int start = isEnum && init && descriptor == null ? 2 : 0; + int params = descriptor == null ? md.params.length : descriptor.params.size(); + for (int i = start; i < params; i++) { + if (signFields == null || signFields.get(i) == null) { + + if (!firstpar) { + bufstrwriter.write(", "); + } + + if (lstParAnn.size() > param_count_explicit) { + List<AnnotationExprent> annotations = lstParAnn.get(param_count_explicit); + for (int j = 0; j < annotations.size(); j++) { + AnnotationExprent annexpr = annotations.get(j); + if (annexpr.getAnnotationType() == AnnotationExprent.ANNOTATION_NORMAL) { + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + bufstrwriter.write(annexpr.toJava(indent + 1)); + } + else { + bufstrwriter.write(annexpr.toJava(0)); + } + bufstrwriter.write(" "); + } + } + + if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + bufstrwriter.write("final "); + } + + + if (descriptor != null) { + GenericType partype = descriptor.params.get(i); + + boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 + && partype.arraydim > 0); + + if (isVarArgs) { + partype.arraydim--; + } + + String strpartype = GenericMain.getGenericCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + + if (isVarArgs) { + bufstrwriter.write(" ..."); + } + } + else { + VarType partype = md.params[i].copy(); + + boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 + && partype.arraydim > 0); + + if (isVarArgs) { + partype.decArrayDim(); + } + + String strpartype = ExprProcessor.getCastTypeName(partype); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + bufstrwriter.write(strpartype); + + if (isVarArgs) { + bufstrwriter.write(" ..."); + } + } + + bufstrwriter.write(" "); + String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); + bufstrwriter.write(parname == null ? "param" + index : parname); // null iff decompiled with errors + firstpar = false; + param_count_explicit++; + } + + index += md.params[i].stack_size; + } + + bufstrwriter.write(")"); + + StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); + if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { + throwsExceptions = true; + bufstrwriter.write(" throws "); + + for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { + if (i > 0) { + bufstrwriter.write(", "); + } + if (descriptor != null && !descriptor.exceptions.isEmpty()) { + bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.exceptions.get(i))); + } + else { + VarType exctype = new VarType(attr.getExcClassname(i, cl.getPool()), true); + bufstrwriter.write(ExprProcessor.getCastTypeName(exctype)); + } + } + } + } + + boolean hidemethod = false; + + if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) + if (isAnnotation) { + StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); + if (attr != null) { + bufstrwriter.write(" default "); + bufstrwriter.write(attr.getDefaultValue().toJava(indent + 1)); + } + } + + bufstrwriter.write(";"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + else { + if (!clinit && !dinit) { + bufstrwriter.write(" "); + } + bufstrwriter.write("{"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + + RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + + if (root != null && !meth.decompiledWithErrors) { // check for existence + try { + String code = root.toJava(indent + 1); + + boolean singleinit = false; + if (init && + param_count_explicit == 0 && + !throwsExceptions && + DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { + int init_counter = 0; + for (MethodWrapper mth : wrapper.getMethods()) { + if ("<init>".equals(mth.methodStruct.getName())) { + init_counter++; + } + } + singleinit = (init_counter == 1); + } + + hidemethod = (clinit || dinit || singleinit) && code.length() == 0; + + bufstrwriter.write(code); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + meth.decompiledWithErrors = true; + } + } + + if (meth.decompiledWithErrors) { + bufstrwriter.write(InterpreterUtil.getIndentString(indent + 1)); + bufstrwriter.write("// $FF: Couldn't be decompiled"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.write(indstr + "}"); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } + + bufstrwriter.flush(); + + if (!hidemethod) { + writer.write(strwriter.toString()); + } + + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); + + return !hidemethod; + } + + private List<AnnotationExprent> getAllAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { + + String[] annattrnames = new String[]{StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, + StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; + + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + + for (String attrname : annattrnames) { + StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(attrname); + if (attr != null) { + lst.addAll(attr.getAnnotations()); + } + } + + return lst; + } + + private List<List<AnnotationExprent>> getAllParameterAnnotations(VBStyleCollection<StructGeneralAttribute, String> attributes) { + + String[] annattrnames = new String[]{StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, + StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + + List<List<AnnotationExprent>> ret = new ArrayList<List<AnnotationExprent>>(); + + for (String attrname : annattrnames) { + StructAnnotationParameterAttribute attr = (StructAnnotationParameterAttribute)attributes.getWithKey(attrname); + if (attr != null) { + for (int i = 0; i < attr.getParamAnnotations().size(); i++) { + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + boolean isnew = (ret.size() <= i); + + if (!isnew) { + lst = ret.get(i); + } + lst.addAll(attr.getParamAnnotations().get(i)); + + if (isnew) { + ret.add(lst); + } + else { + ret.set(i, lst); + } + } + } + } + + return ret; + } + + private String getDescriptorPrintOut(String descriptor, int element) { + + switch (element) { + case 0: // class + return ExprProcessor.buildJavaClassName(descriptor); + case 1: // field + return getTypePrintOut(FieldDescriptor.parseDescriptor(descriptor).type); + case 2: // method + default: + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + + StringBuilder buffer = new StringBuilder("("); + + boolean first = true; + for (VarType partype : md.params) { + if (first) { + first = false; + } + else { + buffer.append(", "); + } + buffer.append(getTypePrintOut(partype)); + } + buffer.append(") "); + buffer.append(getTypePrintOut(md.ret)); + + return buffer.toString(); + } + } + + private String getTypePrintOut(VarType type) { + String strtype = ExprProcessor.getCastTypeName(type, false); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(strtype) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + strtype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); + } + return strtype; + } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index e0e144f..155dee0 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -1,32 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; @@ -46,242 +34,254 @@ import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; + public class ClassesProcessor { - private HashMap<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>(); - - public ClassesProcessor(StructContext context) { - - HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>(); - - HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>(); - HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>(); - - HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>(); - - boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER); - - // create class nodes - for(StructClass cl: context.getClasses().values()) { - if(cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) { - - if(bDecompileInner) { - StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses"); - if(inner != null) { - - for(int i=0;i<inner.getClassentries().size();i++) { - - int[] entry = inner.getClassentries().get(i); - String[] strentry = inner.getStringentries().get(i); - - Object[] arr = new Object[4]; // arr[0] not used - - String innername = strentry[0]; - - // nested class type - arr[2] = entry[1] == 0?(entry[2]==0?ClassNode.CLASS_ANONYMOUS:ClassNode.CLASS_LOCAL):ClassNode.CLASS_MEMBER; - - // original simple name - String simpleName = strentry[2]; - String savedName = mapNewSimpleNames.get(innername); - - if(savedName != null) { - simpleName = savedName; - } else if(simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper(); - if(renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) { - simpleName = renamer.getNextClassname(innername, simpleName); - mapNewSimpleNames.put(innername, simpleName); - } - } - - arr[1] = simpleName; - - // original access flags - arr[3] = entry[3]; - - // enclosing class - String enclClassName = null; - if(entry[1] != 0) { - enclClassName = strentry[1]; - } else { - enclClassName = cl.qualifiedName; - } - - if(!innername.equals(enclClassName)) { // self reference - StructClass enclosing_class = context.getClasses().get(enclClassName); - if(enclosing_class != null && enclosing_class.isOwn()) { // own classes only - - Object[] arrold = mapInnerClasses.get(innername); - if(arrold == null) { - mapInnerClasses.put(innername, arr); - } else { - if(!InterpreterUtil.equalObjectArrays(arrold, arr)){ - DecompilerContext.getLogger().writeMessage("Inconsistent inner class entries for "+innername+"!", IFernflowerLogger.WARNING); - } - } - - // reference to the nested class - HashSet<String> set = mapNestedClassReferences.get(enclClassName); - if(set == null) { - mapNestedClassReferences.put(enclClassName, set = new HashSet<String>()); - } - set.add(innername); - - // reference to the enclosing class - set = mapEnclosingClassReferences.get(innername); - if(set == null) { - mapEnclosingClassReferences.put(innername, set = new HashSet<String>()); - } - set.add(enclClassName); - } - } - } - } - } - - ClassNode node = new ClassNode(ClassNode.CLASS_ROOT, cl); - node.access = cl.access_flags; - mapRootClasses.put(cl.qualifiedName, node); - } - } - - if(bDecompileInner) { - - // connect nested classes - for(Entry<String, ClassNode> ent: mapRootClasses.entrySet()) { - // root class? - if(!mapInnerClasses.containsKey(ent.getKey())) { - - HashSet<String> setVisited = new HashSet<String>(); - LinkedList<String> stack = new LinkedList<String>(); - - stack.add(ent.getKey()); - setVisited.add(ent.getKey()); - - while(!stack.isEmpty()) { - - String superClass = stack.removeFirst(); - ClassNode supernode = mapRootClasses.get(superClass); - - HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass); - if(setNestedClasses != null) { - - StructClass scl = supernode.classStruct; - StructInnerClassesAttribute inner = (StructInnerClassesAttribute) scl.getAttributes().getWithKey("InnerClasses"); - for(int i = 0; i < inner.getStringentries().size(); i++) { - String nestedClass = inner.getStringentries().get(i)[0]; - if (!setNestedClasses.contains(nestedClass)) { - continue; - } - - if(setVisited.contains(nestedClass)) { - continue; - } - setVisited.add(nestedClass); - - ClassNode nestednode = mapRootClasses.get(nestedClass); - if(nestednode == null) { - DecompilerContext.getLogger().writeMessage("Nested class "+nestedClass+" missing!", IFernflowerLogger.WARNING); - continue; - } - - Object[] arr = mapInnerClasses.get(nestedClass); - - if((Integer)arr[2] == ClassNode.CLASS_MEMBER) { - // FIXME: check for consistent naming - } - - nestednode.type = (Integer)arr[2]; - nestednode.simpleName = (String)arr[1]; - nestednode.access = (Integer)arr[3]; - - if(nestednode.type == ClassNode.CLASS_ANONYMOUS) { - StructClass cl = nestednode.classStruct; - - // remove static if anonymous class - // a common compiler bug - nestednode.access &= ~CodeConstants.ACC_STATIC; - - int[] interfaces = cl.getInterfaces(); - - if(interfaces.length > 0) { - if(interfaces.length > 1) { - DecompilerContext.getLogger().writeMessage("Inconsistent anonymous class definition: "+cl.qualifiedName, IFernflowerLogger.WARNING); - } - nestednode.anonimousClassType = new VarType(cl.getInterface(0), true); - } else { - nestednode.anonimousClassType = new VarType(cl.superClass.getString(), true); - } - } else if(nestednode.type == ClassNode.CLASS_LOCAL) { - // only abstract and final are permitted - // a common compiler bug - nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL); - } - - supernode.nested.add(nestednode); - nestednode.parent = supernode; - - nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass)); - - stack.add(nestedClass); - } - } - } - } - } - - } - - } - - - public void writeClass(StructContext context, StructClass cl, BufferedWriter outwriter) throws IOException { - - ClassNode root = mapRootClasses.get(cl.qualifiedName); - if(root.type != ClassNode.CLASS_ROOT) { - return; - } - - try { - DecompilerContext.setImpcollector(new ImportCollector(root)); - DecompilerContext.setCountercontainer(new CounterContainer()); - - // lambda processing - LambdaProcessor lambda_proc = new LambdaProcessor(); - lambda_proc.processClass(root); - - // add simple class names to implicit import - addClassnameToImport(root, DecompilerContext.getImpcollector()); - // build wrappers for all nested classes - // that's where the actual processing takes place - initWrappers(root); - - NestedClassProcessor nestedproc = new NestedClassProcessor(); - nestedproc.processClass(root, root); - - NestedMemberAccess nstmember = new NestedMemberAccess(); - nstmember.propagateMemberAccess(root); - - ClassWriter clwriter = new ClassWriter(); - - StringWriter strwriter = new StringWriter(); - clwriter.classToJava(root, new BufferedWriter(strwriter), 0); - - if(DecompilerContext.getOption(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT)) { - outwriter.write("// Decompiled by: Fernflower "+Fernflower.version); - outwriter.write(DecompilerContext.getNewLineSeparator()); - outwriter.write("// Date: "+new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date())); - outwriter.write(DecompilerContext.getNewLineSeparator()); - outwriter.write("// Copyright: 2008-2010, Stiver"); - outwriter.write(DecompilerContext.getNewLineSeparator()); - outwriter.write("// Home page: http://www.reversed-java.com"); - outwriter.write(DecompilerContext.getNewLineSeparator()); - outwriter.write(DecompilerContext.getNewLineSeparator()); - } - - int index = cl.qualifiedName.lastIndexOf("/"); - if(index >= 0) { + private HashMap<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>(); + + public ClassesProcessor(StructContext context) { + + HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>(); + + HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>(); + HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>(); + + HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>(); + + boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER); + + // create class nodes + for (StructClass cl : context.getClasses().values()) { + if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) { + + if (bDecompileInner) { + StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses"); + if (inner != null) { + + for (int i = 0; i < inner.getClassentries().size(); i++) { + + int[] entry = inner.getClassentries().get(i); + String[] strentry = inner.getStringentries().get(i); + + Object[] arr = new Object[4]; // arr[0] not used + + String innername = strentry[0]; + + // nested class type + arr[2] = entry[1] == 0 ? (entry[2] == 0 ? ClassNode.CLASS_ANONYMOUS : ClassNode.CLASS_LOCAL) : ClassNode.CLASS_MEMBER; + + // original simple name + String simpleName = strentry[2]; + String savedName = mapNewSimpleNames.get(innername); + + if (savedName != null) { + simpleName = savedName; + } + else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { + IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper(); + if (renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) { + simpleName = renamer.getNextClassname(innername, simpleName); + mapNewSimpleNames.put(innername, simpleName); + } + } + + arr[1] = simpleName; + + // original access flags + arr[3] = entry[3]; + + // enclosing class + String enclClassName = null; + if (entry[1] != 0) { + enclClassName = strentry[1]; + } + else { + enclClassName = cl.qualifiedName; + } + + if (!innername.equals(enclClassName)) { // self reference + StructClass enclosing_class = context.getClasses().get(enclClassName); + if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only + + Object[] arrold = mapInnerClasses.get(innername); + if (arrold == null) { + mapInnerClasses.put(innername, arr); + } + else { + if (!InterpreterUtil.equalObjectArrays(arrold, arr)) { + DecompilerContext.getLogger() + .writeMessage("Inconsistent inner class entries for " + innername + "!", IFernflowerLogger.WARNING); + } + } + + // reference to the nested class + HashSet<String> set = mapNestedClassReferences.get(enclClassName); + if (set == null) { + mapNestedClassReferences.put(enclClassName, set = new HashSet<String>()); + } + set.add(innername); + + // reference to the enclosing class + set = mapEnclosingClassReferences.get(innername); + if (set == null) { + mapEnclosingClassReferences.put(innername, set = new HashSet<String>()); + } + set.add(enclClassName); + } + } + } + } + } + + ClassNode node = new ClassNode(ClassNode.CLASS_ROOT, cl); + node.access = cl.access_flags; + mapRootClasses.put(cl.qualifiedName, node); + } + } + + if (bDecompileInner) { + + // connect nested classes + for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) { + // root class? + if (!mapInnerClasses.containsKey(ent.getKey())) { + + HashSet<String> setVisited = new HashSet<String>(); + LinkedList<String> stack = new LinkedList<String>(); + + stack.add(ent.getKey()); + setVisited.add(ent.getKey()); + + while (!stack.isEmpty()) { + + String superClass = stack.removeFirst(); + ClassNode supernode = mapRootClasses.get(superClass); + + HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass); + if (setNestedClasses != null) { + + StructClass scl = supernode.classStruct; + StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses"); + for (int i = 0; i < inner.getStringentries().size(); i++) { + String nestedClass = inner.getStringentries().get(i)[0]; + if (!setNestedClasses.contains(nestedClass)) { + continue; + } + + if (setVisited.contains(nestedClass)) { + continue; + } + setVisited.add(nestedClass); + + ClassNode nestednode = mapRootClasses.get(nestedClass); + if (nestednode == null) { + DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.WARNING); + continue; + } + + Object[] arr = mapInnerClasses.get(nestedClass); + + if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) { + // FIXME: check for consistent naming + } + + nestednode.type = (Integer)arr[2]; + nestednode.simpleName = (String)arr[1]; + nestednode.access = (Integer)arr[3]; + + if (nestednode.type == ClassNode.CLASS_ANONYMOUS) { + StructClass cl = nestednode.classStruct; + + // remove static if anonymous class + // a common compiler bug + nestednode.access &= ~CodeConstants.ACC_STATIC; + + int[] interfaces = cl.getInterfaces(); + + if (interfaces.length > 0) { + if (interfaces.length > 1) { + DecompilerContext.getLogger() + .writeMessage("Inconsistent anonymous class definition: " + cl.qualifiedName, IFernflowerLogger.WARNING); + } + nestednode.anonimousClassType = new VarType(cl.getInterface(0), true); + } + else { + nestednode.anonimousClassType = new VarType(cl.superClass.getString(), true); + } + } + else if (nestednode.type == ClassNode.CLASS_LOCAL) { + // only abstract and final are permitted + // a common compiler bug + nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL); + } + + supernode.nested.add(nestednode); + nestednode.parent = supernode; + + nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass)); + + stack.add(nestedClass); + } + } + } + } + } + } + } + + + public void writeClass(StructContext context, StructClass cl, BufferedWriter outwriter) throws IOException { + + ClassNode root = mapRootClasses.get(cl.qualifiedName); + if (root.type != ClassNode.CLASS_ROOT) { + return; + } + + try { + DecompilerContext.setImpcollector(new ImportCollector(root)); + DecompilerContext.setCountercontainer(new CounterContainer()); + + // lambda processing + LambdaProcessor lambda_proc = new LambdaProcessor(); + lambda_proc.processClass(root); + + // add simple class names to implicit import + addClassnameToImport(root, DecompilerContext.getImpcollector()); + // build wrappers for all nested classes + // that's where the actual processing takes place + initWrappers(root); + + NestedClassProcessor nestedproc = new NestedClassProcessor(); + nestedproc.processClass(root, root); + + NestedMemberAccess nstmember = new NestedMemberAccess(); + nstmember.propagateMemberAccess(root); + + ClassWriter clwriter = new ClassWriter(); + + StringWriter strwriter = new StringWriter(); + clwriter.classToJava(root, new BufferedWriter(strwriter), 0); + + if (DecompilerContext.getOption(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT)) { + outwriter.write("// Decompiled by: Fernflower " + Fernflower.version); + outwriter.write(DecompilerContext.getNewLineSeparator()); + outwriter.write("// Date: " + new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date())); + outwriter.write(DecompilerContext.getNewLineSeparator()); + outwriter.write("// Copyright: 2008-2010, Stiver"); + outwriter.write(DecompilerContext.getNewLineSeparator()); + outwriter.write("// Home page: http://www.reversed-java.com"); + outwriter.write(DecompilerContext.getNewLineSeparator()); + outwriter.write(DecompilerContext.getNewLineSeparator()); + } + + int index = cl.qualifiedName.lastIndexOf("/"); + if (index >= 0) { String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); outwriter.write("package "); outwriter.write(packageName); @@ -289,161 +289,169 @@ public class ClassesProcessor { outwriter.write(DecompilerContext.getNewLineSeparator()); outwriter.write(DecompilerContext.getNewLineSeparator()); } - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, root); - - DecompilerContext.getImpcollector().writeImports(outwriter); - outwriter.write(DecompilerContext.getNewLineSeparator()); - - outwriter.write(strwriter.toString()); - outwriter.flush(); - - } finally { - destroyWrappers(root); - } - } - - private void initWrappers(ClassNode node) throws IOException { - - if(node.type == ClassNode.CLASS_LAMBDA) { - return; - } - - ClassWrapper wrapper = new ClassWrapper(node.classStruct); - wrapper.init(); - - node.wrapper = wrapper; - - for(ClassNode nd: node.nested) { - initWrappers(nd); - } - } - - private void addClassnameToImport(ClassNode node, ImportCollector imp) { - - if(node.simpleName != null && node.simpleName.length() > 0) { - imp.getShortName(node.type == ClassNode.CLASS_ROOT?node.classStruct.qualifiedName:node.simpleName, false); - } - - for(ClassNode nd: node.nested) { - addClassnameToImport(nd, imp); - } - } - - private void destroyWrappers(ClassNode node) { - - node.wrapper = null; - node.classStruct.releaseResources(); - - for(ClassNode nd: node.nested) { - destroyWrappers(nd); - } - } - - public HashMap<String, ClassNode> getMapRootClasses() { - return mapRootClasses; - } - - - public class ClassNode { - - public static final int CLASS_ROOT = 0; - public static final int CLASS_MEMBER = 1; - public static final int CLASS_ANONYMOUS = 2; - public static final int CLASS_LOCAL = 4; - public static final int CLASS_LAMBDA = 8; - - public int type; - - public int access; - - public String simpleName; - - public StructClass classStruct; - - public ClassWrapper wrapper; - - public String enclosingMethod; - - public InvocationExprent superInvocation; - - public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>(); - - public VarType anonimousClassType; - - public List<ClassNode> nested = new ArrayList<ClassNode>(); - - public Set<String> enclosingClasses = new HashSet<String>(); - - public ClassNode parent; - - public LambdaInformation lambda_information; - - public ClassNode(String content_class_name, String content_method_name, String content_method_descriptor, int content_method_invokation_type, - String lambda_class_name, String lambda_method_name, String lambda_method_descriptor, StructClass classStruct) { // lambda class constructor - this.type = CLASS_LAMBDA; - this.classStruct = classStruct; // 'parent' class containing the static function - - lambda_information = new LambdaInformation(); - - lambda_information.class_name = lambda_class_name; - lambda_information.method_name = lambda_method_name; - lambda_information.method_descriptor = lambda_method_descriptor; - - lambda_information.content_class_name = content_class_name; - lambda_information.content_method_name = content_method_name; - lambda_information.content_method_descriptor = content_method_descriptor; - lambda_information.content_method_invokation_type = content_method_invokation_type; - - lambda_information.content_method_key = InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor); - - anonimousClassType = new VarType(lambda_class_name, true); - - boolean is_method_reference = (content_class_name != classStruct.qualifiedName); - StructMethod mt = null; - - if(!is_method_reference) { // content method in the same class, check synthetic flag - mt = classStruct.getMethod(content_method_name, content_method_descriptor); - is_method_reference = !((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic")); // if not synthetic -> method reference - } - - lambda_information.is_method_reference = is_method_reference; - lambda_information.is_content_method_static = (lambda_information.content_method_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant? - } - - public ClassNode(int type, StructClass classStruct) { - this.type = type; - this.classStruct = classStruct; - - simpleName = classStruct.qualifiedName.substring(classStruct.qualifiedName.lastIndexOf('/')+1); - } - - public ClassNode getClassNode(String qualifiedName) { - for(ClassNode node : nested) { - if(qualifiedName.equals(node.classStruct.qualifiedName)) { - return node; - } - } - return null; - } - - public class LambdaInformation { - - - - public String class_name; - public String method_name; - public String method_descriptor; - - public String content_class_name; - public String content_method_name; - public String content_method_descriptor; - public int content_method_invokation_type; // values from CONSTANT_MethodHandle_REF_* - - public String content_method_key; - - public boolean is_method_reference; - public boolean is_content_method_static; - } - } + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, root); + + DecompilerContext.getImpcollector().writeImports(outwriter); + outwriter.write(DecompilerContext.getNewLineSeparator()); + + outwriter.write(strwriter.toString()); + outwriter.flush(); + } + finally { + destroyWrappers(root); + } + } + + private void initWrappers(ClassNode node) throws IOException { + + if (node.type == ClassNode.CLASS_LAMBDA) { + return; + } + + ClassWrapper wrapper = new ClassWrapper(node.classStruct); + wrapper.init(); + + node.wrapper = wrapper; + + for (ClassNode nd : node.nested) { + initWrappers(nd); + } + } + + private void addClassnameToImport(ClassNode node, ImportCollector imp) { + + if (node.simpleName != null && node.simpleName.length() > 0) { + imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false); + } + + for (ClassNode nd : node.nested) { + addClassnameToImport(nd, imp); + } + } + + private void destroyWrappers(ClassNode node) { + + node.wrapper = null; + node.classStruct.releaseResources(); + + for (ClassNode nd : node.nested) { + destroyWrappers(nd); + } + } + + public HashMap<String, ClassNode> getMapRootClasses() { + return mapRootClasses; + } + + + public class ClassNode { + + public static final int CLASS_ROOT = 0; + public static final int CLASS_MEMBER = 1; + public static final int CLASS_ANONYMOUS = 2; + public static final int CLASS_LOCAL = 4; + public static final int CLASS_LAMBDA = 8; + + public int type; + + public int access; + + public String simpleName; + + public StructClass classStruct; + + public ClassWrapper wrapper; + + public String enclosingMethod; + + public InvocationExprent superInvocation; + + public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>(); + + public VarType anonimousClassType; + + public List<ClassNode> nested = new ArrayList<ClassNode>(); + + public Set<String> enclosingClasses = new HashSet<String>(); + + public ClassNode parent; + + public LambdaInformation lambda_information; + + public ClassNode(String content_class_name, + String content_method_name, + String content_method_descriptor, + int content_method_invokation_type, + String lambda_class_name, + String lambda_method_name, + String lambda_method_descriptor, + StructClass classStruct) { // lambda class constructor + this.type = CLASS_LAMBDA; + this.classStruct = classStruct; // 'parent' class containing the static function + + lambda_information = new LambdaInformation(); + + lambda_information.class_name = lambda_class_name; + lambda_information.method_name = lambda_method_name; + lambda_information.method_descriptor = lambda_method_descriptor; + + lambda_information.content_class_name = content_class_name; + lambda_information.content_method_name = content_method_name; + lambda_information.content_method_descriptor = content_method_descriptor; + lambda_information.content_method_invokation_type = content_method_invokation_type; + + lambda_information.content_method_key = + InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor); + + anonimousClassType = new VarType(lambda_class_name, true); + + boolean is_method_reference = (content_class_name != classStruct.qualifiedName); + StructMethod mt = null; + + if (!is_method_reference) { // content method in the same class, check synthetic flag + mt = classStruct.getMethod(content_method_name, content_method_descriptor); + is_method_reference = !((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || + mt.getAttributes().containsKey("Synthetic")); // if not synthetic -> method reference + } + + lambda_information.is_method_reference = is_method_reference; + lambda_information.is_content_method_static = + (lambda_information.content_method_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant? + } + + public ClassNode(int type, StructClass classStruct) { + this.type = type; + this.classStruct = classStruct; + + simpleName = classStruct.qualifiedName.substring(classStruct.qualifiedName.lastIndexOf('/') + 1); + } + + public ClassNode getClassNode(String qualifiedName) { + for (ClassNode node : nested) { + if (qualifiedName.equals(node.classStruct.qualifiedName)) { + return node; + } + } + return null; + } + + public class LambdaInformation { + + + public String class_name; + public String method_name; + public String method_descriptor; + + public String content_class_name; + public String content_method_name; + public String content_method_descriptor; + public int content_method_invokation_type; // values from CONSTANT_MethodHandle_REF_* + + public String content_method_key; + + public boolean is_method_reference; + public boolean is_content_method_static; + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index fc721b7..cb4bbc8 100644 --- a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -1,21 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.util.HashMap; - import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; @@ -24,174 +23,176 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructContext; +import java.util.HashMap; + public class DecompilerContext { - - public static final String CURRENT_CLASS = "CURRENT_CLASS"; - public static final String CURRENT_METHOD = "CURRENT_METHOD"; - public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR"; - public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR"; - - public static final String CURRENT_CLASSNODE = "CURRENT_CLASSNODE"; - public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; - - private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>(); - - private HashMap<String, Object> properties = new HashMap<String, Object>(); - - private StructContext structcontext; - - private ImportCollector impcollector; - - private VarNamesCollector varncollector; - - private CounterContainer countercontainer; - - private ClassesProcessor classprocessor; - - private PoolInterceptor poolinterceptor; - - private IFernflowerLogger logger; - - - private DecompilerContext(HashMap<String, Object> properties) { - this.properties.putAll(properties); - } - - public static void initContext(HashMap<String, Object> propertiesCustom) { - - HashMap<String, Object> mapDefault = new HashMap<String, Object>(); - - // default settings - mapDefault.put(IFernflowerPreferences.DECOMPILE_INNER, "1"); - mapDefault.put(IFernflowerPreferences.DECOMPILE_CLASS_1_4, "1"); - mapDefault.put(IFernflowerPreferences.DECOMPILE_ASSERTIONS, "1"); - mapDefault.put(IFernflowerPreferences.REMOVE_BRIDGE, "1"); - mapDefault.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "0"); - mapDefault.put(IFernflowerPreferences.HIDE_EMPTY_SUPER, "1"); - mapDefault.put(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR, "1"); - mapDefault.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "0"); - mapDefault.put(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT, "0"); - mapDefault.put(IFernflowerPreferences.NO_EXCEPTIONS_RETURN, "1"); - mapDefault.put(IFernflowerPreferences.DECOMPILE_ENUM, "1"); - mapDefault.put(IFernflowerPreferences.FINALLY_DEINLINE, "1"); - mapDefault.put(IFernflowerPreferences.REMOVE_GETCLASS_NEW, "1"); - mapDefault.put(IFernflowerPreferences.LITERALS_AS_IS, "0"); - mapDefault.put(IFernflowerPreferences.ASCII_STRING_CHARACTERS, "0"); - mapDefault.put(IFernflowerPreferences.BOOLEAN_TRUE_ONE, "1"); - mapDefault.put(IFernflowerPreferences.SYNTHETIC_NOT_SET, "1"); - mapDefault.put(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT, "1"); - - mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1"); - mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0"); - - mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1"); - - mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0"); - mapDefault.put(IFernflowerPreferences.INDENT_STRING, " "); - - mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1"); - - if(propertiesCustom != null) { - mapDefault.putAll(propertiesCustom); - } - - currentContext.set(new DecompilerContext(mapDefault)); - } - - public static DecompilerContext getCurrentContext() { - return currentContext.get(); - } - - public static void setCurrentContext(DecompilerContext context) { - currentContext.set(context); - } - - public static Object getProperty(String key) { - return getCurrentContext().properties.get(key); - } - - public static void setProperty(String key, Object value) { - getCurrentContext().properties.put(key, value); - } - - public static boolean getOption(String key) { - return "1".equals(getCurrentContext().properties.get(key)); - } - - public static ImportCollector getImpcollector() { - return getCurrentContext().impcollector; - } - - public static void setImpcollector(ImportCollector impcollector) { - getCurrentContext().impcollector = impcollector; - } - - public static VarNamesCollector getVarncollector() { - return getCurrentContext().varncollector; - } - - public static void setVarncollector(VarNamesCollector varncollector) { - getCurrentContext().varncollector = varncollector; - } - - public static StructContext getStructcontext() { - return getCurrentContext().structcontext; - } - - public static void setStructcontext(StructContext structcontext) { - getCurrentContext().structcontext = structcontext; - } - - public static CounterContainer getCountercontainer() { - return getCurrentContext().countercontainer; - } - - public static void setCountercontainer(CounterContainer countercontainer) { - getCurrentContext().countercontainer = countercontainer; - } - - public static ClassesProcessor getClassprocessor() { - return getCurrentContext().classprocessor; - } - - public static void setClassprocessor(ClassesProcessor classprocessor) { - getCurrentContext().classprocessor = classprocessor; - } - - public static PoolInterceptor getPoolInterceptor() { - return getCurrentContext().poolinterceptor; - } - - public static void setPoolInterceptor(PoolInterceptor poolinterceptor) { - getCurrentContext().poolinterceptor = poolinterceptor; - } - - public static IFernflowerLogger getLogger() { - return getCurrentContext().logger; - } - - public static void setLogger(IFernflowerLogger logger) { - getCurrentContext().logger = logger; - setLogSeverity(); - } - - private static void setLogSeverity() { - IFernflowerLogger logger = getCurrentContext().logger; - - if(logger != null) { - String severity = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); - if(severity != null) { - Integer iSeverity = IFernflowerLogger.mapLogLevel.get(severity.toUpperCase()); - if(iSeverity != null) { - logger.setSeverity(iSeverity); - } - } - } - } - - public static String getNewLineSeparator() { - return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ? - IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN ; - } + + public static final String CURRENT_CLASS = "CURRENT_CLASS"; + public static final String CURRENT_METHOD = "CURRENT_METHOD"; + public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR"; + public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR"; + + public static final String CURRENT_CLASSNODE = "CURRENT_CLASSNODE"; + public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; + + private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>(); + + private HashMap<String, Object> properties = new HashMap<String, Object>(); + + private StructContext structcontext; + + private ImportCollector impcollector; + + private VarNamesCollector varncollector; + + private CounterContainer countercontainer; + + private ClassesProcessor classprocessor; + + private PoolInterceptor poolinterceptor; + + private IFernflowerLogger logger; + + + private DecompilerContext(HashMap<String, Object> properties) { + this.properties.putAll(properties); + } + + public static void initContext(HashMap<String, Object> propertiesCustom) { + + HashMap<String, Object> mapDefault = new HashMap<String, Object>(); + + // default settings + mapDefault.put(IFernflowerPreferences.DECOMPILE_INNER, "1"); + mapDefault.put(IFernflowerPreferences.DECOMPILE_CLASS_1_4, "1"); + mapDefault.put(IFernflowerPreferences.DECOMPILE_ASSERTIONS, "1"); + mapDefault.put(IFernflowerPreferences.REMOVE_BRIDGE, "1"); + mapDefault.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "0"); + mapDefault.put(IFernflowerPreferences.HIDE_EMPTY_SUPER, "1"); + mapDefault.put(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR, "1"); + mapDefault.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "0"); + mapDefault.put(IFernflowerPreferences.OUTPUT_COPYRIGHT_COMMENT, "0"); + mapDefault.put(IFernflowerPreferences.NO_EXCEPTIONS_RETURN, "1"); + mapDefault.put(IFernflowerPreferences.DECOMPILE_ENUM, "1"); + mapDefault.put(IFernflowerPreferences.FINALLY_DEINLINE, "1"); + mapDefault.put(IFernflowerPreferences.REMOVE_GETCLASS_NEW, "1"); + mapDefault.put(IFernflowerPreferences.LITERALS_AS_IS, "0"); + mapDefault.put(IFernflowerPreferences.ASCII_STRING_CHARACTERS, "0"); + mapDefault.put(IFernflowerPreferences.BOOLEAN_TRUE_ONE, "1"); + mapDefault.put(IFernflowerPreferences.SYNTHETIC_NOT_SET, "1"); + mapDefault.put(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT, "1"); + + mapDefault.put(IFernflowerPreferences.USE_DEBUG_VARNAMES, "1"); + mapDefault.put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "0"); + + mapDefault.put(IFernflowerPreferences.REMOVE_EMPTY_RANGES, "1"); + + mapDefault.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "0"); + mapDefault.put(IFernflowerPreferences.INDENT_STRING, " "); + + mapDefault.put(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION, "1"); + + if (propertiesCustom != null) { + mapDefault.putAll(propertiesCustom); + } + + currentContext.set(new DecompilerContext(mapDefault)); + } + + public static DecompilerContext getCurrentContext() { + return currentContext.get(); + } + + public static void setCurrentContext(DecompilerContext context) { + currentContext.set(context); + } + + public static Object getProperty(String key) { + return getCurrentContext().properties.get(key); + } + + public static void setProperty(String key, Object value) { + getCurrentContext().properties.put(key, value); + } + + public static boolean getOption(String key) { + return "1".equals(getCurrentContext().properties.get(key)); + } + + public static ImportCollector getImpcollector() { + return getCurrentContext().impcollector; + } + + public static void setImpcollector(ImportCollector impcollector) { + getCurrentContext().impcollector = impcollector; + } + + public static VarNamesCollector getVarncollector() { + return getCurrentContext().varncollector; + } + + public static void setVarncollector(VarNamesCollector varncollector) { + getCurrentContext().varncollector = varncollector; + } + + public static StructContext getStructcontext() { + return getCurrentContext().structcontext; + } + + public static void setStructcontext(StructContext structcontext) { + getCurrentContext().structcontext = structcontext; + } + + public static CounterContainer getCountercontainer() { + return getCurrentContext().countercontainer; + } + + public static void setCountercontainer(CounterContainer countercontainer) { + getCurrentContext().countercontainer = countercontainer; + } + + public static ClassesProcessor getClassprocessor() { + return getCurrentContext().classprocessor; + } + + public static void setClassprocessor(ClassesProcessor classprocessor) { + getCurrentContext().classprocessor = classprocessor; + } + + public static PoolInterceptor getPoolInterceptor() { + return getCurrentContext().poolinterceptor; + } + + public static void setPoolInterceptor(PoolInterceptor poolinterceptor) { + getCurrentContext().poolinterceptor = poolinterceptor; + } + + public static IFernflowerLogger getLogger() { + return getCurrentContext().logger; + } + + public static void setLogger(IFernflowerLogger logger) { + getCurrentContext().logger = logger; + setLogSeverity(); + } + + private static void setLogSeverity() { + IFernflowerLogger logger = getCurrentContext().logger; + + if (logger != null) { + String severity = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); + if (severity != null) { + Integer iSeverity = IFernflowerLogger.mapLogLevel.get(severity.toUpperCase()); + if (iSeverity != null) { + logger.setSeverity(iSeverity); + } + } + } + } + + public static String getNewLineSeparator() { + return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ? + IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN; + } } diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index 7537596..e991827 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -33,127 +34,125 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; public class EnumProcessor { - public static void clearEnum(ClassWrapper wrapper) { - - StructClass cl = wrapper.getClassStruct(); - - // hide values() and valueOf() - for(StructMethod meth : cl.getMethods()) { - - String name = meth.getName(); - int flag = 0; - - if("values".equals(name)) { - flag = 1; - } else if("valueOf".equals(name)) { - flag = 2; - } - - if(flag>0) { - String[] arr = meth.getDescriptor().split("[()]"); - String par = arr[1]; - - if((flag == 1 && par.length() == 0) || - flag == 2 && "Ljava/lang/String;".equals(par)) { - wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(name, meth.getDescriptor())); - } - } - } - - // hide all super invocations - for(MethodWrapper meth : wrapper.getMethods()) { - if("<init>".equals(meth.methodStruct.getName())) { - Statement firstdata = findFirstData(meth.root); - if(firstdata == null || firstdata.getExprents().isEmpty()) { - return; - } - - Exprent exprent = firstdata.getExprents().get(0); - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if(isInvocationSuperConstructor(invexpr, meth, wrapper)) { - firstdata.getExprents().remove(0); - } - } - } - } - - // hide dummy synthetic fields of enum constants - for(StructField fd: cl.getFields()) { - if((fd.access_flags & CodeConstants.ACC_ENUM) != 0) { - Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - if(initializer != null && initializer.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)initializer; - if(nexpr.isAnonymous()) { - ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(nexpr.getNewtype().value); - hideDummyFieldInConstant(child.wrapper); + public static void clearEnum(ClassWrapper wrapper) { + + StructClass cl = wrapper.getClassStruct(); + + // hide values() and valueOf() + for (StructMethod meth : cl.getMethods()) { + + String name = meth.getName(); + int flag = 0; + + if ("values".equals(name)) { + flag = 1; + } + else if ("valueOf".equals(name)) { + flag = 2; + } + + if (flag > 0) { + String[] arr = meth.getDescriptor().split("[()]"); + String par = arr[1]; + + if ((flag == 1 && par.length() == 0) || + flag == 2 && "Ljava/lang/String;".equals(par)) { + wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(name, meth.getDescriptor())); + } + } + } + + // hide all super invocations + for (MethodWrapper meth : wrapper.getMethods()) { + if ("<init>".equals(meth.methodStruct.getName())) { + Statement firstdata = findFirstData(meth.root); + if (firstdata == null || firstdata.getExprents().isEmpty()) { + return; + } + + Exprent exprent = firstdata.getExprents().get(0); + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)exprent; + if (isInvocationSuperConstructor(invexpr, meth, wrapper)) { + firstdata.getExprents().remove(0); } - } - } - } - - - - } - - private static void hideDummyFieldInConstant(ClassWrapper wrapper) { - - StructClass cl = wrapper.getClassStruct(); - for(StructField fd: cl.getFields()) { - if((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0) { - FieldDescriptor descr = FieldDescriptor.parseDescriptor(fd.getDescriptor()); - VarType ret = descr.type; - - if(ret.type == CodeConstants.TYPE_OBJECT && ret.arraydim == 1 && cl.qualifiedName.equals(ret.value)) { - wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } - } - } - - } - - // FIXME: move to a util class (see also InitializerProcessor) - private static Statement findFirstData(Statement stat) { - - if(stat.getExprents() != null) { - return stat; - } else { - if(stat.isLabeled()) { - return null; - } - - switch(stat.type) { - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - return findFirstData(stat.getFirst()); - default: - return null; - } - } - } - - // FIXME: move to util class (see also InitializerProcessor) - private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) { - - if(inv.getFunctype() == InvocationExprent.TYP_INIT) { - if(inv.getInstance().type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)inv.getInstance(); - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - String classname = meth.varproc.getThisvars().get(varpaar); - - if(classname!=null) { // any this instance. TODO: Restrict to current class? - if(!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { - return true; - } - } - } - } - - return false; - } - + } + } + } + + // hide dummy synthetic fields of enum constants + for (StructField fd : cl.getFields()) { + if ((fd.access_flags & CodeConstants.ACC_ENUM) != 0) { + Exprent initializer = + wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + if (initializer != null && initializer.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)initializer; + if (nexpr.isAnonymous()) { + ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(nexpr.getNewtype().value); + hideDummyFieldInConstant(child.wrapper); + } + } + } + } + } + + private static void hideDummyFieldInConstant(ClassWrapper wrapper) { + + StructClass cl = wrapper.getClassStruct(); + for (StructField fd : cl.getFields()) { + if ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0) { + FieldDescriptor descr = FieldDescriptor.parseDescriptor(fd.getDescriptor()); + VarType ret = descr.type; + + if (ret.type == CodeConstants.TYPE_OBJECT && ret.arraydim == 1 && cl.qualifiedName.equals(ret.value)) { + wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + } + } + } + + // FIXME: move to a util class (see also InitializerProcessor) + private static Statement findFirstData(Statement stat) { + + if (stat.getExprents() != null) { + return stat; + } + else { + if (stat.isLabeled()) { + return null; + } + + switch (stat.type) { + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return findFirstData(stat.getFirst()); + default: + return null; + } + } + } + + // FIXME: move to util class (see also InitializerProcessor) + private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) { + + if (inv.getFunctype() == InvocationExprent.TYP_INIT) { + if (inv.getInstance().type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)inv.getInstance(); + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + String classname = meth.varproc.getThisvars().get(varpaar); + + if (classname != null) { // any this instance. TODO: Restrict to current class? + if (!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { + return true; + } + } + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 021ab1a..4365ca1 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -1,23 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.io.BufferedWriter; -import java.io.StringWriter; -import java.util.HashMap; - import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; @@ -29,82 +26,88 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import java.io.BufferedWriter; +import java.io.StringWriter; +import java.util.HashMap; + public class Fernflower implements IDecompiledData { - - public static final String version = "v0.8.4"; - - private StructContext structcontext; - - private ClassesProcessor clprocessor; - - public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver, - HashMap<String, Object> propertiesCustom) { - - StructContext context = new StructContext(saver, this, new LazyLoader(provider)); - - structcontext = context; - - DecompilerContext.initContext(propertiesCustom); - DecompilerContext.setCountercontainer(new CounterContainer()); - - } - - public void decompileContext() { - - if(DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - IdentifierConverter ren = new IdentifierConverter(); - ren.rename(structcontext); - ren = null; - } - - clprocessor = new ClassesProcessor(structcontext); - - DecompilerContext.setClassprocessor(clprocessor); - DecompilerContext.setStructcontext(structcontext); - - structcontext.saveContext(); - } + + public static final String version = "v0.8.4"; + + private StructContext structcontext; + + private ClassesProcessor clprocessor; + + public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver, + HashMap<String, Object> propertiesCustom) { + + StructContext context = new StructContext(saver, this, new LazyLoader(provider)); + + structcontext = context; + + DecompilerContext.initContext(propertiesCustom); + DecompilerContext.setCountercontainer(new CounterContainer()); + } + + public void decompileContext() { + + if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { + IdentifierConverter ren = new IdentifierConverter(); + ren.rename(structcontext); + ren = null; + } + + clprocessor = new ClassesProcessor(structcontext); + + DecompilerContext.setClassprocessor(clprocessor); + DecompilerContext.setStructcontext(structcontext); + + structcontext.saveContext(); + } public void clearContext() { DecompilerContext.setCurrentContext(null); } - public String getClassEntryName(StructClass cl, String entryname) { - - ClassNode node = clprocessor.getMapRootClasses().get(cl.qualifiedName); - if(node.type != ClassNode.CLASS_ROOT) { - return null; - } else { - if(DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/')+1); - return entryname.substring(0, entryname.lastIndexOf('/')+1)+simple_classname+".java"; - } else { - return entryname.substring(0, entryname.lastIndexOf(".class"))+".java"; - } - } - } - - public StructContext getStructcontext() { - return structcontext; - } - - public String getClassContent(StructClass cl) { - - String res = null; - - try { - StringWriter strwriter = new StringWriter(); - clprocessor.writeClass(structcontext, cl, new BufferedWriter(strwriter)); - - res = strwriter.toString(); - } catch(ThreadDeath ex) { - throw ex; - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Class "+cl.qualifiedName+" couldn't be fully decompiled.", ex); - } - - return res; - } - + public String getClassEntryName(StructClass cl, String entryname) { + + ClassNode node = clprocessor.getMapRootClasses().get(cl.qualifiedName); + if (node.type != ClassNode.CLASS_ROOT) { + return null; + } + else { + if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { + String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1); + return entryname.substring(0, entryname.lastIndexOf('/') + 1) + simple_classname + ".java"; + } + else { + return entryname.substring(0, entryname.lastIndexOf(".class")) + ".java"; + } + } + } + + public StructContext getStructcontext() { + return structcontext; + } + + public String getClassContent(StructClass cl) { + + String res = null; + + try { + StringWriter strwriter = new StringWriter(); + clprocessor.writeClass(structcontext, cl, new BufferedWriter(strwriter)); + + res = strwriter.toString(); + } + catch (ThreadDeath ex) { + throw ex; + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex); + } + + return res; + } } diff --git a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java index ff6233c..c738163 100644 --- a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java @@ -1,32 +1,26 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; @@ -34,288 +28,296 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class InitializerProcessor { - public static void extractInitializers(ClassWrapper wrapper) { - - MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V"); - if(meth != null && meth.root != null) { // successfully decompiled static constructor - extractStaticInitializers(wrapper, meth); - } - - extractDynamicInitializers(wrapper); - - // required e.g. if anonymous class is being decompiled as a standard one. - // This can happen if InnerClasses attributes are erased - liftConstructor(wrapper); - - if(DecompilerContext.getOption(IFernflowerPreferences.HIDE_EMPTY_SUPER)) { - hideEmptySuper(wrapper); - } - } - - - private static void liftConstructor(ClassWrapper wrapper) { - - for(MethodWrapper meth : wrapper.getMethods()) { - if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { - Statement firstdata = findFirstData(meth.root); - if(firstdata == null) { - return; - } - - - int index = 0; - List<Exprent> lstExprents = firstdata.getExprents(); - - for(Exprent exprent : lstExprents) { - - int action = 0; - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if(fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) { - StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString); - if(structField != null && (structField.access_flags & CodeConstants.ACC_FINAL) != 0) { - action = 1; - } - } - } - } else if(index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION && - isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) { - // this() or super() - lstExprents.add(0, lstExprents.remove(index)); - action = 2; - } - - if(action != 1) { - break; - } - - index++; - } - } - } - } - - - private static void hideEmptySuper(ClassWrapper wrapper) { - - for(MethodWrapper meth : wrapper.getMethods()) { - if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { - Statement firstdata = findFirstData(meth.root); - if(firstdata == null || firstdata.getExprents().isEmpty()) { - return; - } - - Exprent exprent = firstdata.getExprents().get(0); - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if(isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) { - firstdata.getExprents().remove(0); - } - } - } - } - } - - private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) { - - RootStatement root = meth.root; - StructClass cl = wrapper.getClassStruct(); - - Statement firstdata = findFirstData(root); - if(firstdata != null) { - while(!firstdata.getExprents().isEmpty()) { - Exprent exprent = firstdata.getExprents().get(0); - - boolean found = false; - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if(fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && - cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { - - if(isExprentIndependent(asexpr.getRight(), meth)) { - - String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); - if(!wrapper.getStaticFieldInitializers().containsKey(keyField)) { - wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField); - firstdata.getExprents().remove(0); - found = true; - } - } - } - } - } - - if(!found) { - break; - } - } - } - } - - private static void extractDynamicInitializers(ClassWrapper wrapper) { - - StructClass cl = wrapper.getClassStruct(); - - boolean isAnonymous = DecompilerContext.getClassprocessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS; - - List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>(); - List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>(); - - for(MethodWrapper meth : wrapper.getMethods()) { - if("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor - Statement firstdata = findFirstData(meth.root); - if(firstdata == null || firstdata.getExprents().isEmpty()) { - return; - } - lstFirst.add(firstdata.getExprents()); - lstMethWrappers.add(meth); - - Exprent exprent = firstdata.getExprents().get(0); - if(!isAnonymous) { // FIXME: doesn't make sense - if(exprent.type != Exprent.EXPRENT_INVOCATION || !isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) { - return; - } - } - } - } - - if(lstFirst.isEmpty()) { - return; - } - - for(;;) { - - String fieldWithDescr = null; - Exprent value = null; - - for(int i=0; i<lstFirst.size(); i++) { - - List<Exprent> lst = lstFirst.get(i); - - if(lst.size() < (isAnonymous?1:2)) { - return; - } - - Exprent exprent = lst.get(isAnonymous?0:1); - - boolean found = false; - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if(!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && - cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass. - - if(isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) { - String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); - if(fieldWithDescr == null) { - fieldWithDescr = fieldKey; - value = asexpr.getRight(); - } else { - if(!fieldWithDescr.equals(fieldKey) || - !value.equals(asexpr.getRight())) { - return; - } - } - found = true; - } - } - } - } - - if(!found) { - return; - } - } - - if(!wrapper.getDynamicFieldInitializers().containsKey(fieldWithDescr)) { - wrapper.getDynamicFieldInitializers().addWithKey(value, fieldWithDescr); - - for(List<Exprent> lst : lstFirst) { - lst.remove(isAnonymous?0:1); - } - } else { - return; - } - } - - } - - private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr : lst) { - switch(expr.type) { - case Exprent.EXPRENT_VAR: - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr); - if(!meth.varproc.getExternvars().contains(varpaar)) { - String varname = meth.varproc.getVarName(varpaar); - - if(!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings - return false; - } - } - break; - case Exprent.EXPRENT_FIELD: - return false; - } - } - - return true; - } - - - private static Statement findFirstData(Statement stat) { - - if(stat.getExprents() != null) { - return stat; - } else { - if(stat.isLabeled()) { // FIXME: Why?? - return null; - } - - switch(stat.type) { - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - return findFirstData(stat.getFirst()); - default: - return null; - } - } - } - - private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) { - - if(inv.getFunctype() == InvocationExprent.TYP_INIT) { - if(inv.getInstance().type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)inv.getInstance(); - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - String classname = meth.varproc.getThisvars().get(varpaar); - - if(classname!=null) { // any this instance. TODO: Restrict to current class? - if(withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { - return true; - } - } - } - } - - return false; - } + public static void extractInitializers(ClassWrapper wrapper) { + + MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V"); + if (meth != null && meth.root != null) { // successfully decompiled static constructor + extractStaticInitializers(wrapper, meth); + } + + extractDynamicInitializers(wrapper); + + // required e.g. if anonymous class is being decompiled as a standard one. + // This can happen if InnerClasses attributes are erased + liftConstructor(wrapper); + + if (DecompilerContext.getOption(IFernflowerPreferences.HIDE_EMPTY_SUPER)) { + hideEmptySuper(wrapper); + } + } + + + private static void liftConstructor(ClassWrapper wrapper) { + + for (MethodWrapper meth : wrapper.getMethods()) { + if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { + Statement firstdata = findFirstData(meth.root); + if (firstdata == null) { + return; + } + + + int index = 0; + List<Exprent> lstExprents = firstdata.getExprents(); + + for (Exprent exprent : lstExprents) { + + int action = 0; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) { + StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString); + if (structField != null && (structField.access_flags & CodeConstants.ACC_FINAL) != 0) { + action = 1; + } + } + } + } + else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION && + isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) { + // this() or super() + lstExprents.add(0, lstExprents.remove(index)); + action = 2; + } + + if (action != 1) { + break; + } + + index++; + } + } + } + } + + + private static void hideEmptySuper(ClassWrapper wrapper) { + + for (MethodWrapper meth : wrapper.getMethods()) { + if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { + Statement firstdata = findFirstData(meth.root); + if (firstdata == null || firstdata.getExprents().isEmpty()) { + return; + } + + Exprent exprent = firstdata.getExprents().get(0); + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)exprent; + if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) { + firstdata.getExprents().remove(0); + } + } + } + } + } + + private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) { + + RootStatement root = meth.root; + StructClass cl = wrapper.getClassStruct(); + + Statement firstdata = findFirstData(root); + if (firstdata != null) { + while (!firstdata.getExprents().isEmpty()) { + Exprent exprent = firstdata.getExprents().get(0); + + boolean found = false; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && + cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { + + if (isExprentIndependent(asexpr.getRight(), meth)) { + + String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); + if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) { + wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField); + firstdata.getExprents().remove(0); + found = true; + } + } + } + } + } + + if (!found) { + break; + } + } + } + } + + private static void extractDynamicInitializers(ClassWrapper wrapper) { + + StructClass cl = wrapper.getClassStruct(); + + boolean isAnonymous = DecompilerContext.getClassprocessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS; + + List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>(); + List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>(); + + for (MethodWrapper meth : wrapper.getMethods()) { + if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor + Statement firstdata = findFirstData(meth.root); + if (firstdata == null || firstdata.getExprents().isEmpty()) { + return; + } + lstFirst.add(firstdata.getExprents()); + lstMethWrappers.add(meth); + + Exprent exprent = firstdata.getExprents().get(0); + if (!isAnonymous) { // FIXME: doesn't make sense + if (exprent.type != Exprent.EXPRENT_INVOCATION || + !isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) { + return; + } + } + } + } + + if (lstFirst.isEmpty()) { + return; + } + + for (; ; ) { + + String fieldWithDescr = null; + Exprent value = null; + + for (int i = 0; i < lstFirst.size(); i++) { + + List<Exprent> lst = lstFirst.get(i); + + if (lst.size() < (isAnonymous ? 1 : 2)) { + return; + } + + Exprent exprent = lst.get(isAnonymous ? 0 : 1); + + boolean found = false; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && + cl.hasField(fexpr.getName(), fexpr + .getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass. + + if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) { + String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); + if (fieldWithDescr == null) { + fieldWithDescr = fieldKey; + value = asexpr.getRight(); + } + else { + if (!fieldWithDescr.equals(fieldKey) || + !value.equals(asexpr.getRight())) { + return; + } + } + found = true; + } + } + } + } + + if (!found) { + return; + } + } + + if (!wrapper.getDynamicFieldInitializers().containsKey(fieldWithDescr)) { + wrapper.getDynamicFieldInitializers().addWithKey(value, fieldWithDescr); + + for (List<Exprent> lst : lstFirst) { + lst.remove(isAnonymous ? 0 : 1); + } + } + else { + return; + } + } + } + + private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + switch (expr.type) { + case Exprent.EXPRENT_VAR: + VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr); + if (!meth.varproc.getExternvars().contains(varpaar)) { + String varname = meth.varproc.getVarName(varpaar); + + if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings + return false; + } + } + break; + case Exprent.EXPRENT_FIELD: + return false; + } + } + + return true; + } + + + private static Statement findFirstData(Statement stat) { + + if (stat.getExprents() != null) { + return stat; + } + else { + if (stat.isLabeled()) { // FIXME: Why?? + return null; + } + + switch (stat.type) { + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return findFirstData(stat.getFirst()); + default: + return null; + } + } + } + + private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) { + + if (inv.getFunctype() == InvocationExprent.TYP_INIT) { + if (inv.getInstance().type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)inv.getInstance(); + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + String classname = meth.varproc.getThisvars().get(varpaar); + + if (classname != null) { // any this instance. TODO: Restrict to current class? + if (withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { + return true; + } + } + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java index dcc1968..04d50a7 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java @@ -1,37 +1,37 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.collectors; public class CounterContainer { - public static final int STATEMENT_COUNTER = 0; - public static final int EXPRENT_COUNTER = 1; - public static final int VAR_COUNTER = 2; - - private int[] values = new int[]{1, 1, 1}; - - public void setCounter(int counter, int value) { - values[counter] = value; - } + public static final int STATEMENT_COUNTER = 0; + public static final int EXPRENT_COUNTER = 1; + public static final int VAR_COUNTER = 2; + + private int[] values = new int[]{1, 1, 1}; + + public void setCounter(int counter, int value) { + values[counter] = value; + } - public int getCounter(int counter) { - return values[counter]; - } + public int getCounter(int counter) { + return values[counter]; + } - public int getCounterAndIncrement(int counter) { - return values[counter]++; - } - + public int getCounterAndIncrement(int counter) { + return values[counter]++; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index cdc6529..f45bf27 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -1,157 +1,152 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.collectors; -import java.io.BufferedWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.main.ClassesProcessor; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.struct.StructContext; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; + public class ImportCollector { - private static final String JAVA_LANG_PACKAGE = "java.lang"; - - private HashMap<String, String> mapSimpleNames = new HashMap<String, String>(); - - private HashSet<String> setNotImportedNames = new HashSet<String>(); - - private String currentPackageSlash = ""; - - private String currentPackagePoint = ""; - - public ImportCollector(ClassNode root) { - - String clname = root.classStruct.qualifiedName; - int index = clname.lastIndexOf("/"); - if(index >= 0) { - currentPackageSlash = clname.substring(0, index); - currentPackagePoint = currentPackageSlash.replace('/', '.'); - currentPackageSlash += "/"; - } - } - - public String getShortName(String fullname) { - return getShortName(fullname, true); - } - - public String getShortName(String fullname, boolean imported) { - - ClassesProcessor clproc = DecompilerContext.getClassprocessor(); - ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/')); - - String retname = null; - - if(node != null && node.classStruct.isOwn()) { - - retname = node.simpleName; - - while(node.parent != null && node.type == ClassNode.CLASS_MEMBER) { - retname = node.parent.simpleName+"."+retname; - node = node.parent; - } - - if(node.type == ClassNode.CLASS_ROOT) { - fullname = node.classStruct.qualifiedName; - fullname = fullname.replace('/', '.'); - } else { - return retname; - } - - } else if(node == null || !node.classStruct.isOwn()) { - fullname = fullname.replace('$', '.'); - } - - String nshort = fullname; - String npackage = ""; - - int lastpoint = fullname.lastIndexOf("."); - - if(lastpoint >= 0) { - nshort = fullname.substring(lastpoint+1); - npackage = fullname.substring(0, lastpoint); - } - - StructContext context = DecompilerContext.getStructcontext(); - - boolean existsDefaultClass = (context.getClass(currentPackageSlash+nshort) != null - && !npackage.equals(currentPackagePoint)) // current package - || (context.getClass(nshort) != null); // default package - - if(existsDefaultClass || - (mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) { - return fullname; - } else if(!mapSimpleNames.containsKey(nshort)) { - mapSimpleNames.put(nshort, npackage); - - if(!imported) { - setNotImportedNames.add(nshort); - } - } - - return retname==null?nshort:retname; - } - - public void writeImports(BufferedWriter writer) throws IOException { - - for(String s: packImports()) { - writer.write("import "); - writer.write(s); - writer.write(";"); - writer.write(DecompilerContext.getNewLineSeparator()); - } - - } - - private List<String> packImports() { - - List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet()); - - Collections.sort(lst, new Comparator<Entry<String, String>>() { - public int compare(Entry<String, String> par0, Entry<String, String> par1) { - int res = par0.getValue().compareTo(par1.getValue()); - if(res == 0) { - res = par0.getKey().compareTo(par1.getKey()); - } - return res; - } - }); - - List<String> res = new ArrayList<String>(); - for(Entry<String, String> ent: lst) { - if(!setNotImportedNames.contains(ent.getKey()) // not the current class or one of the nested ones. Also not the empty package. - && !JAVA_LANG_PACKAGE.equals(ent.getValue()) - && ent.getValue().length() > 0) { - - String imp = ent.getValue()+"."+ent.getKey(); - res.add(imp); - } - } - - return res; - } - - + private static final String JAVA_LANG_PACKAGE = "java.lang"; + + private HashMap<String, String> mapSimpleNames = new HashMap<String, String>(); + + private HashSet<String> setNotImportedNames = new HashSet<String>(); + + private String currentPackageSlash = ""; + + private String currentPackagePoint = ""; + + public ImportCollector(ClassNode root) { + + String clname = root.classStruct.qualifiedName; + int index = clname.lastIndexOf("/"); + if (index >= 0) { + currentPackageSlash = clname.substring(0, index); + currentPackagePoint = currentPackageSlash.replace('/', '.'); + currentPackageSlash += "/"; + } + } + + public String getShortName(String fullname) { + return getShortName(fullname, true); + } + + public String getShortName(String fullname, boolean imported) { + + ClassesProcessor clproc = DecompilerContext.getClassprocessor(); + ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/')); + + String retname = null; + + if (node != null && node.classStruct.isOwn()) { + + retname = node.simpleName; + + while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) { + retname = node.parent.simpleName + "." + retname; + node = node.parent; + } + + if (node.type == ClassNode.CLASS_ROOT) { + fullname = node.classStruct.qualifiedName; + fullname = fullname.replace('/', '.'); + } + else { + return retname; + } + } + else if (node == null || !node.classStruct.isOwn()) { + fullname = fullname.replace('$', '.'); + } + + String nshort = fullname; + String npackage = ""; + + int lastpoint = fullname.lastIndexOf("."); + + if (lastpoint >= 0) { + nshort = fullname.substring(lastpoint + 1); + npackage = fullname.substring(0, lastpoint); + } + + StructContext context = DecompilerContext.getStructcontext(); + + boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null + && !npackage.equals(currentPackagePoint)) // current package + || (context.getClass(nshort) != null); // default package + + if (existsDefaultClass || + (mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) { + return fullname; + } + else if (!mapSimpleNames.containsKey(nshort)) { + mapSimpleNames.put(nshort, npackage); + + if (!imported) { + setNotImportedNames.add(nshort); + } + } + + return retname == null ? nshort : retname; + } + + public void writeImports(BufferedWriter writer) throws IOException { + + for (String s : packImports()) { + writer.write("import "); + writer.write(s); + writer.write(";"); + writer.write(DecompilerContext.getNewLineSeparator()); + } + } + + private List<String> packImports() { + + List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet()); + + Collections.sort(lst, new Comparator<Entry<String, String>>() { + public int compare(Entry<String, String> par0, Entry<String, String> par1) { + int res = par0.getValue().compareTo(par1.getValue()); + if (res == 0) { + res = par0.getKey().compareTo(par1.getKey()); + } + return res; + } + }); + + List<String> res = new ArrayList<String>(); + for (Entry<String, String> ent : lst) { + if (!setNotImportedNames.contains(ent.getKey()) // not the current class or one of the nested ones. Also not the empty package. + && !JAVA_LANG_PACKAGE.equals(ent.getValue()) + && ent.getValue().length() > 0) { + + String imp = ent.getValue() + "." + ent.getKey(); + res.add(imp); + } + } + + return res; + } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java index 5fd33c0..dfde971 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java @@ -1,50 +1,51 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.collectors; import java.util.HashSet; public class VarNamesCollector { - private HashSet<String> usedNames = new HashSet<String>(); - - public VarNamesCollector() {} - - public VarNamesCollector(HashSet<String> setNames) { - usedNames.addAll(setNames); - } - - public void addName(String value) { - usedNames.add(value); - } - - public String getFreeName(int index) { - return getFreeName("var"+index); - } - - public String getFreeName(String proposition) { - - while(usedNames.contains(proposition)) { - proposition+="x"; - } - usedNames.add(proposition); - return proposition; - } - - public HashSet<String> getUsedNames() { - return usedNames; - } - + private HashSet<String> usedNames = new HashSet<String>(); + + public VarNamesCollector() { + } + + public VarNamesCollector(HashSet<String> setNames) { + usedNames.addAll(setNames); + } + + public void addName(String value) { + usedNames.add(value); + } + + public String getFreeName(int index) { + return getFreeName("var" + index); + } + + public String getFreeName(String proposition) { + + while (usedNames.contains(proposition)) { + proposition += "x"; + } + usedNames.add(proposition); + return proposition; + } + + public HashSet<String> getUsedNames() { + return usedNames; + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java index 408c430..995f8c2 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java @@ -1,37 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.decompiler; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.decompiler.helper.PrintStreamLogger; @@ -40,284 +23,301 @@ import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.io.*; +import java.util.*; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + public class ConsoleDecompiler implements IBytecodeProvider, IDecompilatSaver { - private File root; - - private Fernflower fernflower; - - private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); - - private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>(); - - public ConsoleDecompiler() { - this(null); - } - - public ConsoleDecompiler(HashMap<String, Object> propertiesCustom) { - this(new PrintStreamLogger(IFernflowerLogger.WARNING, System.out), propertiesCustom); - } - - protected ConsoleDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { - fernflower = new Fernflower(this, this, propertiesCustom); - DecompilerContext.setLogger(logger); - } - - public static void main(String[] args) { - - try { - - if(args != null && args.length > 1) { - - HashMap<String, Object> mapOptions = new HashMap<String, Object>(); - - List<String> lstSources = new ArrayList<String>(); - List<String> lstLibraries = new ArrayList<String>(); - - boolean isOption = true; - for(int i = 0; i < args.length - 1; ++i) { // last parameter - destination - String arg = args[i]; - - if(isOption && arg.startsWith("-") && - arg.length()>5 && arg.charAt(4) == '=') { - String value = arg.substring(5).toUpperCase(); - if("TRUE".equals(value)) { - value = "1"; - } else if("FALSE".equals(value)) { - value = "0"; - } - - mapOptions.put(arg.substring(1, 4), value); - } else { - isOption = false; - - if(arg.startsWith("-e=")) { - lstLibraries.add(arg.substring(3)); - } else { - lstSources.add(arg); - } - } - } - - if(lstSources.isEmpty()) { - printHelp(); - } else { - ConsoleDecompiler decompiler = new ConsoleDecompiler( - new PrintStreamLogger(IFernflowerLogger.INFO, System.out), - mapOptions); - - for(String source : lstSources) { - decompiler.addSpace(new File(source), true); - } - - for(String library : lstLibraries) { - decompiler.addSpace(new File(library), false); - } - - decompiler.decompileContext(new File(args[args.length-1])); - } - - } else { - printHelp(); - } - - } catch(Exception ex) { - ex.printStackTrace(); - } - - } - - private static void printHelp() { - System.out.println("Usage: java ConsoleDecompiler ( -<option>=<value>)* (<source>)+ <destination>"); - System.out.println("Example: java ConsoleDecompiler -dgs=true c:\\mysource\\ c:\\my.jar d:\\decompiled\\"); - } - - public void addSpace(File file, boolean isOwn) throws IOException { - fernflower.getStructcontext().addSpace(file, isOwn); - } - - public void decompileContext(File root) { - this.root = root; - fernflower.decompileContext(); - } - - // ******************************************************************* - // Interface IBytecodeProvider - // ******************************************************************* - - public InputStream getBytecodeStream(String externPath, String internPath) { - - try { - File file = new File(externPath); - - if(internPath == null) { - return new FileInputStream(file); - } else { // archive file - ZipFile archive = new ZipFile(file); - - Enumeration<? extends ZipEntry> en = archive.entries(); - while(en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); - - if(entr.getName().equals(internPath)) { - return archive.getInputStream(entr); - } - } - } - } catch(IOException ex) { - ex.printStackTrace(); - } - - return null; - } - - // ******************************************************************* - // Interface IDecompilatSaver - // ******************************************************************* - - private String getAbsolutePath(String path) { - return new File(root, path).getAbsolutePath(); - } - - private boolean addEntryName(String filename, String entry) { - HashSet<String> set = mapArchiveEntries.get(filename); - if(set == null) { - mapArchiveEntries.put(filename, set = new HashSet<String>()); - } - - return set.add(entry); - } - - public void copyEntry(String source, String destpath, String archivename, String entryName) { - - try { - String filename = new File(getAbsolutePath(destpath), archivename).getAbsolutePath(); - - if(!addEntryName(filename, entryName)) { - DecompilerContext.getLogger().writeMessage("Zip entry already exists: "+ - destpath+","+archivename+","+entryName, IFernflowerLogger.WARNING); - return; - } - - ZipFile srcarchive = new ZipFile(new File(source)); - - Enumeration<? extends ZipEntry> en = srcarchive.entries(); - while(en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); - - if(entr.getName().equals(entryName)) { - InputStream in = srcarchive.getInputStream(entr); - - ZipOutputStream out = mapArchiveStreams.get(filename); - out.putNextEntry(new ZipEntry(entryName)); - - InterpreterUtil.copyInputStream(in, out); - in.close(); - } - } - - srcarchive.close(); - - } catch(IOException ex) { - DecompilerContext.getLogger().writeMessage("Error copying zip file entry: "+source+","+destpath+","+archivename+","+entryName, IFernflowerLogger.WARNING); - ex.printStackTrace(); - } - } - - public void copyFile(String source, String destpath, String destfilename) { - try { - InterpreterUtil.copyFile(new File(source), new File(destfilename)); - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - public void saveFile(String path, String filename, String content) { - try { - BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(getAbsolutePath(path), filename)), "UTF8")); - out.write(content); - out.flush(); - out.close(); - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - public void createArchive(String path, String archivename, Manifest manifest) { - - try { - File file = new File(getAbsolutePath(path), archivename); - file.createNewFile(); - - ZipOutputStream out; - if(manifest != null) { // jar - out = new JarOutputStream(new FileOutputStream(file), manifest); - } else { - out = new ZipOutputStream(new FileOutputStream(file)); - } - mapArchiveStreams.put(file.getAbsolutePath(), out); - - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - public void saveClassEntry(String path, String archivename, - String qualifiedName, String entryName, String content) { - saveEntry(path, archivename, entryName, content); - } - - public void saveClassFile(String path, String qualifiedName, String entryName, String content) { - saveFile(path, entryName, content); - } - - public void saveEntry(String path, String archivename, String entryName, - String content) { - - try { - String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); - - if(!addEntryName(filename, entryName)) { - DecompilerContext.getLogger().writeMessage("Zip entry already exists: "+ - path+","+archivename+","+entryName, IFernflowerLogger.WARNING); - return; - } - - ZipOutputStream out = mapArchiveStreams.get(filename); - out.putNextEntry(new ZipEntry(entryName)); - - if(content != null) { - BufferedWriter outwriter = new BufferedWriter(new OutputStreamWriter(out, "UTF8")); - outwriter.write(content); - outwriter.flush(); - } - - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - public void saveFolder(String path) { - File f = new File(getAbsolutePath(path)); - f.mkdirs(); - } - - - public void closeArchive(String path, String archivename) { - try { - String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); - - mapArchiveEntries.remove(filename); - ZipOutputStream out = mapArchiveStreams.remove(filename); - - out.flush(); - out.close(); - - } catch(IOException ex) { - ex.printStackTrace(); - } - } - - + private File root; + + private Fernflower fernflower; + + private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); + + private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>(); + + public ConsoleDecompiler() { + this(null); + } + + public ConsoleDecompiler(HashMap<String, Object> propertiesCustom) { + this(new PrintStreamLogger(IFernflowerLogger.WARNING, System.out), propertiesCustom); + } + + protected ConsoleDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { + fernflower = new Fernflower(this, this, propertiesCustom); + DecompilerContext.setLogger(logger); + } + + public static void main(String[] args) { + + try { + + if (args != null && args.length > 1) { + + HashMap<String, Object> mapOptions = new HashMap<String, Object>(); + + List<String> lstSources = new ArrayList<String>(); + List<String> lstLibraries = new ArrayList<String>(); + + boolean isOption = true; + for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination + String arg = args[i]; + + if (isOption && arg.startsWith("-") && + arg.length() > 5 && arg.charAt(4) == '=') { + String value = arg.substring(5).toUpperCase(); + if ("TRUE".equals(value)) { + value = "1"; + } + else if ("FALSE".equals(value)) { + value = "0"; + } + + mapOptions.put(arg.substring(1, 4), value); + } + else { + isOption = false; + + if (arg.startsWith("-e=")) { + lstLibraries.add(arg.substring(3)); + } + else { + lstSources.add(arg); + } + } + } + + if (lstSources.isEmpty()) { + printHelp(); + } + else { + ConsoleDecompiler decompiler = new ConsoleDecompiler( + new PrintStreamLogger(IFernflowerLogger.INFO, System.out), + mapOptions); + + for (String source : lstSources) { + decompiler.addSpace(new File(source), true); + } + + for (String library : lstLibraries) { + decompiler.addSpace(new File(library), false); + } + + decompiler.decompileContext(new File(args[args.length - 1])); + } + } + else { + printHelp(); + } + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + + private static void printHelp() { + System.out.println("Usage: java ConsoleDecompiler ( -<option>=<value>)* (<source>)+ <destination>"); + System.out.println("Example: java ConsoleDecompiler -dgs=true c:\\mysource\\ c:\\my.jar d:\\decompiled\\"); + } + + public void addSpace(File file, boolean isOwn) throws IOException { + fernflower.getStructcontext().addSpace(file, isOwn); + } + + public void decompileContext(File root) { + this.root = root; + fernflower.decompileContext(); + } + + // ******************************************************************* + // Interface IBytecodeProvider + // ******************************************************************* + + public InputStream getBytecodeStream(String externPath, String internPath) { + + try { + File file = new File(externPath); + + if (internPath == null) { + return new FileInputStream(file); + } + else { // archive file + ZipFile archive = new ZipFile(file); + + Enumeration<? extends ZipEntry> en = archive.entries(); + while (en.hasMoreElements()) { + ZipEntry entr = en.nextElement(); + + if (entr.getName().equals(internPath)) { + return archive.getInputStream(entr); + } + } + } + } + catch (IOException ex) { + ex.printStackTrace(); + } + + return null; + } + + // ******************************************************************* + // Interface IDecompilatSaver + // ******************************************************************* + + private String getAbsolutePath(String path) { + return new File(root, path).getAbsolutePath(); + } + + private boolean addEntryName(String filename, String entry) { + HashSet<String> set = mapArchiveEntries.get(filename); + if (set == null) { + mapArchiveEntries.put(filename, set = new HashSet<String>()); + } + + return set.add(entry); + } + + public void copyEntry(String source, String destpath, String archivename, String entryName) { + + try { + String filename = new File(getAbsolutePath(destpath), archivename).getAbsolutePath(); + + if (!addEntryName(filename, entryName)) { + DecompilerContext.getLogger().writeMessage("Zip entry already exists: " + + destpath + "," + archivename + "," + entryName, IFernflowerLogger.WARNING); + return; + } + + ZipFile srcarchive = new ZipFile(new File(source)); + + Enumeration<? extends ZipEntry> en = srcarchive.entries(); + while (en.hasMoreElements()) { + ZipEntry entr = en.nextElement(); + + if (entr.getName().equals(entryName)) { + InputStream in = srcarchive.getInputStream(entr); + + ZipOutputStream out = mapArchiveStreams.get(filename); + out.putNextEntry(new ZipEntry(entryName)); + + InterpreterUtil.copyInputStream(in, out); + in.close(); + } + } + + srcarchive.close(); + } + catch (IOException ex) { + DecompilerContext.getLogger() + .writeMessage("Error copying zip file entry: " + source + "," + destpath + "," + archivename + "," + entryName, + IFernflowerLogger.WARNING); + ex.printStackTrace(); + } + } + + public void copyFile(String source, String destpath, String destfilename) { + try { + InterpreterUtil.copyFile(new File(source), new File(destfilename)); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void saveFile(String path, String filename, String content) { + try { + BufferedWriter out = + new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(getAbsolutePath(path), filename)), "UTF8")); + out.write(content); + out.flush(); + out.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void createArchive(String path, String archivename, Manifest manifest) { + + try { + File file = new File(getAbsolutePath(path), archivename); + file.createNewFile(); + + ZipOutputStream out; + if (manifest != null) { // jar + out = new JarOutputStream(new FileOutputStream(file), manifest); + } + else { + out = new ZipOutputStream(new FileOutputStream(file)); + } + mapArchiveStreams.put(file.getAbsolutePath(), out); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void saveClassEntry(String path, String archivename, + String qualifiedName, String entryName, String content) { + saveEntry(path, archivename, entryName, content); + } + + public void saveClassFile(String path, String qualifiedName, String entryName, String content) { + saveFile(path, entryName, content); + } + + public void saveEntry(String path, String archivename, String entryName, + String content) { + + try { + String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); + + if (!addEntryName(filename, entryName)) { + DecompilerContext.getLogger().writeMessage("Zip entry already exists: " + + path + "," + archivename + "," + entryName, IFernflowerLogger.WARNING); + return; + } + + ZipOutputStream out = mapArchiveStreams.get(filename); + out.putNextEntry(new ZipEntry(entryName)); + + if (content != null) { + BufferedWriter outwriter = new BufferedWriter(new OutputStreamWriter(out, "UTF8")); + outwriter.write(content); + outwriter.flush(); + } + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void saveFolder(String path) { + File f = new File(getAbsolutePath(path)); + f.mkdirs(); + } + + + public void closeArchive(String path, String archivename) { + try { + String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); + + mapArchiveEntries.remove(filename); + ZipOutputStream out = mapArchiveStreams.remove(filename); + + out.flush(); + out.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java index 07dd0f0..dab59e9 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java @@ -1,43 +1,43 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.decompiler; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; + public class IdeDecompiler { - private Fernflower fernflower; - - public IdeDecompiler(IBytecodeProvider provider, + private Fernflower fernflower; + + public IdeDecompiler(IBytecodeProvider provider, IDecompilatSaver saver, IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { - fernflower = new Fernflower(provider, saver, propertiesCustom); - - DecompilerContext.setLogger(logger); - - } + fernflower = new Fernflower(provider, saver, propertiesCustom); + + DecompilerContext.setLogger(logger); + } public void addSpace(File file, boolean isOwn) throws IOException { fernflower.getStructcontext().addSpace(file, isOwn); @@ -46,9 +46,9 @@ public class IdeDecompiler { public void decompileContext() { try { fernflower.decompileContext(); - } finally { + } + finally { fernflower.clearContext(); } } - } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java index 5f20c01..06e4c6f 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java @@ -1,78 +1,78 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.decompiler; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; + import java.io.File; import java.util.HashMap; import java.util.HashSet; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; - public class WebDecompiler extends ConsoleDecompiler { - private HashMap<String, File> mapInputFilenames = new HashMap<String, File>(); - - private HashSet<String> setClassFiles = new HashSet<String>(); - - private File root; - - public WebDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { - super(logger, propertiesCustom); - } - - @Override - public void decompileContext(File root) { - this.root = root; - super.decompileContext(root); - } - - @Override - public void copyFile(String source, String destpath, String destfilename) { - super.copyFile(source, destpath, destfilename); - mapInputFilenames.put(destfilename, new File(getAbsolutePath(destpath), destfilename)); - } - - @Override - public void saveFile(String path, String filename, String content) { - super.saveFile(path, filename, content); - - mapInputFilenames.put(setClassFiles.contains(filename)? - filename.substring(0, filename.lastIndexOf(".java"))+".class": - filename, new File(getAbsolutePath(path), filename)); - } - - @Override - public void saveClassFile(String path, String qualifiedName, String entryName, String content) { - setClassFiles.add(entryName); - saveFile(path, entryName, content); - } - - @Override - public void closeArchive(String path, String archivename) { - super.closeArchive(path, archivename); - mapInputFilenames.put(archivename, new File(getAbsolutePath(path), archivename)); - } - - private String getAbsolutePath(String path) { - return new File(root, path).getAbsolutePath(); - } - - public HashMap<String, File> getMapInputFilenames() { - return mapInputFilenames; - } - + private HashMap<String, File> mapInputFilenames = new HashMap<String, File>(); + + private HashSet<String> setClassFiles = new HashSet<String>(); + + private File root; + + public WebDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { + super(logger, propertiesCustom); + } + + @Override + public void decompileContext(File root) { + this.root = root; + super.decompileContext(root); + } + + @Override + public void copyFile(String source, String destpath, String destfilename) { + super.copyFile(source, destpath, destfilename); + mapInputFilenames.put(destfilename, new File(getAbsolutePath(destpath), destfilename)); + } + + @Override + public void saveFile(String path, String filename, String content) { + super.saveFile(path, filename, content); + + mapInputFilenames.put(setClassFiles.contains(filename) ? + filename.substring(0, filename.lastIndexOf(".java")) + ".class" : + filename, new File(getAbsolutePath(path), filename)); + } + + @Override + public void saveClassFile(String path, String qualifiedName, String entryName, String content) { + setClassFiles.add(entryName); + saveFile(path, entryName, content); + } + + @Override + public void closeArchive(String path, String archivename) { + super.closeArchive(path, archivename); + mapInputFilenames.put(archivename, new File(getAbsolutePath(path), archivename)); + } + + private String getAbsolutePath(String path) { + return new File(root, path).getAbsolutePath(); + } + + public HashMap<String, File> getMapInputFilenames() { + return mapInputFilenames; + } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java b/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java index 6d03b34..3a6c7e8 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java @@ -1,83 +1,84 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.decompiler.helper; -import java.io.PrintStream; - import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.io.PrintStream; + public class PrintStreamLogger implements IFernflowerLogger { - private int severity; - - private int indent; - - private PrintStream stream; - - public PrintStreamLogger(int severity, PrintStream stream) { - this.severity = severity; - this.indent = 0; - this.stream = stream; - } - - - public void writeMessage(String message, int severity) { - if (severity >= this.severity) { - stream.println(InterpreterUtil.getIndentString(indent) + names[severity] + ": " + message); - } - } - - public void writeMessage(String message, Throwable t) { - t.printStackTrace(stream); - writeMessage(message, ERROR); - } - - public void startClass(String classname) { - stream.println(InterpreterUtil.getIndentString(indent++)+"Processing class "+classname+" ..."); - } - - public void endClass() { - stream.println(InterpreterUtil.getIndentString(--indent)+"... proceeded."); - } - - public void startWriteClass(String classname) { - stream.println(InterpreterUtil.getIndentString(indent++)+"Writing class "+classname+" ..."); - } - - public void endWriteClass() { - stream.println(InterpreterUtil.getIndentString(--indent)+"... written."); - } - - public void startMethod(String method) { - if(severity <= INFO) { - stream.println(InterpreterUtil.getIndentString(indent)+"Processing method "+method+" ..."); - } - } - - public void endMethod() { - if(severity <= INFO) { - stream.println(InterpreterUtil.getIndentString(indent)+"... proceeded."); - } - } - - public int getSeverity() { - return severity; - } - - public void setSeverity(int severity) { - this.severity = severity; - } + private int severity; + + private int indent; + + private PrintStream stream; + + public PrintStreamLogger(int severity, PrintStream stream) { + this.severity = severity; + this.indent = 0; + this.stream = stream; + } + + + public void writeMessage(String message, int severity) { + if (severity >= this.severity) { + stream.println(InterpreterUtil.getIndentString(indent) + names[severity] + ": " + message); + } + } + + public void writeMessage(String message, Throwable t) { + t.printStackTrace(stream); + writeMessage(message, ERROR); + } + + public void startClass(String classname) { + stream.println(InterpreterUtil.getIndentString(indent++) + "Processing class " + classname + " ..."); + } + + public void endClass() { + stream.println(InterpreterUtil.getIndentString(--indent) + "... proceeded."); + } + + public void startWriteClass(String classname) { + stream.println(InterpreterUtil.getIndentString(indent++) + "Writing class " + classname + " ..."); + } + + public void endWriteClass() { + stream.println(InterpreterUtil.getIndentString(--indent) + "... written."); + } + + public void startMethod(String method) { + if (severity <= INFO) { + stream.println(InterpreterUtil.getIndentString(indent) + "Processing method " + method + " ..."); + } + } + + public void endMethod() { + if (severity <= INFO) { + stream.println(InterpreterUtil.getIndentString(indent) + "... proceeded."); + } + } + + public int getSeverity() { + return severity; + } + + public void setSeverity(int severity) { + this.severity = severity; + } } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java index 6e13e51..7ff43fc 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java @@ -1,23 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.extern; import java.io.InputStream; public interface IBytecodeProvider { - public InputStream getBytecodeStream(String externPath, String internPath); - + public InputStream getBytecodeStream(String externPath, String internPath); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java index 855f95c..0270b5e 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java @@ -1,39 +1,39 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.extern; import java.util.jar.Manifest; public interface IDecompilatSaver { - public void copyFile(String source, String destpath, String destfilename); + public void copyFile(String source, String destpath, String destfilename); + + public void saveFolder(String path); + + public void saveClassFile(String path, String qualifiedName, String entryName, String content); + + public void saveFile(String path, String filename, String content); - public void saveFolder(String path); - - public void saveClassFile(String path, String qualifiedName, String entryName, String content); + public void createArchive(String path, String archivename, Manifest manifest); - public void saveFile(String path, String filename, String content); - - public void createArchive(String path, String archivename, Manifest manifest); + public void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content); - public void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content); + public void saveEntry(String path, String archivename, String entryName, String content); - public void saveEntry(String path, String archivename, String entryName, String content); + public void copyEntry(String source, String destpath, String archivename, String entry); - public void copyEntry(String source, String destpath, String archivename, String entry); - - public void closeArchive(String path, String archivename); - + public void closeArchive(String path, String archivename); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java index eb1427c..c80039d 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java @@ -1,56 +1,57 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.extern; import java.util.HashMap; public interface IFernflowerLogger { - public static final int TRACE = 1; - public static final int INFO = 2; - public static final int WARNING = 3; - public static final int ERROR = 4; - public static final int IMMEDIATE = 5; - - public static final HashMap<String, Integer> mapLogLevel = new HashMap<String, Integer>() {{ - put("TRACE", 1); - put("INFO", 2); - put("WARN", 3); - put("ERROR", 4); - put("IMME", 5); - }}; - - public static final String[] names = new String[] {""/*DUMMY ENTRY*/, "TRACE", "INFO", "WARNING", "ERROR", ""/*IMMEDIATE*/}; - - public void writeMessage(String message, int severity); - - public void writeMessage(String message, Throwable t); - - public void startClass(String classname); - - public void endClass(); - - public void startWriteClass(String classname); - - public void endWriteClass(); - - public void startMethod(String method); - - public void endMethod(); - - public int getSeverity(); - - public void setSeverity(int severity); + public static final int TRACE = 1; + public static final int INFO = 2; + public static final int WARNING = 3; + public static final int ERROR = 4; + public static final int IMMEDIATE = 5; + + public static final HashMap<String, Integer> mapLogLevel = new HashMap<String, Integer>() {{ + put("TRACE", 1); + put("INFO", 2); + put("WARN", 3); + put("ERROR", 4); + put("IMME", 5); + }}; + + public static final String[] names = new String[]{""/*DUMMY ENTRY*/, "TRACE", "INFO", "WARNING", "ERROR", ""/*IMMEDIATE*/}; + + public void writeMessage(String message, int severity); + + public void writeMessage(String message, Throwable t); + + public void startClass(String classname); + + public void endClass(); + + public void startWriteClass(String classname); + + public void endWriteClass(); + + public void startMethod(String method); + + public void endMethod(); + + public int getSeverity(); + + public void setSeverity(int severity); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index 9f19127..2fcc5e1 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -1,57 +1,58 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.extern; public interface IFernflowerPreferences { - public static final String REMOVE_BRIDGE = "rbr"; - public static final String REMOVE_SYNTHETIC = "rsy"; - public static final String DECOMPILE_INNER = "din"; - public static final String DECOMPILE_CLASS_1_4 = "dc4"; - public static final String DECOMPILE_ASSERTIONS = "das"; - public static final String HIDE_EMPTY_SUPER = "hes"; - public static final String HIDE_DEFAULT_CONSTRUCTOR = "hdc"; - public static final String DECOMPILE_GENERIC_SIGNATURES = "dgs"; - public static final String OUTPUT_COPYRIGHT_COMMENT = "occ"; - public static final String NO_EXCEPTIONS_RETURN = "ner"; - public static final String DECOMPILE_ENUM = "den"; - public static final String REMOVE_GETCLASS_NEW = "rgn"; - public static final String LITERALS_AS_IS = "lit"; - public static final String BOOLEAN_TRUE_ONE = "bto"; - public static final String SYNTHETIC_NOT_SET = "nns"; - public static final String UNDEFINED_PARAM_TYPE_OBJECT = "uto"; - public static final String USE_DEBUG_VARNAMES = "udv"; - public static final String MAX_PROCESSING_METHOD = "mpm"; - public static final String REMOVE_EMPTY_RANGES = "rer"; - public static final String ASCII_STRING_CHARACTERS = "asc"; - - public static final String FINALLY_DEINLINE = "fdi"; - - public static final String FINALLY_CATCHALL = "FINALLY_CATCHALL"; - public static final String FINALLY_SEMAPHOR = "FINALLY_SEMAPHOR"; - - public static final String RENAME_ENTITIES = "ren"; - public static final String USER_RENAMER_CLASS = "urc"; - - public static final String LOG_LEVEL = "log"; - - public static final String NEW_LINE_SEPARATOR = "nls"; - public static final String IDEA_NOT_NULL_ANNOTATION = "inn"; - public static final String LAMBDA_TO_ANONYMOUS_CLASS = "lac"; + public static final String REMOVE_BRIDGE = "rbr"; + public static final String REMOVE_SYNTHETIC = "rsy"; + public static final String DECOMPILE_INNER = "din"; + public static final String DECOMPILE_CLASS_1_4 = "dc4"; + public static final String DECOMPILE_ASSERTIONS = "das"; + public static final String HIDE_EMPTY_SUPER = "hes"; + public static final String HIDE_DEFAULT_CONSTRUCTOR = "hdc"; + public static final String DECOMPILE_GENERIC_SIGNATURES = "dgs"; + public static final String OUTPUT_COPYRIGHT_COMMENT = "occ"; + public static final String NO_EXCEPTIONS_RETURN = "ner"; + public static final String DECOMPILE_ENUM = "den"; + public static final String REMOVE_GETCLASS_NEW = "rgn"; + public static final String LITERALS_AS_IS = "lit"; + public static final String BOOLEAN_TRUE_ONE = "bto"; + public static final String SYNTHETIC_NOT_SET = "nns"; + public static final String UNDEFINED_PARAM_TYPE_OBJECT = "uto"; + public static final String USE_DEBUG_VARNAMES = "udv"; + public static final String MAX_PROCESSING_METHOD = "mpm"; + public static final String REMOVE_EMPTY_RANGES = "rer"; + public static final String ASCII_STRING_CHARACTERS = "asc"; + + public static final String FINALLY_DEINLINE = "fdi"; + + public static final String FINALLY_CATCHALL = "FINALLY_CATCHALL"; + public static final String FINALLY_SEMAPHOR = "FINALLY_SEMAPHOR"; + + public static final String RENAME_ENTITIES = "ren"; + public static final String USER_RENAMER_CLASS = "urc"; + + public static final String LOG_LEVEL = "log"; + + public static final String NEW_LINE_SEPARATOR = "nls"; + public static final String IDEA_NOT_NULL_ANNOTATION = "inn"; + public static final String LAMBDA_TO_ANONYMOUS_CLASS = "lac"; public static final String INDENT_STRING = "ind"; - public static final String LINE_SEPARATOR_WIN = "\r\n"; - public static final String LINE_SEPARATOR_LIN = "\n"; + public static final String LINE_SEPARATOR_WIN = "\r\n"; + public static final String LINE_SEPARATOR_LIN = "\n"; } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java index f379da6..88b1ca4 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java @@ -1,20 +1,35 @@ +/* + * 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.main.extern; public interface IIdentifierRenamer { - public static final int ELEMENT_CLASS = 1; - - public static final int ELEMENT_FIELD = 2; + public static final int ELEMENT_CLASS = 1; - public static final int ELEMENT_METHOD = 3; - - - public boolean toBeRenamed(int element_type, String classname, String element, String descriptor); - - public String getNextClassname(String fullname, String shortname); - - public String getNextFieldname(String classname, String field, String descriptor); + public static final int ELEMENT_FIELD = 2; - public String getNextMethodname(String classname, String method, String descriptor); + public static final int ELEMENT_METHOD = 3; + + + public boolean toBeRenamed(int element_type, String classname, String element, String descriptor); + + public String getNextClassname(String fullname, String shortname); + + public String getNextFieldname(String classname, String field, String descriptor); + + public String getNextMethodname(String classname, String method, String descriptor); } diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index dcbeaea..f0e8591 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -1,22 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.rels; -import java.io.IOException; -import java.util.HashSet; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -36,181 +34,191 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.IOException; +import java.util.HashSet; + public class ClassWrapper { - private StructClass classStruct; - - private HashSet<String> hideMembers = new HashSet<String>(); - - private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>(); - - private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>(); - - private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>(); - - - public ClassWrapper(StructClass classStruct) { - this.classStruct = classStruct; - } - - @SuppressWarnings("deprecation") - public void init() throws IOException { - - DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct); - - DecompilerContext.getLogger().startClass(classStruct.qualifiedName); - - // collect field names - HashSet<String> setFieldNames = new HashSet<String>(); - for(StructField fd: classStruct.getFields()) { - setFieldNames.add(fd.getName()); - } - - for(StructMethod mt: classStruct.getMethods()) { - - DecompilerContext.getLogger().startMethod(mt.getName()+" "+mt.getDescriptor()); - - VarNamesCollector vc = new VarNamesCollector(); - DecompilerContext.setVarncollector(vc); - - CounterContainer counter = new CounterContainer(); - DecompilerContext.setCountercontainer(counter); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor())); - - VarProcessor varproc = new VarProcessor(); - DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc); - - Thread mtthread = null; - RootStatement root = null; - - boolean isError = false; - - try { - if(mt.containsCode()) { - - int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); - - if(maxsec == 0) { // blocking wait - root = MethodProcessorThread.codeToJava(mt, varproc); - } else { - MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext()); - mtthread = new Thread(mtproc); - - mtthread.start(); - - int sec = 0; - while(mtthread.isAlive()) { - - synchronized(mtproc) { - mtproc.wait(100); - } - - if(maxsec > 0 && ++sec > maxsec) { - DecompilerContext.getLogger().writeMessage("Processing time limit ("+maxsec+" sec.) for method " + - mt.getName()+" "+mt.getDescriptor()+ " exceeded, execution interrupted.", IFernflowerLogger.ERROR); - mtthread.stop(); - isError = true; - break; - } - } - - if(!isError) { - if(mtproc.getError() != null) { - throw mtproc.getError(); - } else { - root = mtproc.getRoot(); - } - } - } - - } else { - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - int paramcount = 0; - if(thisvar) { - varproc.getThisvars().put(new VarVersionPaar(0,0), classStruct.qualifiedName); - paramcount = 1; - } - paramcount += md.params.length; - - int varindex = 0; - for(int i=0;i<paramcount;i++) { - varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); - - if(thisvar) { - if(i==0) { - varindex++; - } else { - varindex+=md.params[i-1].stack_size; - } - } else { - varindex+=md.params[i].stack_size; - } - } - } - - } catch(ThreadDeath ex) { - try { - if(mtthread != null) { - mtthread.stop(); - } - } catch(Throwable ignored) { } - - throw ex; - } catch(Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be decompiled.", ex); - isError = true; - } - - MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter); - meth.decompiledWithErrors = isError; - - methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); - - // rename vars so that no one has the same name as a field - varproc.refreshVarNames(new VarNamesCollector(setFieldNames)); - - // if debug information present and should be used - if(DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) { - StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey( - StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); - - if(attr != null) { - varproc.setDebugVarNames(attr.getMapVarNames()); - } - } - - DecompilerContext.getLogger().endMethod(); - } - - DecompilerContext.getLogger().endClass(); - } - - public MethodWrapper getMethodWrapper(String name, String descriptor) { - return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); - } - - public StructClass getClassStruct() { - return classStruct; - } - - public VBStyleCollection<MethodWrapper, String> getMethods() { - return methods; - } - - public HashSet<String> getHideMembers() { - return hideMembers; - } - - public VBStyleCollection<Exprent, String> getStaticFieldInitializers() { - return staticFieldInitializers; - } - - public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() { - return dynamicFieldInitializers; - } + private StructClass classStruct; + + private HashSet<String> hideMembers = new HashSet<String>(); + + private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>(); + + private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>(); + + private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>(); + + + public ClassWrapper(StructClass classStruct) { + this.classStruct = classStruct; + } + + @SuppressWarnings("deprecation") + public void init() throws IOException { + + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct); + + DecompilerContext.getLogger().startClass(classStruct.qualifiedName); + // collect field names + HashSet<String> setFieldNames = new HashSet<String>(); + for (StructField fd : classStruct.getFields()) { + setFieldNames.add(fd.getName()); + } + + for (StructMethod mt : classStruct.getMethods()) { + + DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor()); + + VarNamesCollector vc = new VarNamesCollector(); + DecompilerContext.setVarncollector(vc); + + CounterContainer counter = new CounterContainer(); + DecompilerContext.setCountercontainer(counter); + + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor())); + + VarProcessor varproc = new VarProcessor(); + DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc); + + Thread mtthread = null; + RootStatement root = null; + + boolean isError = false; + + try { + if (mt.containsCode()) { + + int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); + + if (maxsec == 0) { // blocking wait + root = MethodProcessorThread.codeToJava(mt, varproc); + } + else { + MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext()); + mtthread = new Thread(mtproc); + + mtthread.start(); + + int sec = 0; + while (mtthread.isAlive()) { + + synchronized (mtproc) { + mtproc.wait(100); + } + + if (maxsec > 0 && ++sec > maxsec) { + DecompilerContext.getLogger().writeMessage("Processing time limit (" + maxsec + " sec.) for method " + + mt.getName() + " " + mt.getDescriptor() + " exceeded, execution interrupted.", + IFernflowerLogger.ERROR); + mtthread.stop(); + isError = true; + break; + } + } + + if (!isError) { + if (mtproc.getError() != null) { + throw mtproc.getError(); + } + else { + root = mtproc.getRoot(); + } + } + } + } + else { + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = 0; + if (thisvar) { + varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName); + paramcount = 1; + } + paramcount += md.params.length; + + int varindex = 0; + for (int i = 0; i < paramcount; i++) { + varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); + + if (thisvar) { + if (i == 0) { + varindex++; + } + else { + varindex += md.params[i - 1].stack_size; + } + } + else { + varindex += md.params[i].stack_size; + } + } + } + } + catch (ThreadDeath ex) { + try { + if (mtthread != null) { + mtthread.stop(); + } + } + catch (Throwable ignored) { + } + + throw ex; + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex); + isError = true; + } + + MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter); + meth.decompiledWithErrors = isError; + + methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); + + // rename vars so that no one has the same name as a field + varproc.refreshVarNames(new VarNamesCollector(setFieldNames)); + + // if debug information present and should be used + if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) { + StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey( + StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); + + if (attr != null) { + varproc.setDebugVarNames(attr.getMapVarNames()); + } + } + + DecompilerContext.getLogger().endMethod(); + } + + DecompilerContext.getLogger().endClass(); + } + + public MethodWrapper getMethodWrapper(String name, String descriptor) { + return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); + } + + public StructClass getClassStruct() { + return classStruct; + } + + public VBStyleCollection<MethodWrapper, String> getMethods() { + return methods; + } + + public HashSet<String> getHideMembers() { + return hideMembers; + } + + public VBStyleCollection<Exprent, String> getStaticFieldInitializers() { + return staticFieldInitializers; + } + + public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() { + return dynamicFieldInitializers; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index bf5f72f..3d42bda 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -1,12 +1,20 @@ +/* + * 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.main.rels; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; @@ -23,116 +31,121 @@ import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.io.IOException; +import java.util.*; + public class LambdaProcessor { - private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory"; - private static final String JAVAC_LAMBDA_METHOD = "metafactory"; - private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; - - public void processClass(ClassNode node) throws IOException { - - for(ClassNode child : node.nested) { - processClass(child); - } - - if(node.nested.isEmpty()) { - hasLambda(node); - } - } - - public boolean hasLambda(ClassNode node) throws IOException { - - ClassesProcessor clprocessor = DecompilerContext.getClassprocessor(); - StructClass cl = node.classStruct; - - if(cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8 - return false; - } - - StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); - if(bootstrap == null || bootstrap.getMethodsNumber() == 0) { - return false; // no bootstrap constants in pool - } - - Set<Integer> lambda_methods = new HashSet<Integer>(); - - // find lambda bootstrap constants - for(int i = 0; i < bootstrap.getMethodsNumber(); ++i) { - LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle - - if(JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && - JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) && - JAVAC_LAMBDA_METHOD_DESCRIPTOR.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point - lambda_methods.add(i); - } - } - - if(lambda_methods.isEmpty()) { - return false; // no lambda bootstrap constant found - } - - Map<String, String> mapMethodsLambda = new HashMap<String, String>(); - - // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes. - for(StructMethod mt: cl.getMethods()) { - mt.expandData(); - - InstructionSequence seq = mt.getInstructionSequence(); - if(seq != null && seq.length() > 0) { - int len = seq.length(); - - for(int i = 0; i < len; ++i) { - Instruction instr = seq.getInstr(i); - - if(instr.opcode == CodeConstants.opc_invokedynamic) { - LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0)); - - if(lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found - - List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); - MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); - - String lambda_class_name = md.ret.value; - String lambda_method_name = invoke_dynamic.elementname; - String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type - - LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); - - ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname, - content_method_handle.descriptor, content_method_handle.index1, - lambda_class_name, lambda_method_name, lambda_method_descriptor, cl); - node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2; - node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()); - - node.nested.add(node_lambda); - node_lambda.parent = node; - - clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda); - mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName); - } - } - } - } - - mt.releaseResources(); - } - - // build class hierarchy on lambda - for(ClassNode nd : node.nested) { - if(nd.type == ClassNode.CLASS_LAMBDA) { - String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod); - if(parent_class_name != null) { - ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name); - - parent_class.nested.add(nd); - nd.parent = parent_class; - } - } - } - - // FIXME: mixed hierarchy? - - return false; - } - + private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory"; + private static final String JAVAC_LAMBDA_METHOD = "metafactory"; + private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR = + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; + + public void processClass(ClassNode node) throws IOException { + + for (ClassNode child : node.nested) { + processClass(child); + } + + if (node.nested.isEmpty()) { + hasLambda(node); + } + } + + public boolean hasLambda(ClassNode node) throws IOException { + + ClassesProcessor clprocessor = DecompilerContext.getClassprocessor(); + StructClass cl = node.classStruct; + + if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8 + return false; + } + + StructBootstrapMethodsAttribute bootstrap = + (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { + return false; // no bootstrap constants in pool + } + + Set<Integer> lambda_methods = new HashSet<Integer>(); + + // find lambda bootstrap constants + for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { + LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle + + if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && + JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) && + JAVAC_LAMBDA_METHOD_DESCRIPTOR + .equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point + lambda_methods.add(i); + } + } + + if (lambda_methods.isEmpty()) { + return false; // no lambda bootstrap constant found + } + + Map<String, String> mapMethodsLambda = new HashMap<String, String>(); + + // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes. + for (StructMethod mt : cl.getMethods()) { + mt.expandData(); + + InstructionSequence seq = mt.getInstructionSequence(); + if (seq != null && seq.length() > 0) { + int len = seq.length(); + + for (int i = 0; i < len; ++i) { + Instruction instr = seq.getInstr(i); + + if (instr.opcode == CodeConstants.opc_invokedynamic) { + LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0)); + + if (lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found + + List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); + MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); + + String lambda_class_name = md.ret.value; + String lambda_method_name = invoke_dynamic.elementname; + String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type + + LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); + + ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname, + content_method_handle.descriptor, content_method_handle.index1, + lambda_class_name, lambda_method_name, lambda_method_descriptor, cl); + node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2; + node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()); + + node.nested.add(node_lambda); + node_lambda.parent = node; + + clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda); + mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName); + } + } + } + } + + mt.releaseResources(); + } + + // build class hierarchy on lambda + for (ClassNode nd : node.nested) { + if (nd.type == ClassNode.CLASS_LAMBDA) { + String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod); + if (parent_class_name != null) { + ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name); + + parent_class.nested.add(nd); + nd.parent = parent_class; + } + } + } + + // FIXME: mixed hierarchy? + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java index c3c180b..038e057 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java @@ -1,21 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.rels; -import java.io.IOException; - import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -23,252 +22,239 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; -import org.jetbrains.java.decompiler.modules.decompiler.ClearStructHelper; -import org.jetbrains.java.decompiler.modules.decompiler.DomHelper; -import org.jetbrains.java.decompiler.modules.decompiler.ExitHelper; -import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.FinallyProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.IdeaNotNullHelper; -import org.jetbrains.java.decompiler.modules.decompiler.IfHelper; -import org.jetbrains.java.decompiler.modules.decompiler.InlineSingleBlockHelper; -import org.jetbrains.java.decompiler.modules.decompiler.LabelHelper; -import org.jetbrains.java.decompiler.modules.decompiler.LoopExtractHelper; -import org.jetbrains.java.decompiler.modules.decompiler.MergeHelper; -import org.jetbrains.java.decompiler.modules.decompiler.PPandMMHelper; -import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper; -import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper; -import org.jetbrains.java.decompiler.modules.decompiler.StackVarsProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.*; import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; +import java.io.IOException; + public class MethodProcessorThread implements Runnable { - private StructMethod method; - private VarProcessor varproc; - private DecompilerContext parentContext; - - private RootStatement root; - - private Throwable error; - - public MethodProcessorThread(StructMethod method, VarProcessor varproc, - DecompilerContext parentContext) { - this.method = method; - this.varproc = varproc; - this.parentContext = parentContext; - } - - public void run() { - - DecompilerContext.setCurrentContext(parentContext); - - error = null; - root = null; - - try { - root = codeToJava(method, varproc); - - synchronized(this) { - this.notify(); - } - - } catch(ThreadDeath ex) { - ; - } catch(Throwable ex) { - error = ex; - } - - } - - public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException { - - StructClass cl = mt.getClassStruct(); - - boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only - - mt.expandData(); - InstructionSequence seq = mt.getInstructionSequence(); - ControlFlowGraph graph = new ControlFlowGraph(seq); - -// System.out.println(graph.toString()); - - -// if(mt.getName().endsWith("_getActiveServers")) { -// System.out.println(); -// } - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); - - DeadCodeHelper.removeDeadBlocks(graph); - graph.inlineJsr(mt); - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); - - // TODO: move to the start, before jsr inlining - DeadCodeHelper.connectDummyExitBlock(graph); - - DeadCodeHelper.removeGotos(graph); - ExceptionDeobfuscator.removeCircularRanges(graph); - //DeadCodeHelper.removeCircularRanges(graph); - - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - - ExceptionDeobfuscator.restorePopRanges(graph); - - if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { - ExceptionDeobfuscator.removeEmptyRanges(graph); - } - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - - if(DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { - // special case: single return instruction outside of a protected range - DeadCodeHelper.incorporateValueReturns(graph); - } - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); - -// ExceptionDeobfuscator.restorePopRanges(graph); - ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); - - DeadCodeHelper.mergeBasicBlocks(graph); - - DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - //System.out.println(graph.toString()); - - if(ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { - DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING); - } - - RootStatement root = DomHelper.parseGraph(graph); - - if(!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) { - FinallyProcessor fproc = new FinallyProcessor(varproc); - while(fproc.iterateGraph(mt, root, graph)) { - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true); - //System.out.println(graph.toString()); - - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - root = DomHelper.parseGraph(graph); - } - } - - // remove synchronized exception handler - // not until now because of comparison between synchronized statements in the finally cycle - DomHelper.removeSynchronizedHandler(root); - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); -// System.out.println(graph.toString()); - -// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); - - SequenceHelper.condenseSequences(root); - - ClearStructHelper.clearStatements(root); - - ExprProcessor proc = new ExprProcessor(); - proc.processStatement(root, cl); - -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); -// System.out.println(graph.toString()); - - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - for(;;) { - StackVarsProcessor stackproc = new StackVarsProcessor(); - stackproc.simplifyStackVars(root, mt, cl); - - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - varproc.setVarVersions(root); - -// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - if(!new PPandMMHelper().findPPandMM(root)) { - break; - } - } - - for(;;) { - - LabelHelper.cleanUpEdges(root); - - for(;;) { - - MergeHelper.enhanceLoops(root); - - if(LoopExtractHelper.extractLoops(root)) { - continue; - } - - if(!IfHelper.mergeAllIfs(root)) { - break; - } - } - - if(DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { - - if(IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { - - SequenceHelper.condenseSequences(root); - - StackVarsProcessor stackproc = new StackVarsProcessor(); - stackproc.simplifyStackVars(root, mt, cl); - - varproc.setVarVersions(root); - } - } - - LabelHelper.identifyLabels(root); - -// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - if(InlineSingleBlockHelper.inlineSingleBlocks(root)) { - continue; - } - - // initializer may have at most one return point, so no transformation of method exits permitted - if(isInitializer || !ExitHelper.condenseExits(root)) { - break; - } - - // FIXME: !! -// if(!EliminateLoopsHelper.eliminateLoops(root)) { -// break; -// } - } - - ExitHelper.removeRedundantReturns(root); - - SecondaryFunctionsHelper.identifySecondaryFunctions(root); - - varproc.setVarDefinitions(root); - - // must be the last invocation, because it makes the statement structure inconsistent - // FIXME: new edge type needed - LabelHelper.replaceContinueWithBreak(root); - - mt.releaseResources(); - -// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava()); - - return root; - } - - public RootStatement getRoot() { - return root; - } - - public Throwable getError() { - return error; - } - + private StructMethod method; + private VarProcessor varproc; + private DecompilerContext parentContext; + + private RootStatement root; + + private Throwable error; + + public MethodProcessorThread(StructMethod method, VarProcessor varproc, + DecompilerContext parentContext) { + this.method = method; + this.varproc = varproc; + this.parentContext = parentContext; + } + + public void run() { + + DecompilerContext.setCurrentContext(parentContext); + + error = null; + root = null; + + try { + root = codeToJava(method, varproc); + + synchronized (this) { + this.notify(); + } + } + catch (ThreadDeath ex) { + ; + } + catch (Throwable ex) { + error = ex; + } + } + + public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException { + + StructClass cl = mt.getClassStruct(); + + boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only + + mt.expandData(); + InstructionSequence seq = mt.getInstructionSequence(); + ControlFlowGraph graph = new ControlFlowGraph(seq); + + // System.out.println(graph.toString()); + + + // if(mt.getName().endsWith("_getActiveServers")) { + // System.out.println(); + // } + + //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); + + DeadCodeHelper.removeDeadBlocks(graph); + graph.inlineJsr(mt); + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); + + // TODO: move to the start, before jsr inlining + DeadCodeHelper.connectDummyExitBlock(graph); + + DeadCodeHelper.removeGotos(graph); + ExceptionDeobfuscator.removeCircularRanges(graph); + //DeadCodeHelper.removeCircularRanges(graph); + + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + + ExceptionDeobfuscator.restorePopRanges(graph); + + if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { + ExceptionDeobfuscator.removeEmptyRanges(graph); + } + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + + if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { + // special case: single return instruction outside of a protected range + DeadCodeHelper.incorporateValueReturns(graph); + } + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); + + // ExceptionDeobfuscator.restorePopRanges(graph); + ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); + + DeadCodeHelper.mergeBasicBlocks(graph); + + DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); + + //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + //System.out.println(graph.toString()); + + if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { + DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING); + } + + RootStatement root = DomHelper.parseGraph(graph); + + if (!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) { + FinallyProcessor fproc = new FinallyProcessor(varproc); + while (fproc.iterateGraph(mt, root, graph)) { + + //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true); + //System.out.println(graph.toString()); + + //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + root = DomHelper.parseGraph(graph); + } + } + + // remove synchronized exception handler + // not until now because of comparison between synchronized statements in the finally cycle + DomHelper.removeSynchronizedHandler(root); + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + // System.out.println(graph.toString()); + + // LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); + + SequenceHelper.condenseSequences(root); + + ClearStructHelper.clearStatements(root); + + ExprProcessor proc = new ExprProcessor(); + proc.processStatement(root, cl); + + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + // System.out.println(graph.toString()); + + //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + for (; ; ) { + StackVarsProcessor stackproc = new StackVarsProcessor(); + stackproc.simplifyStackVars(root, mt, cl); + + //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + varproc.setVarVersions(root); + + // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + if (!new PPandMMHelper().findPPandMM(root)) { + break; + } + } + + for (; ; ) { + + LabelHelper.cleanUpEdges(root); + + for (; ; ) { + + MergeHelper.enhanceLoops(root); + + if (LoopExtractHelper.extractLoops(root)) { + continue; + } + + if (!IfHelper.mergeAllIfs(root)) { + break; + } + } + + if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { + + if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { + + SequenceHelper.condenseSequences(root); + + StackVarsProcessor stackproc = new StackVarsProcessor(); + stackproc.simplifyStackVars(root, mt, cl); + + varproc.setVarVersions(root); + } + } + + LabelHelper.identifyLabels(root); + + // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + if (InlineSingleBlockHelper.inlineSingleBlocks(root)) { + continue; + } + + // initializer may have at most one return point, so no transformation of method exits permitted + if (isInitializer || !ExitHelper.condenseExits(root)) { + break; + } + + // FIXME: !! + // if(!EliminateLoopsHelper.eliminateLoops(root)) { + // break; + // } + } + + ExitHelper.removeRedundantReturns(root); + + SecondaryFunctionsHelper.identifySecondaryFunctions(root); + + varproc.setVarDefinitions(root); + + // must be the last invocation, because it makes the statement structure inconsistent + // FIXME: new edge type needed + LabelHelper.replaceContinueWithBreak(root); + + mt.releaseResources(); + + // System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava()); + + return root; + } + + public RootStatement getRoot() { + return root; + } + + public Throwable getError() { + return error; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java index 4f8d6f8..d36b8bc 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java @@ -1,22 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.rels; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; @@ -25,38 +23,40 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.struct.StructMethod; +import java.util.HashSet; +import java.util.List; + public class MethodWrapper { - public RootStatement root; - - public VarProcessor varproc; - - public StructMethod methodStruct; - - public CounterContainer counter; - - public DirectGraph graph; - - public List<VarVersionPaar> signatureFields; - - public boolean decompiledWithErrors; - - public HashSet<String> setOuterVarNames = new HashSet<String>(); - - public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) { - this.root = root; - this.varproc = varproc; - this.methodStruct = methodStruct; - this.counter = counter; - } - - public DirectGraph getOrBuildGraph() { - if(graph == null && root != null) { - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - graph = flatthelper.buildDirectGraph(root); - } - return graph; - } - + public RootStatement root; + + public VarProcessor varproc; + + public StructMethod methodStruct; + + public CounterContainer counter; + + public DirectGraph graph; + + public List<VarVersionPaar> signatureFields; + + public boolean decompiledWithErrors; + + public HashSet<String> setOuterVarNames = new HashSet<String>(); + + public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) { + this.root = root; + this.varproc = varproc; + this.methodStruct = methodStruct; + this.counter = counter; + } + + public DirectGraph getOrBuildGraph() { + if (graph == null && root != null) { + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + graph = flatthelper.buildDirectGraph(root); + } + return graph; + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 1c962ec..9dba114 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -1,41 +1,28 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.rels; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +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.stats.DoStatement; @@ -51,970 +38,995 @@ 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.*; +import java.util.Map.Entry; + public class NestedClassProcessor { - - - public void processClass(ClassNode root, ClassNode node) { - - // hide synthetic lambda content methods - if(node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { - ClassNode node_content = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName); - if(node_content != null && node_content.wrapper != null) { - node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key); - } - } - - if(node.nested.isEmpty()) { - return; - } - - if(node.type != ClassNode.CLASS_LAMBDA) { - - computeLocalVarsAndDefinitions(node); - - // for each local or anonymous class ensure not empty enclosing method - checkNotFoundClasses(root, node); - } - - int nameless = 0, synthetics = 0; - for(ClassNode child : node.nested) { - // ensure not-empty class name - if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { - StructClass cl = child.classStruct; - if (((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { - child.simpleName = "SyntheticClass_" + (++synthetics); - } else { - DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); - child.simpleName = "NamelessClass_" + (++nameless); - } - } - } - - for(ClassNode child : node.nested) { - if(child.type == ClassNode.CLASS_LAMBDA) { - setLambdaVars(node, child); - } else { - if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { - insertLocalVars(node, child); - - if(child.type == ClassNode.CLASS_LOCAL) { - setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); - } - } - } - } - - for(ClassNode child : node.nested) { - processClass(root, child); - } - - } - - private void setLambdaVars(ClassNode parent, ClassNode child) { - - if(child.lambda_information.is_method_reference) { // method reference, no code and no parameters - return; - } - - final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); - final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); - - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); - final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); - - final int vars_count = md_content.params.length - md_lambda.params.length; -// if(vars_count < 0) { // should not happen, but just in case... -// vars_count = 0; -// } - - final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; - - final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; - final String lambda_class_name = child.simpleName; - - final VarType lambda_class_type = new VarType(lambda_class_name, true); - - // this pointer - if(!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { - meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); - meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); - } - - // local variables - DirectGraph graph = encmeth.getOrBuildGraph(); - - final HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - - if(expr.type == Exprent.EXPRENT_NEW) { - NewExprent new_expr = (NewExprent)expr; - if(new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { - - InvocationExprent inv_dynamic = new_expr.getConstructor(); - - int param_index = is_static_lambda_content ? 0 : 1;; - int varindex = is_static_lambda_content ? 0 : 1; - - for(int i = 0; i < vars_count; ++i) { - - Exprent param = inv_dynamic.getLstParameters().get(param_index + i); - - if(param.type == Exprent.EXPRENT_VAR) { - VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param); - String enc_varname = encmeth.varproc.getVarName(enc_varpaar); - - //meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname); - mapNewNames.put(new VarVersionPaar(varindex, 0), enc_varname); - } - - varindex+=md_content.params[i].stack_size; - } - - } - } - } - - return 0; - } - }); - - // update names of local variables - HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { - meth.varproc.setVarName(entr.getKey(), entr.getValue()); - } - - } - - private void checkNotFoundClasses(ClassNode root, ClassNode node) { - - List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested); - - for(ClassNode child : lstChildren) { - - if((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { - - Set<String> setEnclosing = child.enclosingClasses; - - if(setEnclosing.size() == 1) { - StructEnclosingMethodAttribute attr = (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); - if(attr != null && attr.getMethodName() != null) { - if(node.classStruct.qualifiedName.equals(attr.getClassname()) && - node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { - child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); - continue; - } - } - } - - node.nested.remove(child); - child.parent = null; - setEnclosing.remove(node.classStruct.qualifiedName); - - boolean hasEnclosing = !setEnclosing.isEmpty(); - if(hasEnclosing) { - hasEnclosing = insertNestedClass(root, child); - } - - if(!hasEnclosing) { - if(child.type == ClassNode.CLASS_ANONYMOUS) { - DecompilerContext.getLogger().writeMessage("Unreferenced anonymous class "+child.classStruct.qualifiedName+"!", IFernflowerLogger.WARNING); - } else if(child.type == ClassNode.CLASS_LOCAL) { - DecompilerContext.getLogger().writeMessage("Unreferenced local class "+child.classStruct.qualifiedName+"!", IFernflowerLogger.WARNING); - } - } - } - } - } - - private boolean insertNestedClass(ClassNode root, ClassNode child) { - - Set<String> setEnclosing = child.enclosingClasses; - - LinkedList<ClassNode> stack = new LinkedList<ClassNode>(); - stack.add(root); - - while(!stack.isEmpty()) { - - ClassNode node = stack.removeFirst(); - - if(setEnclosing.contains(node.classStruct.qualifiedName)) { - node.nested.add(child); - child.parent = node; - - return true; - } - - // note: ordered list - stack.addAll(node.nested); - } - - return false; - } - - - private void computeLocalVarsAndDefinitions(final ClassNode node) { - - // local var masks - // class name, constructor descriptor, field mask - final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarMasks = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); - - int cltypes = 0; - - for(ClassNode nd: node.nested) { - if(nd.type != ClassNode.CLASS_LAMBDA) { - if((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { - - cltypes |= nd.type; - - HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); - if(mask.isEmpty()) { - DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING); - } else { - mapVarMasks.put(nd.classStruct.qualifiedName, mask); - } - } - } - } - - // local var masks - final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); - - if(cltypes != ClassNode.CLASS_MEMBER) { - - // iterate enclosing class - for(final MethodWrapper meth: node.wrapper.getMethods()) { - - if(meth.root != null) { // neither abstract, nor native - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - - if(expr.type == Exprent.EXPRENT_NEW) { - InvocationExprent constr = ((NewExprent)expr).getConstructor(); - - if(constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor - - String refclname = constr.getClassname(); - - ClassNode nestedClassNode = node.getClassNode(refclname); - - if(nestedClassNode.type != ClassNode.CLASS_MEMBER) { - - List<VarFieldPair> mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); - - if(!mapVarFieldPairs.containsKey(refclname)) { - mapVarFieldPairs.put(refclname, new HashMap<String, List<VarFieldPair>>()); - } - - List<VarFieldPair> lstTemp = new ArrayList<VarFieldPair>(); - - for(int i=0;i<mask.size();i++) { - Exprent param = constr.getLstParameters().get(i); - VarFieldPair pair = null; - - if(param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); - - // FIXME: final flags of variables are wrong! Correct the entire final functionality. -// if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { - pair = new VarFieldPair(mask.get(i).keyfield, varpaar); -// } - } - - lstTemp.add(pair); - } - - List<VarFieldPair> pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); - - if(pairmask == null) { - pairmask = lstTemp; - } else { - for(int i=0;i<pairmask.size();i++) { - if(!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { - pairmask.set(i, null); - } - } - } - - mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); - nestedClassNode.enclosingMethod = InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); - } - } - } - - } - return 0; - } - }); - } - } - } - - // merge var masks - for(Entry<String, HashMap<String, List<VarFieldPair>>> entcl : mapVarMasks.entrySet()) { - - ClassNode nestedNode = node.getClassNode(entcl.getKey()); - - // intersection - List<VarFieldPair> intrPairMask = null; - // merge referenced constructors - if(mapVarFieldPairs.containsKey(entcl.getKey())) { - for(List<VarFieldPair> mask : mapVarFieldPairs.get(entcl.getKey()).values()) { - if(intrPairMask == null) { - intrPairMask = new ArrayList<VarFieldPair>(mask); - } else { - mergeListSignatures(intrPairMask, mask, false); - } - } - } - - List<VarFieldPair> intrMask = null; - // merge all constructors - for(List<VarFieldPair> mask : entcl.getValue().values()) { - if(intrMask == null) { - intrMask = new ArrayList<VarFieldPair>(mask); - } else { - mergeListSignatures(intrMask, mask, false); - } - } - - if(intrPairMask == null) { // member or local and never instantiated - intrPairMask = new ArrayList<VarFieldPair>(intrMask); - - boolean found = false; - - for(int i=0;i<intrPairMask.size();i++) { - if(intrPairMask.get(i) != null) { - if(found) { - intrPairMask.set(i, null); - } - found = true; - } - } - } - - mergeListSignatures(intrPairMask, intrMask, true); - - for(int i=0;i<intrPairMask.size();i++) { - VarFieldPair pair = intrPairMask.get(i); - if(pair != null && pair.keyfield.length() > 0) { - nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); - } - } - - // set resulting constructor signatures - for(Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { - mergeListSignatures(entmt.getValue(), intrPairMask, false); - - MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); - meth.signatureFields = new ArrayList<VarVersionPaar>(); - - for(VarFieldPair pair : entmt.getValue()) { - meth.signatureFields.add(pair==null?null:pair.varpaar); - } - } - - } - - } - - private void insertLocalVars(final ClassNode parent, final ClassNode child) { - - // enclosing method, is null iff member class - MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); - - // iterate all child methods - for(final MethodWrapper meth : child.wrapper.getMethods()) { - - if(meth.root != null) { // neither abstract nor native - - // local var names - HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); - // local var types - HashMap<VarVersionPaar, VarType> mapNewTypes = new HashMap<VarVersionPaar, VarType>(); - - final HashMap<Integer, VarVersionPaar> mapParamsToNewVars = new HashMap<Integer, VarVersionPaar>(); - if(meth.signatureFields != null) { - int index = 0; - int varindex = 1; - MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); - - for(VarVersionPaar paar : meth.signatureFields) { - if(paar != null) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - - mapParamsToNewVars.put(varindex, newvar); - - String varname = null; - VarType vartype = null; - - if(child.type != ClassNode.CLASS_MEMBER) { - varname = encmeth.varproc.getVarName(paar); - vartype = encmeth.varproc.getVarType(paar); - - encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); - } - - if(paar.var == -1 || "this".equals(varname)) { - if(parent.simpleName == null) { - // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; - } else { - varname = parent.simpleName+".this"; - } - meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); - } - - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); - } - varindex+=md.params[index++].stack_size; - } - } - - // new vars - final HashMap<String, VarVersionPaar> mapFieldsToNewVars = new HashMap<String, VarVersionPaar>(); - - for(ClassNode clnode = child; clnode != null; clnode = clnode.parent) { - - for(Entry<String, VarVersionPaar> entr : clnode.mapFieldsToVars.entrySet()) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - - mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); - - String varname = null; - VarType vartype = null; - - if(clnode.type != ClassNode.CLASS_MEMBER) { - - MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); - - varname = enclosing_method.varproc.getVarName(entr.getValue()); - vartype = enclosing_method.varproc.getVarType(entr.getValue()); - - enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); - } - - if(entr.getValue().var == -1 || "this".equals(varname)) { - if(clnode.parent.simpleName == null) { - // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; - } else { - varname = clnode.parent.simpleName+".this"; - } - meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); - } - - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); - - // hide synthetic field - if(clnode == child) { // fields higher up the chain were already handled with their classes - StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); - child.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); - } - } - } - - HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); - - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); - - for(Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { - VarVersionPaar varpaar = entr.getKey(); - VarType vartype = mapNewTypes.get(varpaar); - - meth.varproc.setVarName(varpaar, entr.getValue()); - if(vartype != null) { - meth.varproc.setVarType(varpaar, vartype); - } - } - - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - - if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only - mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, - InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)))) { - return 2; - } - - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { - // return 2; - //} - } - } - - if(child.type == ClassNode.CLASS_ANONYMOUS && "<init>".equals(meth.methodStruct.getName()) - && exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if(invexpr.getFunctype() == InvocationExprent.TYP_INIT) { - // invocation of the super constructor in an anonymous class - child.superInvocation = invexpr; // FIXME: save original names of parameters - return 2; - } - } - - replaceExprent(exprent); - - return 0; - } - - private Exprent replaceExprent(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent)exprent).getIndex(); - if(mapParamsToNewVars.containsKey(varindex)) { - VarVersionPaar newvar = mapParamsToNewVars.get(varindex); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); - } - } else if(exprent.type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)exprent; - - String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); - - if(mapFieldsToNewVars.containsKey(keyField)) { - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(keyField)) { - VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); - } - } - - boolean replaced = true; - while(replaced) { - replaced = false; - - for(Exprent expr: exprent.getAllExprents()) { - Exprent retexpr = replaceExprent(expr); - if(retexpr != null) { - exprent.replaceExprent(expr, retexpr); - replaced = true; - break; - } - } - } - - return null; - } - }); - - } - } - - } - - private HashMap<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) { - - HashMap<String, List<VarFieldPair>> mapMasks = new HashMap<String, List<VarFieldPair>>(); - - StructClass cl = wrapper.getClassStruct(); - - // iterate over constructors - for(StructMethod mt: cl.getMethods()) { - if("<init>".equals(mt.getName())) { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - MethodWrapper meth = wrapper.getMethodWrapper("<init>", mt.getDescriptor()); - DirectGraph graph = meth.getOrBuildGraph(); - - if(graph != null) { // something gone wrong, should not be null - List<VarFieldPair> fields = new ArrayList<VarFieldPair>(); - - int varindex = 1; - for(int i=0;i<md.params.length;i++) { // no static methods allowed - String keyField = getEnclosingVarField(cl, meth, graph, varindex); - fields.add(keyField==null?null:new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? - varindex+=md.params[i].stack_size; - } - mapMasks.put(mt.getDescriptor(), fields); - } - } - } - - return mapMasks; - } - - private String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { - - String field = ""; - - // parameter variable final - if(meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { - return null; - } - - boolean notsynth = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - - // no loop at the begin - DirectNode firstnode = graph.first; - if(firstnode.preds.isEmpty()) { - // assignment to a final synthetic field? - for(Exprent exprent: firstnode.exprents) { - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if(asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - - FieldExprent left = (FieldExprent)asexpr.getLeft(); - StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); - - if(fd != null) { // local (== not inherited) field - if(cl.qualifiedName.equals(left.getClassname()) && - (fd.access_flags & CodeConstants.ACC_FINAL) != 0 && - ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || - fd.getAttributes().containsKey("Synthetic") || - (notsynth && (fd.access_flags & CodeConstants.ACC_PRIVATE) != 0))) { - field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); - break; - } - } - } - } - } - } - } - - return field; - } - - private void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) { - - int i=1; - for(;;) { - if(first.size() <= i || second.size() <= i) { - break; - } - - VarFieldPair fobj = first.get(first.size() - i); - VarFieldPair sobj = second.get(second.size() - i); - - boolean eq = false; - if(fobj == null || sobj == null) { - eq = (fobj == sobj); - } else { - eq = true; - if(fobj.keyfield.length()==0) { - fobj.keyfield = sobj.keyfield; - } else if(sobj.keyfield.length() == 0) { - if(both) { - sobj.keyfield = fobj.keyfield; - } - } else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if(!eq) { - first.set(first.size() - i, null); - if(both) { - second.set(second.size() - i, null); - } - } else { - if(fobj != null) { - if(fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; - } else { - sobj.varpaar = fobj.varpaar; - } - } - } - i++; - } - - for(int j=1;j<=first.size()-i;j++) { - first.set(j, null); - } - - if(both) { - for(int j=1;j<=second.size()-i;j++) { - second.set(j, null); - } - } - - // first - if(first.isEmpty()) { - if(!second.isEmpty() && both) { - second.set(0, null); - } - } else if(second.isEmpty()) { - first.set(0, null); - } else { - VarFieldPair fobj = first.get(0); - VarFieldPair sobj = second.get(0); - - boolean eq = false; - if(fobj == null || sobj == null) { - eq = (fobj == sobj); - } else { - eq = true; - if(fobj.keyfield.length()==0) { - fobj.keyfield = sobj.keyfield; - } else if(sobj.keyfield.length() == 0) { - if(both) { - sobj.keyfield = fobj.keyfield; - } - } else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if(!eq) { - first.set(0, null); - if(both) { - second.set(0, null); - } - } else if(fobj != null) { - if(fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; - } else { - sobj.varpaar = fobj.varpaar; - } - } - } - - } - - - private void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { - - RootStatement root = meth.root; - - HashSet<Statement> setStats = new HashSet<Statement>(); - VarType classtype = new VarType(node.classStruct.qualifiedName, true); - - Statement stdef = getDefStatement(root, classtype, setStats); - if(stdef == null) { - // unreferenced local class - stdef = root.getFirst(); - } - - Statement first = findFirstBlock(stdef, setStats); - - List<Exprent> lst; - if(first == null) { - lst = stdef.getVarDefinitions(); - } else if(first.getExprents() == null) { - lst = first.getVarDefinitions(); - } else { - lst = first.getExprents(); - } - - - int addindex = 0; - for(Exprent expr: lst) { - if(searchForClass(expr, classtype)) { - break; - } - addindex++; - } - - VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), - classtype, meth.varproc); - var.setDefinition(true); - var.setClassdef(true); - - lst.add(addindex, var); - - } - - - - private Statement findFirstBlock(Statement stat, HashSet<Statement> setStats) { - - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(stat); - - while(!stack.isEmpty()) { - Statement st = stack.remove(0); - - if(stack.isEmpty() || setStats.contains(st)) { - - if(st.isLabeled() && !stack.isEmpty()) { - return st; - } - - if(st.getExprents() != null) { - return st; - } else { - stack.clear(); - - switch(st.type) { - case Statement.TYPE_SEQUENCE: - stack.addAll(0, st.getStats()); - break; - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - stack.add(st.getFirst()); - break; - default: - return st; - } - } - } - } - - return null; - } - - - private Statement getDefStatement(Statement stat, VarType classtype, HashSet<Statement> setStats) { - - List<Exprent> condlst = new ArrayList<Exprent>(); - Statement retstat = null; - - if(stat.getExprents() == null) { - int counter = 0; - - for(Object obj: stat.getSequentialObjects()) { - if(obj instanceof Statement) { - Statement st = (Statement)obj; - - Statement stTemp = getDefStatement(st, classtype, setStats); - - if(stTemp != null) { - if(counter == 1) { - retstat = stat; - break; - } - retstat = stTemp; - counter++; - } - - if(st.type == DoStatement.TYPE_DO) { - DoStatement dost = (DoStatement)st; - - condlst.addAll(dost.getInitExprentList()); - condlst.addAll(dost.getConditionExprentList()); - } - - } else if(obj instanceof Exprent) { - condlst.add((Exprent)obj); - } - } - } else { - condlst = stat.getExprents(); - } - - if(retstat != stat) { - for(Exprent exprent : condlst) { - if(exprent!=null && searchForClass(exprent, classtype)) { - retstat = stat; - break; - } - } - } - - if(retstat != null) { - setStats.add(stat); - } - - return retstat; - } - - private boolean searchForClass(Exprent exprent, VarType classtype) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - String classname = classtype.value; - - for(Exprent expr : lst) { - - boolean res = false; - - switch(expr.type) { - case Exprent.EXPRENT_CONST: - ConstExprent cexpr = (ConstExprent)expr; - res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || - classtype.equals(cexpr.getConsttype())); - break; - case Exprent.EXPRENT_FIELD: - res = classname.equals(((FieldExprent)expr).getClassname()); - break; - case Exprent.EXPRENT_INVOCATION: - res = classname.equals(((InvocationExprent)expr).getClassname()); - break; - case Exprent.EXPRENT_NEW: - VarType newType = expr.getExprType(); - res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); - break; - case Exprent.EXPRENT_VAR: - VarExprent vexpr = (VarExprent)expr; - if(vexpr.isDefinition()) { - VarType vtype = vexpr.getVartype(); - if(classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { - res = true; - } - } - } - - if(res) { - return true; - } - } - - return false; - } - - - private class VarFieldPair { - - public String keyfield = ""; - public VarVersionPaar varpaar; - - public VarFieldPair(String field, VarVersionPaar varpaar) { - this.keyfield = field; - this.varpaar = varpaar; - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof VarFieldPair)) return false; - - VarFieldPair pair = (VarFieldPair)o; - return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); - } - - @Override - public int hashCode() { - return keyfield.hashCode()+varpaar.hashCode(); - } - - } - + + + public void processClass(ClassNode root, ClassNode node) { + + // hide synthetic lambda content methods + if (node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { + ClassNode node_content = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName); + if (node_content != null && node_content.wrapper != null) { + node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key); + } + } + + if (node.nested.isEmpty()) { + return; + } + + if (node.type != ClassNode.CLASS_LAMBDA) { + + computeLocalVarsAndDefinitions(node); + + // for each local or anonymous class ensure not empty enclosing method + checkNotFoundClasses(root, node); + } + + int nameless = 0, synthetics = 0; + for (ClassNode child : node.nested) { + // ensure not-empty class name + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { + StructClass cl = child.classStruct; + if (((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { + child.simpleName = "SyntheticClass_" + (++synthetics); + } + else { + DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); + child.simpleName = "NamelessClass_" + (++nameless); + } + } + } + + for (ClassNode child : node.nested) { + if (child.type == ClassNode.CLASS_LAMBDA) { + setLambdaVars(node, child); + } + else { + if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { + insertLocalVars(node, child); + + if (child.type == ClassNode.CLASS_LOCAL) { + setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); + } + } + } + } + + for (ClassNode child : node.nested) { + processClass(root, child); + } + } + + private void setLambdaVars(ClassNode parent, ClassNode child) { + + if (child.lambda_information.is_method_reference) { // method reference, no code and no parameters + return; + } + + final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); + final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); + final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); + + final int vars_count = md_content.params.length - md_lambda.params.length; + // if(vars_count < 0) { // should not happen, but just in case... + // vars_count = 0; + // } + + final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; + + final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; + final String lambda_class_name = child.simpleName; + + final VarType lambda_class_type = new VarType(lambda_class_name, true); + + // this pointer + if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { + meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); + meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); + } + + // local variables + DirectGraph graph = encmeth.getOrBuildGraph(); + + final HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + NewExprent new_expr = (NewExprent)expr; + if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { + + InvocationExprent inv_dynamic = new_expr.getConstructor(); + + int param_index = is_static_lambda_content ? 0 : 1; + ; + int varindex = is_static_lambda_content ? 0 : 1; + + for (int i = 0; i < vars_count; ++i) { + + Exprent param = inv_dynamic.getLstParameters().get(param_index + i); + + if (param.type == Exprent.EXPRENT_VAR) { + VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param); + String enc_varname = encmeth.varproc.getVarName(enc_varpaar); + + //meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname); + mapNewNames.put(new VarVersionPaar(varindex, 0), enc_varname); + } + + varindex += md_content.params[i].stack_size; + } + } + } + } + + return 0; + } + }); + + // update names of local variables + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + meth.varproc.setVarName(entr.getKey(), entr.getValue()); + } + } + + private void checkNotFoundClasses(ClassNode root, ClassNode node) { + + List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested); + + for (ClassNode child : lstChildren) { + + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { + + Set<String> setEnclosing = child.enclosingClasses; + + if (setEnclosing.size() == 1) { + StructEnclosingMethodAttribute attr = + (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); + if (attr != null && attr.getMethodName() != null) { + if (node.classStruct.qualifiedName.equals(attr.getClassname()) && + node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { + child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); + continue; + } + } + } + + node.nested.remove(child); + child.parent = null; + setEnclosing.remove(node.classStruct.qualifiedName); + + boolean hasEnclosing = !setEnclosing.isEmpty(); + if (hasEnclosing) { + hasEnclosing = insertNestedClass(root, child); + } + + if (!hasEnclosing) { + if (child.type == ClassNode.CLASS_ANONYMOUS) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced anonymous class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + } + else if (child.type == ClassNode.CLASS_LOCAL) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced local class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + } + } + } + } + } + + private boolean insertNestedClass(ClassNode root, ClassNode child) { + + Set<String> setEnclosing = child.enclosingClasses; + + LinkedList<ClassNode> stack = new LinkedList<ClassNode>(); + stack.add(root); + + while (!stack.isEmpty()) { + + ClassNode node = stack.removeFirst(); + + if (setEnclosing.contains(node.classStruct.qualifiedName)) { + node.nested.add(child); + child.parent = node; + + return true; + } + + // note: ordered list + stack.addAll(node.nested); + } + + return false; + } + + + private void computeLocalVarsAndDefinitions(final ClassNode node) { + + // local var masks + // class name, constructor descriptor, field mask + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarMasks = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + int cltypes = 0; + + for (ClassNode nd : node.nested) { + if (nd.type != ClassNode.CLASS_LAMBDA) { + if ((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { + + cltypes |= nd.type; + + HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); + if (mask.isEmpty()) { + DecompilerContext.getLogger() + .writeMessage("Nested class " + nd.classStruct.qualifiedName + " has no constructor!", IFernflowerLogger.WARNING); + } + else { + mapVarMasks.put(nd.classStruct.qualifiedName, mask); + } + } + } + } + + // local var masks + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = + new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + if (cltypes != ClassNode.CLASS_MEMBER) { + + // iterate enclosing class + for (final MethodWrapper meth : node.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract, nor native + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + InvocationExprent constr = ((NewExprent)expr).getConstructor(); + + if (constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor + + String refclname = constr.getClassname(); + + ClassNode nestedClassNode = node.getClassNode(refclname); + + if (nestedClassNode.type != ClassNode.CLASS_MEMBER) { + + List<VarFieldPair> mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); + + if (!mapVarFieldPairs.containsKey(refclname)) { + mapVarFieldPairs.put(refclname, new HashMap<String, List<VarFieldPair>>()); + } + + List<VarFieldPair> lstTemp = new ArrayList<VarFieldPair>(); + + for (int i = 0; i < mask.size(); i++) { + Exprent param = constr.getLstParameters().get(i); + VarFieldPair pair = null; + + if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { + VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); + + // FIXME: final flags of variables are wrong! Correct the entire final functionality. + // if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { + pair = new VarFieldPair(mask.get(i).keyfield, varpaar); + // } + } + + lstTemp.add(pair); + } + + List<VarFieldPair> pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); + + if (pairmask == null) { + pairmask = lstTemp; + } + else { + for (int i = 0; i < pairmask.size(); i++) { + if (!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { + pairmask.set(i, null); + } + } + } + + mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); + nestedClassNode.enclosingMethod = + InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); + } + } + } + } + return 0; + } + }); + } + } + } + + // merge var masks + for (Entry<String, HashMap<String, List<VarFieldPair>>> entcl : mapVarMasks.entrySet()) { + + ClassNode nestedNode = node.getClassNode(entcl.getKey()); + + // intersection + List<VarFieldPair> intrPairMask = null; + // merge referenced constructors + if (mapVarFieldPairs.containsKey(entcl.getKey())) { + for (List<VarFieldPair> mask : mapVarFieldPairs.get(entcl.getKey()).values()) { + if (intrPairMask == null) { + intrPairMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrPairMask, mask, false); + } + } + } + + List<VarFieldPair> intrMask = null; + // merge all constructors + for (List<VarFieldPair> mask : entcl.getValue().values()) { + if (intrMask == null) { + intrMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrMask, mask, false); + } + } + + if (intrPairMask == null) { // member or local and never instantiated + intrPairMask = new ArrayList<VarFieldPair>(intrMask); + + boolean found = false; + + for (int i = 0; i < intrPairMask.size(); i++) { + if (intrPairMask.get(i) != null) { + if (found) { + intrPairMask.set(i, null); + } + found = true; + } + } + } + + mergeListSignatures(intrPairMask, intrMask, true); + + for (int i = 0; i < intrPairMask.size(); i++) { + VarFieldPair pair = intrPairMask.get(i); + if (pair != null && pair.keyfield.length() > 0) { + nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); + } + } + + // set resulting constructor signatures + for (Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { + mergeListSignatures(entmt.getValue(), intrPairMask, false); + + MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); + meth.signatureFields = new ArrayList<VarVersionPaar>(); + + for (VarFieldPair pair : entmt.getValue()) { + meth.signatureFields.add(pair == null ? null : pair.varpaar); + } + } + } + } + + private void insertLocalVars(final ClassNode parent, final ClassNode child) { + + // enclosing method, is null iff member class + MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + // iterate all child methods + for (final MethodWrapper meth : child.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract nor native + + // local var names + HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + // local var types + HashMap<VarVersionPaar, VarType> mapNewTypes = new HashMap<VarVersionPaar, VarType>(); + + final HashMap<Integer, VarVersionPaar> mapParamsToNewVars = new HashMap<Integer, VarVersionPaar>(); + if (meth.signatureFields != null) { + int index = 0; + int varindex = 1; + MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + + for (VarVersionPaar paar : meth.signatureFields) { + if (paar != null) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapParamsToNewVars.put(varindex, newvar); + + String varname = null; + VarType vartype = null; + + if (child.type != ClassNode.CLASS_MEMBER) { + varname = encmeth.varproc.getVarName(paar); + vartype = encmeth.varproc.getVarType(paar); + + encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (paar.var == -1 || "this".equals(varname)) { + if (parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + } + varindex += md.params[index++].stack_size; + } + } + + // new vars + final HashMap<String, VarVersionPaar> mapFieldsToNewVars = new HashMap<String, VarVersionPaar>(); + + for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) { + + for (Entry<String, VarVersionPaar> entr : clnode.mapFieldsToVars.entrySet()) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); + + String varname = null; + VarType vartype = null; + + if (clnode.type != ClassNode.CLASS_MEMBER) { + + MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); + + varname = enclosing_method.varproc.getVarName(entr.getValue()); + vartype = enclosing_method.varproc.getVarType(entr.getValue()); + + enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (entr.getValue().var == -1 || "this".equals(varname)) { + if (clnode.parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = clnode.parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + + // hide synthetic field + if (clnode == child) { // fields higher up the chain were already handled with their classes + StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); + child.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + } + } + + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + VarVersionPaar varpaar = entr.getKey(); + VarType vartype = mapNewTypes.get(varpaar); + + meth.varproc.setVarName(varpaar, entr.getValue()); + if (vartype != null) { + meth.varproc.setVarType(varpaar, vartype); + } + } + + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + + if (fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only + mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, + InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr + .getDescriptor().descriptorString)))) { + return 2; + } + + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { + // return 2; + //} + } + } + + if (child.type == ClassNode.CLASS_ANONYMOUS && "<init>".equals(meth.methodStruct.getName()) + && exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)exprent; + if (invexpr.getFunctype() == InvocationExprent.TYP_INIT) { + // invocation of the super constructor in an anonymous class + child.superInvocation = invexpr; // FIXME: save original names of parameters + return 2; + } + } + + replaceExprent(exprent); + + return 0; + } + + private Exprent replaceExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)exprent).getIndex(); + if (mapParamsToNewVars.containsKey(varindex)) { + VarVersionPaar newvar = mapParamsToNewVars.get(varindex); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + else if (exprent.type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)exprent; + + String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil + .makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); + + if (mapFieldsToNewVars.containsKey(keyField)) { + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(keyField)) { + VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = replaceExprent(expr); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + return null; + } + }); + } + } + } + + private HashMap<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) { + + HashMap<String, List<VarFieldPair>> mapMasks = new HashMap<String, List<VarFieldPair>>(); + + StructClass cl = wrapper.getClassStruct(); + + // iterate over constructors + for (StructMethod mt : cl.getMethods()) { + if ("<init>".equals(mt.getName())) { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + MethodWrapper meth = wrapper.getMethodWrapper("<init>", mt.getDescriptor()); + DirectGraph graph = meth.getOrBuildGraph(); + + if (graph != null) { // something gone wrong, should not be null + List<VarFieldPair> fields = new ArrayList<VarFieldPair>(); + + int varindex = 1; + for (int i = 0; i < md.params.length; i++) { // no static methods allowed + String keyField = getEnclosingVarField(cl, meth, graph, varindex); + fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? + varindex += md.params[i].stack_size; + } + mapMasks.put(mt.getDescriptor(), fields); + } + } + } + + return mapMasks; + } + + private String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { + + String field = ""; + + // parameter variable final + if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { + return null; + } + + boolean notsynth = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + // no loop at the begin + DirectNode firstnode = graph.first; + if (firstnode.preds.isEmpty()) { + // assignment to a final synthetic field? + for (Exprent exprent : firstnode.exprents) { + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + + FieldExprent left = (FieldExprent)asexpr.getLeft(); + StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); + + if (fd != null) { // local (== not inherited) field + if (cl.qualifiedName.equals(left.getClassname()) && + (fd.access_flags & CodeConstants.ACC_FINAL) != 0 && + ((fd.access_flags & CodeConstants.ACC_SYNTHETIC) != 0 || + fd.getAttributes().containsKey("Synthetic") || + (notsynth && (fd.access_flags & CodeConstants.ACC_PRIVATE) != 0))) { + field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); + break; + } + } + } + } + } + } + } + + return field; + } + + private void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) { + + int i = 1; + for (; ; ) { + if (first.size() <= i || second.size() <= i) { + break; + } + + VarFieldPair fobj = first.get(first.size() - i); + VarFieldPair sobj = second.get(second.size() - i); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(first.size() - i, null); + if (both) { + second.set(second.size() - i, null); + } + } + else { + if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + i++; + } + + for (int j = 1; j <= first.size() - i; j++) { + first.set(j, null); + } + + if (both) { + for (int j = 1; j <= second.size() - i; j++) { + second.set(j, null); + } + } + + // first + if (first.isEmpty()) { + if (!second.isEmpty() && both) { + second.set(0, null); + } + } + else if (second.isEmpty()) { + first.set(0, null); + } + else { + VarFieldPair fobj = first.get(0); + VarFieldPair sobj = second.get(0); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(0, null); + if (both) { + second.set(0, null); + } + } + else if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + } + + + private void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { + + RootStatement root = meth.root; + + HashSet<Statement> setStats = new HashSet<Statement>(); + VarType classtype = new VarType(node.classStruct.qualifiedName, true); + + Statement stdef = getDefStatement(root, classtype, setStats); + if (stdef == null) { + // unreferenced local class + stdef = root.getFirst(); + } + + Statement first = findFirstBlock(stdef, setStats); + + List<Exprent> lst; + if (first == null) { + lst = stdef.getVarDefinitions(); + } + else if (first.getExprents() == null) { + lst = first.getVarDefinitions(); + } + else { + lst = first.getExprents(); + } + + + int addindex = 0; + for (Exprent expr : lst) { + if (searchForClass(expr, classtype)) { + break; + } + addindex++; + } + + VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), + classtype, meth.varproc); + var.setDefinition(true); + var.setClassdef(true); + + lst.add(addindex, var); + } + + + private Statement findFirstBlock(Statement stat, HashSet<Statement> setStats) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(stat); + + while (!stack.isEmpty()) { + Statement st = stack.remove(0); + + if (stack.isEmpty() || setStats.contains(st)) { + + if (st.isLabeled() && !stack.isEmpty()) { + return st; + } + + if (st.getExprents() != null) { + return st; + } + else { + stack.clear(); + + switch (st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; + } + } + } + } + + return null; + } + + + private Statement getDefStatement(Statement stat, VarType classtype, HashSet<Statement> setStats) { + + List<Exprent> condlst = new ArrayList<Exprent>(); + Statement retstat = null; + + if (stat.getExprents() == null) { + int counter = 0; + + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + Statement st = (Statement)obj; + + Statement stTemp = getDefStatement(st, classtype, setStats); + + if (stTemp != null) { + if (counter == 1) { + retstat = stat; + break; + } + retstat = stTemp; + counter++; + } + + if (st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + + condlst.addAll(dost.getInitExprentList()); + condlst.addAll(dost.getConditionExprentList()); + } + } + else if (obj instanceof Exprent) { + condlst.add((Exprent)obj); + } + } + } + else { + condlst = stat.getExprents(); + } + + if (retstat != stat) { + for (Exprent exprent : condlst) { + if (exprent != null && searchForClass(exprent, classtype)) { + retstat = stat; + break; + } + } + } + + if (retstat != null) { + setStats.add(stat); + } + + return retstat; + } + + private boolean searchForClass(Exprent exprent, VarType classtype) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + String classname = classtype.value; + + for (Exprent expr : lst) { + + boolean res = false; + + switch (expr.type) { + case Exprent.EXPRENT_CONST: + ConstExprent cexpr = (ConstExprent)expr; + res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || + classtype.equals(cexpr.getConsttype())); + break; + case Exprent.EXPRENT_FIELD: + res = classname.equals(((FieldExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_INVOCATION: + res = classname.equals(((InvocationExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_NEW: + VarType newType = expr.getExprType(); + res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); + break; + case Exprent.EXPRENT_VAR: + VarExprent vexpr = (VarExprent)expr; + if (vexpr.isDefinition()) { + VarType vtype = vexpr.getVartype(); + if (classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { + res = true; + } + } + } + + if (res) { + return true; + } + } + + return false; + } + + + private class VarFieldPair { + + public String keyfield = ""; + public VarVersionPaar varpaar; + + public VarFieldPair(String field, VarVersionPaar varpaar) { + this.keyfield = field; + this.varpaar = varpaar; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarFieldPair)) return false; + + VarFieldPair pair = (VarFieldPair)o; + return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); + } + + @Override + public int hashCode() { + return keyfield.hashCode() + varpaar.hashCode(); + } + } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java index 3eda632..e81a69e 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java @@ -1,35 +1,27 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.main.rels; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +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.vars.VarVersionPaar; @@ -37,421 +29,426 @@ import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; + public class NestedMemberAccess { - - private static final int METHOD_ACCESS_NORMAL = 1; - private static final int METHOD_ACCESS_FIELDGET = 2; - private static final int METHOD_ACCESS_FIELDSET = 3; - private static final int METHOD_ACCESS_METHOD = 4; - - private boolean notSetSync; - - private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>(); - - - public void propagateMemberAccess(ClassNode root) { - - if(root.nested.isEmpty()) { - return; - } - - notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - - computeMethodTypes(root); - - eliminateStaticAccess(root); - } - - - private void computeMethodTypes(ClassNode node) { - - if(node.type == ClassNode.CLASS_LAMBDA) { - return; - } - - for(ClassNode nd : node.nested) { - computeMethodTypes(nd); - } - - for(MethodWrapper meth : node.wrapper.getMethods()) { - computeMethodType(node, meth); - } - - } - - private void computeMethodType(ClassNode node, MethodWrapper meth) { - - int type = METHOD_ACCESS_NORMAL; - - if(meth.root != null) { - - DirectGraph graph = meth.getOrBuildGraph(); - - int flags = meth.methodStruct.getAccessFlags(); - if(((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) && - (flags & CodeConstants.ACC_STATIC) != 0) { - if(graph.nodes.size() == 2) { // incl. dummy exit node - if(graph.first.exprents.size() == 1) { - Exprent exprent = graph.first.exprents.get(0); - - MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); - int parcount = mtdesc.params.length; - - Exprent exprCore = exprent; - - if(exprent.type == Exprent.EXPRENT_EXIT) { - ExitExprent exexpr = (ExitExprent)exprent; - if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { - exprCore = exexpr.getValue(); - } - } - - switch(exprCore.type) { - case Exprent.EXPRENT_FIELD: - FieldExprent fexpr = (FieldExprent)exprCore; - if((parcount == 1 && !fexpr.isStatic()) || - (parcount == 0 && fexpr.isStatic())) { - if(fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field - if(fexpr.isStatic() || (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) { - type = METHOD_ACCESS_FIELDGET; - } - } - } - break; - case Exprent.EXPRENT_VAR: // qualified this - if(parcount == 1) { - // this or final variable - if(((VarExprent)exprCore).getIndex() != 0) { - type = METHOD_ACCESS_FIELDGET; - } - } - - break; - case Exprent.EXPRENT_INVOCATION: - type = METHOD_ACCESS_METHOD; - break; - case Exprent.EXPRENT_ASSIGNMENT: - AssignmentExprent asexpr = (AssignmentExprent)exprCore; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { - FieldExprent fexpras = (FieldExprent)asexpr.getLeft(); - if((parcount == 2 && !fexpras.isStatic()) || - (parcount == 1 && fexpras.isStatic())) { - if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field - if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { - if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { - type = METHOD_ACCESS_FIELDSET; - } - } - } - } - } - } - - - if(type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method - - type = METHOD_ACCESS_NORMAL; - - InvocationExprent invexpr = (InvocationExprent)exprCore; - - if((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR - && ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount-1)) { - - boolean equalpars = true; - - for(int i=0;i<invexpr.getLstParameters().size();i++) { - Exprent parexpr = invexpr.getLstParameters().get(i); - if(parexpr.type != Exprent.EXPRENT_VAR || - ((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic()?0:1)) { - equalpars = false; - break; - } - } - - if(equalpars) { - type = METHOD_ACCESS_METHOD; - } - } - } - } else if(graph.first.exprents.size() == 2) { - Exprent exprentFirst = graph.first.exprents.get(0); - Exprent exprentSecond = graph.first.exprents.get(1); - - if(exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT && - exprentSecond.type == Exprent.EXPRENT_EXIT) { - - MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); - int parcount = mtdesc.params.length; - - AssignmentExprent asexpr = (AssignmentExprent)exprentFirst; - if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { - FieldExprent fexpras = (FieldExprent)asexpr.getLeft(); - if((parcount == 2 && !fexpras.isStatic()) || - (parcount == 1 && fexpras.isStatic())) { - if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field - if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { - if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { - - ExitExprent exexpr = (ExitExprent)exprentSecond; - if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { - if(exexpr.getValue().type == Exprent.EXPRENT_VAR && - ((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { - type = METHOD_ACCESS_FIELDSET; - } - } - } - } - } - } - } - } - } - - - } - } - } - - if(type != METHOD_ACCESS_NORMAL) { - mapMethodType.put(meth, type); - } else { - mapMethodType.remove(meth); - } - } - - - - private void eliminateStaticAccess(ClassNode node) { - - if(node.type == ClassNode.CLASS_LAMBDA) { - return; - } - - for(MethodWrapper meth : node.wrapper.getMethods()) { - - if(meth.root != null) { - - boolean replaced = false; - - DirectGraph graph = meth.getOrBuildGraph(); - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); - stack.add(graph.first); - - while(!stack.isEmpty()) { // TODO: replace with interface iterator? - - DirectNode nd = stack.removeFirst(); - - if(setVisited.contains(nd)) { - continue; - } - setVisited.add(nd); - - for(int i=0;i<nd.exprents.size();i++) { - Exprent exprent = nd.exprents.get(i); - - replaced |= replaceInvocations(node, meth, exprent); - - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent); - - if(ret != null) { - nd.exprents.set(i, ret); - replaced = true; - } - } - } - - for(DirectNode ndx: nd.succs) { - stack.add(ndx); - } - } - - if(replaced) { - computeMethodType(node, meth); - } - - } - } - - for(ClassNode child : node.nested) { - eliminateStaticAccess(child); - } - - } - - - private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) { - - boolean res = false; - - for(Exprent expr : exprent.getAllExprents()) { - res |= replaceInvocations(caller, meth, expr); - } - - for(;;) { - - boolean found = false; - - for(Exprent expr : exprent.getAllExprents()) { - if(expr.type == Exprent.EXPRENT_INVOCATION) { - Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr); - if(newexpr != null) { - exprent.replaceExprent(expr, newexpr); - found = true; - res = true; - break; - } - } - } - - if(!found) { - break; - } - } - - return res; - } - - private boolean sameTree(ClassNode caller, ClassNode callee) { - - if(caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) { - return false; - } - - while(caller.parent != null) { - caller = caller.parent; - } - - while(callee.parent != null) { - callee = callee.parent; - } - - return caller == callee; - } - - private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) { - - ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname()); - - MethodWrapper methsource = null; - if(node != null && node.wrapper != null) { - methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); - } - - if(methsource == null || !mapMethodType.containsKey(methsource)) { - return null; - } - - // if same method, return - if(node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) && - methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) && - methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) { - // no recursive invocations permitted! - return null; - } - - int type = mapMethodType.get(methsource); - -// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map -// if(type == METHOD_ACCESS_NORMAL) { -// return null; -// } - - if(!sameTree(caller, node)) { - return null; - } - - DirectGraph graph = methsource.getOrBuildGraph(); - Exprent source = graph.first.exprents.get(0); - - Exprent retexprent = null; - - switch(type) { - case METHOD_ACCESS_FIELDGET: - ExitExprent exsource = (ExitExprent)source; - if(exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this - VarExprent var = (VarExprent)exsource.getValue(); - String varname = methsource.varproc.getVarName(new VarVersionPaar(var)); - - if(!methdest.setOuterVarNames.contains(varname)) { - VarNamesCollector vnc = new VarNamesCollector(); - vnc.addName(varname); - - methdest.varproc.refreshVarNames(vnc); - methdest.setOuterVarNames.add(varname); - } - - int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER); - VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc); - methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname); - - retexprent = ret; - } else { // field - FieldExprent ret = (FieldExprent)exsource.getValue().copy(); - if(!ret.isStatic()) { - ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0)); - } - retexprent = ret; - } - break; - case METHOD_ACCESS_FIELDSET: - AssignmentExprent ret; - if(source.type == Exprent.EXPRENT_EXIT) { - ExitExprent extex = (ExitExprent)source; - ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy(); - } else { - ret = (AssignmentExprent)((AssignmentExprent)source).copy(); - } - FieldExprent fexpr = (FieldExprent)ret.getLeft(); - - if(fexpr.isStatic()) { - ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0)); - } else { - ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1)); - fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0)); - } - retexprent = ret; - break; - case METHOD_ACCESS_METHOD: - if(source.type == Exprent.EXPRENT_EXIT) { - source = ((ExitExprent)source).getValue(); - } - - InvocationExprent invret = (InvocationExprent)source.copy(); - - int index = 0; - if(!invret.isStatic()) { - invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0)); - index = 1; - } - - for(int i=0;i<invret.getLstParameters().size();i++) { - invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index)); - } - - retexprent = invret; - } - - - if(retexprent != null) { - // hide synthetic access method - boolean hide = true; - - if(node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) { - StructMethod mt = methsource.methodStruct; - if((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) { - hide = false; - } - } - if(hide) { - node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); - } - } - - return retexprent; - } - - + + private static final int METHOD_ACCESS_NORMAL = 1; + private static final int METHOD_ACCESS_FIELDGET = 2; + private static final int METHOD_ACCESS_FIELDSET = 3; + private static final int METHOD_ACCESS_METHOD = 4; + + private boolean notSetSync; + + private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>(); + + + public void propagateMemberAccess(ClassNode root) { + + if (root.nested.isEmpty()) { + return; + } + + notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + computeMethodTypes(root); + + eliminateStaticAccess(root); + } + + + private void computeMethodTypes(ClassNode node) { + + if (node.type == ClassNode.CLASS_LAMBDA) { + return; + } + + for (ClassNode nd : node.nested) { + computeMethodTypes(nd); + } + + for (MethodWrapper meth : node.wrapper.getMethods()) { + computeMethodType(node, meth); + } + } + + private void computeMethodType(ClassNode node, MethodWrapper meth) { + + int type = METHOD_ACCESS_NORMAL; + + if (meth.root != null) { + + DirectGraph graph = meth.getOrBuildGraph(); + + int flags = meth.methodStruct.getAccessFlags(); + if (((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) && + (flags & CodeConstants.ACC_STATIC) != 0) { + if (graph.nodes.size() == 2) { // incl. dummy exit node + if (graph.first.exprents.size() == 1) { + Exprent exprent = graph.first.exprents.get(0); + + MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + int parcount = mtdesc.params.length; + + Exprent exprCore = exprent; + + if (exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exexpr = (ExitExprent)exprent; + if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { + exprCore = exexpr.getValue(); + } + } + + switch (exprCore.type) { + case Exprent.EXPRENT_FIELD: + FieldExprent fexpr = (FieldExprent)exprCore; + if ((parcount == 1 && !fexpr.isStatic()) || + (parcount == 0 && fexpr.isStatic())) { + if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field + if (fexpr.isStatic() || + (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) { + type = METHOD_ACCESS_FIELDGET; + } + } + } + break; + case Exprent.EXPRENT_VAR: // qualified this + if (parcount == 1) { + // this or final variable + if (((VarExprent)exprCore).getIndex() != 0) { + type = METHOD_ACCESS_FIELDGET; + } + } + + break; + case Exprent.EXPRENT_INVOCATION: + type = METHOD_ACCESS_METHOD; + break; + case Exprent.EXPRENT_ASSIGNMENT: + AssignmentExprent asexpr = (AssignmentExprent)exprCore; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { + FieldExprent fexpras = (FieldExprent)asexpr.getLeft(); + if ((parcount == 2 && !fexpras.isStatic()) || + (parcount == 1 && fexpras.isStatic())) { + if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field + if (fexpras.isStatic() || + (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { + if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { + type = METHOD_ACCESS_FIELDSET; + } + } + } + } + } + } + + + if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method + + type = METHOD_ACCESS_NORMAL; + + InvocationExprent invexpr = (InvocationExprent)exprCore; + + if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || + (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR + && ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) { + + boolean equalpars = true; + + for (int i = 0; i < invexpr.getLstParameters().size(); i++) { + Exprent parexpr = invexpr.getLstParameters().get(i); + if (parexpr.type != Exprent.EXPRENT_VAR || + ((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) { + equalpars = false; + break; + } + } + + if (equalpars) { + type = METHOD_ACCESS_METHOD; + } + } + } + } + else if (graph.first.exprents.size() == 2) { + Exprent exprentFirst = graph.first.exprents.get(0); + Exprent exprentSecond = graph.first.exprents.get(1); + + if (exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT && + exprentSecond.type == Exprent.EXPRENT_EXIT) { + + MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + int parcount = mtdesc.params.length; + + AssignmentExprent asexpr = (AssignmentExprent)exprentFirst; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { + FieldExprent fexpras = (FieldExprent)asexpr.getLeft(); + if ((parcount == 2 && !fexpras.isStatic()) || + (parcount == 1 && fexpras.isStatic())) { + if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field + if (fexpras.isStatic() || + (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { + if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { + + ExitExprent exexpr = (ExitExprent)exprentSecond; + if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { + if (exexpr.getValue().type == Exprent.EXPRENT_VAR && + ((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { + type = METHOD_ACCESS_FIELDSET; + } + } + } + } + } + } + } + } + } + } + } + } + + if (type != METHOD_ACCESS_NORMAL) { + mapMethodType.put(meth, type); + } + else { + mapMethodType.remove(meth); + } + } + + + private void eliminateStaticAccess(ClassNode node) { + + if (node.type == ClassNode.CLASS_LAMBDA) { + return; + } + + for (MethodWrapper meth : node.wrapper.getMethods()) { + + if (meth.root != null) { + + boolean replaced = false; + + DirectGraph graph = meth.getOrBuildGraph(); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + stack.add(graph.first); + + while (!stack.isEmpty()) { // TODO: replace with interface iterator? + + DirectNode nd = stack.removeFirst(); + + if (setVisited.contains(nd)) { + continue; + } + setVisited.add(nd); + + for (int i = 0; i < nd.exprents.size(); i++) { + Exprent exprent = nd.exprents.get(i); + + replaced |= replaceInvocations(node, meth, exprent); + + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent); + + if (ret != null) { + nd.exprents.set(i, ret); + replaced = true; + } + } + } + + for (DirectNode ndx : nd.succs) { + stack.add(ndx); + } + } + + if (replaced) { + computeMethodType(node, meth); + } + } + } + + for (ClassNode child : node.nested) { + eliminateStaticAccess(child); + } + } + + + private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) { + + boolean res = false; + + for (Exprent expr : exprent.getAllExprents()) { + res |= replaceInvocations(caller, meth, expr); + } + + for (; ; ) { + + boolean found = false; + + for (Exprent expr : exprent.getAllExprents()) { + if (expr.type == Exprent.EXPRENT_INVOCATION) { + Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr); + if (newexpr != null) { + exprent.replaceExprent(expr, newexpr); + found = true; + res = true; + break; + } + } + } + + if (!found) { + break; + } + } + + return res; + } + + private boolean sameTree(ClassNode caller, ClassNode callee) { + + if (caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) { + return false; + } + + while (caller.parent != null) { + caller = caller.parent; + } + + while (callee.parent != null) { + callee = callee.parent; + } + + return caller == callee; + } + + private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) { + + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname()); + + MethodWrapper methsource = null; + if (node != null && node.wrapper != null) { + methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); + } + + if (methsource == null || !mapMethodType.containsKey(methsource)) { + return null; + } + + // if same method, return + if (node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) && + methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) && + methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) { + // no recursive invocations permitted! + return null; + } + + int type = mapMethodType.get(methsource); + + // // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map + // if(type == METHOD_ACCESS_NORMAL) { + // return null; + // } + + if (!sameTree(caller, node)) { + return null; + } + + DirectGraph graph = methsource.getOrBuildGraph(); + Exprent source = graph.first.exprents.get(0); + + Exprent retexprent = null; + + switch (type) { + case METHOD_ACCESS_FIELDGET: + ExitExprent exsource = (ExitExprent)source; + if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this + VarExprent var = (VarExprent)exsource.getValue(); + String varname = methsource.varproc.getVarName(new VarVersionPaar(var)); + + if (!methdest.setOuterVarNames.contains(varname)) { + VarNamesCollector vnc = new VarNamesCollector(); + vnc.addName(varname); + + methdest.varproc.refreshVarNames(vnc); + methdest.setOuterVarNames.add(varname); + } + + int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER); + VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc); + methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname); + + retexprent = ret; + } + else { // field + FieldExprent ret = (FieldExprent)exsource.getValue().copy(); + if (!ret.isStatic()) { + ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0)); + } + retexprent = ret; + } + break; + case METHOD_ACCESS_FIELDSET: + AssignmentExprent ret; + if (source.type == Exprent.EXPRENT_EXIT) { + ExitExprent extex = (ExitExprent)source; + ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy(); + } + else { + ret = (AssignmentExprent)((AssignmentExprent)source).copy(); + } + FieldExprent fexpr = (FieldExprent)ret.getLeft(); + + if (fexpr.isStatic()) { + ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0)); + } + else { + ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1)); + fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0)); + } + retexprent = ret; + break; + case METHOD_ACCESS_METHOD: + if (source.type == Exprent.EXPRENT_EXIT) { + source = ((ExitExprent)source).getValue(); + } + + InvocationExprent invret = (InvocationExprent)source.copy(); + + int index = 0; + if (!invret.isStatic()) { + invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0)); + index = 1; + } + + for (int i = 0; i < invret.getLstParameters().size(); i++) { + invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index)); + } + + retexprent = invret; + } + + + if (retexprent != null) { + // hide synthetic access method + boolean hide = true; + + if (node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) { + StructMethod mt = methsource.methodStruct; + if ((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) { + hide = false; + } + } + if (hide) { + node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); + } + } + + return retexprent; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index 4d39e68..386f6ae 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -1,24 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.code; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; @@ -28,411 +24,416 @@ import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + public class DeadCodeHelper { - public static void removeDeadBlocks(ControlFlowGraph graph) { - - LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); - HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>(); - - stack.add(graph.getFirst()); - setStacked.add(graph.getFirst()); - - while(!stack.isEmpty()) { - BasicBlock block = stack.removeFirst(); - - List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); - lstSuccs.addAll(block.getSuccExceptions()); - - for(BasicBlock succ : lstSuccs) { - if(!setStacked.contains(succ)) { - stack.add(succ); - setStacked.add(succ); - } - } - } - - HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks()); - setAllBlocks.removeAll(setStacked); - - for(BasicBlock block : setAllBlocks) { - graph.removeBlock(block); - } - } - - public static void removeEmptyBlocks(ControlFlowGraph graph) { - - List<BasicBlock> blocks = graph.getBlocks(); - - boolean cont; - do { - cont = false; - - for(int i=blocks.size()-1;i>=0;i--) { - BasicBlock block = (BasicBlock)blocks.get(i); - - if(removeEmptyBlock(graph, block, false)) { - cont = true; - break; - } - } - - } while(cont); - } - - private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) { - - boolean deletedRanges = false; - - if(block.getSeq().isEmpty()) { - - if(block.getSuccs().size() > 1) { - if(block.getPreds().size()>1) { - // ambiguous block - throw new RuntimeException("ERROR: empty block with multiple predecessors and successors found"); - } else if(!merging) { - throw new RuntimeException("ERROR: empty block with multiple successors found"); - } - } - - HashSet<BasicBlock> setExits = new HashSet<BasicBlock>(graph.getLast().getPreds()); - - if(block.getPredExceptions().isEmpty() && - (!setExits.contains(block) || block.getPreds().size() == 1)) { - - if(setExits.contains(block)) { - BasicBlock pred = block.getPreds().get(0); - - // FIXME: flag in the basic block - if(pred.getSuccs().size() != 1 || (!pred.getSeq().isEmpty() - && pred.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) { - return false; - } - } - - HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds()); - HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs()); - - // collect common exception ranges of predecessors and successors - HashSet<BasicBlock> setCommonExceptionHandlers = null; - for(int i = 0; i < 2; ++i) { - for(BasicBlock pred : i == 0 ? setPreds : setSuccs) { - if(setCommonExceptionHandlers == null) { - setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions()); - } else { - setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); - } - } - } - - // check the block to be in each of the common ranges - if(setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) { - for(BasicBlock handler : setCommonExceptionHandlers) { - if(!block.getSuccExceptions().contains(handler)) { - return false; - } - } - } - - // remove ranges consisting of this one block - List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); - for(int i=lstRanges.size()-1;i>=0;i--) { - ExceptionRangeCFG range = lstRanges.get(i); - List<BasicBlock> lst = range.getProtectedRange(); - - if(lst.size() == 1 && lst.get(0) == block) { - if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { - block.removeSuccessorException(range.getHandler()); - lstRanges.remove(i); - - deletedRanges = true; - } else { - return false; - } - } - } - - - // connect remaining nodes - if(merging) { - BasicBlock pred = block.getPreds().get(0); - pred.removeSuccessor(block); - - List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); - for(BasicBlock succ : lstSuccs) { - block.removeSuccessor(succ); - pred.addSuccessor(succ); - } - - } else { - for(BasicBlock pred : setPreds) { - for(BasicBlock succ : setSuccs) { - pred.replaceSuccessor(block, succ); - } - } - } - - // finally exit edges - HashSet<BasicBlock> setFinallyExits = graph.getFinallyExits(); - if(setFinallyExits.contains(block)) { - setFinallyExits.remove(block); - setFinallyExits.add(setPreds.iterator().next()); - } - - // replace first if necessary - if(graph.getFirst() == block) { - if(setSuccs.size() != 1) { - throw new RuntimeException("multiple or no entry blocks!"); - } else { - graph.setFirst(setSuccs.iterator().next()); - } - } - - // remove this block - graph.removeBlock(block); - - if(deletedRanges) { - DeadCodeHelper.removeDeadBlocks(graph); - } - } - } - - return deletedRanges; - } - - - public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { - - HashSet<BasicBlock> marked = new HashSet<BasicBlock>(); - - if(block == dom) { - return true; - } - - LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); - lstNodes.add(block); - - while(!lstNodes.isEmpty()) { - - BasicBlock node = (BasicBlock)lstNodes.remove(0); - if(marked.contains(node)) { - continue; - } else { - marked.add(node); - } - - if(node == graph.getFirst()) { - return false; - } - - for(int i=0;i<node.getPreds().size();i++) { - BasicBlock pred = (BasicBlock)node.getPreds().get(i); - if(!marked.contains(pred) && pred != dom) { - lstNodes.add(pred); - } - } - - for(int i=0;i<node.getPredExceptions().size();i++) { - BasicBlock pred = (BasicBlock)node.getPredExceptions().get(i); - if(!marked.contains(pred) && pred != dom) { - lstNodes.add(pred); - } - } - } - - return true; - } - - public static void removeGotos(ControlFlowGraph graph) { - - for(BasicBlock block : graph.getBlocks()) { - Instruction instr = block.getLastInstruction(); - - if(instr!=null && instr.opcode == CodeConstants.opc_goto) { - block.getSeq().removeInstruction(block.getSeq().length()-1); - } - } - - DeadCodeHelper.removeEmptyBlocks(graph); - } - - public static void connectDummyExitBlock(ControlFlowGraph graph) { - - BasicBlock exit = graph.getLast(); - for(BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) { - exit.removePredecessor(block); - block.addSuccessor(exit); - } - } - - public static void incorporateValueReturns(ControlFlowGraph graph) { - - for(BasicBlock block: graph.getBlocks()) { - InstructionSequence seq = block.getSeq(); - - int len = seq.length(); - if(len > 0 && len < 3) { - - boolean ok = false; - - if(seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) { - if(len == 1) { - ok = true; - } else if(seq.getLastInstr().opcode != CodeConstants.opc_return){ - switch(seq.getInstr(0).opcode) { - case CodeConstants.opc_iload: - case CodeConstants.opc_lload: - case CodeConstants.opc_fload: - case CodeConstants.opc_dload: - case CodeConstants.opc_aload: - case CodeConstants.opc_aconst_null: - case CodeConstants.opc_bipush: - case CodeConstants.opc_sipush: - case CodeConstants.opc_lconst_0: - case CodeConstants.opc_lconst_1: - case CodeConstants.opc_fconst_0: - case CodeConstants.opc_fconst_1: - case CodeConstants.opc_fconst_2: - case CodeConstants.opc_dconst_0: - case CodeConstants.opc_dconst_1: - case CodeConstants.opc_ldc: - case CodeConstants.opc_ldc_w: - case CodeConstants.opc_ldc2_w: - ok = true; - } - } - } - - if(ok) { - - if(!block.getPreds().isEmpty()) { - - HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>(); - HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>(); - - boolean firstpred = true; - for(BasicBlock pred : block.getPreds()) { - if(firstpred) { - setPredHandlersIntersection.addAll(pred.getSuccExceptions()); - firstpred = false; - } else { - setPredHandlersIntersection.retainAll(pred.getSuccExceptions()); - } - - setPredHandlersUnion.addAll(pred.getSuccExceptions()); - } - - // add exception ranges from predecessors - setPredHandlersIntersection.removeAll(block.getSuccExceptions()); - BasicBlock predecessor = block.getPreds().get(0); - - for(BasicBlock handler : setPredHandlersIntersection) { - ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor); - - range.getProtectedRange().add(block); - block.addSuccessorException(handler); - } - - // remove redundant ranges - HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions()); - setRangesToBeRemoved.removeAll(setPredHandlersUnion); - - for(BasicBlock handler : setRangesToBeRemoved) { - ExceptionRangeCFG range = graph.getExceptionRange(handler, block); - - if(range.getProtectedRange().size() > 1) { - range.getProtectedRange().remove(block); - block.removeSuccessorException(handler); - } - } - } - - - if(block.getPreds().size() == 1 && block.getPredExceptions().isEmpty()) { - - BasicBlock bpred = block.getPreds().get(0); - if(bpred.getSuccs().size() == 1) { - - // add exception ranges of predecessor - for(BasicBlock succ : bpred.getSuccExceptions()) { - if(!block.getSuccExceptions().contains(succ)) { - ExceptionRangeCFG range = graph.getExceptionRange(succ, bpred); - - range.getProtectedRange().add(block); - block.addSuccessorException(succ); - } - } - - // remove superfluous ranges from successors - for(BasicBlock succ : new HashSet<BasicBlock>(block.getSuccExceptions())) { - if(!bpred.getSuccExceptions().contains(succ)) { - ExceptionRangeCFG range = graph.getExceptionRange(succ, block); - - if(range.getProtectedRange().size() > 1) { - range.getProtectedRange().remove(block); - block.removeSuccessorException(succ); - } - } - } - } - } - - } - } - - } - - } - - - public static void mergeBasicBlocks(ControlFlowGraph graph) { - - for(;;) { - - boolean merged = false; - - for(BasicBlock block: graph.getBlocks()) { - - InstructionSequence seq = block.getSeq(); - - if(block.getSuccs().size() == 1) { - BasicBlock next = block.getSuccs().get(0); - - if(next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) { - - if(next.getPreds().size() == 1 && next.getPredExceptions().isEmpty() - && next != graph.getFirst()) { - // TODO: implement a dummy start block - boolean sameRanges = true; - for(ExceptionRangeCFG range : graph.getExceptions()) { - if(range.getProtectedRange().contains(block) ^ - range.getProtectedRange().contains(next)) { - sameRanges = false; - break; - } - } - - if(sameRanges) { - seq.addSequence(next.getSeq()); - next.getSeq().clear(); - - removeEmptyBlock(graph, next, true); - - merged = true; - break; - } - } - } - } - - } - - if(!merged) { - break; - } - } - - } - - + public static void removeDeadBlocks(ControlFlowGraph graph) { + + LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); + HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>(); + + stack.add(graph.getFirst()); + setStacked.add(graph.getFirst()); + + while (!stack.isEmpty()) { + BasicBlock block = stack.removeFirst(); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); + lstSuccs.addAll(block.getSuccExceptions()); + + for (BasicBlock succ : lstSuccs) { + if (!setStacked.contains(succ)) { + stack.add(succ); + setStacked.add(succ); + } + } + } + + HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks()); + setAllBlocks.removeAll(setStacked); + + for (BasicBlock block : setAllBlocks) { + graph.removeBlock(block); + } + } + + public static void removeEmptyBlocks(ControlFlowGraph graph) { + + List<BasicBlock> blocks = graph.getBlocks(); + + boolean cont; + do { + cont = false; + + for (int i = blocks.size() - 1; i >= 0; i--) { + BasicBlock block = (BasicBlock)blocks.get(i); + + if (removeEmptyBlock(graph, block, false)) { + cont = true; + break; + } + } + } + while (cont); + } + + private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) { + + boolean deletedRanges = false; + + if (block.getSeq().isEmpty()) { + + if (block.getSuccs().size() > 1) { + if (block.getPreds().size() > 1) { + // ambiguous block + throw new RuntimeException("ERROR: empty block with multiple predecessors and successors found"); + } + else if (!merging) { + throw new RuntimeException("ERROR: empty block with multiple successors found"); + } + } + + HashSet<BasicBlock> setExits = new HashSet<BasicBlock>(graph.getLast().getPreds()); + + if (block.getPredExceptions().isEmpty() && + (!setExits.contains(block) || block.getPreds().size() == 1)) { + + if (setExits.contains(block)) { + BasicBlock pred = block.getPreds().get(0); + + // FIXME: flag in the basic block + if (pred.getSuccs().size() != 1 || (!pred.getSeq().isEmpty() + && pred.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) { + return false; + } + } + + HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds()); + HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs()); + + // collect common exception ranges of predecessors and successors + HashSet<BasicBlock> setCommonExceptionHandlers = null; + for (int i = 0; i < 2; ++i) { + for (BasicBlock pred : i == 0 ? setPreds : setSuccs) { + if (setCommonExceptionHandlers == null) { + setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions()); + } + else { + setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); + } + } + } + + // check the block to be in each of the common ranges + if (setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) { + for (BasicBlock handler : setCommonExceptionHandlers) { + if (!block.getSuccExceptions().contains(handler)) { + return false; + } + } + } + + // remove ranges consisting of this one block + List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for (int i = lstRanges.size() - 1; i >= 0; i--) { + ExceptionRangeCFG range = lstRanges.get(i); + List<BasicBlock> lst = range.getProtectedRange(); + + if (lst.size() == 1 && lst.get(0) == block) { + if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { + block.removeSuccessorException(range.getHandler()); + lstRanges.remove(i); + + deletedRanges = true; + } + else { + return false; + } + } + } + + + // connect remaining nodes + if (merging) { + BasicBlock pred = block.getPreds().get(0); + pred.removeSuccessor(block); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); + for (BasicBlock succ : lstSuccs) { + block.removeSuccessor(succ); + pred.addSuccessor(succ); + } + } + else { + for (BasicBlock pred : setPreds) { + for (BasicBlock succ : setSuccs) { + pred.replaceSuccessor(block, succ); + } + } + } + + // finally exit edges + HashSet<BasicBlock> setFinallyExits = graph.getFinallyExits(); + if (setFinallyExits.contains(block)) { + setFinallyExits.remove(block); + setFinallyExits.add(setPreds.iterator().next()); + } + + // replace first if necessary + if (graph.getFirst() == block) { + if (setSuccs.size() != 1) { + throw new RuntimeException("multiple or no entry blocks!"); + } + else { + graph.setFirst(setSuccs.iterator().next()); + } + } + + // remove this block + graph.removeBlock(block); + + if (deletedRanges) { + DeadCodeHelper.removeDeadBlocks(graph); + } + } + } + + return deletedRanges; + } + + + public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { + + HashSet<BasicBlock> marked = new HashSet<BasicBlock>(); + + if (block == dom) { + return true; + } + + LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); + lstNodes.add(block); + + while (!lstNodes.isEmpty()) { + + BasicBlock node = (BasicBlock)lstNodes.remove(0); + if (marked.contains(node)) { + continue; + } + else { + marked.add(node); + } + + if (node == graph.getFirst()) { + return false; + } + + for (int i = 0; i < node.getPreds().size(); i++) { + BasicBlock pred = (BasicBlock)node.getPreds().get(i); + if (!marked.contains(pred) && pred != dom) { + lstNodes.add(pred); + } + } + + for (int i = 0; i < node.getPredExceptions().size(); i++) { + BasicBlock pred = (BasicBlock)node.getPredExceptions().get(i); + if (!marked.contains(pred) && pred != dom) { + lstNodes.add(pred); + } + } + } + + return true; + } + + public static void removeGotos(ControlFlowGraph graph) { + + for (BasicBlock block : graph.getBlocks()) { + Instruction instr = block.getLastInstruction(); + + if (instr != null && instr.opcode == CodeConstants.opc_goto) { + block.getSeq().removeInstruction(block.getSeq().length() - 1); + } + } + + DeadCodeHelper.removeEmptyBlocks(graph); + } + + public static void connectDummyExitBlock(ControlFlowGraph graph) { + + BasicBlock exit = graph.getLast(); + for (BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) { + exit.removePredecessor(block); + block.addSuccessor(exit); + } + } + + public static void incorporateValueReturns(ControlFlowGraph graph) { + + for (BasicBlock block : graph.getBlocks()) { + InstructionSequence seq = block.getSeq(); + + int len = seq.length(); + if (len > 0 && len < 3) { + + boolean ok = false; + + if (seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) { + if (len == 1) { + ok = true; + } + else if (seq.getLastInstr().opcode != CodeConstants.opc_return) { + switch (seq.getInstr(0).opcode) { + case CodeConstants.opc_iload: + case CodeConstants.opc_lload: + case CodeConstants.opc_fload: + case CodeConstants.opc_dload: + case CodeConstants.opc_aload: + case CodeConstants.opc_aconst_null: + case CodeConstants.opc_bipush: + case CodeConstants.opc_sipush: + case CodeConstants.opc_lconst_0: + case CodeConstants.opc_lconst_1: + case CodeConstants.opc_fconst_0: + case CodeConstants.opc_fconst_1: + case CodeConstants.opc_fconst_2: + case CodeConstants.opc_dconst_0: + case CodeConstants.opc_dconst_1: + case CodeConstants.opc_ldc: + case CodeConstants.opc_ldc_w: + case CodeConstants.opc_ldc2_w: + ok = true; + } + } + } + + if (ok) { + + if (!block.getPreds().isEmpty()) { + + HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>(); + HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>(); + + boolean firstpred = true; + for (BasicBlock pred : block.getPreds()) { + if (firstpred) { + setPredHandlersIntersection.addAll(pred.getSuccExceptions()); + firstpred = false; + } + else { + setPredHandlersIntersection.retainAll(pred.getSuccExceptions()); + } + + setPredHandlersUnion.addAll(pred.getSuccExceptions()); + } + + // add exception ranges from predecessors + setPredHandlersIntersection.removeAll(block.getSuccExceptions()); + BasicBlock predecessor = block.getPreds().get(0); + + for (BasicBlock handler : setPredHandlersIntersection) { + ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor); + + range.getProtectedRange().add(block); + block.addSuccessorException(handler); + } + + // remove redundant ranges + HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions()); + setRangesToBeRemoved.removeAll(setPredHandlersUnion); + + for (BasicBlock handler : setRangesToBeRemoved) { + ExceptionRangeCFG range = graph.getExceptionRange(handler, block); + + if (range.getProtectedRange().size() > 1) { + range.getProtectedRange().remove(block); + block.removeSuccessorException(handler); + } + } + } + + + if (block.getPreds().size() == 1 && block.getPredExceptions().isEmpty()) { + + BasicBlock bpred = block.getPreds().get(0); + if (bpred.getSuccs().size() == 1) { + + // add exception ranges of predecessor + for (BasicBlock succ : bpred.getSuccExceptions()) { + if (!block.getSuccExceptions().contains(succ)) { + ExceptionRangeCFG range = graph.getExceptionRange(succ, bpred); + + range.getProtectedRange().add(block); + block.addSuccessorException(succ); + } + } + + // remove superfluous ranges from successors + for (BasicBlock succ : new HashSet<BasicBlock>(block.getSuccExceptions())) { + if (!bpred.getSuccExceptions().contains(succ)) { + ExceptionRangeCFG range = graph.getExceptionRange(succ, block); + + if (range.getProtectedRange().size() > 1) { + range.getProtectedRange().remove(block); + block.removeSuccessorException(succ); + } + } + } + } + } + } + } + } + } + + + public static void mergeBasicBlocks(ControlFlowGraph graph) { + + for (; ; ) { + + boolean merged = false; + + for (BasicBlock block : graph.getBlocks()) { + + InstructionSequence seq = block.getSeq(); + + if (block.getSuccs().size() == 1) { + BasicBlock next = block.getSuccs().get(0); + + if (next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) { + + if (next.getPreds().size() == 1 && next.getPredExceptions().isEmpty() + && next != graph.getFirst()) { + // TODO: implement a dummy start block + boolean sameRanges = true; + for (ExceptionRangeCFG range : graph.getExceptions()) { + if (range.getProtectedRange().contains(block) ^ + range.getProtectedRange().contains(next)) { + sameRanges = false; + break; + } + } + + if (sameRanges) { + seq.addSequence(next.getSeq()); + next.getSeq().clear(); + + removeEmptyBlock(graph, next, true); + + merged = true; + break; + } + } + } + } + } + + if (!merged) { + break; + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java index aedca91..c6495e6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java @@ -1,41 +1,40 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.LinkedList; - import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.LinkedList; + public class ClearStructHelper { - public static void clearStatements(RootStatement root) { - - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(root); - - while(!stack.isEmpty()) { - - Statement stat = stack.removeFirst(); - - stat.clearTempInformation(); - - stack.addAll(stat.getStats()); - } - - } - + public static void clearStatements(RootStatement root) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while (!stack.isEmpty()) { + + Statement stat = stack.removeFirst(); + + stat.clearTempInformation(); + + stack.addAll(stat.getStats()); + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java index a07346b..db67902 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java @@ -1,213 +1,209 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.List; - - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.ArrayList; +import java.util.List; + public class ConcatenationHelper { - - private static final String builderClass = "java/lang/StringBuilder"; - private static final String bufferClass = "java/lang/StringBuffer"; - private static final String stringClass = "java/lang/String"; - - private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuilder"); - private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuffer"); - - - public static Exprent contractStringConcat(Exprent expr) { - - Exprent exprTmp = null; - VarType cltype = null; - - // first quick test - if(expr.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent iex = (InvocationExprent)expr; - if("toString".equals(iex.getName())) { - if(builderClass.equals(iex.getClassname())) { - cltype = builderType; - } else if(bufferClass.equals(iex.getClassname())) { - cltype = bufferType; - } - if(cltype!=null) { - exprTmp = iex.getInstance(); - } - } - } - - if(exprTmp == null) { - return expr; - } - - - // iterate in depth, collecting possible operands - List<Exprent> lstOperands = new ArrayList<Exprent>(); - - for(;;) { - - int found = 0; - - switch(exprTmp.type) { - case Exprent.EXPRENT_INVOCATION: - InvocationExprent iex = (InvocationExprent)exprTmp; - if(isAppendConcat(iex, cltype)) { - lstOperands.add(0, iex.getLstParameters().get(0)); - exprTmp = iex.getInstance(); - found = 1; - } - break; - case Exprent.EXPRENT_NEW: - NewExprent nex = (NewExprent)exprTmp; - if(isNewConcat(nex, cltype)) { - VarType[] params = nex.getConstructor().getDescriptor().params; - if(params.length == 1) { - lstOperands.add(0, nex.getConstructor().getLstParameters().get(0)); - } - found = 2; - } - } - - if(found == 0) { - return expr; - } else if(found == 2) { - break; - } - } - - int first2str = 0; - int index=0; - while(index<lstOperands.size() && index<2) { - if(lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) { - first2str |= (index+1); - } - index++; - } - - if(first2str == 0) { - lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "")); - } - - // remove redundant String.valueOf - for(int i=0;i<lstOperands.size();i++) { - Exprent rep = removeStringValueOf(lstOperands.get(i)); - - boolean ok = (i>1); - if(!ok) { - boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING); - ok = isstr || first2str != i+1; - - if(i == 0) { - first2str &= 2; - } - } - - if(ok) { - lstOperands.set(i, rep); - } - } - - // build exprent to return - Exprent func = lstOperands.get(0); - - for(int i=1;i<lstOperands.size();i++) { - List<Exprent> lstTmp = new ArrayList<Exprent>(); - lstTmp.add(func); - lstTmp.add(lstOperands.get(i)); - func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp); - } - - return func; - - } - - private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) { - - if("append".equals(expr.getName())) { - MethodDescriptor md = expr.getDescriptor(); - if(md.ret.equals(cltype) && md.params.length == 1) { - VarType param = md.params[0]; - switch(param.type) { - case CodeConstants.TYPE_OBJECT: - if(!param.equals(VarType.VARTYPE_STRING) && - !param.equals(VarType.VARTYPE_OBJECT)) { - break; - } - case CodeConstants.TYPE_BOOLEAN: - case CodeConstants.TYPE_CHAR: - case CodeConstants.TYPE_DOUBLE: - case CodeConstants.TYPE_FLOAT: - case CodeConstants.TYPE_INT: - case CodeConstants.TYPE_LONG: - return true; - default: - } - } - } - - return false; - } - - private static boolean isNewConcat(NewExprent expr, VarType cltype) { - - if(expr.getNewtype().equals(cltype)) { - VarType[] params = expr.getConstructor().getDescriptor().params; - if(params.length == 0 || (params.length == 1 && - params[0].equals(VarType.VARTYPE_STRING))) { - return true; - } - } - - return false; - } - - private static Exprent removeStringValueOf(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent iex = (InvocationExprent)exprent; - if("valueOf".equals(iex.getName()) && stringClass.equals(iex.getClassname())) { - MethodDescriptor md = iex.getDescriptor(); - if(md.params.length == 1) { - VarType param = md.params[0]; - switch(param.type) { - case CodeConstants.TYPE_OBJECT: - if(!param.equals(VarType.VARTYPE_OBJECT)) { - break; - } - case CodeConstants.TYPE_BOOLEAN: - case CodeConstants.TYPE_CHAR: - case CodeConstants.TYPE_DOUBLE: - case CodeConstants.TYPE_FLOAT: - case CodeConstants.TYPE_INT: - case CodeConstants.TYPE_LONG: - return iex.getLstParameters().get(0); - } - } - } - } - - return exprent; - } - + + private static final String builderClass = "java/lang/StringBuilder"; + private static final String bufferClass = "java/lang/StringBuffer"; + private static final String stringClass = "java/lang/String"; + + private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuilder"); + private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuffer"); + + + public static Exprent contractStringConcat(Exprent expr) { + + Exprent exprTmp = null; + VarType cltype = null; + + // first quick test + if (expr.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent iex = (InvocationExprent)expr; + if ("toString".equals(iex.getName())) { + if (builderClass.equals(iex.getClassname())) { + cltype = builderType; + } + else if (bufferClass.equals(iex.getClassname())) { + cltype = bufferType; + } + if (cltype != null) { + exprTmp = iex.getInstance(); + } + } + } + + if (exprTmp == null) { + return expr; + } + + + // iterate in depth, collecting possible operands + List<Exprent> lstOperands = new ArrayList<Exprent>(); + + for (; ; ) { + + int found = 0; + + switch (exprTmp.type) { + case Exprent.EXPRENT_INVOCATION: + InvocationExprent iex = (InvocationExprent)exprTmp; + if (isAppendConcat(iex, cltype)) { + lstOperands.add(0, iex.getLstParameters().get(0)); + exprTmp = iex.getInstance(); + found = 1; + } + break; + case Exprent.EXPRENT_NEW: + NewExprent nex = (NewExprent)exprTmp; + if (isNewConcat(nex, cltype)) { + VarType[] params = nex.getConstructor().getDescriptor().params; + if (params.length == 1) { + lstOperands.add(0, nex.getConstructor().getLstParameters().get(0)); + } + found = 2; + } + } + + if (found == 0) { + return expr; + } + else if (found == 2) { + break; + } + } + + int first2str = 0; + int index = 0; + while (index < lstOperands.size() && index < 2) { + if (lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) { + first2str |= (index + 1); + } + index++; + } + + if (first2str == 0) { + lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "")); + } + + // remove redundant String.valueOf + for (int i = 0; i < lstOperands.size(); i++) { + Exprent rep = removeStringValueOf(lstOperands.get(i)); + + boolean ok = (i > 1); + if (!ok) { + boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING); + ok = isstr || first2str != i + 1; + + if (i == 0) { + first2str &= 2; + } + } + + if (ok) { + lstOperands.set(i, rep); + } + } + + // build exprent to return + Exprent func = lstOperands.get(0); + + for (int i = 1; i < lstOperands.size(); i++) { + List<Exprent> lstTmp = new ArrayList<Exprent>(); + lstTmp.add(func); + lstTmp.add(lstOperands.get(i)); + func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp); + } + + return func; + } + + private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) { + + if ("append".equals(expr.getName())) { + MethodDescriptor md = expr.getDescriptor(); + if (md.ret.equals(cltype) && md.params.length == 1) { + VarType param = md.params[0]; + switch (param.type) { + case CodeConstants.TYPE_OBJECT: + if (!param.equals(VarType.VARTYPE_STRING) && + !param.equals(VarType.VARTYPE_OBJECT)) { + break; + } + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_FLOAT: + case CodeConstants.TYPE_INT: + case CodeConstants.TYPE_LONG: + return true; + default: + } + } + } + + return false; + } + + private static boolean isNewConcat(NewExprent expr, VarType cltype) { + + if (expr.getNewtype().equals(cltype)) { + VarType[] params = expr.getConstructor().getDescriptor().params; + if (params.length == 0 || (params.length == 1 && + params[0].equals(VarType.VARTYPE_STRING))) { + return true; + } + } + + return false; + } + + private static Exprent removeStringValueOf(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent iex = (InvocationExprent)exprent; + if ("valueOf".equals(iex.getName()) && stringClass.equals(iex.getClassname())) { + MethodDescriptor md = iex.getDescriptor(); + if (md.params.length == 1) { + VarType param = md.params[0]; + switch (param.type) { + case CodeConstants.TYPE_OBJECT: + if (!param.equals(VarType.VARTYPE_OBJECT)) { + break; + } + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_FLOAT: + case CodeConstants.TYPE_INT: + case CodeConstants.TYPE_LONG: + return iex.getLstParameters().get(0); + } + } + } + } + + return exprent; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java index ea8823a..ec1fada 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java @@ -1,217 +1,221 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.*; + public class DecHelper { - - public static boolean checkStatementExceptions(List<Statement> lst) { - - Set<Statement> all = new HashSet<Statement>(lst); - - Set<Statement> handlers = new HashSet<Statement>(); - Set<Statement> intersection = null; - - for(Statement stat : lst) { - Set<Statement> setNew = stat.getNeighboursSet(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD); - - if(intersection == null) { - intersection = setNew; - } else { - HashSet<Statement> interclone = new HashSet<Statement>(intersection); - interclone.removeAll(setNew); - - intersection.retainAll(setNew); - - setNew.removeAll(intersection); - - handlers.addAll(interclone); - handlers.addAll(setNew); - } - } - - for(Statement stat : handlers) { - if(!all.contains(stat) || !all.containsAll(stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD))) { - return false; - } - } - - // check for other handlers (excluding head) - for(int i=1;i<lst.size();i++) { - Statement stat = lst.get(i); - if(!stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() && !handlers.contains(stat)) { - return false; - } - } - - return true; - } - - public static boolean isChoiceStatement(Statement head, List<Statement> lst) { - - Statement post = null; - - Set<Statement> setDest = head.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); - - if(setDest.contains(head)) { - return false; - } - - for(;;) { - - lst.clear(); - - boolean repeat = false; - - setDest.remove(post); - Iterator<Statement> it = setDest.iterator(); - - while(it.hasNext()) { - Statement stat = it.next(); - - if(stat.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { - if(post == null) { - post = stat; - repeat = true; - break; - } else { - return false; - } - } - - // preds - Set<Statement> setPred = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); - setPred.remove(head); - if(setPred.contains(stat)) { - return false; - } - - if(!setDest.containsAll(setPred) || setPred.size()>1) { - if(post == null) { - post = stat; - repeat = true; - break; - } else { - return false; - } - } else if(setPred.size() == 1) { - Statement pred = setPred.iterator().next(); - while(lst.contains(pred)) { - Set<Statement> setPredTemp = pred.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); - setPredTemp.remove(head); - - if(!setPredTemp.isEmpty()) { // at most 1 predecessor - pred = setPredTemp.iterator().next(); - if(pred == stat) { - return false; // loop found - } - } else { - break; - } - } - } - - // succs - List<StatEdge> lstEdges = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); - if(lstEdges.size() > 1) { - Set<Statement> setSucc = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); - setSucc.retainAll(setDest); - - if(setSucc.size()>0) { - return false; - } else { - if(post == null) { - post = stat; - repeat = true; - break; - } else { - return false; - } - } - } else if(lstEdges.size() == 1) { - - StatEdge edge = lstEdges.get(0); - if(edge.getType() == StatEdge.TYPE_REGULAR) { - Statement statd = edge.getDestination(); - if(head == statd) { - return false; - } - if(!setDest.contains(statd) && post!=statd) { - if(post!=null) { - return false; - } else { - Set<Statement> set = statd.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); - if(set.size()>1){ - post = statd; - repeat = true; - break; - } else { - return false; - } - } - } - } - } - - lst.add(stat); - } - - if(!repeat) { - break; - } - - } - - lst.add(head); - lst.remove(post); - - lst.add(0, post); - - return true; - - } - - - public static HashSet<Statement> getUniquePredExceptions(Statement head) { - - HashSet<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); - - Iterator<Statement> it = setHandlers.iterator(); - while(it.hasNext()) { - if(it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size()>1) { - it.remove(); - } - } - return setHandlers; - } - - public static List<Exprent> copyExprentList(List<Exprent> lst) { - List<Exprent> ret = new ArrayList<Exprent>(); - for(Exprent expr: lst) { - ret.add(expr.copy()); - } - return ret; - } - + + public static boolean checkStatementExceptions(List<Statement> lst) { + + Set<Statement> all = new HashSet<Statement>(lst); + + Set<Statement> handlers = new HashSet<Statement>(); + Set<Statement> intersection = null; + + for (Statement stat : lst) { + Set<Statement> setNew = stat.getNeighboursSet(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD); + + if (intersection == null) { + intersection = setNew; + } + else { + HashSet<Statement> interclone = new HashSet<Statement>(intersection); + interclone.removeAll(setNew); + + intersection.retainAll(setNew); + + setNew.removeAll(intersection); + + handlers.addAll(interclone); + handlers.addAll(setNew); + } + } + + for (Statement stat : handlers) { + if (!all.contains(stat) || !all.containsAll(stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD))) { + return false; + } + } + + // check for other handlers (excluding head) + for (int i = 1; i < lst.size(); i++) { + Statement stat = lst.get(i); + if (!stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() && !handlers.contains(stat)) { + return false; + } + } + + return true; + } + + public static boolean isChoiceStatement(Statement head, List<Statement> lst) { + + Statement post = null; + + Set<Statement> setDest = head.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); + + if (setDest.contains(head)) { + return false; + } + + for (; ; ) { + + lst.clear(); + + boolean repeat = false; + + setDest.remove(post); + Iterator<Statement> it = setDest.iterator(); + + while (it.hasNext()) { + Statement stat = it.next(); + + if (stat.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + if (post == null) { + post = stat; + repeat = true; + break; + } + else { + return false; + } + } + + // preds + Set<Statement> setPred = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + setPred.remove(head); + if (setPred.contains(stat)) { + return false; + } + + if (!setDest.containsAll(setPred) || setPred.size() > 1) { + if (post == null) { + post = stat; + repeat = true; + break; + } + else { + return false; + } + } + else if (setPred.size() == 1) { + Statement pred = setPred.iterator().next(); + while (lst.contains(pred)) { + Set<Statement> setPredTemp = pred.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + setPredTemp.remove(head); + + if (!setPredTemp.isEmpty()) { // at most 1 predecessor + pred = setPredTemp.iterator().next(); + if (pred == stat) { + return false; // loop found + } + } + else { + break; + } + } + } + + // succs + List<StatEdge> lstEdges = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); + if (lstEdges.size() > 1) { + Set<Statement> setSucc = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); + setSucc.retainAll(setDest); + + if (setSucc.size() > 0) { + return false; + } + else { + if (post == null) { + post = stat; + repeat = true; + break; + } + else { + return false; + } + } + } + else if (lstEdges.size() == 1) { + + StatEdge edge = lstEdges.get(0); + if (edge.getType() == StatEdge.TYPE_REGULAR) { + Statement statd = edge.getDestination(); + if (head == statd) { + return false; + } + if (!setDest.contains(statd) && post != statd) { + if (post != null) { + return false; + } + else { + Set<Statement> set = statd.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + if (set.size() > 1) { + post = statd; + repeat = true; + break; + } + else { + return false; + } + } + } + } + } + + lst.add(stat); + } + + if (!repeat) { + break; + } + } + + lst.add(head); + lst.remove(post); + + lst.add(0, post); + + return true; + } + + + public static HashSet<Statement> getUniquePredExceptions(Statement head) { + + HashSet<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + + Iterator<Statement> it = setHandlers.iterator(); + while (it.hasNext()) { + if (it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1) { + it.remove(); + } + } + return setHandlers; + } + + public static List<Exprent> copyExprentList(List<Exprent> lst) { + List<Exprent> ret = new ArrayList<Exprent>(); + for (Exprent expr : lst) { + ret.add(expr.copy()); + } + return ret; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index ba6f839..79bea88 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -1,30 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; @@ -32,689 +22,690 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.modules.decompiler.decompose.FastExtendedPostdominanceHelper; import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.IrreducibleCFGDeobfuscator; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.GeneralStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.util.FastFixedSetFactory; +import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; -import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; + +import java.util.*; public class DomHelper { - - - private static RootStatement graphToStatement(ControlFlowGraph graph) { - - VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); - VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks(); - - for(BasicBlock block: blocks) { - stats.addWithKey(new BasicBlockStatement(block), block.id); - } - - BasicBlock firstblock = graph.getFirst(); - // head statement - Statement firstst = stats.getWithKey(firstblock.id); - // dummy exit statement - Statement dummyexit = new Statement(); - dummyexit.type = Statement.TYPE_DUMMYEXIT; - - Statement general; - if(stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block - general = new GeneralStatement(firstst, stats, null); - } else { // one straightforward basic block - RootStatement root = new RootStatement(firstst, dummyexit); - firstst.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, firstst, dummyexit, root)); - - return root; - } - - for(BasicBlock block: blocks) { - Statement stat = stats.getWithKey(block.id); - - for(BasicBlock succ: block.getSuccs()) { - Statement stsucc = stats.getWithKey(succ.id); - - int type; - if(stsucc==firstst) { - type = StatEdge.TYPE_CONTINUE; - } else if(graph.getFinallyExits().contains(block)) { - type = StatEdge.TYPE_FINALLYEXIT; - stsucc = dummyexit; - } else if(succ.id == graph.getLast().id) { - type = StatEdge.TYPE_BREAK; - stsucc = dummyexit; - } else { - type = StatEdge.TYPE_REGULAR; - } - - stat.addSuccessor(new StatEdge(type, stat, (type == StatEdge.TYPE_CONTINUE)?general:stsucc, - (type == StatEdge.TYPE_REGULAR)?null:general)); - } - - // exceptions edges - for(BasicBlock succex: block.getSuccExceptions()) { - Statement stsuccex = stats.getWithKey(succex.id); - - ExceptionRangeCFG range = graph.getExceptionRange(succex, block); - if(!range.isCircular()) { - stat.addSuccessor(new StatEdge(stat, stsuccex, range.getExceptionTypes())); - } - } - - } - - general.buildContinueSet(); - general.buildMonitorFlags(); - return new RootStatement(general, dummyexit); - } - - public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) { - - HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>(); - - StrongConnectivityHelper schelper = new StrongConnectivityHelper(container); - List<List<Statement>> components = schelper.getComponents(); - - List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components)); - - FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats); - - FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet(); - setFlagNodes.setAllElements(); - - FastFixedSet<Statement> initSet = factory.spawnEmptySet(); - initSet.setAllElements(); - - for(List<Statement> lst: components) { - FastFixedSet<Statement> tmpSet; - - if(StrongConnectivityHelper.isExitComponent(lst)) { - tmpSet = factory.spawnEmptySet(); - tmpSet.addAll(lst); - } else { - tmpSet = initSet.getCopy(); - } - - for(Statement stat : lst) { - lists.put(stat, tmpSet); - } - } - - do { - - for(Statement stat : lstStats) { - - if(!setFlagNodes.contains(stat)) { - continue; - } - setFlagNodes.remove(stat); - - FastFixedSet<Statement> doms = lists.get(stat); - FastFixedSet<Statement> domsSuccs = factory.spawnEmptySet(); - - List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); - for(int j=0;j<lstSuccs.size();j++) { - Statement succ = lstSuccs.get(j); - FastFixedSet<Statement> succlst = lists.get(succ); - - if(j == 0) { - domsSuccs.union(succlst); - } else { - domsSuccs.intersection(succlst); - } - } - - if(!domsSuccs.contains(stat)) { - domsSuccs.add(stat); - } - - if(!InterpreterUtil.equalObjects(domsSuccs, doms)) { - - lists.put(stat, domsSuccs); - - List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); - for(Statement pred : lstPreds) { - setFlagNodes.add(pred); - } - } - } - - } while(!setFlagNodes.isEmpty()); - - VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>(); - List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial! - - final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>(); - for(int i=0;i<lstRevPost.size();i++) { - mapSortOrder.put(lstRevPost.get(i).id, i); - } - - for(Statement st: lstStats) { - - List<Integer> lstPosts = new ArrayList<Integer>(); - for(Statement stt : lists.get(st)) { - lstPosts.add(stt.id); - } - - Collections.sort(lstPosts, new Comparator<Integer>() { - public int compare(Integer o1, Integer o2) { - return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2)); - } - }); - - if(lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) { - lstPosts.add(lstPosts.remove(0)); - } - - ret.addWithKey(lstPosts, st.id); - } - - return ret; - } - - public static RootStatement parseGraph(ControlFlowGraph graph) { - - RootStatement root = graphToStatement(graph); - - if(!processStatement(root, new HashMap<Integer, Set<Integer>>())) { - DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.ERROR); - -// try { -// DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); -// } catch (Exception ex) { -// ex.printStackTrace(); -// } - throw new RuntimeException("parsing failure!"); - } - - LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); - - SequenceHelper.condenseSequences(root); - root.buildMonitorFlags(); - - // build synchronized statements - buildSynchronized(root); - - return root; - } - - public static void removeSynchronizedHandler(Statement stat) { - - for(Statement st : stat.getStats()) { - removeSynchronizedHandler(st); - } - - if(stat.type == Statement.TYPE_SYNCRONIZED) { - ((SynchronizedStatement)stat).removeExc(); - } - } - - - private static void buildSynchronized(Statement stat) { - - for(Statement st : stat.getStats()) { - buildSynchronized(st); - } - - if(stat.type == Statement.TYPE_SEQUENCE) { - - for(;;) { - - boolean found = false; - - List<Statement> lst = stat.getStats(); - for(int i=0;i<lst.size()-1;i++) { - Statement current = lst.get(i); // basic block - - if(current.isMonitorEnter()) { - - Statement next = lst.get(i+1); - Statement nextDirect = next; - - while(next.type == Statement.TYPE_SEQUENCE) { - next = next.getFirst(); - } - - if(next.type == Statement.TYPE_CATCHALL) { - - CatchAllStatement ca = (CatchAllStatement)next; - - if(ca.getFirst().isContainsMonitorExit() && ca.getHandler().isContainsMonitorExit()) { - - // remove the head block from sequence - current.removeSuccessor(current.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); - - for(StatEdge edge: current.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { - current.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, nextDirect); - nextDirect.addPredecessor(edge); - } - - stat.getStats().removeWithKey(current.id); - stat.setFirst(stat.getStats().get(0)); - - // new statement - SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler()); - sync.setAllParent(); - - for(StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) { - sync.addLabeledEdge(edge); - } - - current.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, current, ca.getFirst())); - - ca.getParent().replaceStatement(ca, sync); - found = true; - break; - } - } - } - } - - if(!found) { - break; - } - } - - } - - } - - private static boolean processStatement(Statement general, HashMap<Integer, Set<Integer>> mapExtPost) { - - if(general.type == Statement.TYPE_ROOT) { - Statement stat = general.getFirst(); - if(stat.type != Statement.TYPE_GENERAL) { - return true; - } else { - boolean complete = processStatement(stat, mapExtPost); - if(complete) { - // replace general purpose statement with simple one - general.replaceStatement(stat, stat.getFirst()); - } - return complete; - } - } - - boolean mapRefreshed = mapExtPost.isEmpty(); - - for(int mapstage = 0; mapstage < 2; mapstage++) { - - for(int reducibility=0; reducibility < 5; reducibility++) { // FIXME: implement proper node splitting. For now up to 5 nodes in sequence are splitted. - - if(reducibility > 0) { - -// try { -// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); -// } catch(Exception ex) {ex.printStackTrace();} - - // take care of irreducible control flow graphs - if(IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) { - if(!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) { - DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.ERROR); - break; - } - } else { - if(mapstage == 2 || mapRefreshed) { // last chance lost - DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!", IFernflowerLogger.ERROR); - } - break; - } - -// try { -// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); -// } catch(Exception ex) {ex.printStackTrace();} - - mapExtPost = new HashMap<Integer, Set<Integer>>(); - mapRefreshed = true; - } - - for(int i=0;i<2;i++) { - - boolean forceall = i!=0; - - for(;;) { - - if(findSimpleStatements(general, mapExtPost)) { - reducibility = 0; - } - - if(general.type == Statement.TYPE_PLACEHOLDER) { - return true; - } - - Statement stat = findGeneralStatement(general, forceall, mapExtPost); - - if(stat!=null) { - boolean complete = processStatement(stat, general.getFirst() == stat?mapExtPost:new HashMap<Integer, Set<Integer>>()); - - if(complete) { - // replace general purpose statement with simple one - general.replaceStatement(stat, stat.getFirst()); - } else { - return false; - } - - mapExtPost = new HashMap<Integer, Set<Integer>>(); - mapRefreshed = true; - reducibility = 0; - - } else { - break; - } - } - } - -// try { -// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); -// } catch (Exception ex) { -// ex.printStackTrace(); -// } - } - - if(mapRefreshed) { - break; - } else { - mapExtPost = new HashMap<Integer, Set<Integer>>(); - } - } - - return false; - } - - private static Statement findGeneralStatement(Statement stat, boolean forceall, HashMap<Integer, Set<Integer>> mapExtPost) { - - VBStyleCollection<Statement, Integer> stats = stat.getStats(); - VBStyleCollection<List<Integer>, Integer> vbPost; - - if(mapExtPost.isEmpty()) { - FastExtendedPostdominanceHelper extpost = new FastExtendedPostdominanceHelper(); - mapExtPost.putAll(extpost.getExtendedPostdominators(stat)); - } - - if(forceall) { - vbPost = new VBStyleCollection<List<Integer>, Integer>(); - List<Statement> lstAll = stat.getPostReversePostOrderList(); - - for(Statement st: lstAll) { - Set<Integer> set = mapExtPost.get(st.id); - if(set != null) { - vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!! - } - } - - // tail statements - Set<Integer> setFirst = mapExtPost.get(stat.getFirst().id); - if(setFirst != null) { - for(Integer id : setFirst) { - List<Integer> lst = vbPost.getWithKey(id); - if(lst == null) { - vbPost.addWithKey(lst = new ArrayList<Integer>(), id); - } - lst.add(id); - } - } - - } else { - vbPost = calcPostDominators(stat); - } - - for(int k=0;k<vbPost.size();k++) { - - Integer headid = vbPost.getKey(k); - List<Integer> posts = vbPost.get(k); - - if(!mapExtPost.containsKey(headid) && - !(posts.size() == 1 && posts.get(0).equals(headid))) { - continue; - } - - Statement head = stats.getWithKey(headid); - - Set<Integer> setExtPosts = mapExtPost.get(headid); - - for(int i=0;i<posts.size();i++) { - - Integer postid = posts.get(i); - if(!postid.equals(headid) && !setExtPosts.contains(postid)) { - continue; - } - - Statement post = stats.getWithKey(postid); - - if(post == null) { // possible in case of an inherited postdominance set - continue; - } - - boolean same = (post == head); - - HashSet<Statement> setNodes = new HashSet<Statement>(); - HashSet<Statement> setPreds = new HashSet<Statement>(); - - // collect statement nodes - HashSet<Statement> setHandlers = new HashSet<Statement>(); - setHandlers.add(head); - for(;;) { - - boolean hdfound = false; - Iterator<Statement> itHandlers = setHandlers.iterator(); - while(itHandlers.hasNext()) { - Statement handler = itHandlers.next(); - - if(setNodes.contains(handler)) { - continue; - } - - boolean addhd = (setNodes.size() == 0); // first handler == head - if(!addhd) { - List<Statement> hdsupp = handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); - addhd = (setNodes.containsAll(hdsupp) && (setNodes.size() > hdsupp.size() - || setNodes.size() == 1)); // strict subset - } - - if(addhd) { - LinkedList<Statement> lstStack = new LinkedList<Statement>(); - lstStack.add(handler); - - while(!lstStack.isEmpty()) { - Statement st = lstStack.remove(0); - - if(!(setNodes.contains(st) || (!same && st == post))) { - setNodes.add(st); - if(st != head) { - // record predeccessors except for the head - setPreds.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD)); - } - - // put successors on the stack - lstStack.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); - - // exception edges - setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); - } - } - - hdfound = true; - setHandlers.remove(handler); - break; - } - } - - if(!hdfound) { - break; - } - } - - // check exception handlers - setHandlers.clear(); - for(Statement st : setNodes) { - setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); - } - setHandlers.removeAll(setNodes); - - boolean excok = true; - Iterator<Statement> itt = setHandlers.iterator(); - while(itt.hasNext()) { - Statement handler = itt.next(); - if(!handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD).containsAll(setNodes)) { - excok = false; - break; - } - } - - // build statement and return - if(excok) { - Statement res = null; - - setPreds.removeAll(setNodes); - if(setPreds.size() == 0) { - if((setNodes.size() > 1 || - head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head)) - && setNodes.size() < stats.size()) { - if(checkSynchronizedCompleteness(head, setNodes)) { - res = new GeneralStatement(head, setNodes, same?null:post); - stat.collapseNodesToStatement(res); - - return res; - } - } - } - } - } - } - - return null; - } - - private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) { - - // check exit nodes - for(Statement stat : setNodes) { - if(stat.isMonitorEnter()) { - List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); - if(lstSuccs.size() != 1 || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { - return false; - } - - if(!setNodes.contains(lstSuccs.get(0).getDestination())) { - return false; - } - } - } - - return true; - } - - private static boolean findSimpleStatements(Statement stat, HashMap<Integer, Set<Integer>> mapExtPost) { - - boolean found, success = false; - - do { - found = false; - - List<Statement> lstStats = stat.getPostReversePostOrderList(); - for(Statement st: lstStats) { - - Statement result = detectStatement(st); - - if(result != null) { - - if(stat.type == Statement.TYPE_GENERAL && result.getFirst() == stat.getFirst() && - stat.getStats().size() == result.getStats().size()) { - // mark general statement - stat.type = Statement.TYPE_PLACEHOLDER; - } - - stat.collapseNodesToStatement(result); - - // update the postdominator map - if(!mapExtPost.isEmpty()) { - HashSet<Integer> setOldNodes = new HashSet<Integer>(); - for(Statement old : result.getStats()) { - setOldNodes.add(old.id); - } - - Integer newid = result.id; - - for(Integer key : new ArrayList<Integer>(mapExtPost.keySet())) { - Set<Integer> set = mapExtPost.get(key); - - int oldsize = set.size(); - set.removeAll(setOldNodes); - - if(setOldNodes.contains(key)) { - Set<Integer> setNew = mapExtPost.get(newid); - if(setNew == null) { - mapExtPost.put(newid, setNew = new HashSet<Integer>()); - } - setNew.addAll(set); - - mapExtPost.remove(key); - } else { - if(set.size() < oldsize) { - set.add(newid); - } - } - } - } - - - found = true; - break; - } - } - - if(found) { - success = true; - } - - } while(found); - - return success; - } - - - private static Statement detectStatement(Statement head) { - - Statement res; - - if((res = DoStatement.isHead(head)) != null) { - return res; - } - - if((res = SwitchStatement.isHead(head)) != null) { - return res; - } - - if((res = IfStatement.isHead(head)) != null) { - return res; - } - - // synchronized statements will be identified later - // right now they are recognized as catchall - - if((res = SequenceStatement.isHead2Block(head)) != null) { - return res; - } - - if((res = CatchStatement.isHead(head)) != null) { - return res; - } - - if((res = CatchAllStatement.isHead(head)) != null) { - return res; - } - - return null; - } - + + + private static RootStatement graphToStatement(ControlFlowGraph graph) { + + VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); + VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks(); + + for (BasicBlock block : blocks) { + stats.addWithKey(new BasicBlockStatement(block), block.id); + } + + BasicBlock firstblock = graph.getFirst(); + // head statement + Statement firstst = stats.getWithKey(firstblock.id); + // dummy exit statement + Statement dummyexit = new Statement(); + dummyexit.type = Statement.TYPE_DUMMYEXIT; + + Statement general; + if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block + general = new GeneralStatement(firstst, stats, null); + } + else { // one straightforward basic block + RootStatement root = new RootStatement(firstst, dummyexit); + firstst.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, firstst, dummyexit, root)); + + return root; + } + + for (BasicBlock block : blocks) { + Statement stat = stats.getWithKey(block.id); + + for (BasicBlock succ : block.getSuccs()) { + Statement stsucc = stats.getWithKey(succ.id); + + int type; + if (stsucc == firstst) { + type = StatEdge.TYPE_CONTINUE; + } + else if (graph.getFinallyExits().contains(block)) { + type = StatEdge.TYPE_FINALLYEXIT; + stsucc = dummyexit; + } + else if (succ.id == graph.getLast().id) { + type = StatEdge.TYPE_BREAK; + stsucc = dummyexit; + } + else { + type = StatEdge.TYPE_REGULAR; + } + + stat.addSuccessor(new StatEdge(type, stat, (type == StatEdge.TYPE_CONTINUE) ? general : stsucc, + (type == StatEdge.TYPE_REGULAR) ? null : general)); + } + + // exceptions edges + for (BasicBlock succex : block.getSuccExceptions()) { + Statement stsuccex = stats.getWithKey(succex.id); + + ExceptionRangeCFG range = graph.getExceptionRange(succex, block); + if (!range.isCircular()) { + stat.addSuccessor(new StatEdge(stat, stsuccex, range.getExceptionTypes())); + } + } + } + + general.buildContinueSet(); + general.buildMonitorFlags(); + return new RootStatement(general, dummyexit); + } + + public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) { + + HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>(); + + StrongConnectivityHelper schelper = new StrongConnectivityHelper(container); + List<List<Statement>> components = schelper.getComponents(); + + List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components)); + + FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats); + + FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet(); + setFlagNodes.setAllElements(); + + FastFixedSet<Statement> initSet = factory.spawnEmptySet(); + initSet.setAllElements(); + + for (List<Statement> lst : components) { + FastFixedSet<Statement> tmpSet; + + if (StrongConnectivityHelper.isExitComponent(lst)) { + tmpSet = factory.spawnEmptySet(); + tmpSet.addAll(lst); + } + else { + tmpSet = initSet.getCopy(); + } + + for (Statement stat : lst) { + lists.put(stat, tmpSet); + } + } + + do { + + for (Statement stat : lstStats) { + + if (!setFlagNodes.contains(stat)) { + continue; + } + setFlagNodes.remove(stat); + + FastFixedSet<Statement> doms = lists.get(stat); + FastFixedSet<Statement> domsSuccs = factory.spawnEmptySet(); + + List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); + for (int j = 0; j < lstSuccs.size(); j++) { + Statement succ = lstSuccs.get(j); + FastFixedSet<Statement> succlst = lists.get(succ); + + if (j == 0) { + domsSuccs.union(succlst); + } + else { + domsSuccs.intersection(succlst); + } + } + + if (!domsSuccs.contains(stat)) { + domsSuccs.add(stat); + } + + if (!InterpreterUtil.equalObjects(domsSuccs, doms)) { + + lists.put(stat, domsSuccs); + + List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + for (Statement pred : lstPreds) { + setFlagNodes.add(pred); + } + } + } + } + while (!setFlagNodes.isEmpty()); + + VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>(); + List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial! + + final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>(); + for (int i = 0; i < lstRevPost.size(); i++) { + mapSortOrder.put(lstRevPost.get(i).id, i); + } + + for (Statement st : lstStats) { + + List<Integer> lstPosts = new ArrayList<Integer>(); + for (Statement stt : lists.get(st)) { + lstPosts.add(stt.id); + } + + Collections.sort(lstPosts, new Comparator<Integer>() { + public int compare(Integer o1, Integer o2) { + return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2)); + } + }); + + if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) { + lstPosts.add(lstPosts.remove(0)); + } + + ret.addWithKey(lstPosts, st.id); + } + + return ret; + } + + public static RootStatement parseGraph(ControlFlowGraph graph) { + + RootStatement root = graphToStatement(graph); + + if (!processStatement(root, new HashMap<Integer, Set<Integer>>())) { + DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.ERROR); + + // try { + // DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); + // } catch (Exception ex) { + // ex.printStackTrace(); + // } + throw new RuntimeException("parsing failure!"); + } + + LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); + + SequenceHelper.condenseSequences(root); + root.buildMonitorFlags(); + + // build synchronized statements + buildSynchronized(root); + + return root; + } + + public static void removeSynchronizedHandler(Statement stat) { + + for (Statement st : stat.getStats()) { + removeSynchronizedHandler(st); + } + + if (stat.type == Statement.TYPE_SYNCRONIZED) { + ((SynchronizedStatement)stat).removeExc(); + } + } + + + private static void buildSynchronized(Statement stat) { + + for (Statement st : stat.getStats()) { + buildSynchronized(st); + } + + if (stat.type == Statement.TYPE_SEQUENCE) { + + for (; ; ) { + + boolean found = false; + + List<Statement> lst = stat.getStats(); + for (int i = 0; i < lst.size() - 1; i++) { + Statement current = lst.get(i); // basic block + + if (current.isMonitorEnter()) { + + Statement next = lst.get(i + 1); + Statement nextDirect = next; + + while (next.type == Statement.TYPE_SEQUENCE) { + next = next.getFirst(); + } + + if (next.type == Statement.TYPE_CATCHALL) { + + CatchAllStatement ca = (CatchAllStatement)next; + + if (ca.getFirst().isContainsMonitorExit() && ca.getHandler().isContainsMonitorExit()) { + + // remove the head block from sequence + current.removeSuccessor(current.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); + + for (StatEdge edge : current.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + current.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, nextDirect); + nextDirect.addPredecessor(edge); + } + + stat.getStats().removeWithKey(current.id); + stat.setFirst(stat.getStats().get(0)); + + // new statement + SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler()); + sync.setAllParent(); + + for (StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) { + sync.addLabeledEdge(edge); + } + + current.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, current, ca.getFirst())); + + ca.getParent().replaceStatement(ca, sync); + found = true; + break; + } + } + } + } + + if (!found) { + break; + } + } + } + } + + private static boolean processStatement(Statement general, HashMap<Integer, Set<Integer>> mapExtPost) { + + if (general.type == Statement.TYPE_ROOT) { + Statement stat = general.getFirst(); + if (stat.type != Statement.TYPE_GENERAL) { + return true; + } + else { + boolean complete = processStatement(stat, mapExtPost); + if (complete) { + // replace general purpose statement with simple one + general.replaceStatement(stat, stat.getFirst()); + } + return complete; + } + } + + boolean mapRefreshed = mapExtPost.isEmpty(); + + for (int mapstage = 0; mapstage < 2; mapstage++) { + + for (int reducibility = 0; + reducibility < 5; + reducibility++) { // FIXME: implement proper node splitting. For now up to 5 nodes in sequence are splitted. + + if (reducibility > 0) { + + // try { + // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + // take care of irreducible control flow graphs + if (IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) { + if (!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) { + DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.ERROR); + break; + } + } + else { + if (mapstage == 2 || mapRefreshed) { // last chance lost + DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!", IFernflowerLogger.ERROR); + } + break; + } + + // try { + // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + mapExtPost = new HashMap<Integer, Set<Integer>>(); + mapRefreshed = true; + } + + for (int i = 0; i < 2; i++) { + + boolean forceall = i != 0; + + for (; ; ) { + + if (findSimpleStatements(general, mapExtPost)) { + reducibility = 0; + } + + if (general.type == Statement.TYPE_PLACEHOLDER) { + return true; + } + + Statement stat = findGeneralStatement(general, forceall, mapExtPost); + + if (stat != null) { + boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<Integer, Set<Integer>>()); + + if (complete) { + // replace general purpose statement with simple one + general.replaceStatement(stat, stat.getFirst()); + } + else { + return false; + } + + mapExtPost = new HashMap<Integer, Set<Integer>>(); + mapRefreshed = true; + reducibility = 0; + } + else { + break; + } + } + } + + // try { + // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); + // } catch (Exception ex) { + // ex.printStackTrace(); + // } + } + + if (mapRefreshed) { + break; + } + else { + mapExtPost = new HashMap<Integer, Set<Integer>>(); + } + } + + return false; + } + + private static Statement findGeneralStatement(Statement stat, boolean forceall, HashMap<Integer, Set<Integer>> mapExtPost) { + + VBStyleCollection<Statement, Integer> stats = stat.getStats(); + VBStyleCollection<List<Integer>, Integer> vbPost; + + if (mapExtPost.isEmpty()) { + FastExtendedPostdominanceHelper extpost = new FastExtendedPostdominanceHelper(); + mapExtPost.putAll(extpost.getExtendedPostdominators(stat)); + } + + if (forceall) { + vbPost = new VBStyleCollection<List<Integer>, Integer>(); + List<Statement> lstAll = stat.getPostReversePostOrderList(); + + for (Statement st : lstAll) { + Set<Integer> set = mapExtPost.get(st.id); + if (set != null) { + vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!! + } + } + + // tail statements + Set<Integer> setFirst = mapExtPost.get(stat.getFirst().id); + if (setFirst != null) { + for (Integer id : setFirst) { + List<Integer> lst = vbPost.getWithKey(id); + if (lst == null) { + vbPost.addWithKey(lst = new ArrayList<Integer>(), id); + } + lst.add(id); + } + } + } + else { + vbPost = calcPostDominators(stat); + } + + for (int k = 0; k < vbPost.size(); k++) { + + Integer headid = vbPost.getKey(k); + List<Integer> posts = vbPost.get(k); + + if (!mapExtPost.containsKey(headid) && + !(posts.size() == 1 && posts.get(0).equals(headid))) { + continue; + } + + Statement head = stats.getWithKey(headid); + + Set<Integer> setExtPosts = mapExtPost.get(headid); + + for (int i = 0; i < posts.size(); i++) { + + Integer postid = posts.get(i); + if (!postid.equals(headid) && !setExtPosts.contains(postid)) { + continue; + } + + Statement post = stats.getWithKey(postid); + + if (post == null) { // possible in case of an inherited postdominance set + continue; + } + + boolean same = (post == head); + + HashSet<Statement> setNodes = new HashSet<Statement>(); + HashSet<Statement> setPreds = new HashSet<Statement>(); + + // collect statement nodes + HashSet<Statement> setHandlers = new HashSet<Statement>(); + setHandlers.add(head); + for (; ; ) { + + boolean hdfound = false; + Iterator<Statement> itHandlers = setHandlers.iterator(); + while (itHandlers.hasNext()) { + Statement handler = itHandlers.next(); + + if (setNodes.contains(handler)) { + continue; + } + + boolean addhd = (setNodes.size() == 0); // first handler == head + if (!addhd) { + List<Statement> hdsupp = handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); + addhd = (setNodes.containsAll(hdsupp) && (setNodes.size() > hdsupp.size() + || setNodes.size() == 1)); // strict subset + } + + if (addhd) { + LinkedList<Statement> lstStack = new LinkedList<Statement>(); + lstStack.add(handler); + + while (!lstStack.isEmpty()) { + Statement st = lstStack.remove(0); + + if (!(setNodes.contains(st) || (!same && st == post))) { + setNodes.add(st); + if (st != head) { + // record predeccessors except for the head + setPreds.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD)); + } + + // put successors on the stack + lstStack.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); + + // exception edges + setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + } + } + + hdfound = true; + setHandlers.remove(handler); + break; + } + } + + if (!hdfound) { + break; + } + } + + // check exception handlers + setHandlers.clear(); + for (Statement st : setNodes) { + setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + } + setHandlers.removeAll(setNodes); + + boolean excok = true; + Iterator<Statement> itt = setHandlers.iterator(); + while (itt.hasNext()) { + Statement handler = itt.next(); + if (!handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD).containsAll(setNodes)) { + excok = false; + break; + } + } + + // build statement and return + if (excok) { + Statement res = null; + + setPreds.removeAll(setNodes); + if (setPreds.size() == 0) { + if ((setNodes.size() > 1 || + head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head)) + && setNodes.size() < stats.size()) { + if (checkSynchronizedCompleteness(head, setNodes)) { + res = new GeneralStatement(head, setNodes, same ? null : post); + stat.collapseNodesToStatement(res); + + return res; + } + } + } + } + } + } + + return null; + } + + private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) { + + // check exit nodes + for (Statement stat : setNodes) { + if (stat.isMonitorEnter()) { + List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); + if (lstSuccs.size() != 1 || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { + return false; + } + + if (!setNodes.contains(lstSuccs.get(0).getDestination())) { + return false; + } + } + } + + return true; + } + + private static boolean findSimpleStatements(Statement stat, HashMap<Integer, Set<Integer>> mapExtPost) { + + boolean found, success = false; + + do { + found = false; + + List<Statement> lstStats = stat.getPostReversePostOrderList(); + for (Statement st : lstStats) { + + Statement result = detectStatement(st); + + if (result != null) { + + if (stat.type == Statement.TYPE_GENERAL && result.getFirst() == stat.getFirst() && + stat.getStats().size() == result.getStats().size()) { + // mark general statement + stat.type = Statement.TYPE_PLACEHOLDER; + } + + stat.collapseNodesToStatement(result); + + // update the postdominator map + if (!mapExtPost.isEmpty()) { + HashSet<Integer> setOldNodes = new HashSet<Integer>(); + for (Statement old : result.getStats()) { + setOldNodes.add(old.id); + } + + Integer newid = result.id; + + for (Integer key : new ArrayList<Integer>(mapExtPost.keySet())) { + Set<Integer> set = mapExtPost.get(key); + + int oldsize = set.size(); + set.removeAll(setOldNodes); + + if (setOldNodes.contains(key)) { + Set<Integer> setNew = mapExtPost.get(newid); + if (setNew == null) { + mapExtPost.put(newid, setNew = new HashSet<Integer>()); + } + setNew.addAll(set); + + mapExtPost.remove(key); + } + else { + if (set.size() < oldsize) { + set.add(newid); + } + } + } + } + + + found = true; + break; + } + } + + if (found) { + success = true; + } + } + while (found); + + return success; + } + + + private static Statement detectStatement(Statement head) { + + Statement res; + + if ((res = DoStatement.isHead(head)) != null) { + return res; + } + + if ((res = SwitchStatement.isHead(head)) != null) { + return res; + } + + if ((res = IfStatement.isHead(head)) != null) { + return res; + } + + // synchronized statements will be identified later + // right now they are recognized as catchall + + if ((res = SequenceStatement.isHead2Block(head)) != null) { + return res; + } + + if ((res = CatchStatement.isHead(head)) != null) { + return res; + } + + if ((res = CatchAllStatement.isHead(head)) != null) { + return res; + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java index 6854cd3..16f7da0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java @@ -1,214 +1,214 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; - public class EliminateLoopsHelper { - -// public static boolean eliminateLoops(Statement root) { -// -// boolean ret = eliminateLoopsRec(root); -// -// if(ret) { -// SequenceHelper.condenseSequences(root); -// -// HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); -// -// SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); -// while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) { -// SequenceHelper.condenseSequences(root); -// } -// } -// -// return ret; -// } - - private static boolean eliminateLoopsRec(Statement stat) { - - for(Statement st: stat.getStats()) { - if(eliminateLoopsRec(st)) { - return true; - } - } - - if(stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) { - return true; - } - - return false; - } - - private static boolean isLoopRedundant(DoStatement loop) { - - if(loop.getLooptype() != DoStatement.LOOP_DO) { - return false; - } - - // get parent loop if exists - Statement parentloop = loop.getParent(); - while(parentloop != null && parentloop.type != Statement.TYPE_DO) { - parentloop = parentloop.getParent(); - } - - if(parentloop == null || parentloop.getBasichead() != loop.getBasichead()) { - return false; - } - - // collect relevant break edges - List<StatEdge> lstBreakEdges = new ArrayList<StatEdge>(); - for(StatEdge edge: loop.getLabelEdges()) { - if(edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type - lstBreakEdges.add(edge); - } - } - - - Statement loopcontent = loop.getFirst(); - - boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty(); - if(!firstok) { - StatEdge edge = loopcontent.getAllSuccessorEdges().get(0); - firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK); - if(firstok) { - lstBreakEdges.remove(edge); - } - } - - - if(!lstBreakEdges.isEmpty()) { - if(firstok) { - - HashMap<Integer, Boolean> statLabeled = new HashMap<Integer, Boolean>(); - List<Statement> lstEdgeClosures = new ArrayList<Statement>(); - - for(StatEdge edge: lstBreakEdges) { - Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource()); - lstEdgeClosures.add(minclosure); - } - - int precount = loop.isLabeled()?1:0; - for(Statement st: lstEdgeClosures) { - if(!statLabeled.containsKey(st.id)) { - boolean btemp = st.isLabeled(); - precount+=btemp?1:0; - statLabeled.put(st.id, btemp); - } - } - - for(int i=0;i<lstBreakEdges.size();i++) { - Statement st = lstEdgeClosures.get(i); - statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); - } - - for(int i=0;i<lstBreakEdges.size();i++) { - lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop)); - } - - statLabeled.clear(); - for(Statement st: lstEdgeClosures) { - statLabeled.put(st.id, st.isLabeled()); - } - - for(int i=0;i<lstBreakEdges.size();i++) { - Statement st = lstEdgeClosures.get(i); - statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); - } - - int postcount = 0; - for(Boolean val: statLabeled.values()) { - postcount+=val?1:0; - } - - if(precount <= postcount) { - return false; - } else { - for(int i=0;i<lstBreakEdges.size();i++) { - lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i)); - } - } - - } else { - return false; - } - } - - eliminateLoop(loop, parentloop); - - return true; - } - - private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { - - Statement closure = stat; - Statement newclosure = stat; - - while((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) { - closure = newclosure; - } - - return closure; - } - - private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { - - Statement closure = stat.getParent(); - - while(closure!=null && closure!=max && !closure.containsStatementStrict(edge.getDestination())) { - - boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure); - boolean stat_labeled = statLabeled.containsKey(closure.id)?statLabeled.get(closure.id):closure.isLabeled(); - - if(stat_labeled || !edge_labeled) { - return closure; - } - - closure = closure.getParent(); - } - - return null; - } - - private static void eliminateLoop(Statement loop, Statement parentloop) { - - // move continue edges to the parent loop - List<StatEdge> lst = new ArrayList<StatEdge>(loop.getLabelEdges()); - for(StatEdge edge: lst) { - loop.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop); - parentloop.addPredecessor(edge); - - parentloop.addLabeledEdge(edge); - } - - // remove the last break edge, if exists - Statement loopcontent = loop.getFirst(); - if(!loopcontent.getAllSuccessorEdges().isEmpty()) { - loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0)); - } - - // replace loop with its content - loop.getParent().replaceStatement(loop, loopcontent); - } - + + // public static boolean eliminateLoops(Statement root) { + // + // boolean ret = eliminateLoopsRec(root); + // + // if(ret) { + // SequenceHelper.condenseSequences(root); + // + // HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); + // + // SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); + // while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) { + // SequenceHelper.condenseSequences(root); + // } + // } + // + // return ret; + // } + + private static boolean eliminateLoopsRec(Statement stat) { + + for (Statement st : stat.getStats()) { + if (eliminateLoopsRec(st)) { + return true; + } + } + + if (stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) { + return true; + } + + return false; + } + + private static boolean isLoopRedundant(DoStatement loop) { + + if (loop.getLooptype() != DoStatement.LOOP_DO) { + return false; + } + + // get parent loop if exists + Statement parentloop = loop.getParent(); + while (parentloop != null && parentloop.type != Statement.TYPE_DO) { + parentloop = parentloop.getParent(); + } + + if (parentloop == null || parentloop.getBasichead() != loop.getBasichead()) { + return false; + } + + // collect relevant break edges + List<StatEdge> lstBreakEdges = new ArrayList<StatEdge>(); + for (StatEdge edge : loop.getLabelEdges()) { + if (edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type + lstBreakEdges.add(edge); + } + } + + + Statement loopcontent = loop.getFirst(); + + boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty(); + if (!firstok) { + StatEdge edge = loopcontent.getAllSuccessorEdges().get(0); + firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK); + if (firstok) { + lstBreakEdges.remove(edge); + } + } + + + if (!lstBreakEdges.isEmpty()) { + if (firstok) { + + HashMap<Integer, Boolean> statLabeled = new HashMap<Integer, Boolean>(); + List<Statement> lstEdgeClosures = new ArrayList<Statement>(); + + for (StatEdge edge : lstBreakEdges) { + Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource()); + lstEdgeClosures.add(minclosure); + } + + int precount = loop.isLabeled() ? 1 : 0; + for (Statement st : lstEdgeClosures) { + if (!statLabeled.containsKey(st.id)) { + boolean btemp = st.isLabeled(); + precount += btemp ? 1 : 0; + statLabeled.put(st.id, btemp); + } + } + + for (int i = 0; i < lstBreakEdges.size(); i++) { + Statement st = lstEdgeClosures.get(i); + statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); + } + + for (int i = 0; i < lstBreakEdges.size(); i++) { + lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop)); + } + + statLabeled.clear(); + for (Statement st : lstEdgeClosures) { + statLabeled.put(st.id, st.isLabeled()); + } + + for (int i = 0; i < lstBreakEdges.size(); i++) { + Statement st = lstEdgeClosures.get(i); + statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); + } + + int postcount = 0; + for (Boolean val : statLabeled.values()) { + postcount += val ? 1 : 0; + } + + if (precount <= postcount) { + return false; + } + else { + for (int i = 0; i < lstBreakEdges.size(); i++) { + lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i)); + } + } + } + else { + return false; + } + } + + eliminateLoop(loop, parentloop); + + return true; + } + + private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { + + Statement closure = stat; + Statement newclosure = stat; + + while ((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) { + closure = newclosure; + } + + return closure; + } + + private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { + + Statement closure = stat.getParent(); + + while (closure != null && closure != max && !closure.containsStatementStrict(edge.getDestination())) { + + boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure); + boolean stat_labeled = statLabeled.containsKey(closure.id) ? statLabeled.get(closure.id) : closure.isLabeled(); + + if (stat_labeled || !edge_labeled) { + return closure; + } + + closure = closure.getParent(); + } + + return null; + } + + private static void eliminateLoop(Statement loop, Statement parentloop) { + + // move continue edges to the parent loop + List<StatEdge> lst = new ArrayList<StatEdge>(loop.getLabelEdges()); + for (StatEdge edge : lst) { + loop.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop); + parentloop.addPredecessor(edge); + + parentloop.addLabeledEdge(edge); + } + + // remove the last break edge, if exists + Statement loopcontent = loop.getFirst(); + if (!loopcontent.getAllSuccessorEdges().isEmpty()) { + loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0)); + } + + // replace loop with its content + loop.getParent().replaceStatement(loop, loopcontent); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java index 352415a..cddbc21 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -1,348 +1,345 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + public class ExitHelper { - - public static boolean condenseExits(RootStatement root) { - - int changed = integrateExits(root); - - if(changed > 0) { - - cleanUpUnreachableBlocks(root); - - SequenceHelper.condenseSequences(root); - } - - return (changed > 0); - } - - - private static void cleanUpUnreachableBlocks(Statement stat) { - - boolean found; - do { - - found = false; - - for(int i=0;i<stat.getStats().size();i++) { - - Statement st = stat.getStats().get(i); - - cleanUpUnreachableBlocks(st); - - if(st.type == Statement.TYPE_SEQUENCE && st.getStats().size() > 1) { - - Statement last = st.getStats().getLast(); - Statement secondlast = st.getStats().get(st.getStats().size()-2); - - if(last.getExprents() == null || !last.getExprents().isEmpty()) { - if(!secondlast.hasBasicSuccEdge()) { - - Set<Statement> set = last.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_BACKWARD); - set.remove(secondlast); - - if(set.isEmpty()) { - last.setExprents(new ArrayList<Exprent>()); - found = true; - break; - } - } - } - } - } - - } while(found); - - } - - - private static int integrateExits(Statement stat) { - - int ret = 0; - Statement dest = null; - - if(stat.getExprents() == null) { - - for(;;) { - - int changed = 0; - - for(Statement st: stat.getStats()) { - changed = integrateExits(st); - if(changed > 0) { - ret = 1; - break; - } - } - - if(changed == 0) { - break; - } - } - - - switch(stat.type) { - case Statement.TYPE_IF: - IfStatement ifst = (IfStatement)stat; - if(ifst.getIfstat() == null) { - StatEdge ifedge = ifst.getIfEdge(); - dest = isExitEdge(ifedge); - if(dest != null) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); - - ifst.getFirst().removeSuccessor(ifedge); - StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); - ifst.getFirst().addSuccessor(newedge); - ifst.setIfEdge(newedge); - ifst.setIfstat(bstat); - ifst.getStats().addWithKey(bstat, bstat.id); - bstat.setParent(ifst); - - StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); - StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); - bstat.addSuccessor(newexitedge); - oldexitedge.closure.addLabeledEdge(newexitedge); - ret = 1; - } - } - } - } - - - if(stat.getAllSuccessorEdges().size() == 1 && stat.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_BREAK && stat.getLabelEdges().isEmpty()) { - Statement parent = stat.getParent(); - if(stat != parent.getFirst() || (parent.type != Statement.TYPE_IF && - parent.type != Statement.TYPE_SWITCH)) { - - StatEdge destedge = stat.getAllSuccessorEdges().get(0); - dest = isExitEdge(destedge); - if(dest != null) { - stat.removeSuccessor(destedge); - - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); - - StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); - StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); - bstat.addSuccessor(newexitedge); - oldexitedge.closure.addLabeledEdge(newexitedge); - - SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[] {stat, bstat})); - block.setAllParent(); - - parent.replaceStatement(stat, block); - // LabelHelper.lowContinueLabels not applicable because of forward continue edges - // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); - // do it by hand - for(StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { - - block.removePredecessor(prededge); - prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat); - stat.addPredecessor(prededge); - - stat.addLabeledEdge(prededge); - } - - - stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat)); - - for(StatEdge edge : dest.getAllPredecessorEdges()) { - if(!edge.explicit && stat.containsStatementStrict(edge.getSource()) && - MergeHelper.isDirectPath(edge.getSource().getParent(), bstat)) { - - dest.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, bstat); - bstat.addPredecessor(edge); - - if(!stat.containsStatementStrict(edge.closure)) { - stat.addLabeledEdge(edge); - } - } - } - - ret = 2; - } - } - } - - return ret; - } - - private static Statement isExitEdge(StatEdge edge) { - - Statement dest = edge.getDestination(); - - if(edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK - && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { - List<Exprent> data = dest.getExprents(); - - if(data != null && data.size() == 1) { - if(data.get(0).type == Exprent.EXPRENT_EXIT) { - return dest; - } - } - } - - return null; - } - - private static boolean isOnlyEdge(StatEdge edge) { - - Statement stat = edge.getDestination(); - - for(StatEdge ed: stat.getAllPredecessorEdges()) { - if(ed != edge) { - if(ed.getType() == StatEdge.TYPE_REGULAR) { - Statement source = ed.getSource(); - - if(source.type == Statement.TYPE_BASICBLOCK || (source.type == Statement.TYPE_IF && - ((IfStatement)source).iftype == IfStatement.IFTYPE_IF) || - (source.type == Statement.TYPE_DO && ((DoStatement)source).getLooptype() != DoStatement.LOOP_DO)) { - return false; - } - } else { - return false; - } - } - } - - return true; - } - - public static boolean removeRedundantReturns(RootStatement root) { - - boolean res = false; - - for(StatEdge edge: root.getDummyExit().getAllPredecessorEdges()) { - if(!edge.explicit) { - Statement source = edge.getSource(); + + public static boolean condenseExits(RootStatement root) { + + int changed = integrateExits(root); + + if (changed > 0) { + + cleanUpUnreachableBlocks(root); + + SequenceHelper.condenseSequences(root); + } + + return (changed > 0); + } + + + private static void cleanUpUnreachableBlocks(Statement stat) { + + boolean found; + do { + + found = false; + + for (int i = 0; i < stat.getStats().size(); i++) { + + Statement st = stat.getStats().get(i); + + cleanUpUnreachableBlocks(st); + + if (st.type == Statement.TYPE_SEQUENCE && st.getStats().size() > 1) { + + Statement last = st.getStats().getLast(); + Statement secondlast = st.getStats().get(st.getStats().size() - 2); + + if (last.getExprents() == null || !last.getExprents().isEmpty()) { + if (!secondlast.hasBasicSuccEdge()) { + + Set<Statement> set = last.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_BACKWARD); + set.remove(secondlast); + + if (set.isEmpty()) { + last.setExprents(new ArrayList<Exprent>()); + found = true; + break; + } + } + } + } + } + } + while (found); + } + + + private static int integrateExits(Statement stat) { + + int ret = 0; + Statement dest = null; + + if (stat.getExprents() == null) { + + for (; ; ) { + + int changed = 0; + + for (Statement st : stat.getStats()) { + changed = integrateExits(st); + if (changed > 0) { + ret = 1; + break; + } + } + + if (changed == 0) { + break; + } + } + + + switch (stat.type) { + case Statement.TYPE_IF: + IfStatement ifst = (IfStatement)stat; + if (ifst.getIfstat() == null) { + StatEdge ifedge = ifst.getIfEdge(); + dest = isExitEdge(ifedge); + if (dest != null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); + + ifst.getFirst().removeSuccessor(ifedge); + StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); + ifst.getFirst().addSuccessor(newedge); + ifst.setIfEdge(newedge); + ifst.setIfstat(bstat); + ifst.getStats().addWithKey(bstat, bstat.id); + bstat.setParent(ifst); + + StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); + StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); + bstat.addSuccessor(newexitedge); + oldexitedge.closure.addLabeledEdge(newexitedge); + ret = 1; + } + } + } + } + + + if (stat.getAllSuccessorEdges().size() == 1 && + stat.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_BREAK && + stat.getLabelEdges().isEmpty()) { + Statement parent = stat.getParent(); + if (stat != parent.getFirst() || (parent.type != Statement.TYPE_IF && + parent.type != Statement.TYPE_SWITCH)) { + + StatEdge destedge = stat.getAllSuccessorEdges().get(0); + dest = isExitEdge(destedge); + if (dest != null) { + stat.removeSuccessor(destedge); + + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); + + StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); + StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); + bstat.addSuccessor(newexitedge); + oldexitedge.closure.addLabeledEdge(newexitedge); + + SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[]{stat, bstat})); + block.setAllParent(); + + parent.replaceStatement(stat, block); + // LabelHelper.lowContinueLabels not applicable because of forward continue edges + // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); + // do it by hand + for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + + block.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat); + stat.addPredecessor(prededge); + + stat.addLabeledEdge(prededge); + } + + + stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat)); + + for (StatEdge edge : dest.getAllPredecessorEdges()) { + if (!edge.explicit && stat.containsStatementStrict(edge.getSource()) && + MergeHelper.isDirectPath(edge.getSource().getParent(), bstat)) { + + dest.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, bstat); + bstat.addPredecessor(edge); + + if (!stat.containsStatementStrict(edge.closure)) { + stat.addLabeledEdge(edge); + } + } + } + + ret = 2; + } + } + } + + return ret; + } + + private static Statement isExitEdge(StatEdge edge) { + + Statement dest = edge.getDestination(); + + if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK + && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { + List<Exprent> data = dest.getExprents(); + + if (data != null && data.size() == 1) { + if (data.get(0).type == Exprent.EXPRENT_EXIT) { + return dest; + } + } + } + + return null; + } + + private static boolean isOnlyEdge(StatEdge edge) { + + Statement stat = edge.getDestination(); + + for (StatEdge ed : stat.getAllPredecessorEdges()) { + if (ed != edge) { + if (ed.getType() == StatEdge.TYPE_REGULAR) { + Statement source = ed.getSource(); + + if (source.type == Statement.TYPE_BASICBLOCK || (source.type == Statement.TYPE_IF && + ((IfStatement)source).iftype == IfStatement.IFTYPE_IF) || + (source.type == Statement.TYPE_DO && ((DoStatement)source).getLooptype() != DoStatement.LOOP_DO)) { + return false; + } + } + else { + return false; + } + } + } + + return true; + } + + public static boolean removeRedundantReturns(RootStatement root) { + + boolean res = false; + + for (StatEdge edge : root.getDummyExit().getAllPredecessorEdges()) { + if (!edge.explicit) { + Statement source = edge.getSource(); List<Exprent> lstExpr = source.getExprents(); - if(lstExpr != null && !lstExpr.isEmpty()) { - Exprent expr = lstExpr.get(lstExpr.size() - 1); - if(expr.type == Exprent.EXPRENT_EXIT) { - ExitExprent ex = (ExitExprent)expr; - if(ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { - // remove redundant return - lstExpr.remove(lstExpr.size() - 1); - res = true; - } - } - } - } - } - - return res; - } - - public static boolean handleReturnFromInitializer(RootStatement root) { - - boolean res = false; - - Statement exit = root.getDummyExit(); - Statement top = root.getFirst(); - Statement newret = null; - - boolean sharedcreated = false; - - for(StatEdge edge: exit.getAllPredecessorEdges()) { - if(edge.explicit) { - - if(!sharedcreated) { - newret = addSharedInitializerReturn(root); - sharedcreated = true; - } - - Statement source = edge.getSource(); + if (lstExpr != null && !lstExpr.isEmpty()) { + Exprent expr = lstExpr.get(lstExpr.size() - 1); + if (expr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ex = (ExitExprent)expr; + if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { + // remove redundant return + lstExpr.remove(lstExpr.size() - 1); + res = true; + } + } + } + } + } + + return res; + } + + public static boolean handleReturnFromInitializer(RootStatement root) { + + boolean res = false; + + Statement exit = root.getDummyExit(); + Statement top = root.getFirst(); + Statement newret = null; + + boolean sharedcreated = false; + + for (StatEdge edge : exit.getAllPredecessorEdges()) { + if (edge.explicit) { + + if (!sharedcreated) { + newret = addSharedInitializerReturn(root); + sharedcreated = true; + } + + Statement source = edge.getSource(); List<Exprent> lstExpr = source.getExprents(); - if(lstExpr != null && !lstExpr.isEmpty()) { - Exprent expr = lstExpr.get(lstExpr.size() - 1); - if(expr.type == Exprent.EXPRENT_EXIT) { - ExitExprent ex = (ExitExprent)expr; - if(ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { - lstExpr.remove(lstExpr.size() - 1); - - source.removeSuccessor(edge); - source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top)); - - res = true; - } - } - } - } - } - - return res; - } - - private static Statement addSharedInitializerReturn(RootStatement root) { - - Statement exit = root.getDummyExit(); - Statement top = root.getFirst(); - - // build a new statement with the single instruction 'return' - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - - ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null, - ((MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret); - // a changeable list needed - bstat.setExprents(new ArrayList<Exprent>(Arrays.asList(new Exprent[]{retexpr}))); - - // build sequence to replace the former top statement - SequenceStatement seq = new SequenceStatement(Arrays.asList(new Statement[]{top, bstat})); - top.setParent(seq); - bstat.setParent(seq); - seq.setParent(root); - - root.getStats().removeWithKey(top.id); - root.getStats().addWithKeyAndIndex(0, seq, seq.id); - root.setFirst(seq); - - for(StatEdge succedge : top.getAllSuccessorEdges()) { - top.removeSuccessor(succedge); - } - - top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat)); - bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq)); - - return bstat; - } - - + if (lstExpr != null && !lstExpr.isEmpty()) { + Exprent expr = lstExpr.get(lstExpr.size() - 1); + if (expr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ex = (ExitExprent)expr; + if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { + lstExpr.remove(lstExpr.size() - 1); + + source.removeSuccessor(edge); + source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top)); + + res = true; + } + } + } + } + } + + return res; + } + + private static Statement addSharedInitializerReturn(RootStatement root) { + + Statement exit = root.getDummyExit(); + Statement top = root.getFirst(); + + // build a new statement with the single instruction 'return' + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + + ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null, + ((MethodDescriptor)DecompilerContext + .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret); + // a changeable list needed + bstat.setExprents(new ArrayList<Exprent>(Arrays.asList(new Exprent[]{retexpr}))); + + // build sequence to replace the former top statement + SequenceStatement seq = new SequenceStatement(Arrays.asList(new Statement[]{top, bstat})); + top.setParent(seq); + bstat.setParent(seq); + seq.setParent(root); + + root.getStats().removeWithKey(top.id); + root.getStats().addWithKeyAndIndex(0, seq, seq.id); + root.setFirst(seq); + + for (StatEdge succedge : top.getAllSuccessorEdges()) { + top.removeSuccessor(succedge); + } + + top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat)); + bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq)); + + return bstat; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 362e1a2..3b35d36 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -1,54 +1,31 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - 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.ArrayExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +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.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +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; @@ -61,826 +38,876 @@ 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>(); + 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); + } + } - 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 }; + 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 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 final VarType[] vartypes = new VarType[] { VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT }; + private 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 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 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 void initStatementExprents(Statement stat) { + stat.initExprents(); - 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 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 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 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 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 = ExprProcessor.buildJavaClassName(type.value); - if (getShort) { - ret = DecompilerContext.getImpcollector().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) { - StringBuffer buf = new StringBuffer(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 == true && 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" + edge.closure.id); - } - buf.append(";" + new_line_separator); - } - } - - if (buf.length() == 0 && semicolon) { - buf.append(InterpreterUtil.getIndentString(indent) + ";" + new_line_separator); - } - - return buf.toString(); - } - - public static String buildJavaClassName(String name) { - String res = name.replace('/', '.'); - - if (res.indexOf("$") >= 0) { // 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) { + 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 = ExprProcessor.buildJavaClassName(type.value); + if (getShort) { + ret = DecompilerContext.getImpcollector().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) { + StringBuffer buf = new StringBuffer(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 == true && 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" + edge.closure.id); + } + buf.append(";" + new_line_separator); + } + } + + if (buf.length() == 0 && semicolon) { + buf.append(InterpreterUtil.getIndentString(indent) + ";" + new_line_separator); + } + + return buf.toString(); + } + + public static String buildJavaClassName(String name) { + String res = name.replace('/', '.'); + + if (res.indexOf("$") >= 0) { // 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(); - - StringBuffer buf = new StringBuffer(); - - 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 (ExprProcessor.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 = "(" + ExprProcessor.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; - } + String indstr = InterpreterUtil.getIndentString(indent); + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + 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 (ExprProcessor.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 = "(" + ExprProcessor.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; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java index 468dc41..dafc655 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java @@ -1,46 +1,47 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.util.ListStack; public class ExprentStack extends ListStack<Exprent> { - - public ExprentStack() {} - - public ExprentStack(ListStack<Exprent> list) { - super(list); - pointer = list.getPointer(); - } - - public Exprent push(Exprent item) { - super.push(item); - - return item; - } - - public Exprent pop() { - - Exprent o = this.remove(--pointer); - - return o; - } - - public ExprentStack clone() { - return new ExprentStack(this); - } - + + public ExprentStack() { + } + + public ExprentStack(ListStack<Exprent> list) { + super(list); + pointer = list.getPointer(); + } + + public Exprent push(Exprent item) { + super.push(item); + + return item; + } + + public Exprent pop() { + + Exprent o = this.remove(--pointer); + + return o; + } + + public ExprentStack clone() { + return new ExprentStack(this); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 3631090..a56c0f2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -1,31 +1,21 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; - -import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.ConstantsUtil; -import org.jetbrains.java.decompiler.code.Instruction; -import org.jetbrains.java.decompiler.code.InstructionSequence; -import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; +import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; @@ -51,981 +41,1028 @@ import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.*; +import java.util.Map.Entry; + public class FinallyProcessor { - - - private HashMap<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>(); - private HashMap<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>(); - - private VarProcessor varprocessor; - - public FinallyProcessor(VarProcessor varprocessor) { - this.varprocessor = varprocessor; - } - - public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { -// return processStatement(mt, root, graph, root); - return processStatementEx(mt, root, graph); - } - - private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - - int bytecode_version = mt.getClassStruct().getBytecodeVersion(); - - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(root); - - while(!stack.isEmpty()) { - - Statement stat = stack.removeLast(); - - Statement parent = stat.getParent(); - if(parent != null && parent.type == Statement.TYPE_CATCHALL && - stat == parent.getFirst() && !parent.isCopied()) { - - CatchAllStatement fin = (CatchAllStatement)parent; - BasicBlock head = fin.getBasichead().getBlock(); - BasicBlock handler = fin.getHandler().getBasichead().getBlock(); - - if(catchallBlockIDs.containsKey(handler.id)) { - ; // do nothing - }else if(finallyBlockIDs.containsKey(handler.id)) { - - fin.setFinally(true); - - Integer var = finallyBlockIDs.get(handler.id); - fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); - - } else { - - Object[] inf = getFinallyInformation(mt, root, fin); - - if(inf == null) { // inconsistent finally - catchallBlockIDs.put(handler.id, null); - } else { - - if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { - finallyBlockIDs.put(handler.id, null); - } else { - - int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version); - - finallyBlockIDs.put(handler.id, varindex); - } - - DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally - DeadCodeHelper.removeEmptyBlocks(graph); - DeadCodeHelper.mergeBasicBlocks(graph); - } - - return true; - } - } - - stack.addAll(stat.getStats()); - } - - return false; - } - - - -// private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) { -// -// boolean res = false; -// -// for(int i=stat.getStats().size()-1;i>=0;i--) { -// if(processStatement(mt, root, graph, stat.getStats().get(i))) { -// return true; -// } -// } -// -// -// if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) { -// -// CatchAllStatement fin = (CatchAllStatement)stat; -// BasicBlock head = fin.getBasichead().getBlock(); -// BasicBlock handler = fin.getHandler().getBasichead().getBlock(); -// -// if(catchallBlockIDs.containsKey(handler.id)) { -// ; // do nothing -// }else if(finallyBlockIDs.containsKey(handler.id)) { -// -// fin.setFinally(true); -// -// Integer var = finallyBlockIDs.get(handler.id); -// fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); -// -// } else { -// -// Object[] inf = getFinallyInformation(mt, root, fin); -// -// if(inf == null) { // inconsistent finally -// catchallBlockIDs.put(handler.id, null); -// } else { -// -// if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { -// finallyBlockIDs.put(handler.id, null); -// } else { -// -// int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); -// insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf); -// -// finallyBlockIDs.put(handler.id, varindex); -// } -// -// DeadCodeHelper.removeEmptyBlocks(graph); -// DeadCodeHelper.mergeBasicBlocks(graph); -// } -// -// res = true; -// } -// } -// -// return res; -// } - - - private Object[] getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { - - HashMap<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>(); - - BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); - BasicBlock firstBasicBlock = firstBlockStatement.getBlock(); - Instruction instrFirst = firstBasicBlock.getInstruction(0); - - int firstcode = 0; - - switch(instrFirst.opcode) { - case CodeConstants.opc_pop: - firstcode = 1; - break; - case CodeConstants.opc_astore: - firstcode = 2; - } - - ExprProcessor proc = new ExprProcessor(); - proc.processStatement(root, mt.getClassStruct()); - - SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); - ssa.splitVariables(root, mt); - - List<Exprent> lstExprents = firstBlockStatement.getExprents(); - - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode==2?1:0)).getLeft()); - - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); - - LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); - stack.add(dgraph.first); - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - - while(!stack.isEmpty()) { - - DirectNode node = stack.removeFirst(); - - if(setVisited.contains(node)) { - continue; - } - setVisited.add(node); - - BasicBlockStatement blockStatement = null; - if(node.block != null) { - blockStatement = node.block; - } else if(node.preds.size() == 1) { - blockStatement = node.preds.get(0).block; - } - - boolean isTrueExit = true; - - if(firstcode != 1) { - - isTrueExit = false; - - for(int i=0;i<node.exprents.size();i++) { - Exprent exprent = node.exprents.get(i); - - if(firstcode == 0) { - List<Exprent> lst = exprent.getAllExprents(); - lst.add(exprent); - - boolean found = false; - for(Exprent expr : lst) { - if(expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) { - found = true; - break; - } - } - - if(found) { - found = false; - if(exprent.type == Exprent.EXPRENT_EXIT) { - ExitExprent exexpr = (ExitExprent)exprent; - if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) { - found = true; - } - } - - if(!found) { - return null; - } else { - isTrueExit = true; - } - } - } else if(firstcode == 2) { - // search for a load instruction - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent assexpr = (AssignmentExprent)exprent; - if(assexpr.getRight().type == Exprent.EXPRENT_VAR && - new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) { - - Exprent next = null; - if(i == node.exprents.size()-1) { - if(node.succs.size() == 1) { - DirectNode nd = node.succs.get(0); - if(!nd.exprents.isEmpty()) { - next = nd.exprents.get(0); - } - } - } else { - next = node.exprents.get(i+1); - } - - boolean found = false; - if(next != null && next.type == Exprent.EXPRENT_EXIT) { - ExitExprent exexpr = (ExitExprent)next; - if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR - && assexpr.getLeft().equals(exexpr.getValue())) { - found = true; - } - } - - if(!found) { - return null; - } else { - isTrueExit = true; - } - } - } - } - } - } - - // find finally exits - if(blockStatement != null && blockStatement.getBlock() != null) { - Statement handler = fstat.getHandler(); - for(StatEdge edge : blockStatement.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { - if(edge.getType() != StatEdge.TYPE_REGULAR && handler.containsStatement(blockStatement) - && !handler.containsStatement(edge.getDestination())) { - Boolean existingFlag = mapLast.get(blockStatement.getBlock()); - // note: the dummy node is also processed! - if(existingFlag == null || !existingFlag) { - mapLast.put(blockStatement.getBlock(), isTrueExit); - break; - } - } - } - } - - stack.addAll(node.succs); - } - - // empty finally block? - if(fstat.getHandler().type == Statement.TYPE_BASICBLOCK) { - - boolean isEmpty = false; - boolean isFirstLast = mapLast.containsKey(firstBasicBlock); - InstructionSequence seq = firstBasicBlock.getSeq(); - - switch(firstcode) { - case 0: - isEmpty = isFirstLast && seq.length() == 1; - break; - case 1: - isEmpty = seq.length() == 1; - break; - case 2: - isEmpty = isFirstLast?seq.length()==3:seq.length()==1; - } - - if(isEmpty) { - firstcode = 3; - } - } - - return new Object[] {firstcode, mapLast}; - } - - private void insertSemaphore(ControlFlowGraph graph, HashSet<BasicBlock> setTry, BasicBlock head, BasicBlock handler, int var, Object[] information, int bytecode_version) { - - HashSet<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry); - - int finallytype = (Integer)information[0]; - HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1]; - - // first and last statements - removeExceptionInstructionsEx(handler, 1, finallytype); - for(Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) { - BasicBlock last = entry.getKey(); - - if(entry.getValue()) { - removeExceptionInstructionsEx(last, 2, finallytype); - graph.getFinallyExits().add(last); - } - } - - // disable semaphore at statement exit points - for(BasicBlock block: setTry) { - - List<BasicBlock> lstSucc = block.getSuccs(); - for(BasicBlock dest : lstSucc) { - - // break out - if(!setCopy.contains(dest) && dest != graph.getLast()) { - // disable semaphore - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); - - // build a separate block - BasicBlock newblock = new BasicBlock(++graph.last_id); - newblock.setSeq(seq); - - // insert between block and dest - block.replaceSuccessor(dest, newblock); - newblock.addSuccessor(dest); - setCopy.add(newblock); - graph.getBlocks().addWithKey(newblock, newblock.id); - - // exception ranges - // FIXME: special case synchronized - - // copy exception edges and extend protected ranges - for(int j=0;j<block.getSuccExceptions().size();j++) { - BasicBlock hd = block.getSuccExceptions().get(j); - newblock.addSuccessorException(hd); - - ExceptionRangeCFG range = graph.getExceptionRange(hd, block); - range.getProtectedRange().add(newblock); - } - } - } - } - - // enable semaphor at the statement entrance - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); - - BasicBlock newhead = new BasicBlock(++graph.last_id); - newhead.setSeq(seq); - - insertBlockBefore(graph, head, newhead); - - // initialize semaphor with false - seq = new SimpleInstructionSequence(); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1); - seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1); - - BasicBlock newheadinit = new BasicBlock(++graph.last_id); - newheadinit.setSeq(seq); - - insertBlockBefore(graph, newhead, newheadinit); - - setCopy.add(newhead); - setCopy.add(newheadinit); - - for(BasicBlock hd: new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) { - ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit); - - if(setCopy.containsAll(range.getProtectedRange())) { - newheadinit.removeSuccessorException(hd); - range.getProtectedRange().remove(newheadinit); - } - } - - return; - } - - - private void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) { - - List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); - lstTemp.addAll(oldblock.getPreds()); - lstTemp.addAll(oldblock.getPredExceptions()); - - // replace predecessors - for(BasicBlock pred: lstTemp) { - pred.replaceSuccessor(oldblock, newblock); - } - - // copy exception edges and extend protected ranges - for(BasicBlock hd: oldblock.getSuccExceptions()) { - newblock.addSuccessorException(hd); - - ExceptionRangeCFG range = graph.getExceptionRange(hd, oldblock); - range.getProtectedRange().add(newblock); - } - - // replace handler - for(ExceptionRangeCFG range: graph.getExceptions()) { - if(range.getHandler() == oldblock) { - range.setHandler(newblock); - } - } - - newblock.addSuccessor(oldblock); - graph.getBlocks().addWithKey(newblock, newblock.id); - if(graph.getFirst() == oldblock) { - graph.setFirst(newblock); - } - - } - - private HashSet<BasicBlock> getAllBasicBlocks(Statement stat) { - - List<Statement> lst = new LinkedList<Statement>(); - lst.add(stat); - - int index = 0; - do { - Statement st = lst.get(index); - - if(st.type == Statement.TYPE_BASICBLOCK) { - index++; - } else { - lst.addAll(st.getStats()); - lst.remove(index); - } - } while(index<lst.size()); - - HashSet<BasicBlock> res = new HashSet<BasicBlock>(); - - for(Statement st: lst) { - res.add(((BasicBlockStatement)st).getBlock()); - } - - return res; - } - - - private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Object[] information) { - - HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst()); - HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler()); - - int finallytype = (Integer)information[0]; - HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1]; - - BasicBlock first = fstat.getHandler().getBasichead().getBlock(); - boolean skippedFirst = false; - - if(finallytype == 3) { - // empty finally - removeExceptionInstructionsEx(first, 3, finallytype); - - if(mapLast.containsKey(first)) { - graph.getFinallyExits().add(first); - } - - return true; - } else { - if(first.getSeq().length() == 1 && finallytype>0) { - BasicBlock firstsuc = first.getSuccs().get(0); - if(catchBlocks.contains(firstsuc)) { - first = firstsuc; - skippedFirst = true; - } - } - } - - // identify start blocks - HashSet<BasicBlock> startBlocks = new HashSet<BasicBlock>(); - for(BasicBlock block: tryBlocks) { - startBlocks.addAll(block.getSuccs()); - } - // throw in the try body will point directly to the dummy exit - // so remove dummy exit - startBlocks.remove(graph.getLast()); - startBlocks.removeAll(tryBlocks); - - List<Object[]> lstAreas = new ArrayList<Object[]>(); - - for(BasicBlock start: startBlocks) { - - Object[] arr = compareSubgraphsEx(graph, start, catchBlocks, first, finallytype, mapLast, skippedFirst); - if(arr == null) { - return false; - } - - lstAreas.add(new Object[] {start, arr[0], arr[1]}); - } - -// try { -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); -// } catch(Exception ex){ex.printStackTrace();} - - // delete areas - for(Object[] area: lstAreas) { - deleteArea(graph, area); - } - -// try { -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); -// } catch(Exception ex){ex.printStackTrace();} - - // INFO: empty basic blocks may remain in the graph! - for(Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) { - BasicBlock last = entry.getKey(); - - if(entry.getValue()) { - removeExceptionInstructionsEx(last, 2, finallytype); - graph.getFinallyExits().add(last); - } - } - - removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallytype); - - return true; - } - - private Object[] compareSubgraphsEx(ControlFlowGraph graph, BasicBlock startSample, HashSet<BasicBlock> catchBlocks, BasicBlock startCatch, - int finallytype, HashMap<BasicBlock, Boolean> mapLast, boolean skippedFirst) { - - class BlockStackEntry { - public BasicBlock blockCatch; - public BasicBlock blockSample; - - // TODO: correct handling (merging) of multiple paths - public List<int[]> lstStoreVars; - - public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) { - this.blockCatch = blockCatch; - this.blockSample = blockSample; - this.lstStoreVars = new ArrayList<int[]>(lstStoreVars); - } - } - - List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>(); - - HashSet<BasicBlock> setSample = new HashSet<BasicBlock>(); - - HashMap<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>(); - - stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>())); - - while(!stack.isEmpty()) { - - BlockStackEntry entry = stack.remove(0); - BasicBlock blockCatch = entry.blockCatch; - BasicBlock blockSample = entry.blockSample; - - boolean isFirstBlock = !skippedFirst && blockCatch == startCatch; - boolean isLastBlock = mapLast.containsKey(blockCatch); - boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch); - - if(!compareBasicBlocksEx(graph, blockCatch, blockSample, (isFirstBlock?1:0) | (isTrueLastBlock?2:0), finallytype, entry.lstStoreVars)) { - return null; - } - - if(blockSample.getSuccs().size() != blockCatch.getSuccs().size()) { - return null; - } - - setSample.add(blockSample); - - // direct successors - for(int i=0;i<blockCatch.getSuccs().size();i++) { - BasicBlock sucCatch = blockCatch.getSuccs().get(i); - BasicBlock sucSample = blockSample.getSuccs().get(i); - - if(catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) { - stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars)); - } - } - - - // exception successors - if(isLastBlock && blockSample.getSeq().isEmpty()) { - ; // do nothing, blockSample will be removed anyway - } else { - if(blockCatch.getSuccExceptions().size() == blockSample.getSuccExceptions().size()) { - for(int i=0;i<blockCatch.getSuccExceptions().size();i++) { - BasicBlock sucCatch = blockCatch.getSuccExceptions().get(i); - BasicBlock sucSample = blockSample.getSuccExceptions().get(i); - - String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString(); - String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString(); - - // FIXME: compare handlers if possible - boolean equalexc = excCatch == null?excSample == null:excCatch.equals(excSample); - - if(equalexc) { - if(catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) { - - List<int[]> lst = entry.lstStoreVars; - - if(sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) { - Instruction instrCatch = sucCatch.getSeq().getInstr(0); - Instruction instrSample = sucSample.getSeq().getInstr(0); - - if(instrCatch.opcode == CodeConstants.opc_astore && - instrSample.opcode == CodeConstants.opc_astore) { - lst = new ArrayList<int[]>(lst); - lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)}); - } - } - - stack.add(new BlockStackEntry(sucCatch, sucSample, lst)); - } - } else { - return null; - } - } - } else { - return null; - } - } - - if(isLastBlock) { - HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs()); - setSuccs.removeAll(setSample); - - for(BlockStackEntry stackent : stack) { - setSuccs.remove(stackent.blockSample); - } - - for(BasicBlock succ : setSuccs) { - if(graph.getLast() != succ) { // FIXME: why? - mapNext.put(blockSample.id+"#"+succ.id, new BasicBlock[] {blockSample, succ, isTrueLastBlock?succ:null}); - } - } - } - } - - return new Object[]{setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values()))}; - } - - private BasicBlock getUniqueNext(ControlFlowGraph graph, HashSet<BasicBlock[]> setNext) { - - // precondition: there is at most one true exit path in a finally statement - - BasicBlock next = null; - boolean multiple = false; - - for(BasicBlock[] arr : setNext) { - - if(arr[2] != null) { - next = arr[1]; - multiple = false; - break; - } else { - if(next == null) { - next = arr[1]; - } else if(next != arr[1]) { - multiple = true; - } - - if(arr[1].getPreds().size() == 1) { - next = arr[1]; - } - } - } - - if(multiple) { // TODO: generic solution - for(BasicBlock[] arr : setNext) { - BasicBlock block = arr[1]; - - if(block != next) { - if(InterpreterUtil.equalSets(next.getSuccs(), block.getSuccs())) { - InstructionSequence seqNext = next.getSeq(); - InstructionSequence seqBlock = block.getSeq(); - - if(seqNext.length() == seqBlock.length()) { - for(int i=0;i<seqNext.length();i++) { - Instruction instrNext = seqNext.getInstr(i); - Instruction instrBlock = seqBlock.getInstr(i); - - if(instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide - || instrNext.operandsCount() != instrBlock.operandsCount()) { - return null; - } - - for(int j=0;i<instrNext.getOperands().length;j++) { - if(instrNext.getOperand(j) != instrBlock.getOperand(j)) { - return null; - } - } - } - } else { - return null; - } - } else { - return null; - } - } - } - -// try { -// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); -// } catch(IOException ex) { -// ex.printStackTrace(); -// } - - for(BasicBlock[] arr : setNext) { - if(arr[1] != next) { - // FIXME: exception edge possible? - arr[0].removeSuccessor(arr[1]); - arr[0].addSuccessor(next); - } - } - - DeadCodeHelper.removeDeadBlocks(graph); - - } - - return next; - } - - private boolean compareBasicBlocksEx(ControlFlowGraph graph, BasicBlock pattern, BasicBlock sample, int type, int finallytype, List<int[]> lstStoreVars) { - - InstructionSequence seqPattern = pattern.getSeq(); - InstructionSequence seqSample = sample.getSeq(); - - if(type!=0) { - seqPattern = seqPattern.clone(); - - if((type & 1) > 0) { // first - if(finallytype > 0) { - seqPattern.removeInstruction(0); - } - } - - if((type & 2) > 0) { // last - if(finallytype == 0 || finallytype == 2) { - seqPattern.removeInstruction(seqPattern.length()-1); - } - - if(finallytype == 2) { - seqPattern.removeInstruction(seqPattern.length()-1); - } - } - } - - if(seqPattern.length() > seqSample.length()) { - return false; - } - - for(int i=0;i<seqPattern.length();i++) { - Instruction instrPattern = seqPattern.getInstr(i); - Instruction instrSample = seqSample.getInstr(i); - - // compare instructions with respect to jumps - if(!equalInstructions(instrPattern, instrSample, lstStoreVars)) { - return false; - } - } - - if(seqPattern.length() < seqSample.length()) { // split in two blocks - - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - for(int i=seqSample.length()-1;i>=seqPattern.length();i--) { - seq.addInstruction(0, seqSample.getInstr(i), -1); - seqSample.removeInstruction(i); - } - - BasicBlock newblock = new BasicBlock(++graph.last_id); - newblock.setSeq(seq); - - List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); - lstTemp.addAll(sample.getSuccs()); - - // move successors - for(BasicBlock suc: lstTemp) { - sample.removeSuccessor(suc); - newblock.addSuccessor(suc); - } - - sample.addSuccessor(newblock); - - graph.getBlocks().addWithKey(newblock, newblock.id); - - HashSet<BasicBlock> setFinallyExits = graph.getFinallyExits(); - if(setFinallyExits.contains(sample)) { - setFinallyExits.remove(sample); - setFinallyExits.add(newblock); - } - - // copy exception edges and extend protected ranges - for(int j=0;j<sample.getSuccExceptions().size();j++) { - BasicBlock hd = sample.getSuccExceptions().get(j); - newblock.addSuccessorException(hd); - - ExceptionRangeCFG range = graph.getExceptionRange(hd, sample); - range.getProtectedRange().add(newblock); - } - } - - return true; - } - - public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) { - if(first.opcode != second.opcode || first.wide != second.wide - || first.operandsCount() != second.operandsCount()) { - return false; - } - - if(first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison - for(int i=0;i<first.getOperands().length;i++) { - - int firstOp = first.getOperand(i); - int secondOp = second.getOperand(i); - - if(firstOp != secondOp) { - - // a-load/store instructions - if(first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) { - for(int[] arr : lstStoreVars) { - if(arr[0] == firstOp && arr[1] == secondOp) { - return true; - } - } - } - - return false; - } - } - } - - return true; - } - - private void deleteArea(ControlFlowGraph graph, Object[] area) { - - BasicBlock start = (BasicBlock)area[0]; - BasicBlock next = (BasicBlock)area[2]; - - if(start == next) { - return; - } - - if(next == null) { - // dummy exit block - next = graph.getLast(); - } - - // collect common exception ranges of predecessors and successors - HashSet<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions()); - for(BasicBlock pred : start.getPreds()) { - setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); - } - - boolean is_outside_range = false; - - HashSet<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds()); - - // replace start with next - for(BasicBlock pred: setPredecessors) { - pred.replaceSuccessor(start, next); - } - - HashSet<BasicBlock> setBlocks = (HashSet<BasicBlock>)area[1]; - - HashSet<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null; - - // remove all the blocks inbetween - for(BasicBlock block: setBlocks) { - - // artificial basic blocks (those resulted from splitting) - // can belong to more than one area - if(graph.getBlocks().containsKey(block.id)) { - - if(!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) { - is_outside_range = true; - } - - HashSet<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>(); - for(BasicBlock handler : block.getSuccExceptions()) { - setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); - } - - if(setCommonRemovedExceptionRanges == null) { - setCommonRemovedExceptionRanges = setRemovedExceptionRanges; - } else { - setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges); - } - - // shift extern edges on splitted blocks - if(block.getSeq().isEmpty() && block.getSuccs().size() == 1) { - BasicBlock succs = block.getSuccs().get(0); - for(BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) { - if(!setBlocks.contains(pred)) { - pred.replaceSuccessor(block, succs); - } - } - - if(graph.getFirst() == block) { - graph.setFirst(succs); - } - } - - graph.removeBlock(block); - } - } - - if(is_outside_range) { - - // new empty block - BasicBlock emptyblock = new BasicBlock(++graph.last_id); - emptyblock.setSeq(new SimpleInstructionSequence()); - graph.getBlocks().addWithKey(emptyblock, emptyblock.id); - - // add to ranges if necessary - if(setCommonRemovedExceptionRanges != null) { - for(ExceptionRangeCFG range : setCommonRemovedExceptionRanges) { - emptyblock.addSuccessorException(range.getHandler()); - range.getProtectedRange().add(emptyblock); - } - } - - // insert between predecessors and next - emptyblock.addSuccessor(next); - for(BasicBlock pred: setPredecessors) { - pred.replaceSuccessor(next, emptyblock); - } - } - } - - private void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) { - - InstructionSequence seq = block.getSeq(); - - if(finallytype == 3) { // empty finally handler - for(int i=seq.length()-1;i>=0;i--) { - seq.removeInstruction(i); - } - - } else { - if((blocktype & 1) > 0) { // first - if(finallytype == 2 || finallytype == 1) { // astore or pop - seq.removeInstruction(0); - } - } - - if((blocktype & 2) > 0) { // last - if(finallytype == 2 || finallytype == 0) { - seq.removeInstruction(seq.length()-1); - } - - if(finallytype == 2) { // astore - seq.removeInstruction(seq.length()-1); - } - } - } - } - + + + private HashMap<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>(); + private HashMap<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>(); + + private VarProcessor varprocessor; + + public FinallyProcessor(VarProcessor varprocessor) { + this.varprocessor = varprocessor; + } + + public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { + // return processStatement(mt, root, graph, root); + return processStatementEx(mt, root, graph); + } + + private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { + + int bytecode_version = mt.getClassStruct().getBytecodeVersion(); + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while (!stack.isEmpty()) { + + Statement stat = stack.removeLast(); + + Statement parent = stat.getParent(); + if (parent != null && parent.type == Statement.TYPE_CATCHALL && + stat == parent.getFirst() && !parent.isCopied()) { + + CatchAllStatement fin = (CatchAllStatement)parent; + BasicBlock head = fin.getBasichead().getBlock(); + BasicBlock handler = fin.getHandler().getBasichead().getBlock(); + + if (catchallBlockIDs.containsKey(handler.id)) { + ; // do nothing + } + else if (finallyBlockIDs.containsKey(handler.id)) { + + fin.setFinally(true); + + Integer var = finallyBlockIDs.get(handler.id); + fin.setMonitor(var == null ? null : new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); + } + else { + + Object[] inf = getFinallyInformation(mt, root, fin); + + if (inf == null) { // inconsistent finally + catchallBlockIDs.put(handler.id, null); + } + else { + + if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { + finallyBlockIDs.put(handler.id, null); + } + else { + + int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version); + + finallyBlockIDs.put(handler.id, varindex); + } + + DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally + DeadCodeHelper.removeEmptyBlocks(graph); + DeadCodeHelper.mergeBasicBlocks(graph); + } + + return true; + } + } + + stack.addAll(stat.getStats()); + } + + return false; + } + + + // private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) { + // + // boolean res = false; + // + // for(int i=stat.getStats().size()-1;i>=0;i--) { + // if(processStatement(mt, root, graph, stat.getStats().get(i))) { + // return true; + // } + // } + // + // + // if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) { + // + // CatchAllStatement fin = (CatchAllStatement)stat; + // BasicBlock head = fin.getBasichead().getBlock(); + // BasicBlock handler = fin.getHandler().getBasichead().getBlock(); + // + // if(catchallBlockIDs.containsKey(handler.id)) { + // ; // do nothing + // }else if(finallyBlockIDs.containsKey(handler.id)) { + // + // fin.setFinally(true); + // + // Integer var = finallyBlockIDs.get(handler.id); + // fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); + // + // } else { + // + // Object[] inf = getFinallyInformation(mt, root, fin); + // + // if(inf == null) { // inconsistent finally + // catchallBlockIDs.put(handler.id, null); + // } else { + // + // if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { + // finallyBlockIDs.put(handler.id, null); + // } else { + // + // int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + // insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf); + // + // finallyBlockIDs.put(handler.id, varindex); + // } + // + // DeadCodeHelper.removeEmptyBlocks(graph); + // DeadCodeHelper.mergeBasicBlocks(graph); + // } + // + // res = true; + // } + // } + // + // return res; + // } + + + private Object[] getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { + + HashMap<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>(); + + BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); + BasicBlock firstBasicBlock = firstBlockStatement.getBlock(); + Instruction instrFirst = firstBasicBlock.getInstruction(0); + + int firstcode = 0; + + switch (instrFirst.opcode) { + case CodeConstants.opc_pop: + firstcode = 1; + break; + case CodeConstants.opc_astore: + firstcode = 2; + } + + ExprProcessor proc = new ExprProcessor(); + proc.processStatement(root, mt.getClassStruct()); + + SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); + ssa.splitVariables(root, mt); + + List<Exprent> lstExprents = firstBlockStatement.getExprents(); + + VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft()); + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + stack.add(dgraph.first); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + while (!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if (setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + BasicBlockStatement blockStatement = null; + if (node.block != null) { + blockStatement = node.block; + } + else if (node.preds.size() == 1) { + blockStatement = node.preds.get(0).block; + } + + boolean isTrueExit = true; + + if (firstcode != 1) { + + isTrueExit = false; + + for (int i = 0; i < node.exprents.size(); i++) { + Exprent exprent = node.exprents.get(i); + + if (firstcode == 0) { + List<Exprent> lst = exprent.getAllExprents(); + lst.add(exprent); + + boolean found = false; + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) { + found = true; + break; + } + } + + if (found) { + found = false; + if (exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exexpr = (ExitExprent)exprent; + if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) { + found = true; + } + } + + if (!found) { + return null; + } + else { + isTrueExit = true; + } + } + } + else if (firstcode == 2) { + // search for a load instruction + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent assexpr = (AssignmentExprent)exprent; + if (assexpr.getRight().type == Exprent.EXPRENT_VAR && + new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) { + + Exprent next = null; + if (i == node.exprents.size() - 1) { + if (node.succs.size() == 1) { + DirectNode nd = node.succs.get(0); + if (!nd.exprents.isEmpty()) { + next = nd.exprents.get(0); + } + } + } + else { + next = node.exprents.get(i + 1); + } + + boolean found = false; + if (next != null && next.type == Exprent.EXPRENT_EXIT) { + ExitExprent exexpr = (ExitExprent)next; + if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR + && assexpr.getLeft().equals(exexpr.getValue())) { + found = true; + } + } + + if (!found) { + return null; + } + else { + isTrueExit = true; + } + } + } + } + } + } + + // find finally exits + if (blockStatement != null && blockStatement.getBlock() != null) { + Statement handler = fstat.getHandler(); + for (StatEdge edge : blockStatement.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + if (edge.getType() != StatEdge.TYPE_REGULAR && handler.containsStatement(blockStatement) + && !handler.containsStatement(edge.getDestination())) { + Boolean existingFlag = mapLast.get(blockStatement.getBlock()); + // note: the dummy node is also processed! + if (existingFlag == null || !existingFlag) { + mapLast.put(blockStatement.getBlock(), isTrueExit); + break; + } + } + } + } + + stack.addAll(node.succs); + } + + // empty finally block? + if (fstat.getHandler().type == Statement.TYPE_BASICBLOCK) { + + boolean isEmpty = false; + boolean isFirstLast = mapLast.containsKey(firstBasicBlock); + InstructionSequence seq = firstBasicBlock.getSeq(); + + switch (firstcode) { + case 0: + isEmpty = isFirstLast && seq.length() == 1; + break; + case 1: + isEmpty = seq.length() == 1; + break; + case 2: + isEmpty = isFirstLast ? seq.length() == 3 : seq.length() == 1; + } + + if (isEmpty) { + firstcode = 3; + } + } + + return new Object[]{firstcode, mapLast}; + } + + private void insertSemaphore(ControlFlowGraph graph, + HashSet<BasicBlock> setTry, + BasicBlock head, + BasicBlock handler, + int var, + Object[] information, + int bytecode_version) { + + HashSet<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry); + + int finallytype = (Integer)information[0]; + HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1]; + + // first and last statements + removeExceptionInstructionsEx(handler, 1, finallytype); + for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) { + BasicBlock last = entry.getKey(); + + if (entry.getValue()) { + removeExceptionInstructionsEx(last, 2, finallytype); + graph.getFinallyExits().add(last); + } + } + + // disable semaphore at statement exit points + for (BasicBlock block : setTry) { + + List<BasicBlock> lstSucc = block.getSuccs(); + for (BasicBlock dest : lstSucc) { + + // break out + if (!setCopy.contains(dest) && dest != graph.getLast()) { + // disable semaphore + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + + seq.addInstruction(ConstantsUtil + .getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, + new int[]{0}), -1); + seq.addInstruction(ConstantsUtil + .getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, + new int[]{var}), -1); + + // build a separate block + BasicBlock newblock = new BasicBlock(++graph.last_id); + newblock.setSeq(seq); + + // insert between block and dest + block.replaceSuccessor(dest, newblock); + newblock.addSuccessor(dest); + setCopy.add(newblock); + graph.getBlocks().addWithKey(newblock, newblock.id); + + // exception ranges + // FIXME: special case synchronized + + // copy exception edges and extend protected ranges + for (int j = 0; j < block.getSuccExceptions().size(); j++) { + BasicBlock hd = block.getSuccExceptions().get(j); + newblock.addSuccessorException(hd); + + ExceptionRangeCFG range = graph.getExceptionRange(hd, block); + range.getProtectedRange().add(newblock); + } + } + } + } + + // enable semaphor at the statement entrance + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + seq.addInstruction( + ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), + -1); + seq.addInstruction( + ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), + -1); + + BasicBlock newhead = new BasicBlock(++graph.last_id); + newhead.setSeq(seq); + + insertBlockBefore(graph, head, newhead); + + // initialize semaphor with false + seq = new SimpleInstructionSequence(); + seq.addInstruction( + ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), + -1); + seq.addInstruction( + ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), + -1); + + BasicBlock newheadinit = new BasicBlock(++graph.last_id); + newheadinit.setSeq(seq); + + insertBlockBefore(graph, newhead, newheadinit); + + setCopy.add(newhead); + setCopy.add(newheadinit); + + for (BasicBlock hd : new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) { + ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit); + + if (setCopy.containsAll(range.getProtectedRange())) { + newheadinit.removeSuccessorException(hd); + range.getProtectedRange().remove(newheadinit); + } + } + + return; + } + + + private void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) { + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + lstTemp.addAll(oldblock.getPreds()); + lstTemp.addAll(oldblock.getPredExceptions()); + + // replace predecessors + for (BasicBlock pred : lstTemp) { + pred.replaceSuccessor(oldblock, newblock); + } + + // copy exception edges and extend protected ranges + for (BasicBlock hd : oldblock.getSuccExceptions()) { + newblock.addSuccessorException(hd); + + ExceptionRangeCFG range = graph.getExceptionRange(hd, oldblock); + range.getProtectedRange().add(newblock); + } + + // replace handler + for (ExceptionRangeCFG range : graph.getExceptions()) { + if (range.getHandler() == oldblock) { + range.setHandler(newblock); + } + } + + newblock.addSuccessor(oldblock); + graph.getBlocks().addWithKey(newblock, newblock.id); + if (graph.getFirst() == oldblock) { + graph.setFirst(newblock); + } + } + + private HashSet<BasicBlock> getAllBasicBlocks(Statement stat) { + + List<Statement> lst = new LinkedList<Statement>(); + lst.add(stat); + + int index = 0; + do { + Statement st = lst.get(index); + + if (st.type == Statement.TYPE_BASICBLOCK) { + index++; + } + else { + lst.addAll(st.getStats()); + lst.remove(index); + } + } + while (index < lst.size()); + + HashSet<BasicBlock> res = new HashSet<BasicBlock>(); + + for (Statement st : lst) { + res.add(((BasicBlockStatement)st).getBlock()); + } + + return res; + } + + + private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Object[] information) { + + HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst()); + HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler()); + + int finallytype = (Integer)information[0]; + HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1]; + + BasicBlock first = fstat.getHandler().getBasichead().getBlock(); + boolean skippedFirst = false; + + if (finallytype == 3) { + // empty finally + removeExceptionInstructionsEx(first, 3, finallytype); + + if (mapLast.containsKey(first)) { + graph.getFinallyExits().add(first); + } + + return true; + } + else { + if (first.getSeq().length() == 1 && finallytype > 0) { + BasicBlock firstsuc = first.getSuccs().get(0); + if (catchBlocks.contains(firstsuc)) { + first = firstsuc; + skippedFirst = true; + } + } + } + + // identify start blocks + HashSet<BasicBlock> startBlocks = new HashSet<BasicBlock>(); + for (BasicBlock block : tryBlocks) { + startBlocks.addAll(block.getSuccs()); + } + // throw in the try body will point directly to the dummy exit + // so remove dummy exit + startBlocks.remove(graph.getLast()); + startBlocks.removeAll(tryBlocks); + + List<Object[]> lstAreas = new ArrayList<Object[]>(); + + for (BasicBlock start : startBlocks) { + + Object[] arr = compareSubgraphsEx(graph, start, catchBlocks, first, finallytype, mapLast, skippedFirst); + if (arr == null) { + return false; + } + + lstAreas.add(new Object[]{start, arr[0], arr[1]}); + } + + // try { + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); + // } catch(Exception ex){ex.printStackTrace();} + + // delete areas + for (Object[] area : lstAreas) { + deleteArea(graph, area); + } + + // try { + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); + // } catch(Exception ex){ex.printStackTrace();} + + // INFO: empty basic blocks may remain in the graph! + for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) { + BasicBlock last = entry.getKey(); + + if (entry.getValue()) { + removeExceptionInstructionsEx(last, 2, finallytype); + graph.getFinallyExits().add(last); + } + } + + removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallytype); + + return true; + } + + private Object[] compareSubgraphsEx(ControlFlowGraph graph, + BasicBlock startSample, + HashSet<BasicBlock> catchBlocks, + BasicBlock startCatch, + int finallytype, + HashMap<BasicBlock, Boolean> mapLast, + boolean skippedFirst) { + + class BlockStackEntry { + public BasicBlock blockCatch; + public BasicBlock blockSample; + + // TODO: correct handling (merging) of multiple paths + public List<int[]> lstStoreVars; + + public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) { + this.blockCatch = blockCatch; + this.blockSample = blockSample; + this.lstStoreVars = new ArrayList<int[]>(lstStoreVars); + } + } + + List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>(); + + HashSet<BasicBlock> setSample = new HashSet<BasicBlock>(); + + HashMap<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>(); + + stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>())); + + while (!stack.isEmpty()) { + + BlockStackEntry entry = stack.remove(0); + BasicBlock blockCatch = entry.blockCatch; + BasicBlock blockSample = entry.blockSample; + + boolean isFirstBlock = !skippedFirst && blockCatch == startCatch; + boolean isLastBlock = mapLast.containsKey(blockCatch); + boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch); + + if (!compareBasicBlocksEx(graph, blockCatch, blockSample, (isFirstBlock ? 1 : 0) | (isTrueLastBlock ? 2 : 0), finallytype, + entry.lstStoreVars)) { + return null; + } + + if (blockSample.getSuccs().size() != blockCatch.getSuccs().size()) { + return null; + } + + setSample.add(blockSample); + + // direct successors + for (int i = 0; i < blockCatch.getSuccs().size(); i++) { + BasicBlock sucCatch = blockCatch.getSuccs().get(i); + BasicBlock sucSample = blockSample.getSuccs().get(i); + + if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) { + stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars)); + } + } + + + // exception successors + if (isLastBlock && blockSample.getSeq().isEmpty()) { + ; // do nothing, blockSample will be removed anyway + } + else { + if (blockCatch.getSuccExceptions().size() == blockSample.getSuccExceptions().size()) { + for (int i = 0; i < blockCatch.getSuccExceptions().size(); i++) { + BasicBlock sucCatch = blockCatch.getSuccExceptions().get(i); + BasicBlock sucSample = blockSample.getSuccExceptions().get(i); + + String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString(); + String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString(); + + // FIXME: compare handlers if possible + boolean equalexc = excCatch == null ? excSample == null : excCatch.equals(excSample); + + if (equalexc) { + if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) { + + List<int[]> lst = entry.lstStoreVars; + + if (sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) { + Instruction instrCatch = sucCatch.getSeq().getInstr(0); + Instruction instrSample = sucSample.getSeq().getInstr(0); + + if (instrCatch.opcode == CodeConstants.opc_astore && + instrSample.opcode == CodeConstants.opc_astore) { + lst = new ArrayList<int[]>(lst); + lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)}); + } + } + + stack.add(new BlockStackEntry(sucCatch, sucSample, lst)); + } + } + else { + return null; + } + } + } + else { + return null; + } + } + + if (isLastBlock) { + HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs()); + setSuccs.removeAll(setSample); + + for (BlockStackEntry stackent : stack) { + setSuccs.remove(stackent.blockSample); + } + + for (BasicBlock succ : setSuccs) { + if (graph.getLast() != succ) { // FIXME: why? + mapNext.put(blockSample.id + "#" + succ.id, new BasicBlock[]{blockSample, succ, isTrueLastBlock ? succ : null}); + } + } + } + } + + return new Object[]{setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values()))}; + } + + private BasicBlock getUniqueNext(ControlFlowGraph graph, HashSet<BasicBlock[]> setNext) { + + // precondition: there is at most one true exit path in a finally statement + + BasicBlock next = null; + boolean multiple = false; + + for (BasicBlock[] arr : setNext) { + + if (arr[2] != null) { + next = arr[1]; + multiple = false; + break; + } + else { + if (next == null) { + next = arr[1]; + } + else if (next != arr[1]) { + multiple = true; + } + + if (arr[1].getPreds().size() == 1) { + next = arr[1]; + } + } + } + + if (multiple) { // TODO: generic solution + for (BasicBlock[] arr : setNext) { + BasicBlock block = arr[1]; + + if (block != next) { + if (InterpreterUtil.equalSets(next.getSuccs(), block.getSuccs())) { + InstructionSequence seqNext = next.getSeq(); + InstructionSequence seqBlock = block.getSeq(); + + if (seqNext.length() == seqBlock.length()) { + for (int i = 0; i < seqNext.length(); i++) { + Instruction instrNext = seqNext.getInstr(i); + Instruction instrBlock = seqBlock.getInstr(i); + + if (instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide + || instrNext.operandsCount() != instrBlock.operandsCount()) { + return null; + } + + for (int j = 0; i < instrNext.getOperands().length; j++) { + if (instrNext.getOperand(j) != instrBlock.getOperand(j)) { + return null; + } + } + } + } + else { + return null; + } + } + else { + return null; + } + } + } + + // try { + // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); + // } catch(IOException ex) { + // ex.printStackTrace(); + // } + + for (BasicBlock[] arr : setNext) { + if (arr[1] != next) { + // FIXME: exception edge possible? + arr[0].removeSuccessor(arr[1]); + arr[0].addSuccessor(next); + } + } + + DeadCodeHelper.removeDeadBlocks(graph); + } + + return next; + } + + private boolean compareBasicBlocksEx(ControlFlowGraph graph, + BasicBlock pattern, + BasicBlock sample, + int type, + int finallytype, + List<int[]> lstStoreVars) { + + InstructionSequence seqPattern = pattern.getSeq(); + InstructionSequence seqSample = sample.getSeq(); + + if (type != 0) { + seqPattern = seqPattern.clone(); + + if ((type & 1) > 0) { // first + if (finallytype > 0) { + seqPattern.removeInstruction(0); + } + } + + if ((type & 2) > 0) { // last + if (finallytype == 0 || finallytype == 2) { + seqPattern.removeInstruction(seqPattern.length() - 1); + } + + if (finallytype == 2) { + seqPattern.removeInstruction(seqPattern.length() - 1); + } + } + } + + if (seqPattern.length() > seqSample.length()) { + return false; + } + + for (int i = 0; i < seqPattern.length(); i++) { + Instruction instrPattern = seqPattern.getInstr(i); + Instruction instrSample = seqSample.getInstr(i); + + // compare instructions with respect to jumps + if (!equalInstructions(instrPattern, instrSample, lstStoreVars)) { + return false; + } + } + + if (seqPattern.length() < seqSample.length()) { // split in two blocks + + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) { + seq.addInstruction(0, seqSample.getInstr(i), -1); + seqSample.removeInstruction(i); + } + + BasicBlock newblock = new BasicBlock(++graph.last_id); + newblock.setSeq(seq); + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + lstTemp.addAll(sample.getSuccs()); + + // move successors + for (BasicBlock suc : lstTemp) { + sample.removeSuccessor(suc); + newblock.addSuccessor(suc); + } + + sample.addSuccessor(newblock); + + graph.getBlocks().addWithKey(newblock, newblock.id); + + HashSet<BasicBlock> setFinallyExits = graph.getFinallyExits(); + if (setFinallyExits.contains(sample)) { + setFinallyExits.remove(sample); + setFinallyExits.add(newblock); + } + + // copy exception edges and extend protected ranges + for (int j = 0; j < sample.getSuccExceptions().size(); j++) { + BasicBlock hd = sample.getSuccExceptions().get(j); + newblock.addSuccessorException(hd); + + ExceptionRangeCFG range = graph.getExceptionRange(hd, sample); + range.getProtectedRange().add(newblock); + } + } + + return true; + } + + public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) { + if (first.opcode != second.opcode || first.wide != second.wide + || first.operandsCount() != second.operandsCount()) { + return false; + } + + if (first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison + for (int i = 0; i < first.getOperands().length; i++) { + + int firstOp = first.getOperand(i); + int secondOp = second.getOperand(i); + + if (firstOp != secondOp) { + + // a-load/store instructions + if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) { + for (int[] arr : lstStoreVars) { + if (arr[0] == firstOp && arr[1] == secondOp) { + return true; + } + } + } + + return false; + } + } + } + + return true; + } + + private void deleteArea(ControlFlowGraph graph, Object[] area) { + + BasicBlock start = (BasicBlock)area[0]; + BasicBlock next = (BasicBlock)area[2]; + + if (start == next) { + return; + } + + if (next == null) { + // dummy exit block + next = graph.getLast(); + } + + // collect common exception ranges of predecessors and successors + HashSet<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions()); + for (BasicBlock pred : start.getPreds()) { + setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); + } + + boolean is_outside_range = false; + + HashSet<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds()); + + // replace start with next + for (BasicBlock pred : setPredecessors) { + pred.replaceSuccessor(start, next); + } + + HashSet<BasicBlock> setBlocks = (HashSet<BasicBlock>)area[1]; + + HashSet<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null; + + // remove all the blocks inbetween + for (BasicBlock block : setBlocks) { + + // artificial basic blocks (those resulted from splitting) + // can belong to more than one area + if (graph.getBlocks().containsKey(block.id)) { + + if (!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) { + is_outside_range = true; + } + + HashSet<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>(); + for (BasicBlock handler : block.getSuccExceptions()) { + setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); + } + + if (setCommonRemovedExceptionRanges == null) { + setCommonRemovedExceptionRanges = setRemovedExceptionRanges; + } + else { + setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges); + } + + // shift extern edges on splitted blocks + if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) { + BasicBlock succs = block.getSuccs().get(0); + for (BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) { + if (!setBlocks.contains(pred)) { + pred.replaceSuccessor(block, succs); + } + } + + if (graph.getFirst() == block) { + graph.setFirst(succs); + } + } + + graph.removeBlock(block); + } + } + + if (is_outside_range) { + + // new empty block + BasicBlock emptyblock = new BasicBlock(++graph.last_id); + emptyblock.setSeq(new SimpleInstructionSequence()); + graph.getBlocks().addWithKey(emptyblock, emptyblock.id); + + // add to ranges if necessary + if (setCommonRemovedExceptionRanges != null) { + for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) { + emptyblock.addSuccessorException(range.getHandler()); + range.getProtectedRange().add(emptyblock); + } + } + + // insert between predecessors and next + emptyblock.addSuccessor(next); + for (BasicBlock pred : setPredecessors) { + pred.replaceSuccessor(next, emptyblock); + } + } + } + + private void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) { + + InstructionSequence seq = block.getSeq(); + + if (finallytype == 3) { // empty finally handler + for (int i = seq.length() - 1; i >= 0; i--) { + seq.removeInstruction(i); + } + } + else { + if ((blocktype & 1) > 0) { // first + if (finallytype == 2 || finallytype == 1) { // astore or pop + seq.removeInstruction(0); + } + } + + if ((blocktype & 2) > 0) { // last + if (finallytype == 2 || finallytype == 0) { + seq.removeInstruction(seq.length() - 1); + } + + if (finallytype == 2) { // astore + seq.removeInstruction(seq.length() - 1); + } + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java index d11753e..0af6de0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java @@ -1,28 +1,22 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; @@ -33,288 +27,310 @@ import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.List; + public class IdeaNotNullHelper { - - public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { - - boolean checks_removed = false; - - // parameter @NotNull annotations - while(findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check. - checks_removed = true; - } - - // method @NotNull annotation - while(findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check. - checks_removed = true; - } - - return checks_removed; - } - - private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) { - - Statement st = stat.getFirst(); - while(st.type == Statement.TYPE_SEQUENCE) { - st = st.getFirst(); - } - - if(st.type == Statement.TYPE_IF) { - - IfStatement ifstat = (IfStatement)st; - Statement ifbranch = ifstat.getIfstat(); - - Exprent if_condition = ifstat.getHeadexprent().getCondition(); - - boolean is_notnull_check = false; - - // TODO: FUNCTION_NE also possible if reversed order (in theory) - if(ifbranch != null && if_condition.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ && - ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { - - FunctionExprent func = (FunctionExprent)if_condition; - Exprent first_param = func.getLstOperands().get(0); - Exprent second_param = func.getLstOperands().get(1); - - if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order - if(first_param.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)first_param; - - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); - - // parameter annotations - StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); - if(param_annotations != null) { - - List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations(); - int method_param_number = md.params.length; - - int index = thisvar ? 1 : 0; - for (int i = 0; i < method_param_number; i++) { - - if(index == var.getIndex()) { - if(param_annotations_lists.size() >= method_param_number - i) { - int shift = method_param_number - param_annotations_lists.size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter - - List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift); - - for(AnnotationExprent ann : annotations) { - if(ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { - is_notnull_check = true; - } - } - } - - break; - } - - index+=md.params[i].stack_size; - } - } - } - } - } - - if(!is_notnull_check) { - return false; - } - - removeParameterCheck(stat, mt); - - return true; - } - - return false; - } - - private static void removeParameterCheck(Statement stat, StructMethod mt) { - - Statement st = stat.getFirst(); - while(st.type == Statement.TYPE_SEQUENCE) { - st = st.getFirst(); - } - - IfStatement ifstat = (IfStatement)st; - - if(ifstat.getElsestat() == null) { // if - // TODO: - } else { // if - else - - StatEdge ifedge = ifstat.getIfEdge(); - StatEdge elseedge = ifstat.getElseEdge(); - - Statement ifbranch = ifstat.getIfstat(); - Statement elsebranch = ifstat.getElsestat(); - - ifstat.getFirst().removeSuccessor(ifedge); - ifstat.getFirst().removeSuccessor(elseedge); - - ifstat.getStats().removeWithKey(ifbranch.id); - ifstat.getStats().removeWithKey(elsebranch.id); - - if(!ifbranch.getAllSuccessorEdges().isEmpty()) { - ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); - } - - ifstat.getParent().replaceStatement(ifstat, elsebranch); - ifstat.getParent().setAllParent(); - } - - } - - private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) { - - VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); - - boolean is_notnull_check = false; - - // method annotation, refers to the return value - StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS); - if(attr != null) { - List<AnnotationExprent> annotations = attr.getAnnotations(); - - for(AnnotationExprent ann : annotations) { - if(ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { - is_notnull_check = true; - } - } - } - - if(is_notnull_check) { - return removeReturnCheck(stat, mt); - } - - return false; - } - - - private static boolean removeReturnCheck(Statement stat, StructMethod mt) { - - Statement parent = stat.getParent(); - - if(parent != null && parent.type == Statement.TYPE_IF && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) { - Exprent exprent = stat.getExprents().get(0); - if(exprent.type == Exprent.EXPRENT_EXIT) { - ExitExprent exit_exprent = (ExitExprent)exprent; - if(exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { - Exprent exprent_value = exit_exprent.getValue(); - //if(exprent_value.type == Exprent.EXPRENT_VAR) { - // VarExprent var_value = (VarExprent)exprent_value; - - IfStatement ifparent = (IfStatement)parent; - Exprent if_condition = ifparent.getHeadexprent().getCondition(); - - if(ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) - - FunctionExprent func = (FunctionExprent)if_condition; - Exprent first_param = func.getLstOperands().get(0); - Exprent second_param = func.getLstOperands().get(1); - - StatEdge ifedge = ifparent.getIfEdge(); - StatEdge elseedge = ifparent.getElseEdge(); - - Statement ifbranch = ifparent.getIfstat(); - Statement elsebranch = ifparent.getElsestat(); - - if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order - //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) { - if(first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. - if(ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && // TODO: special check for IllegalStateException - ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { - - ifparent.getFirst().removeSuccessor(ifedge); - ifparent.getFirst().removeSuccessor(elseedge); - - ifparent.getStats().removeWithKey(ifbranch.id); - ifparent.getStats().removeWithKey(elsebranch.id); - - if(!ifbranch.getAllSuccessorEdges().isEmpty()) { - ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); - } - - if(!ifparent.getFirst().getExprents().isEmpty()) { - elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents()); - } - - ifparent.getParent().replaceStatement(ifparent, elsebranch); - ifparent.getParent().setAllParent(); - - return true; - } - } - } - } - //} - } - } - } else if (parent != null && parent.type == Statement.TYPE_SEQUENCE && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) { - Exprent exprent = stat.getExprents().get(0); - if(exprent.type == Exprent.EXPRENT_EXIT) { - ExitExprent exit_exprent = (ExitExprent)exprent; - if(exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { - Exprent exprent_value = exit_exprent.getValue(); - - SequenceStatement sequence = (SequenceStatement)parent; - int sequence_stats_number = sequence.getStats().size(); - - if(sequence_stats_number > 1 && sequence.getStats().getLast() == stat && sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) { - - IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2); - Exprent if_condition = ifstat.getHeadexprent().getCondition(); - - if(ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) - - FunctionExprent func = (FunctionExprent)if_condition; - Exprent first_param = func.getLstOperands().get(0); - Exprent second_param = func.getLstOperands().get(1); - - Statement ifbranch = ifstat.getIfstat(); - - if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order - if(first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. - if(ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && // TODO: special check for IllegalStateException - ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { - - ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge - - if(!ifstat.getFirst().getExprents().isEmpty()) { - stat.getExprents().addAll(0, ifstat.getFirst().getExprents()); - } - - for(StatEdge edge : ifstat.getAllPredecessorEdges()) { - - ifstat.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); - stat.addPredecessor(edge); - } - - sequence.getStats().removeWithKey(ifstat.id); - sequence.setFirst(sequence.getStats().get(0)); - - return true; - } - } - } - } - } - } - } - } - - - for(Statement st: stat.getStats()) { - if(removeReturnCheck(st, mt)) { - return true; - } - } - - return false; - } - + + public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { + + boolean checks_removed = false; + + // parameter @NotNull annotations + while (findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check. + checks_removed = true; + } + + // method @NotNull annotation + while (findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check. + checks_removed = true; + } + + return checks_removed; + } + + private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) { + + Statement st = stat.getFirst(); + while (st.type == Statement.TYPE_SEQUENCE) { + st = st.getFirst(); + } + + if (st.type == Statement.TYPE_IF) { + + IfStatement ifstat = (IfStatement)st; + Statement ifbranch = ifstat.getIfstat(); + + Exprent if_condition = ifstat.getHeadexprent().getCondition(); + + boolean is_notnull_check = false; + + // TODO: FUNCTION_NE also possible if reversed order (in theory) + if (ifbranch != null && + if_condition.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ && + ifbranch.type == Statement.TYPE_BASICBLOCK && + ifbranch.getExprents().size() == 1 && + ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + if (second_param.type == Exprent.EXPRENT_CONST && + ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + if (first_param.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)first_param; + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); + + // parameter annotations + StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes + .getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); + if (param_annotations != null) { + + List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations(); + int method_param_number = md.params.length; + + int index = thisvar ? 1 : 0; + for (int i = 0; i < method_param_number; i++) { + + if (index == var.getIndex()) { + if (param_annotations_lists.size() >= method_param_number - i) { + int shift = method_param_number - + param_annotations_lists + .size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter + + List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift); + + for (AnnotationExprent ann : annotations) { + if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + is_notnull_check = true; + } + } + } + + break; + } + + index += md.params[i].stack_size; + } + } + } + } + } + + if (!is_notnull_check) { + return false; + } + + removeParameterCheck(stat, mt); + + return true; + } + + return false; + } + + private static void removeParameterCheck(Statement stat, StructMethod mt) { + + Statement st = stat.getFirst(); + while (st.type == Statement.TYPE_SEQUENCE) { + st = st.getFirst(); + } + + IfStatement ifstat = (IfStatement)st; + + if (ifstat.getElsestat() == null) { // if + // TODO: + } + else { // if - else + + StatEdge ifedge = ifstat.getIfEdge(); + StatEdge elseedge = ifstat.getElseEdge(); + + Statement ifbranch = ifstat.getIfstat(); + Statement elsebranch = ifstat.getElsestat(); + + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.getFirst().removeSuccessor(elseedge); + + ifstat.getStats().removeWithKey(ifbranch.id); + ifstat.getStats().removeWithKey(elsebranch.id); + + if (!ifbranch.getAllSuccessorEdges().isEmpty()) { + ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); + } + + ifstat.getParent().replaceStatement(ifstat, elsebranch); + ifstat.getParent().setAllParent(); + } + } + + private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) { + + VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); + + boolean is_notnull_check = false; + + // method annotation, refers to the return value + StructAnnotationAttribute attr = + (StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS); + if (attr != null) { + List<AnnotationExprent> annotations = attr.getAnnotations(); + + for (AnnotationExprent ann : annotations) { + if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + is_notnull_check = true; + } + } + } + + if (is_notnull_check) { + return removeReturnCheck(stat, mt); + } + + return false; + } + + + private static boolean removeReturnCheck(Statement stat, StructMethod mt) { + + Statement parent = stat.getParent(); + + if (parent != null && parent.type == Statement.TYPE_IF && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) { + Exprent exprent = stat.getExprents().get(0); + if (exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exit_exprent = (ExitExprent)exprent; + if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + Exprent exprent_value = exit_exprent.getValue(); + //if(exprent_value.type == Exprent.EXPRENT_VAR) { + // VarExprent var_value = (VarExprent)exprent_value; + + IfStatement ifparent = (IfStatement)parent; + Exprent if_condition = ifparent.getHeadexprent().getCondition(); + + if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + StatEdge ifedge = ifparent.getIfEdge(); + StatEdge elseedge = ifparent.getElseEdge(); + + Statement ifbranch = ifparent.getIfstat(); + Statement elsebranch = ifparent.getElsestat(); + + if (second_param.type == Exprent.EXPRENT_CONST && + ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) { + if (first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. + if (ifbranch.type == Statement.TYPE_BASICBLOCK && + ifbranch.getExprents().size() == 1 && + // TODO: special check for IllegalStateException + ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + ifparent.getFirst().removeSuccessor(ifedge); + ifparent.getFirst().removeSuccessor(elseedge); + + ifparent.getStats().removeWithKey(ifbranch.id); + ifparent.getStats().removeWithKey(elsebranch.id); + + if (!ifbranch.getAllSuccessorEdges().isEmpty()) { + ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); + } + + if (!ifparent.getFirst().getExprents().isEmpty()) { + elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents()); + } + + ifparent.getParent().replaceStatement(ifparent, elsebranch); + ifparent.getParent().setAllParent(); + + return true; + } + } + } + } + //} + } + } + } + else if (parent != null && + parent.type == Statement.TYPE_SEQUENCE && + stat.type == Statement.TYPE_BASICBLOCK && + stat.getExprents().size() == 1) { + Exprent exprent = stat.getExprents().get(0); + if (exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exit_exprent = (ExitExprent)exprent; + if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + Exprent exprent_value = exit_exprent.getValue(); + + SequenceStatement sequence = (SequenceStatement)parent; + int sequence_stats_number = sequence.getStats().size(); + + if (sequence_stats_number > 1 && + sequence.getStats().getLast() == stat && + sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) { + + IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2); + Exprent if_condition = ifstat.getHeadexprent().getCondition(); + + if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + Statement ifbranch = ifstat.getIfstat(); + + if (second_param.type == Exprent.EXPRENT_CONST && + ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + if (first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. + if (ifbranch.type == Statement.TYPE_BASICBLOCK && + ifbranch.getExprents().size() == 1 && + // TODO: special check for IllegalStateException + ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge + + if (!ifstat.getFirst().getExprents().isEmpty()) { + stat.getExprents().addAll(0, ifstat.getFirst().getExprents()); + } + + for (StatEdge edge : ifstat.getAllPredecessorEdges()) { + + ifstat.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); + stat.addPredecessor(edge); + } + + sequence.getStats().removeWithKey(ifstat.id); + sequence.setFirst(sequence.getStats().get(0)); + + return true; + } + } + } + } + } + } + } + } + + + for (Statement st : stat.getStats()) { + if (removeReturnCheck(st, mt)) { + return true; + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java index 6894f71..d5f4c9c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -1,24 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; @@ -27,705 +23,729 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + public class IfHelper { - - public static boolean mergeAllIfs(RootStatement root) { - - boolean res = mergeAllIfsRec(root, new HashSet<Integer>()); - - if(res) { - SequenceHelper.condenseSequences(root); - } - - return res; - } - - - private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) { - - boolean res = false; - - if(stat.getExprents() == null) { - - for(;;) { - - boolean changed = false; - - for(Statement st: stat.getStats()) { - - res |= mergeAllIfsRec(st, setReorderedIfs); - - // collapse composed if's - if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) { - break; - } - } - - res |= changed; - - if(!changed) { - break; - } - } - } - - return res; - } - - - public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) { - - if(statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) { - return false; - } - - boolean res = false; - - for(;;) { - - boolean updated = false; - - List<Statement> lst = new ArrayList<Statement>(); - if(statement.type == Statement.TYPE_IF) { - lst.add(statement); - } else { - lst.addAll(statement.getStats()); - } - - boolean stsingle = (lst.size() == 1); - - for(Statement stat: lst) { - - if(stat.type == Statement.TYPE_IF) { - IfNode rtnode = buildGraph((IfStatement)stat, stsingle); - - if(rtnode == null) { - continue; - } - - if(updated = collapseIfIf(rtnode)) { - break; - } - - if(!setReorderedIfs.contains(stat.id)) { - - if(updated = collapseIfElse(rtnode)) { - break; - } - - if(updated = collapseElse(rtnode)) { - break; - } - } - - if(updated = reorderIf((IfStatement)stat)) { - setReorderedIfs.add(stat.id); - break; - } - - } - } - - if(!updated) { - break; - } - - res |= updated; - } - - return res; - } - - private static boolean collapseIfIf(IfNode rtnode) { - - if(rtnode.edgetypes.get(0) == 0) { - IfNode ifbranch = rtnode.succs.get(0); - if(ifbranch.succs.size() == 2) { - - // if-if branch - if(ifbranch.succs.get(1).value == rtnode.succs.get(1).value) { - - IfStatement ifparent = (IfStatement)rtnode.value; - IfStatement ifchild = (IfStatement)ifbranch.value; - Statement ifinner = ifbranch.succs.get(0).value; - - if(ifchild.getFirst().getExprents().isEmpty()) { - - ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); - ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0)); - ifparent.getStats().removeWithKey(ifchild.id); - - if(ifbranch.edgetypes.get(0).intValue() == 1) { // target null - - ifparent.setIfstat(null); - - StatEdge ifedge = ifchild.getIfEdge(); - - ifchild.getFirst().removeSuccessor(ifedge); - ifedge.setSource(ifparent.getFirst()); - - if(ifedge.closure == ifchild) { - ifedge.closure = null; - } - ifparent.getFirst().addSuccessor(ifedge); - - ifparent.setIfEdge(ifedge); - } else { - ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); - - StatEdge ifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifparent.getFirst(), ifinner); - ifparent.getFirst().addSuccessor(ifedge); - ifparent.setIfEdge(ifedge); - ifparent.setIfstat(ifinner); - - ifparent.getStats().addWithKey(ifinner, ifinner.id); - ifinner.setParent(ifparent); - - if(!ifinner.getAllSuccessorEdges().isEmpty()) { - StatEdge edge = ifinner.getAllSuccessorEdges().get(0); - if(edge.closure == ifchild) { - edge.closure = null; - } - } - } - - // merge if conditions - IfExprent statexpr = ifparent.getHeadexprent(); - - List<Exprent> lstOperands = new ArrayList<Exprent>(); - lstOperands.add(statexpr.getCondition()); - lstOperands.add(ifchild.getHeadexprent().getCondition()); - - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); - - return true; - } - } - } - } - - return false; - } - - private static boolean collapseIfElse(IfNode rtnode) { - - if(rtnode.edgetypes.get(0).intValue() == 0) { - IfNode ifbranch = rtnode.succs.get(0); - if(ifbranch.succs.size() == 2) { - - // if-else branch - if(ifbranch.succs.get(0).value == rtnode.succs.get(1).value) { - - IfStatement ifparent = (IfStatement)rtnode.value; - IfStatement ifchild = (IfStatement)ifbranch.value; - - if(ifchild.getFirst().getExprents().isEmpty()) { - - ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); - ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); - ifparent.getStats().removeWithKey(ifchild.id); - - if(ifbranch.edgetypes.get(1).intValue() == 1 && - ifbranch.edgetypes.get(0).intValue() == 1) { // target null - - ifparent.setIfstat(null); - - StatEdge ifedge = ifchild.getAllSuccessorEdges().get(0); - - ifchild.removeSuccessor(ifedge); - ifedge.setSource(ifparent.getFirst()); - ifparent.getFirst().addSuccessor(ifedge); - - ifparent.setIfEdge(ifedge); - } else { - throw new RuntimeException("inconsistent if structure!"); - } - - // merge if conditions - IfExprent statexpr = ifparent.getHeadexprent(); - - List<Exprent> lstOperands = new ArrayList<Exprent>(); - lstOperands.add(statexpr.getCondition()); - lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()}))); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); - - return true; - } - } - } - } - - return false; - } - - - private static boolean collapseElse(IfNode rtnode) { - - if(rtnode.edgetypes.get(1).intValue() == 0) { - IfNode elsebranch = rtnode.succs.get(1); - if(elsebranch.succs.size() == 2) { - - // else-if or else-else branch - int path = elsebranch.succs.get(1).value == rtnode.succs.get(0).value?2: - (elsebranch.succs.get(0).value == rtnode.succs.get(0).value?1:0); - - if(path > 0) { - - IfStatement firstif = (IfStatement)rtnode.value; - IfStatement secondif = (IfStatement)elsebranch.value; - Statement parent = firstif.getParent(); - - if(secondif.getFirst().getExprents().isEmpty()) { - - firstif.getFirst().removeSuccessor(firstif.getIfEdge()); - - // remove first if - firstif.removeAllSuccessors(secondif); - - for(StatEdge edge: firstif.getAllPredecessorEdges()) { - if(!firstif.containsStatementStrict(edge.getSource())) { - firstif.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, secondif); - secondif.addPredecessor(edge); - } - } - - parent.getStats().removeWithKey(firstif.id); - if(parent.getFirst() == firstif) { - parent.setFirst(secondif); - } - - // merge if conditions - IfExprent statexpr = secondif.getHeadexprent(); - - List<Exprent> lstOperands = new ArrayList<Exprent>(); - lstOperands.add(firstif.getHeadexprent().getCondition()); - - if(path == 2) { - lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{lstOperands.get(0)}))); - } - - lstOperands.add(statexpr.getCondition()); - - statexpr.setCondition(new FunctionExprent(path==1?FunctionExprent.FUNCTION_COR:FunctionExprent.FUNCTION_CADD, lstOperands)); - - if(secondif.getFirst().getExprents().isEmpty() && - !firstif.getFirst().getExprents().isEmpty()) { - - secondif.replaceStatement(secondif.getFirst(), firstif.getFirst()); - } - - return true; - } - } - } else if(elsebranch.succs.size() == 1) { - - if(elsebranch.succs.get(0).value == rtnode.succs.get(0).value) { - - IfStatement firstif = (IfStatement)rtnode.value; - Statement second = elsebranch.value; - - firstif.removeAllSuccessors(second); - - for(StatEdge edge : second.getAllSuccessorEdges()) { - second.removeSuccessor(edge); - edge.setSource(firstif); - firstif.addSuccessor(edge); - } - - StatEdge ifedge = firstif.getIfEdge(); - firstif.getFirst().removeSuccessor(ifedge); - - second.addSuccessor(new StatEdge(ifedge.getType(), second, ifedge.getDestination(), ifedge.closure)); - - StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, firstif.getFirst(), second); - firstif.getFirst().addSuccessor(newifedge); - firstif.setIfstat(second); - - firstif.getStats().addWithKey(second, second.id); - second.setParent(firstif); - - firstif.getParent().getStats().removeWithKey(second.id); - - // negate the if condition - IfExprent statexpr = firstif.getHeadexprent(); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); - - return true; - } - - } - } - - return false; - } - - - private static IfNode buildGraph(IfStatement stat, boolean stsingle) { - - if(stat.iftype == IfStatement.IFTYPE_IFELSE) { - return null; - } - - IfNode res = new IfNode(stat); - - // if branch - Statement ifchild = stat.getIfstat(); - if(ifchild == null) { - StatEdge edge = stat.getIfEdge(); - res.addChild(new IfNode(edge.getDestination()), 1); - } else { - IfNode ifnode = new IfNode(ifchild); - res.addChild(ifnode, 0); - if(ifchild.type == Statement.TYPE_IF && ((IfStatement)ifchild).iftype == IfStatement.IFTYPE_IF) { - IfStatement stat2 = (IfStatement)ifchild; - Statement ifchild2 = stat2.getIfstat(); - if(ifchild2 == null) { - StatEdge edge = stat2.getIfEdge(); - ifnode.addChild(new IfNode(edge.getDestination()), 1); - } else { - ifnode.addChild(new IfNode(ifchild2), 0); - } - } - - if(!ifchild.getAllSuccessorEdges().isEmpty()) { - ifnode.addChild(new IfNode(ifchild.getAllSuccessorEdges().get(0).getDestination()), 1); - } - } - - // else branch - StatEdge edge = stat.getAllSuccessorEdges().get(0); - Statement elsechild = edge.getDestination(); - IfNode elsenode = new IfNode(elsechild); - - if(stsingle || edge.getType() != StatEdge.TYPE_REGULAR) { - res.addChild(elsenode, 1); - } else { - res.addChild(elsenode, 0); - if(elsechild.type == Statement.TYPE_IF && ((IfStatement)elsechild).iftype == IfStatement.IFTYPE_IF) { - IfStatement stat2 = (IfStatement)elsechild; - Statement ifchild2 = stat2.getIfstat(); - if(ifchild2 == null) { - elsenode.addChild(new IfNode(stat2.getIfEdge().getDestination()), 1); - } else { - elsenode.addChild(new IfNode(ifchild2), 0); - } - } - - if(!elsechild.getAllSuccessorEdges().isEmpty()) { - elsenode.addChild(new IfNode(elsechild.getAllSuccessorEdges().get(0).getDestination()), 1); - } - } - - return res; - } - - - // FIXME: rewrite the entire method!!! keep in mind finally exits!! - private static boolean reorderIf(IfStatement ifstat) { - - if(ifstat.iftype == IfStatement.IFTYPE_IFELSE) { - return false; - } - - boolean ifdirect = false, elsedirect = false;; - boolean noifstat = false, noelsestat = false; - boolean ifdirectpath = false, elsedirectpath = false; - - Statement parent = ifstat.getParent(); - Statement from = parent.type == Statement.TYPE_SEQUENCE?parent:ifstat; - - Statement next = getNextStatement(from); - - if(ifstat.getIfstat() == null) { - noifstat = true; - - if(ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) { - ifdirect = true; - } else { - ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination()); - } - } else { - List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges(); - if(!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { - ifdirect = true; - } else { - ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from); - } - } - - Statement last = parent.type == Statement.TYPE_SEQUENCE?((SequenceStatement)parent).getStats().getLast():ifstat; - noelsestat = (last == ifstat); - - if(!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { - elsedirect = true; - } else { - elsedirect = hasDirectEndEdge(last, from); - } - - if(!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) { - return false; - } - - if(!ifdirect && !noifstat) { - ifdirectpath = existsPath(ifstat, next); - } - - if(!elsedirect && !noelsestat) { - SequenceStatement sequence = (SequenceStatement)parent; - - for(int i=sequence.getStats().size()-1;i>=0;i--) { - Statement sttemp = sequence.getStats().get(i); - if(sttemp == ifstat) { - break; - } else { - if(elsedirectpath = existsPath(sttemp, next)) { - break; - } - } - } - } - - if((ifdirect || ifdirectpath) && (elsedirect || elsedirectpath) && !noifstat && !noelsestat) { // if - then - else - - SequenceStatement sequence = (SequenceStatement)parent; - - // build and cut the new else statement - List<Statement> lst = new ArrayList<Statement>(); - for(int i=sequence.getStats().size()-1;i>=0;i--) { - Statement sttemp = sequence.getStats().get(i); - if(sttemp == ifstat) { - break; - } else { - lst.add(0, sttemp); - } - } - - Statement stelse; - if(lst.size() == 1) { - stelse = lst.get(0); - } else { - stelse = new SequenceStatement(lst); - stelse.setAllParent(); - } - - ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); - for(Statement st: lst) { - sequence.getStats().removeWithKey(st.id); - } - - StatEdge elseedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); - ifstat.getFirst().addSuccessor(elseedge); - ifstat.setElsestat(stelse); - ifstat.setElseEdge(elseedge); - - ifstat.getStats().addWithKey(stelse, stelse.id); - stelse.setParent(ifstat); - -// if(next.type != Statement.TYPE_DUMMYEXIT && (ifdirect || elsedirect)) { -// StatEdge breakedge = new StatEdge(StatEdge.TYPE_BREAK, ifstat, next); -// sequence.addLabeledEdge(breakedge); -// ifstat.addSuccessor(breakedge); -// } - - ifstat.iftype = IfStatement.IFTYPE_IFELSE; - - } else if(ifdirect && (!elsedirect || (noifstat && !noelsestat))) { // if - then - - // negate the if condition - IfExprent statexpr = ifstat.getHeadexprent(); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); - - if(noelsestat) { - StatEdge ifedge = ifstat.getIfEdge(); - StatEdge elseedge = ifstat.getAllSuccessorEdges().get(0); - - if(noifstat) { - ifstat.getFirst().removeSuccessor(ifedge); - ifstat.removeSuccessor(elseedge); - - ifedge.setSource(ifstat); - elseedge.setSource(ifstat.getFirst()); - - ifstat.addSuccessor(ifedge); - ifstat.getFirst().addSuccessor(elseedge); - - ifstat.setIfEdge(elseedge); - } else { - Statement ifbranch = ifstat.getIfstat(); - SequenceStatement newseq = new SequenceStatement(Arrays.asList(new Statement[] {ifstat, ifbranch})); - - ifstat.getFirst().removeSuccessor(ifedge); - ifstat.getStats().removeWithKey(ifbranch.id); - ifstat.setIfstat(null); - - ifstat.removeSuccessor(elseedge); - elseedge.setSource(ifstat.getFirst()); - ifstat.getFirst().addSuccessor(elseedge); - - ifstat.setIfEdge(elseedge); - - ifstat.getParent().replaceStatement(ifstat, newseq); - newseq.setAllParent(); - - ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); - } - } else { - - SequenceStatement sequence = (SequenceStatement)parent; - - // build and cut the new else statement - List<Statement> lst = new ArrayList<Statement>(); - for(int i=sequence.getStats().size()-1;i>=0;i--) { - Statement sttemp = sequence.getStats().get(i); - if(sttemp == ifstat) { - break; - } else { - lst.add(0, sttemp); - } - } - - Statement stelse; - if(lst.size() == 1) { - stelse = lst.get(0); - } else { - stelse = new SequenceStatement(lst); - stelse.setAllParent(); - } - - ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); - for(Statement st: lst) { - sequence.getStats().removeWithKey(st.id); - } - - if(noifstat) { - StatEdge ifedge = ifstat.getIfEdge(); - - ifstat.getFirst().removeSuccessor(ifedge); - ifedge.setSource(ifstat); - ifstat.addSuccessor(ifedge); - } else { - Statement ifbranch = ifstat.getIfstat(); - - ifstat.getFirst().removeSuccessor(ifstat.getIfEdge()); - ifstat.getStats().removeWithKey(ifbranch.id); - - ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); - - sequence.getStats().addWithKey(ifbranch, ifbranch.id); - ifbranch.setParent(sequence); - } - - StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); - ifstat.getFirst().addSuccessor(newifedge); - ifstat.setIfstat(stelse); - ifstat.setIfEdge(newifedge); - - ifstat.getStats().addWithKey(stelse, stelse.id); - stelse.setParent(ifstat); - - } - } else { - return false; - } - - return true; - } - - private static boolean hasDirectEndEdge(Statement stat, Statement from) { - - for(StatEdge edge: stat.getAllSuccessorEdges()) { - if(MergeHelper.isDirectPath(from, edge.getDestination())) { - return true; - } - } - - if(stat.getExprents() == null) { - switch(stat.type) { - case Statement.TYPE_SEQUENCE: - return hasDirectEndEdge(stat.getStats().getLast(), from); - case Statement.TYPE_CATCHALL: - case Statement.TYPE_TRYCATCH: - for(Statement st: stat.getStats()) { - if(hasDirectEndEdge(st, from)) { - return true; - } - } - break; - case Statement.TYPE_IF: - IfStatement ifstat = (IfStatement)stat; - if(ifstat.iftype == IfStatement.IFTYPE_IFELSE) { - return hasDirectEndEdge(ifstat.getIfstat(), from) || - hasDirectEndEdge(ifstat.getElsestat(), from); - } - break; - case Statement.TYPE_SYNCRONIZED: - return hasDirectEndEdge(stat.getStats().get(1), from); - case Statement.TYPE_SWITCH: - for(Statement st: stat.getStats()) { - if(hasDirectEndEdge(st, from)) { - return true; - } - } - } - } - - return false; - } - - - private static Statement getNextStatement(Statement stat) { - - Statement parent = stat.getParent(); - switch(parent.type) { - case Statement.TYPE_ROOT: - return ((RootStatement)parent).getDummyExit(); - case Statement.TYPE_DO: - return parent; - case Statement.TYPE_SEQUENCE: - SequenceStatement sequence = (SequenceStatement)parent; - if(sequence.getStats().getLast() != stat) { - for(int i=sequence.getStats().size()-1;i>=0;i--) { - if(sequence.getStats().get(i) == stat) { - return sequence.getStats().get(i+1); - } - } - } - } - - return getNextStatement(parent); - } - - private static boolean existsPath(Statement from, Statement to) { - - for(StatEdge edge: to.getAllPredecessorEdges()) { - if(from.containsStatementStrict(edge.getSource())) { - return true; - } - } - - return false; - } - - private static class IfNode { - public Statement value; - - public List<IfNode> succs = new ArrayList<IfNode>(); - public List<Integer> edgetypes = new ArrayList<Integer>(); - - public IfNode(Statement value) { - this.value = value; - } - - public void addChild(IfNode child, int type) { - succs.add(child); - edgetypes.add(new Integer(type)); - } - } - - + + public static boolean mergeAllIfs(RootStatement root) { + + boolean res = mergeAllIfsRec(root, new HashSet<Integer>()); + + if (res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + + private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) { + + boolean res = false; + + if (stat.getExprents() == null) { + + for (; ; ) { + + boolean changed = false; + + for (Statement st : stat.getStats()) { + + res |= mergeAllIfsRec(st, setReorderedIfs); + + // collapse composed if's + if (changed = IfHelper.mergeIfs(st, setReorderedIfs)) { + break; + } + } + + res |= changed; + + if (!changed) { + break; + } + } + } + + return res; + } + + + public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) { + + if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) { + return false; + } + + boolean res = false; + + for (; ; ) { + + boolean updated = false; + + List<Statement> lst = new ArrayList<Statement>(); + if (statement.type == Statement.TYPE_IF) { + lst.add(statement); + } + else { + lst.addAll(statement.getStats()); + } + + boolean stsingle = (lst.size() == 1); + + for (Statement stat : lst) { + + if (stat.type == Statement.TYPE_IF) { + IfNode rtnode = buildGraph((IfStatement)stat, stsingle); + + if (rtnode == null) { + continue; + } + + if (updated = collapseIfIf(rtnode)) { + break; + } + + if (!setReorderedIfs.contains(stat.id)) { + + if (updated = collapseIfElse(rtnode)) { + break; + } + + if (updated = collapseElse(rtnode)) { + break; + } + } + + if (updated = reorderIf((IfStatement)stat)) { + setReorderedIfs.add(stat.id); + break; + } + } + } + + if (!updated) { + break; + } + + res |= updated; + } + + return res; + } + + private static boolean collapseIfIf(IfNode rtnode) { + + if (rtnode.edgetypes.get(0) == 0) { + IfNode ifbranch = rtnode.succs.get(0); + if (ifbranch.succs.size() == 2) { + + // if-if branch + if (ifbranch.succs.get(1).value == rtnode.succs.get(1).value) { + + IfStatement ifparent = (IfStatement)rtnode.value; + IfStatement ifchild = (IfStatement)ifbranch.value; + Statement ifinner = ifbranch.succs.get(0).value; + + if (ifchild.getFirst().getExprents().isEmpty()) { + + ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); + ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0)); + ifparent.getStats().removeWithKey(ifchild.id); + + if (ifbranch.edgetypes.get(0).intValue() == 1) { // target null + + ifparent.setIfstat(null); + + StatEdge ifedge = ifchild.getIfEdge(); + + ifchild.getFirst().removeSuccessor(ifedge); + ifedge.setSource(ifparent.getFirst()); + + if (ifedge.closure == ifchild) { + ifedge.closure = null; + } + ifparent.getFirst().addSuccessor(ifedge); + + ifparent.setIfEdge(ifedge); + } + else { + ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); + + StatEdge ifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifparent.getFirst(), ifinner); + ifparent.getFirst().addSuccessor(ifedge); + ifparent.setIfEdge(ifedge); + ifparent.setIfstat(ifinner); + + ifparent.getStats().addWithKey(ifinner, ifinner.id); + ifinner.setParent(ifparent); + + if (!ifinner.getAllSuccessorEdges().isEmpty()) { + StatEdge edge = ifinner.getAllSuccessorEdges().get(0); + if (edge.closure == ifchild) { + edge.closure = null; + } + } + } + + // merge if conditions + IfExprent statexpr = ifparent.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(statexpr.getCondition()); + lstOperands.add(ifchild.getHeadexprent().getCondition()); + + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + + return true; + } + } + } + } + + return false; + } + + private static boolean collapseIfElse(IfNode rtnode) { + + if (rtnode.edgetypes.get(0).intValue() == 0) { + IfNode ifbranch = rtnode.succs.get(0); + if (ifbranch.succs.size() == 2) { + + // if-else branch + if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) { + + IfStatement ifparent = (IfStatement)rtnode.value; + IfStatement ifchild = (IfStatement)ifbranch.value; + + if (ifchild.getFirst().getExprents().isEmpty()) { + + ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); + ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); + ifparent.getStats().removeWithKey(ifchild.id); + + if (ifbranch.edgetypes.get(1).intValue() == 1 && + ifbranch.edgetypes.get(0).intValue() == 1) { // target null + + ifparent.setIfstat(null); + + StatEdge ifedge = ifchild.getAllSuccessorEdges().get(0); + + ifchild.removeSuccessor(ifedge); + ifedge.setSource(ifparent.getFirst()); + ifparent.getFirst().addSuccessor(ifedge); + + ifparent.setIfEdge(ifedge); + } + else { + throw new RuntimeException("inconsistent if structure!"); + } + + // merge if conditions + IfExprent statexpr = ifparent.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(statexpr.getCondition()); + lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()}))); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + + return true; + } + } + } + } + + return false; + } + + + private static boolean collapseElse(IfNode rtnode) { + + if (rtnode.edgetypes.get(1).intValue() == 0) { + IfNode elsebranch = rtnode.succs.get(1); + if (elsebranch.succs.size() == 2) { + + // else-if or else-else branch + int path = elsebranch.succs.get(1).value == rtnode.succs.get(0).value ? 2 : + (elsebranch.succs.get(0).value == rtnode.succs.get(0).value ? 1 : 0); + + if (path > 0) { + + IfStatement firstif = (IfStatement)rtnode.value; + IfStatement secondif = (IfStatement)elsebranch.value; + Statement parent = firstif.getParent(); + + if (secondif.getFirst().getExprents().isEmpty()) { + + firstif.getFirst().removeSuccessor(firstif.getIfEdge()); + + // remove first if + firstif.removeAllSuccessors(secondif); + + for (StatEdge edge : firstif.getAllPredecessorEdges()) { + if (!firstif.containsStatementStrict(edge.getSource())) { + firstif.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, secondif); + secondif.addPredecessor(edge); + } + } + + parent.getStats().removeWithKey(firstif.id); + if (parent.getFirst() == firstif) { + parent.setFirst(secondif); + } + + // merge if conditions + IfExprent statexpr = secondif.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(firstif.getHeadexprent().getCondition()); + + if (path == 2) { + lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{lstOperands.get(0)}))); + } + + lstOperands.add(statexpr.getCondition()); + + statexpr + .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands)); + + if (secondif.getFirst().getExprents().isEmpty() && + !firstif.getFirst().getExprents().isEmpty()) { + + secondif.replaceStatement(secondif.getFirst(), firstif.getFirst()); + } + + return true; + } + } + } + else if (elsebranch.succs.size() == 1) { + + if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) { + + IfStatement firstif = (IfStatement)rtnode.value; + Statement second = elsebranch.value; + + firstif.removeAllSuccessors(second); + + for (StatEdge edge : second.getAllSuccessorEdges()) { + second.removeSuccessor(edge); + edge.setSource(firstif); + firstif.addSuccessor(edge); + } + + StatEdge ifedge = firstif.getIfEdge(); + firstif.getFirst().removeSuccessor(ifedge); + + second.addSuccessor(new StatEdge(ifedge.getType(), second, ifedge.getDestination(), ifedge.closure)); + + StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, firstif.getFirst(), second); + firstif.getFirst().addSuccessor(newifedge); + firstif.setIfstat(second); + + firstif.getStats().addWithKey(second, second.id); + second.setParent(firstif); + + firstif.getParent().getStats().removeWithKey(second.id); + + // negate the if condition + IfExprent statexpr = firstif.getHeadexprent(); + statexpr + .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + + return true; + } + } + } + + return false; + } + + + private static IfNode buildGraph(IfStatement stat, boolean stsingle) { + + if (stat.iftype == IfStatement.IFTYPE_IFELSE) { + return null; + } + + IfNode res = new IfNode(stat); + + // if branch + Statement ifchild = stat.getIfstat(); + if (ifchild == null) { + StatEdge edge = stat.getIfEdge(); + res.addChild(new IfNode(edge.getDestination()), 1); + } + else { + IfNode ifnode = new IfNode(ifchild); + res.addChild(ifnode, 0); + if (ifchild.type == Statement.TYPE_IF && ((IfStatement)ifchild).iftype == IfStatement.IFTYPE_IF) { + IfStatement stat2 = (IfStatement)ifchild; + Statement ifchild2 = stat2.getIfstat(); + if (ifchild2 == null) { + StatEdge edge = stat2.getIfEdge(); + ifnode.addChild(new IfNode(edge.getDestination()), 1); + } + else { + ifnode.addChild(new IfNode(ifchild2), 0); + } + } + + if (!ifchild.getAllSuccessorEdges().isEmpty()) { + ifnode.addChild(new IfNode(ifchild.getAllSuccessorEdges().get(0).getDestination()), 1); + } + } + + // else branch + StatEdge edge = stat.getAllSuccessorEdges().get(0); + Statement elsechild = edge.getDestination(); + IfNode elsenode = new IfNode(elsechild); + + if (stsingle || edge.getType() != StatEdge.TYPE_REGULAR) { + res.addChild(elsenode, 1); + } + else { + res.addChild(elsenode, 0); + if (elsechild.type == Statement.TYPE_IF && ((IfStatement)elsechild).iftype == IfStatement.IFTYPE_IF) { + IfStatement stat2 = (IfStatement)elsechild; + Statement ifchild2 = stat2.getIfstat(); + if (ifchild2 == null) { + elsenode.addChild(new IfNode(stat2.getIfEdge().getDestination()), 1); + } + else { + elsenode.addChild(new IfNode(ifchild2), 0); + } + } + + if (!elsechild.getAllSuccessorEdges().isEmpty()) { + elsenode.addChild(new IfNode(elsechild.getAllSuccessorEdges().get(0).getDestination()), 1); + } + } + + return res; + } + + + // FIXME: rewrite the entire method!!! keep in mind finally exits!! + private static boolean reorderIf(IfStatement ifstat) { + + if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) { + return false; + } + + boolean ifdirect = false, elsedirect = false; + ; + boolean noifstat = false, noelsestat = false; + boolean ifdirectpath = false, elsedirectpath = false; + + Statement parent = ifstat.getParent(); + Statement from = parent.type == Statement.TYPE_SEQUENCE ? parent : ifstat; + + Statement next = getNextStatement(from); + + if (ifstat.getIfstat() == null) { + noifstat = true; + + if (ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) { + ifdirect = true; + } + else { + ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination()); + } + } + else { + List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges(); + if (!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { + ifdirect = true; + } + else { + ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from); + } + } + + Statement last = parent.type == Statement.TYPE_SEQUENCE ? ((SequenceStatement)parent).getStats().getLast() : ifstat; + noelsestat = (last == ifstat); + + if (!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { + elsedirect = true; + } + else { + elsedirect = hasDirectEndEdge(last, from); + } + + if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) { + return false; + } + + if (!ifdirect && !noifstat) { + ifdirectpath = existsPath(ifstat, next); + } + + if (!elsedirect && !noelsestat) { + SequenceStatement sequence = (SequenceStatement)parent; + + for (int i = sequence.getStats().size() - 1; i >= 0; i--) { + Statement sttemp = sequence.getStats().get(i); + if (sttemp == ifstat) { + break; + } + else { + if (elsedirectpath = existsPath(sttemp, next)) { + break; + } + } + } + } + + if ((ifdirect || ifdirectpath) && (elsedirect || elsedirectpath) && !noifstat && !noelsestat) { // if - then - else + + SequenceStatement sequence = (SequenceStatement)parent; + + // build and cut the new else statement + List<Statement> lst = new ArrayList<Statement>(); + for (int i = sequence.getStats().size() - 1; i >= 0; i--) { + Statement sttemp = sequence.getStats().get(i); + if (sttemp == ifstat) { + break; + } + else { + lst.add(0, sttemp); + } + } + + Statement stelse; + if (lst.size() == 1) { + stelse = lst.get(0); + } + else { + stelse = new SequenceStatement(lst); + stelse.setAllParent(); + } + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); + for (Statement st : lst) { + sequence.getStats().removeWithKey(st.id); + } + + StatEdge elseedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); + ifstat.getFirst().addSuccessor(elseedge); + ifstat.setElsestat(stelse); + ifstat.setElseEdge(elseedge); + + ifstat.getStats().addWithKey(stelse, stelse.id); + stelse.setParent(ifstat); + + // if(next.type != Statement.TYPE_DUMMYEXIT && (ifdirect || elsedirect)) { + // StatEdge breakedge = new StatEdge(StatEdge.TYPE_BREAK, ifstat, next); + // sequence.addLabeledEdge(breakedge); + // ifstat.addSuccessor(breakedge); + // } + + ifstat.iftype = IfStatement.IFTYPE_IFELSE; + } + else if (ifdirect && (!elsedirect || (noifstat && !noelsestat))) { // if - then + + // negate the if condition + IfExprent statexpr = ifstat.getHeadexprent(); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + + if (noelsestat) { + StatEdge ifedge = ifstat.getIfEdge(); + StatEdge elseedge = ifstat.getAllSuccessorEdges().get(0); + + if (noifstat) { + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.removeSuccessor(elseedge); + + ifedge.setSource(ifstat); + elseedge.setSource(ifstat.getFirst()); + + ifstat.addSuccessor(ifedge); + ifstat.getFirst().addSuccessor(elseedge); + + ifstat.setIfEdge(elseedge); + } + else { + Statement ifbranch = ifstat.getIfstat(); + SequenceStatement newseq = new SequenceStatement(Arrays.asList(new Statement[]{ifstat, ifbranch})); + + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.getStats().removeWithKey(ifbranch.id); + ifstat.setIfstat(null); + + ifstat.removeSuccessor(elseedge); + elseedge.setSource(ifstat.getFirst()); + ifstat.getFirst().addSuccessor(elseedge); + + ifstat.setIfEdge(elseedge); + + ifstat.getParent().replaceStatement(ifstat, newseq); + newseq.setAllParent(); + + ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); + } + } + else { + + SequenceStatement sequence = (SequenceStatement)parent; + + // build and cut the new else statement + List<Statement> lst = new ArrayList<Statement>(); + for (int i = sequence.getStats().size() - 1; i >= 0; i--) { + Statement sttemp = sequence.getStats().get(i); + if (sttemp == ifstat) { + break; + } + else { + lst.add(0, sttemp); + } + } + + Statement stelse; + if (lst.size() == 1) { + stelse = lst.get(0); + } + else { + stelse = new SequenceStatement(lst); + stelse.setAllParent(); + } + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); + for (Statement st : lst) { + sequence.getStats().removeWithKey(st.id); + } + + if (noifstat) { + StatEdge ifedge = ifstat.getIfEdge(); + + ifstat.getFirst().removeSuccessor(ifedge); + ifedge.setSource(ifstat); + ifstat.addSuccessor(ifedge); + } + else { + Statement ifbranch = ifstat.getIfstat(); + + ifstat.getFirst().removeSuccessor(ifstat.getIfEdge()); + ifstat.getStats().removeWithKey(ifbranch.id); + + ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); + + sequence.getStats().addWithKey(ifbranch, ifbranch.id); + ifbranch.setParent(sequence); + } + + StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); + ifstat.getFirst().addSuccessor(newifedge); + ifstat.setIfstat(stelse); + ifstat.setIfEdge(newifedge); + + ifstat.getStats().addWithKey(stelse, stelse.id); + stelse.setParent(ifstat); + } + } + else { + return false; + } + + return true; + } + + private static boolean hasDirectEndEdge(Statement stat, Statement from) { + + for (StatEdge edge : stat.getAllSuccessorEdges()) { + if (MergeHelper.isDirectPath(from, edge.getDestination())) { + return true; + } + } + + if (stat.getExprents() == null) { + switch (stat.type) { + case Statement.TYPE_SEQUENCE: + return hasDirectEndEdge(stat.getStats().getLast(), from); + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + for (Statement st : stat.getStats()) { + if (hasDirectEndEdge(st, from)) { + return true; + } + } + break; + case Statement.TYPE_IF: + IfStatement ifstat = (IfStatement)stat; + if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) { + return hasDirectEndEdge(ifstat.getIfstat(), from) || + hasDirectEndEdge(ifstat.getElsestat(), from); + } + break; + case Statement.TYPE_SYNCRONIZED: + return hasDirectEndEdge(stat.getStats().get(1), from); + case Statement.TYPE_SWITCH: + for (Statement st : stat.getStats()) { + if (hasDirectEndEdge(st, from)) { + return true; + } + } + } + } + + return false; + } + + + private static Statement getNextStatement(Statement stat) { + + Statement parent = stat.getParent(); + switch (parent.type) { + case Statement.TYPE_ROOT: + return ((RootStatement)parent).getDummyExit(); + case Statement.TYPE_DO: + return parent; + case Statement.TYPE_SEQUENCE: + SequenceStatement sequence = (SequenceStatement)parent; + if (sequence.getStats().getLast() != stat) { + for (int i = sequence.getStats().size() - 1; i >= 0; i--) { + if (sequence.getStats().get(i) == stat) { + return sequence.getStats().get(i + 1); + } + } + } + } + + return getNextStatement(parent); + } + + private static boolean existsPath(Statement from, Statement to) { + + for (StatEdge edge : to.getAllPredecessorEdges()) { + if (from.containsStatementStrict(edge.getSource())) { + return true; + } + } + + return false; + } + + private static class IfNode { + public Statement value; + + public List<IfNode> succs = new ArrayList<IfNode>(); + public List<Integer> edgetypes = new ArrayList<Integer>(); + + public IfNode(Statement value) { + this.value = value; + } + + public void addChild(IfNode child, int type) { + succs.add(child); + edgetypes.add(new Integer(type)); + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java index 3fe181a..b9ffb8f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -1,223 +1,220 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.stats.*; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; - public class InlineSingleBlockHelper { - - - public static boolean inlineSingleBlocks(RootStatement root) { - - boolean res = inlineSingleBlocksRec(root); - - if(res) { - SequenceHelper.condenseSequences(root); - } - - return res; - } - - private static boolean inlineSingleBlocksRec(Statement stat) { - - boolean res = false; - - for(Statement st : stat.getStats()) { - res |= inlineSingleBlocksRec(st); - } - - if(stat.type == Statement.TYPE_SEQUENCE) { - - SequenceStatement seq = (SequenceStatement)stat; - for(int i=1;i<seq.getStats().size();i++) { - if(isInlineable(seq, i)) { - inlineBlock(seq, i); - return true; - } - } - } - - return res; - } - - private static void inlineBlock(SequenceStatement seq, int index) { - - Statement first = seq.getStats().get(index); - Statement pre = seq.getStats().get(index-1); - pre.removeSuccessor(pre.getAllSuccessorEdges().get(0)); // single regular edge - - StatEdge edge = first.getPredecessorEdges(StatEdge.TYPE_BREAK).get(0); - Statement source = edge.getSource(); - Statement parent = source.getParent(); - source.removeSuccessor(edge); - - List<Statement> lst = new ArrayList<Statement>(); - for(int i=seq.getStats().size()-1;i>=index;i--) { - lst.add(0, seq.getStats().remove(i)); - } - - if(parent.type == Statement.TYPE_IF && ((IfStatement)parent).iftype == IfStatement.IFTYPE_IF && - source == parent.getFirst()) { - IfStatement ifparent = (IfStatement)parent; - SequenceStatement block = new SequenceStatement(lst); - block.setAllParent(); - - StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, source, block); - source.addSuccessor(newedge); - ifparent.setIfEdge(newedge); - ifparent.setIfstat(block); - - ifparent.getStats().addWithKey(block, block.id); - block.setParent(ifparent); - - } else { - lst.add(0, source); - - SequenceStatement block = new SequenceStatement(lst); - block.setAllParent(); - - parent.replaceStatement(source, block); - - // LabelHelper.lowContinueLabels not applicable because of forward continue edges - // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); - // do it by hand - for(StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { - - block.removePredecessor(prededge); - prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, source); - source.addPredecessor(prededge); - - source.addLabeledEdge(prededge); - } - - - if(parent.type == Statement.TYPE_SWITCH) { - ((SwitchStatement)parent).sortEdgesAndNodes(); - } - - source.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, source, first)); - } - - } - - private static boolean isInlineable(SequenceStatement seq, int index) { - - Statement first = seq.getStats().get(index); - Statement pre = seq.getStats().get(index-1); - - if(pre.hasBasicSuccEdge()) { - return false; - } - - - List<StatEdge> lst = first.getPredecessorEdges(StatEdge.TYPE_BREAK); - - if(lst.size() == 1) { - StatEdge edge = lst.get(0); - - if(sameCatchRanges(edge)) { - if(edge.explicit) { - return true; - } else { - for(int i=index;i<seq.getStats().size();i++) { - if(!noExitLabels(seq.getStats().get(i), seq)) { - return false; - } - } - return true; - } - } - // FIXME: count labels properly - } - - return false; - } - - private static boolean sameCatchRanges(StatEdge edge) { - - Statement from = edge.getSource(); - Statement to = edge.getDestination(); - - for(;;) { - - Statement parent = from.getParent(); - if(parent.containsStatementStrict(to)) { - break; - } - - if(parent.type == Statement.TYPE_TRYCATCH || - parent.type == Statement.TYPE_CATCHALL) { - if(parent.getFirst() == from) { - return false; - } - } else if(parent.type == Statement.TYPE_SYNCRONIZED) { - if(parent.getStats().get(1) == from) { - return false; - } - } - - from = parent; - } - - return true; - } - - private static boolean noExitLabels(Statement block, Statement sequence) { - - for(StatEdge edge : block.getAllSuccessorEdges()) { - if(edge.getType() != StatEdge.TYPE_REGULAR && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { - if(!sequence.containsStatementStrict(edge.getDestination())) { - return false; - } - } - } - - for(Statement st : block.getStats()) { - if(!noExitLabels(st, sequence)) { - return false; - } - } - - return true; - } - - public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { - - if(closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { - - Statement parent = source.getParent(); - - if(parent == closure) { - return false; - } else { - return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || - isBreakEdgeLabeled(parent, closure); - } - } else { - return true; - } - } - - + + public static boolean inlineSingleBlocks(RootStatement root) { + + boolean res = inlineSingleBlocksRec(root); + + if (res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + private static boolean inlineSingleBlocksRec(Statement stat) { + + boolean res = false; + + for (Statement st : stat.getStats()) { + res |= inlineSingleBlocksRec(st); + } + + if (stat.type == Statement.TYPE_SEQUENCE) { + + SequenceStatement seq = (SequenceStatement)stat; + for (int i = 1; i < seq.getStats().size(); i++) { + if (isInlineable(seq, i)) { + inlineBlock(seq, i); + return true; + } + } + } + + return res; + } + + private static void inlineBlock(SequenceStatement seq, int index) { + + Statement first = seq.getStats().get(index); + Statement pre = seq.getStats().get(index - 1); + pre.removeSuccessor(pre.getAllSuccessorEdges().get(0)); // single regular edge + + StatEdge edge = first.getPredecessorEdges(StatEdge.TYPE_BREAK).get(0); + Statement source = edge.getSource(); + Statement parent = source.getParent(); + source.removeSuccessor(edge); + + List<Statement> lst = new ArrayList<Statement>(); + for (int i = seq.getStats().size() - 1; i >= index; i--) { + lst.add(0, seq.getStats().remove(i)); + } + + if (parent.type == Statement.TYPE_IF && ((IfStatement)parent).iftype == IfStatement.IFTYPE_IF && + source == parent.getFirst()) { + IfStatement ifparent = (IfStatement)parent; + SequenceStatement block = new SequenceStatement(lst); + block.setAllParent(); + + StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, source, block); + source.addSuccessor(newedge); + ifparent.setIfEdge(newedge); + ifparent.setIfstat(block); + + ifparent.getStats().addWithKey(block, block.id); + block.setParent(ifparent); + } + else { + lst.add(0, source); + + SequenceStatement block = new SequenceStatement(lst); + block.setAllParent(); + + parent.replaceStatement(source, block); + + // LabelHelper.lowContinueLabels not applicable because of forward continue edges + // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); + // do it by hand + for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + + block.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, source); + source.addPredecessor(prededge); + + source.addLabeledEdge(prededge); + } + + + if (parent.type == Statement.TYPE_SWITCH) { + ((SwitchStatement)parent).sortEdgesAndNodes(); + } + + source.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, source, first)); + } + } + + private static boolean isInlineable(SequenceStatement seq, int index) { + + Statement first = seq.getStats().get(index); + Statement pre = seq.getStats().get(index - 1); + + if (pre.hasBasicSuccEdge()) { + return false; + } + + + List<StatEdge> lst = first.getPredecessorEdges(StatEdge.TYPE_BREAK); + + if (lst.size() == 1) { + StatEdge edge = lst.get(0); + + if (sameCatchRanges(edge)) { + if (edge.explicit) { + return true; + } + else { + for (int i = index; i < seq.getStats().size(); i++) { + if (!noExitLabels(seq.getStats().get(i), seq)) { + return false; + } + } + return true; + } + } + // FIXME: count labels properly + } + + return false; + } + + private static boolean sameCatchRanges(StatEdge edge) { + + Statement from = edge.getSource(); + Statement to = edge.getDestination(); + + for (; ; ) { + + Statement parent = from.getParent(); + if (parent.containsStatementStrict(to)) { + break; + } + + if (parent.type == Statement.TYPE_TRYCATCH || + parent.type == Statement.TYPE_CATCHALL) { + if (parent.getFirst() == from) { + return false; + } + } + else if (parent.type == Statement.TYPE_SYNCRONIZED) { + if (parent.getStats().get(1) == from) { + return false; + } + } + + from = parent; + } + + return true; + } + + private static boolean noExitLabels(Statement block, Statement sequence) { + + for (StatEdge edge : block.getAllSuccessorEdges()) { + if (edge.getType() != StatEdge.TYPE_REGULAR && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { + if (!sequence.containsStatementStrict(edge.getDestination())) { + return false; + } + } + } + + for (Statement st : block.getStats()) { + if (!noExitLabels(st, sequence)) { + return false; + } + } + + return true; + } + + public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { + + if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { + + Statement parent = source.getParent(); + + if (parent == closure) { + return false; + } + else { + return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || + isBreakEdgeLabeled(parent, closure); + } + } + else { + return true; + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java index 6d7a897..2e00d5b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -1,521 +1,513 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; + +import java.util.*; +import java.util.Map.Entry; public class LabelHelper { - - public static void cleanUpEdges(RootStatement root) { - - resetAllEdges(root); - - removeNonImmediateEdges(root); - - liftClosures(root); - - lowContinueLabels(root, new HashSet<StatEdge>()); - - lowClosures(root); - - } - - public static void identifyLabels(RootStatement root) { - - setExplicitEdges(root); - - hideDefaultSwitchEdges(root); - - processStatementLabel(root); - - setRetEdgesUnlabeled(root); - } - - private static void liftClosures(Statement stat) { - - for(StatEdge edge : stat.getAllSuccessorEdges()) { - switch(edge.getType()) { - case StatEdge.TYPE_CONTINUE: - if(edge.getDestination() != edge.closure) { - edge.getDestination().addLabeledEdge(edge); - } - break; - case StatEdge.TYPE_BREAK: - Statement dest = edge.getDestination(); - if(dest.type != Statement.TYPE_DUMMYEXIT) { - Statement parent = dest.getParent(); - - List<Statement> lst = new ArrayList<Statement>(); - if(parent.type == Statement.TYPE_SEQUENCE) { - lst.addAll(((SequenceStatement)parent).getStats()); - } else if(parent.type == Statement.TYPE_SWITCH) { - lst.addAll(((SwitchStatement)parent).getCaseStatements()); - } - - for(int i=0;i<lst.size();i++) { - if(lst.get(i) == dest) { - lst.get(i-1).addLabeledEdge(edge); - break; - } - } - } - } - } - - for(Statement st : stat.getStats()) { - liftClosures(st); - } - - } - - private static void removeNonImmediateEdges(Statement stat) { - - for(Statement st : stat.getStats()) { - removeNonImmediateEdges(st); - } - - if(!stat.hasBasicSuccEdge()) { - for(StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_CONTINUE | StatEdge.TYPE_BREAK)) { - stat.removeSuccessor(edge); - } - } - } - - public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) { - - boolean ok = (stat.type != Statement.TYPE_DO); - if(!ok) { - DoStatement dostat = (DoStatement)stat; - ok = dostat.getLooptype() == DoStatement.LOOP_DO || - dostat.getLooptype() == DoStatement.LOOP_WHILE || - (dostat.getLooptype() == DoStatement.LOOP_FOR && dostat.getIncExprent() == null); - } - - if(ok) { - edges.addAll(stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE)); - } - - if(ok && stat.type == Statement.TYPE_DO) { - for(StatEdge edge: edges) { - if(stat.containsStatementStrict(edge.getSource())) { - - edge.getDestination().removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); - stat.addPredecessor(edge); - - stat.addLabeledEdge(edge); - } - } - } - - for(Statement st: stat.getStats()) { - if(st == stat.getFirst()) { - lowContinueLabels(st, edges); - } else { - lowContinueLabels(st, new HashSet<StatEdge>()); - } - } - } - - public static void lowClosures(Statement stat) { - - for(StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) { - - if(edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ? - for(Statement st : stat.getStats()) { - if(st.containsStatementStrict(edge.getSource())) { - if(MergeHelper.isDirectPath(st, edge.getDestination())) { - st.addLabeledEdge(edge); - } - } - } - } - } - - for(Statement st: stat.getStats()) { - lowClosures(st); - } - - } - - private static void resetAllEdges(Statement stat) { - - for(Statement st: stat.getStats()) { - resetAllEdges(st); - } - - for(StatEdge edge: stat.getAllSuccessorEdges()) { - edge.explicit = true; - edge.labeled = true; - } - } - - private static void setRetEdgesUnlabeled(RootStatement root) { - Statement exit = root.getDummyExit(); - for(StatEdge edge: exit.getAllPredecessorEdges()) { - List<Exprent> lst = edge.getSource().getExprents(); - if(edge.getType() == StatEdge.TYPE_FINALLYEXIT || (lst != null && !lst.isEmpty() && - lst.get(lst.size()-1).type == Exprent.EXPRENT_EXIT)) { - edge.labeled = false; - } - } - } - - private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) { - - HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>(); - - if(stat.getExprents() != null) { - return mapEdges; - } - - - switch(stat.type) { - case Statement.TYPE_TRYCATCH: - case Statement.TYPE_CATCHALL: - - for(Statement st : stat.getStats()) { - HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st); - processEdgesWithNext(st, mapEdges1, null); - - if(stat.type == Statement.TYPE_TRYCATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit - // merge the maps - if(mapEdges1 != null) { - for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { - if(mapEdges.containsKey(entr.getKey())) { - mapEdges.get(entr.getKey()).addAll(entr.getValue()); - } else { - mapEdges.put(entr.getKey(), entr.getValue()); - } - } - } - } - } - - break; - case Statement.TYPE_DO: - mapEdges = setExplicitEdges(stat.getFirst()); - processEdgesWithNext(stat.getFirst(), mapEdges, stat); - break; - case Statement.TYPE_IF: - IfStatement ifstat = (IfStatement)stat; - // head statement is a basic block - if(ifstat.getIfstat() == null) { // empty if - processEdgesWithNext(ifstat.getFirst(), mapEdges, null); - } else { - if(ifstat.getIfstat() != null) { - mapEdges = setExplicitEdges(ifstat.getIfstat()); - processEdgesWithNext(ifstat.getIfstat(), mapEdges, null); - } - - HashMap<Statement, List<StatEdge>> mapEdges1 = null; - if(ifstat.getElsestat() != null) { - mapEdges1 = setExplicitEdges(ifstat.getElsestat()); - processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null); - } - - // merge the maps - if(mapEdges1 != null) { - for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { - if(mapEdges.containsKey(entr.getKey())) { - mapEdges.get(entr.getKey()).addAll(entr.getValue()); - } else { - mapEdges.put(entr.getKey(), entr.getValue()); - } - } - } - } - break; - case Statement.TYPE_ROOT: - mapEdges = setExplicitEdges(stat.getFirst()); - processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit()); - break; - case Statement.TYPE_SEQUENCE: - int index = 0; - while(index < stat.getStats().size()-1) { - Statement st = stat.getStats().get(index); - processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index+1)); - index++; - } - - Statement st = stat.getStats().get(index); - mapEdges = setExplicitEdges(st); - processEdgesWithNext(st, mapEdges, null); - break; - case Statement.TYPE_SWITCH: - SwitchStatement swst = (SwitchStatement)stat; - - for(int i=0;i<swst.getCaseStatements().size()-1;i++) { - Statement stt = swst.getCaseStatements().get(i); - Statement stnext = swst.getCaseStatements().get(i+1); - - if(stnext.getExprents() != null && stnext.getExprents().isEmpty()) { - stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); - } - processEdgesWithNext(stt, setExplicitEdges(stt), stnext); - } - - int last = swst.getCaseStatements().size()-1; - if(last >= 0) { // empty switch possible - Statement stlast = swst.getCaseStatements().get(last); - if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) { - StatEdge edge = stlast.getAllSuccessorEdges().get(0); - mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {edge}))); - } else { - mapEdges = setExplicitEdges(stlast); - processEdgesWithNext(stlast, mapEdges, null); - } - } - - break; - case Statement.TYPE_SYNCRONIZED: - SynchronizedStatement synstat = (SynchronizedStatement)stat; - - processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block? - mapEdges = setExplicitEdges(synstat.getBody()); - processEdgesWithNext(synstat.getBody(), mapEdges, null); - } - - - return mapEdges; - } - - private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) { - - StatEdge statedge = null; - - List<StatEdge> lstSuccs = stat.getAllSuccessorEdges(); - if(!lstSuccs.isEmpty()) { - statedge = lstSuccs.get(0); - - if(statedge.getDestination() == next) { - statedge.explicit = false; - statedge = null; - } else { - next = statedge.getDestination(); - } - } - - // no next for a do statement - if(stat.type == Statement.TYPE_DO && ((DoStatement)stat).getLooptype() == DoStatement.LOOP_DO) { - next = null; - } - - if(next == null) { - if(mapEdges.size() == 1) { - List<StatEdge> lstEdges = mapEdges.values().iterator().next(); - if(lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != Statement.TYPE_DUMMYEXIT) { - StatEdge edge_example = lstEdges.get(0); - - Statement closure = stat.getParent(); - if(!closure.containsStatementStrict(edge_example.closure)) { - closure = edge_example.closure; - } - - StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); - stat.addSuccessor(newedge); - - for(StatEdge edge : lstEdges) { - edge.explicit = false; - } - - mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {newedge}))); - } - } - } else { - - boolean implfound = false; - - for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { - if(entr.getKey() == next) { - for(StatEdge edge : entr.getValue()) { - edge.explicit = false; - } - implfound = true; - break; - } - } - - if(stat.getAllSuccessorEdges().isEmpty() && !implfound) { - List<StatEdge> lstEdges = null; - for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { - if(entr.getKey().type != Statement.TYPE_DUMMYEXIT && - (lstEdges == null || entr.getValue().size() > lstEdges.size())) { - lstEdges = entr.getValue(); - } - } - - if(lstEdges != null && lstEdges.size() > 1) { - StatEdge edge_example = lstEdges.get(0); - - Statement closure = stat.getParent(); - if(!closure.containsStatementStrict(edge_example.closure)) { - closure = edge_example.closure; - } - - StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); - stat.addSuccessor(newedge); - - for(StatEdge edge : lstEdges) { - edge.explicit = false; - } - } - } - - mapEdges.clear(); - } - - if(statedge != null) { - mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {statedge}))); - } - - } - - private static void hideDefaultSwitchEdges(Statement stat) { - - if(stat.type == Statement.TYPE_SWITCH) { - SwitchStatement swst = (SwitchStatement)stat; - - int last = swst.getCaseStatements().size()-1; - if(last >= 0) { // empty switch possible - Statement stlast = swst.getCaseStatements().get(last); - - if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) { - if(!stlast.getAllSuccessorEdges().get(0).explicit) { - List<StatEdge> lstEdges = swst.getCaseEdges().get(last); - lstEdges.remove(swst.getDefault_edge()); - - if(lstEdges.isEmpty()) { - swst.getCaseStatements().remove(last); - swst.getCaseEdges().remove(last); - } - } - } - } - } - - for(Statement st : stat.getStats()) { - hideDefaultSwitchEdges(st); - } - - } - - private static HashSet<Statement>[] processStatementLabel(Statement stat) { - - HashSet<Statement> setBreak = new HashSet<Statement>(); - HashSet<Statement> setContinue = new HashSet<Statement>(); - - if(stat.getExprents() == null) { - for(Statement st: stat.getStats()) { - HashSet<Statement>[] arr = processStatementLabel(st); - - setBreak.addAll(arr[0]); - setContinue.addAll(arr[1]); - } - - boolean shieldtype = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH); - - for(StatEdge edge: stat.getLabelEdges()) { - if(edge.explicit) { - if(shieldtype && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) || - (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) { - edge.labeled = false; - } - } - } - - switch(stat.type) { - case Statement.TYPE_DO: - setContinue.clear(); - case Statement.TYPE_SWITCH: - setBreak.clear(); - } - } - - setBreak.add(stat); - setContinue.add(stat); - - return new HashSet[] {setBreak, setContinue}; - } - - public static void replaceContinueWithBreak(Statement stat) { - - if(stat.type == Statement.TYPE_DO) { - - List<StatEdge> lst = stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE); - - for(StatEdge edge : lst) { - - if(edge.explicit) { - Statement minclosure = getMinContinueClosure(edge); - - if(minclosure != edge.closure && - !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) { - edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); - edge.labeled = false; - minclosure.addLabeledEdge(edge); - } - } - } - } - - for(Statement st : stat.getStats()) { - replaceContinueWithBreak(st); - } - - } - - private static Statement getMinContinueClosure(StatEdge edge) { - - Statement closure = edge.closure; - for(;;) { - - boolean found = false; - - for(Statement st : closure.getStats()) { - if(st.containsStatementStrict(edge.getSource())) { - if(MergeHelper.isDirectPath(st, edge.getDestination())) { - closure = st; - found = true; - break; - } - } - } - - if(!found) { - break; - } - } - - return closure; - } - + + public static void cleanUpEdges(RootStatement root) { + + resetAllEdges(root); + + removeNonImmediateEdges(root); + + liftClosures(root); + + lowContinueLabels(root, new HashSet<StatEdge>()); + + lowClosures(root); + } + + public static void identifyLabels(RootStatement root) { + + setExplicitEdges(root); + + hideDefaultSwitchEdges(root); + + processStatementLabel(root); + + setRetEdgesUnlabeled(root); + } + + private static void liftClosures(Statement stat) { + + for (StatEdge edge : stat.getAllSuccessorEdges()) { + switch (edge.getType()) { + case StatEdge.TYPE_CONTINUE: + if (edge.getDestination() != edge.closure) { + edge.getDestination().addLabeledEdge(edge); + } + break; + case StatEdge.TYPE_BREAK: + Statement dest = edge.getDestination(); + if (dest.type != Statement.TYPE_DUMMYEXIT) { + Statement parent = dest.getParent(); + + List<Statement> lst = new ArrayList<Statement>(); + if (parent.type == Statement.TYPE_SEQUENCE) { + lst.addAll(((SequenceStatement)parent).getStats()); + } + else if (parent.type == Statement.TYPE_SWITCH) { + lst.addAll(((SwitchStatement)parent).getCaseStatements()); + } + + for (int i = 0; i < lst.size(); i++) { + if (lst.get(i) == dest) { + lst.get(i - 1).addLabeledEdge(edge); + break; + } + } + } + } + } + + for (Statement st : stat.getStats()) { + liftClosures(st); + } + } + + private static void removeNonImmediateEdges(Statement stat) { + + for (Statement st : stat.getStats()) { + removeNonImmediateEdges(st); + } + + if (!stat.hasBasicSuccEdge()) { + for (StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_CONTINUE | StatEdge.TYPE_BREAK)) { + stat.removeSuccessor(edge); + } + } + } + + public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) { + + boolean ok = (stat.type != Statement.TYPE_DO); + if (!ok) { + DoStatement dostat = (DoStatement)stat; + ok = dostat.getLooptype() == DoStatement.LOOP_DO || + dostat.getLooptype() == DoStatement.LOOP_WHILE || + (dostat.getLooptype() == DoStatement.LOOP_FOR && dostat.getIncExprent() == null); + } + + if (ok) { + edges.addAll(stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE)); + } + + if (ok && stat.type == Statement.TYPE_DO) { + for (StatEdge edge : edges) { + if (stat.containsStatementStrict(edge.getSource())) { + + edge.getDestination().removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); + stat.addPredecessor(edge); + + stat.addLabeledEdge(edge); + } + } + } + + for (Statement st : stat.getStats()) { + if (st == stat.getFirst()) { + lowContinueLabels(st, edges); + } + else { + lowContinueLabels(st, new HashSet<StatEdge>()); + } + } + } + + public static void lowClosures(Statement stat) { + + for (StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) { + + if (edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ? + for (Statement st : stat.getStats()) { + if (st.containsStatementStrict(edge.getSource())) { + if (MergeHelper.isDirectPath(st, edge.getDestination())) { + st.addLabeledEdge(edge); + } + } + } + } + } + + for (Statement st : stat.getStats()) { + lowClosures(st); + } + } + + private static void resetAllEdges(Statement stat) { + + for (Statement st : stat.getStats()) { + resetAllEdges(st); + } + + for (StatEdge edge : stat.getAllSuccessorEdges()) { + edge.explicit = true; + edge.labeled = true; + } + } + + private static void setRetEdgesUnlabeled(RootStatement root) { + Statement exit = root.getDummyExit(); + for (StatEdge edge : exit.getAllPredecessorEdges()) { + List<Exprent> lst = edge.getSource().getExprents(); + if (edge.getType() == StatEdge.TYPE_FINALLYEXIT || (lst != null && !lst.isEmpty() && + lst.get(lst.size() - 1).type == Exprent.EXPRENT_EXIT)) { + edge.labeled = false; + } + } + } + + private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) { + + HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>(); + + if (stat.getExprents() != null) { + return mapEdges; + } + + + switch (stat.type) { + case Statement.TYPE_TRYCATCH: + case Statement.TYPE_CATCHALL: + + for (Statement st : stat.getStats()) { + HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st); + processEdgesWithNext(st, mapEdges1, null); + + if (stat.type == Statement.TYPE_TRYCATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit + // merge the maps + if (mapEdges1 != null) { + for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { + if (mapEdges.containsKey(entr.getKey())) { + mapEdges.get(entr.getKey()).addAll(entr.getValue()); + } + else { + mapEdges.put(entr.getKey(), entr.getValue()); + } + } + } + } + } + + break; + case Statement.TYPE_DO: + mapEdges = setExplicitEdges(stat.getFirst()); + processEdgesWithNext(stat.getFirst(), mapEdges, stat); + break; + case Statement.TYPE_IF: + IfStatement ifstat = (IfStatement)stat; + // head statement is a basic block + if (ifstat.getIfstat() == null) { // empty if + processEdgesWithNext(ifstat.getFirst(), mapEdges, null); + } + else { + if (ifstat.getIfstat() != null) { + mapEdges = setExplicitEdges(ifstat.getIfstat()); + processEdgesWithNext(ifstat.getIfstat(), mapEdges, null); + } + + HashMap<Statement, List<StatEdge>> mapEdges1 = null; + if (ifstat.getElsestat() != null) { + mapEdges1 = setExplicitEdges(ifstat.getElsestat()); + processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null); + } + + // merge the maps + if (mapEdges1 != null) { + for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { + if (mapEdges.containsKey(entr.getKey())) { + mapEdges.get(entr.getKey()).addAll(entr.getValue()); + } + else { + mapEdges.put(entr.getKey(), entr.getValue()); + } + } + } + } + break; + case Statement.TYPE_ROOT: + mapEdges = setExplicitEdges(stat.getFirst()); + processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit()); + break; + case Statement.TYPE_SEQUENCE: + int index = 0; + while (index < stat.getStats().size() - 1) { + Statement st = stat.getStats().get(index); + processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index + 1)); + index++; + } + + Statement st = stat.getStats().get(index); + mapEdges = setExplicitEdges(st); + processEdgesWithNext(st, mapEdges, null); + break; + case Statement.TYPE_SWITCH: + SwitchStatement swst = (SwitchStatement)stat; + + for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) { + Statement stt = swst.getCaseStatements().get(i); + Statement stnext = swst.getCaseStatements().get(i + 1); + + if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) { + stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); + } + processEdgesWithNext(stt, setExplicitEdges(stt), stnext); + } + + int last = swst.getCaseStatements().size() - 1; + if (last >= 0) { // empty switch possible + Statement stlast = swst.getCaseStatements().get(last); + if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) { + StatEdge edge = stlast.getAllSuccessorEdges().get(0); + mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{edge}))); + } + else { + mapEdges = setExplicitEdges(stlast); + processEdgesWithNext(stlast, mapEdges, null); + } + } + + break; + case Statement.TYPE_SYNCRONIZED: + SynchronizedStatement synstat = (SynchronizedStatement)stat; + + processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block? + mapEdges = setExplicitEdges(synstat.getBody()); + processEdgesWithNext(synstat.getBody(), mapEdges, null); + } + + + return mapEdges; + } + + private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) { + + StatEdge statedge = null; + + List<StatEdge> lstSuccs = stat.getAllSuccessorEdges(); + if (!lstSuccs.isEmpty()) { + statedge = lstSuccs.get(0); + + if (statedge.getDestination() == next) { + statedge.explicit = false; + statedge = null; + } + else { + next = statedge.getDestination(); + } + } + + // no next for a do statement + if (stat.type == Statement.TYPE_DO && ((DoStatement)stat).getLooptype() == DoStatement.LOOP_DO) { + next = null; + } + + if (next == null) { + if (mapEdges.size() == 1) { + List<StatEdge> lstEdges = mapEdges.values().iterator().next(); + if (lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != Statement.TYPE_DUMMYEXIT) { + StatEdge edge_example = lstEdges.get(0); + + Statement closure = stat.getParent(); + if (!closure.containsStatementStrict(edge_example.closure)) { + closure = edge_example.closure; + } + + StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); + stat.addSuccessor(newedge); + + for (StatEdge edge : lstEdges) { + edge.explicit = false; + } + + mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{newedge}))); + } + } + } + else { + + boolean implfound = false; + + for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { + if (entr.getKey() == next) { + for (StatEdge edge : entr.getValue()) { + edge.explicit = false; + } + implfound = true; + break; + } + } + + if (stat.getAllSuccessorEdges().isEmpty() && !implfound) { + List<StatEdge> lstEdges = null; + for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { + if (entr.getKey().type != Statement.TYPE_DUMMYEXIT && + (lstEdges == null || entr.getValue().size() > lstEdges.size())) { + lstEdges = entr.getValue(); + } + } + + if (lstEdges != null && lstEdges.size() > 1) { + StatEdge edge_example = lstEdges.get(0); + + Statement closure = stat.getParent(); + if (!closure.containsStatementStrict(edge_example.closure)) { + closure = edge_example.closure; + } + + StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); + stat.addSuccessor(newedge); + + for (StatEdge edge : lstEdges) { + edge.explicit = false; + } + } + } + + mapEdges.clear(); + } + + if (statedge != null) { + mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{statedge}))); + } + } + + private static void hideDefaultSwitchEdges(Statement stat) { + + if (stat.type == Statement.TYPE_SWITCH) { + SwitchStatement swst = (SwitchStatement)stat; + + int last = swst.getCaseStatements().size() - 1; + if (last >= 0) { // empty switch possible + Statement stlast = swst.getCaseStatements().get(last); + + if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) { + if (!stlast.getAllSuccessorEdges().get(0).explicit) { + List<StatEdge> lstEdges = swst.getCaseEdges().get(last); + lstEdges.remove(swst.getDefault_edge()); + + if (lstEdges.isEmpty()) { + swst.getCaseStatements().remove(last); + swst.getCaseEdges().remove(last); + } + } + } + } + } + + for (Statement st : stat.getStats()) { + hideDefaultSwitchEdges(st); + } + } + + private static HashSet<Statement>[] processStatementLabel(Statement stat) { + + HashSet<Statement> setBreak = new HashSet<Statement>(); + HashSet<Statement> setContinue = new HashSet<Statement>(); + + if (stat.getExprents() == null) { + for (Statement st : stat.getStats()) { + HashSet<Statement>[] arr = processStatementLabel(st); + + setBreak.addAll(arr[0]); + setContinue.addAll(arr[1]); + } + + boolean shieldtype = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH); + + for (StatEdge edge : stat.getLabelEdges()) { + if (edge.explicit) { + if (shieldtype && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) || + (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) { + edge.labeled = false; + } + } + } + + switch (stat.type) { + case Statement.TYPE_DO: + setContinue.clear(); + case Statement.TYPE_SWITCH: + setBreak.clear(); + } + } + + setBreak.add(stat); + setContinue.add(stat); + + return new HashSet[]{setBreak, setContinue}; + } + + public static void replaceContinueWithBreak(Statement stat) { + + if (stat.type == Statement.TYPE_DO) { + + List<StatEdge> lst = stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE); + + for (StatEdge edge : lst) { + + if (edge.explicit) { + Statement minclosure = getMinContinueClosure(edge); + + if (minclosure != edge.closure && + !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); + edge.labeled = false; + minclosure.addLabeledEdge(edge); + } + } + } + } + + for (Statement st : stat.getStats()) { + replaceContinueWithBreak(st); + } + } + + private static Statement getMinContinueClosure(StatEdge edge) { + + Statement closure = edge.closure; + for (; ; ) { + + boolean found = false; + + for (Statement st : closure.getStats()) { + if (st.containsStatementStrict(edge.getSource())) { + if (MergeHelper.isDirectPath(st, edge.getDestination())) { + closure = st; + found = true; + break; + } + } + } + + if (!found) { + break; + } + } + + return closure; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index 5e0130b..180f162 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -1,209 +1,206 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.Set; - import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; public class LoopExtractHelper { - - - public static boolean extractLoops(Statement root) { - - boolean res = (extractLoopsRec(root) != 0); - - if(res) { - SequenceHelper.condenseSequences(root); - } - - return res; - } - - - private static int extractLoopsRec(Statement stat) { - - boolean res = false; - - for(;;) { - - boolean updated = false; - - for(Statement st: stat.getStats()) { - int extr = extractLoopsRec(st); - res |= (extr != 0); - - if(extr == 2) { - updated = true; - break; - } - } - - if(!updated) { - break; - } - } - - if(stat.type == Statement.TYPE_DO) { - if(extractLoop((DoStatement)stat)) { - return 2; - } - } - - return res?1:0; - } - - - - private static boolean extractLoop(DoStatement stat) { - - if(stat.getLooptype() != DoStatement.LOOP_DO) { - return false; - } - - for(StatEdge edge: stat.getLabelEdges()) { - if(edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { - return false; - } - } - - if(!extractLastIf(stat)) { - return extractFirstIf(stat); - } else { - return true; - } - } - - private static boolean extractLastIf(DoStatement stat) { - - // search for an if condition at the end of the loop - Statement last = stat.getFirst(); - while(last.type == Statement.TYPE_SEQUENCE) { - last = last.getStats().getLast(); - } - - if(last.type == Statement.TYPE_IF) { - IfStatement lastif = (IfStatement)last; - if(lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) { - Statement ifstat = lastif.getIfstat(); - StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); - - if(elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat) { - - Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); - set.remove(last); - - if(set.isEmpty()) { // no direct continues in a do{}while loop - if(isExternStatement(stat, ifstat, ifstat)) { - extractIfBlock(stat, lastif); - return true; - } - } - } - } - } - return false; - } - - private static boolean extractFirstIf(DoStatement stat) { - - // search for an if condition at the entrance of the loop - Statement first = stat.getFirst(); - while(first.type == Statement.TYPE_SEQUENCE) { - first = first.getFirst(); - } - - // found an if statement - if(first.type == Statement.TYPE_IF) { - IfStatement firstif = (IfStatement)first; - - if(firstif.getFirst().getExprents().isEmpty()) { - - if(firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat()!=null) { - Statement ifstat = firstif.getIfstat(); - - if(isExternStatement(stat, ifstat, ifstat)) { - extractIfBlock(stat, firstif); - return true; - } - } - } - } - return false; - } - - - - private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) { - - for(StatEdge edge: stat.getAllSuccessorEdges()) { - if(loop.containsStatement(edge.getDestination()) && - !block.containsStatement(edge.getDestination())) { - return false; - } - } - - for(Statement st: stat.getStats()) { - if(!isExternStatement(loop, block, st)) { - return false; - } - } - - return true; - } - - - private static void extractIfBlock(DoStatement loop, IfStatement ifstat) { - - Statement target = ifstat.getIfstat(); - StatEdge ifedge = ifstat.getIfEdge(); - - ifstat.setIfstat(null); - ifedge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, ifedge, StatEdge.TYPE_BREAK); - ifedge.closure = loop; - ifstat.getStats().removeWithKey(target.id); - - loop.addLabeledEdge(ifedge); - - SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[] {loop, target})); - loop.getParent().replaceStatement(loop, block); - block.setAllParent(); - - loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target)); - - for(StatEdge edge: new ArrayList<StatEdge>(block.getLabelEdges())) { - if(edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) { - loop.addLabeledEdge(edge); - } - } - - for(StatEdge edge: block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { - if(loop.containsStatementStrict(edge.getSource())) { - block.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, loop); - loop.addPredecessor(edge); - } - } - } - + + public static boolean extractLoops(Statement root) { + + boolean res = (extractLoopsRec(root) != 0); + + if (res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + + private static int extractLoopsRec(Statement stat) { + + boolean res = false; + + for (; ; ) { + + boolean updated = false; + + for (Statement st : stat.getStats()) { + int extr = extractLoopsRec(st); + res |= (extr != 0); + + if (extr == 2) { + updated = true; + break; + } + } + + if (!updated) { + break; + } + } + + if (stat.type == Statement.TYPE_DO) { + if (extractLoop((DoStatement)stat)) { + return 2; + } + } + + return res ? 1 : 0; + } + + + private static boolean extractLoop(DoStatement stat) { + + if (stat.getLooptype() != DoStatement.LOOP_DO) { + return false; + } + + for (StatEdge edge : stat.getLabelEdges()) { + if (edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { + return false; + } + } + + if (!extractLastIf(stat)) { + return extractFirstIf(stat); + } + else { + return true; + } + } + + private static boolean extractLastIf(DoStatement stat) { + + // search for an if condition at the end of the loop + Statement last = stat.getFirst(); + while (last.type == Statement.TYPE_SEQUENCE) { + last = last.getStats().getLast(); + } + + if (last.type == Statement.TYPE_IF) { + IfStatement lastif = (IfStatement)last; + if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) { + Statement ifstat = lastif.getIfstat(); + StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); + + if (elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat) { + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(last); + + if (set.isEmpty()) { // no direct continues in a do{}while loop + if (isExternStatement(stat, ifstat, ifstat)) { + extractIfBlock(stat, lastif); + return true; + } + } + } + } + } + return false; + } + + private static boolean extractFirstIf(DoStatement stat) { + + // search for an if condition at the entrance of the loop + Statement first = stat.getFirst(); + while (first.type == Statement.TYPE_SEQUENCE) { + first = first.getFirst(); + } + + // found an if statement + if (first.type == Statement.TYPE_IF) { + IfStatement firstif = (IfStatement)first; + + if (firstif.getFirst().getExprents().isEmpty()) { + + if (firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat() != null) { + Statement ifstat = firstif.getIfstat(); + + if (isExternStatement(stat, ifstat, ifstat)) { + extractIfBlock(stat, firstif); + return true; + } + } + } + } + return false; + } + + + private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) { + + for (StatEdge edge : stat.getAllSuccessorEdges()) { + if (loop.containsStatement(edge.getDestination()) && + !block.containsStatement(edge.getDestination())) { + return false; + } + } + + for (Statement st : stat.getStats()) { + if (!isExternStatement(loop, block, st)) { + return false; + } + } + + return true; + } + + + private static void extractIfBlock(DoStatement loop, IfStatement ifstat) { + + Statement target = ifstat.getIfstat(); + StatEdge ifedge = ifstat.getIfEdge(); + + ifstat.setIfstat(null); + ifedge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, ifedge, StatEdge.TYPE_BREAK); + ifedge.closure = loop; + ifstat.getStats().removeWithKey(target.id); + + loop.addLabeledEdge(ifedge); + + SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[]{loop, target})); + loop.getParent().replaceStatement(loop, block); + block.setAllParent(); + + loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target)); + + for (StatEdge edge : new ArrayList<StatEdge>(block.getLabelEdges())) { + if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) { + loop.addLabeledEdge(edge); + } + } + + for (StatEdge edge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + if (loop.containsStatementStrict(edge.getSource())) { + block.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, loop); + loop.addPredecessor(edge); + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java index 5811ef8..dcd501a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java @@ -1,207 +1,208 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import java.util.List; + public class LowBreakHelper { - - public static void lowBreakLabels(Statement root) { - - lowBreakLabelsRec(root); - - liftBreakLabels(root); - } - - private static void lowBreakLabelsRec(Statement stat) { - - for(;;) { - - boolean found = false; - - for(StatEdge edge: stat.getLabelEdges()) { - if(edge.getType() == StatEdge.TYPE_BREAK) { - Statement minclosure = getMinClosure(stat, edge.getSource()); - if(minclosure != stat) { - minclosure.addLabeledEdge(edge); - edge.labeled = isBreakEdgeLabeled(edge.getSource(), minclosure); - found = true; - break; - } - } - } - - if(!found) { - break; - } - } - - for(Statement st: stat.getStats()) { - lowBreakLabelsRec(st); - } - - } - - public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { - - if(closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { - - Statement parent = source.getParent(); - - if(parent == closure) { - return false; - } else { - return isBreakEdgeLabeled(parent, closure) || - (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH); - } - } else { - return true; - } - } - - public static Statement getMinClosure(Statement closure, Statement source) { - - for(;;) { - - Statement newclosure = null; - - switch(closure.type) { - case Statement.TYPE_SEQUENCE: - Statement last = closure.getStats().getLast(); - - if(isOkClosure(closure, source, last)) { - newclosure = last; - } - break; - case Statement.TYPE_IF: - IfStatement ifclosure = (IfStatement)closure; - if(isOkClosure(closure, source, ifclosure.getIfstat())) { - newclosure = ifclosure.getIfstat(); - } else if(isOkClosure(closure, source, ifclosure.getElsestat())) { - newclosure = ifclosure.getElsestat(); - } - break; - case Statement.TYPE_TRYCATCH: - for(Statement st: closure.getStats()) { - if(isOkClosure(closure, source, st)) { - newclosure = st; - break; - } - } - break; - case Statement.TYPE_SYNCRONIZED: - Statement body = ((SynchronizedStatement)closure).getBody(); - - if(isOkClosure(closure, source, body)) { - newclosure = body; - } - } - - if(newclosure == null) { - break; - } - - closure = newclosure; - } - - return closure; - } - - private static boolean isOkClosure(Statement closure, Statement source, Statement stat) { - - boolean ok = false; - - if(stat != null && stat.containsStatementStrict(source)) { - - List<StatEdge> lst = stat.getAllSuccessorEdges(); - - ok = lst.isEmpty(); - if(!ok) { - StatEdge edge = lst.get(0); - ok = (edge.closure == closure && edge.getType() == StatEdge.TYPE_BREAK); - } - } - - return ok; - } - - - private static void liftBreakLabels(Statement stat) { - - for(Statement st: stat.getStats()) { - liftBreakLabels(st); - } - - - for(;;) { - - boolean found = false; - - for(StatEdge edge: stat.getLabelEdges()) { - if(edge.explicit && edge.labeled && edge.getType() == StatEdge.TYPE_BREAK) { - - Statement newclosure = getMaxBreakLift(stat, edge); - - if(newclosure != null) { - newclosure.addLabeledEdge(edge); - edge.labeled = isBreakEdgeLabeled(edge.getSource(), newclosure); - - found = true; - break; - } - } - } - - if(!found) { - break; - } - } - - } - - private static Statement getMaxBreakLift(Statement stat, StatEdge edge) { - - Statement closure = null; - Statement newclosure = stat; - - while((newclosure = getNextBreakLift(newclosure, edge)) != null) { - closure = newclosure; - } - - return closure; - } - - private static Statement getNextBreakLift(Statement stat, StatEdge edge) { - - Statement closure = stat.getParent(); - - while(closure!=null && !closure.containsStatementStrict(edge.getDestination())) { - - boolean labeled = isBreakEdgeLabeled(edge.getSource(), closure); - if(closure.isLabeled() || !labeled) { - return closure; - } - - closure = closure.getParent(); - } - - return null; - } - + + public static void lowBreakLabels(Statement root) { + + lowBreakLabelsRec(root); + + liftBreakLabels(root); + } + + private static void lowBreakLabelsRec(Statement stat) { + + for (; ; ) { + + boolean found = false; + + for (StatEdge edge : stat.getLabelEdges()) { + if (edge.getType() == StatEdge.TYPE_BREAK) { + Statement minclosure = getMinClosure(stat, edge.getSource()); + if (minclosure != stat) { + minclosure.addLabeledEdge(edge); + edge.labeled = isBreakEdgeLabeled(edge.getSource(), minclosure); + found = true; + break; + } + } + } + + if (!found) { + break; + } + } + + for (Statement st : stat.getStats()) { + lowBreakLabelsRec(st); + } + } + + public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { + + if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { + + Statement parent = source.getParent(); + + if (parent == closure) { + return false; + } + else { + return isBreakEdgeLabeled(parent, closure) || + (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH); + } + } + else { + return true; + } + } + + public static Statement getMinClosure(Statement closure, Statement source) { + + for (; ; ) { + + Statement newclosure = null; + + switch (closure.type) { + case Statement.TYPE_SEQUENCE: + Statement last = closure.getStats().getLast(); + + if (isOkClosure(closure, source, last)) { + newclosure = last; + } + break; + case Statement.TYPE_IF: + IfStatement ifclosure = (IfStatement)closure; + if (isOkClosure(closure, source, ifclosure.getIfstat())) { + newclosure = ifclosure.getIfstat(); + } + else if (isOkClosure(closure, source, ifclosure.getElsestat())) { + newclosure = ifclosure.getElsestat(); + } + break; + case Statement.TYPE_TRYCATCH: + for (Statement st : closure.getStats()) { + if (isOkClosure(closure, source, st)) { + newclosure = st; + break; + } + } + break; + case Statement.TYPE_SYNCRONIZED: + Statement body = ((SynchronizedStatement)closure).getBody(); + + if (isOkClosure(closure, source, body)) { + newclosure = body; + } + } + + if (newclosure == null) { + break; + } + + closure = newclosure; + } + + return closure; + } + + private static boolean isOkClosure(Statement closure, Statement source, Statement stat) { + + boolean ok = false; + + if (stat != null && stat.containsStatementStrict(source)) { + + List<StatEdge> lst = stat.getAllSuccessorEdges(); + + ok = lst.isEmpty(); + if (!ok) { + StatEdge edge = lst.get(0); + ok = (edge.closure == closure && edge.getType() == StatEdge.TYPE_BREAK); + } + } + + return ok; + } + + + private static void liftBreakLabels(Statement stat) { + + for (Statement st : stat.getStats()) { + liftBreakLabels(st); + } + + + for (; ; ) { + + boolean found = false; + + for (StatEdge edge : stat.getLabelEdges()) { + if (edge.explicit && edge.labeled && edge.getType() == StatEdge.TYPE_BREAK) { + + Statement newclosure = getMaxBreakLift(stat, edge); + + if (newclosure != null) { + newclosure.addLabeledEdge(edge); + edge.labeled = isBreakEdgeLabeled(edge.getSource(), newclosure); + + found = true; + break; + } + } + } + + if (!found) { + break; + } + } + } + + private static Statement getMaxBreakLift(Statement stat, StatEdge edge) { + + Statement closure = null; + Statement newclosure = stat; + + while ((newclosure = getNextBreakLift(newclosure, edge)) != null) { + closure = newclosure; + } + + return closure; + } + + private static Statement getNextBreakLift(Statement stat, StatEdge edge) { + + Statement closure = stat.getParent(); + + while (closure != null && !closure.containsStatementStrict(edge.getDestination())) { + + boolean labeled = isBreakEdgeLabeled(edge.getSource(), closure); + if (closure.isLabeled() || !labeled) { + return closure; + } + + closure = closure.getParent(); + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index b9149e6..4ce408b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -1,416 +1,421 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; public class MergeHelper { - public static void enhanceLoops(Statement root) { - - while(enhanceLoopsRec(root)); - - SequenceHelper.condenseSequences(root); - } - - private static boolean enhanceLoopsRec(Statement stat) { - - boolean res = false; - - for(Statement st: stat.getStats()) { - if(st.getExprents() == null) { - res |= enhanceLoopsRec(st); - } - } - - if(stat.type == Statement.TYPE_DO) { - res |= enhanceLoop((DoStatement)stat); - } - - return res; - } - - private static boolean enhanceLoop(DoStatement stat) { - - int oldloop = stat.getLooptype(); - - switch(oldloop) { - case DoStatement.LOOP_DO: - - // identify a while loop - if(matchWhile(stat)) { - // identify a for loop - subtype of while - matchFor(stat); - } else { - // identify a do{}while loop - matchDoWhile(stat); - } - - break; - case DoStatement.LOOP_WHILE: - matchFor(stat); - } - - return (stat.getLooptype() != oldloop); - } - - private static boolean matchDoWhile(DoStatement stat) { - - // search for an if condition at the end of the loop - Statement last = stat.getFirst(); - while(last.type == Statement.TYPE_SEQUENCE) { - last = last.getStats().getLast(); - } - - if(last.type == Statement.TYPE_IF) { - IfStatement lastif = (IfStatement)last; - if(lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() == null) { - StatEdge ifedge = lastif.getIfEdge(); - StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); - - if((ifedge.getType() == StatEdge.TYPE_BREAK && elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat - && isDirectPath(stat, ifedge.getDestination())) || - (ifedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.getType() == StatEdge.TYPE_BREAK && ifedge.closure == stat - && isDirectPath(stat, elseedge.getDestination()))) { - - Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); - set.remove(last); - - if(!set.isEmpty()) { - return false; - } - - - stat.setLooptype(DoStatement.LOOP_DOWHILE); - - IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy(); - if(ifedge.getType() == StatEdge.TYPE_BREAK) { - ifexpr.negateIf(); - } - stat.setConditionExprent(ifexpr.getCondition()); - lastif.getFirst().removeSuccessor(ifedge); - lastif.removeSuccessor(elseedge); - - // remove empty if - if(lastif.getFirst().getExprents().isEmpty()) { - removeLastEmptyStatement(stat, lastif); - } else { - lastif.setExprents(lastif.getFirst().getExprents()); - - StatEdge newedge = new StatEdge(StatEdge.TYPE_CONTINUE, lastif, stat); - lastif.addSuccessor(newedge); - stat.addLabeledEdge(newedge); - } - - if(stat.getAllSuccessorEdges().isEmpty()) { - StatEdge edge = elseedge.getType() == StatEdge.TYPE_CONTINUE?ifedge:elseedge; - - edge.setSource(stat); - if(edge.closure == stat) { - edge.closure = stat.getParent(); - } - stat.addSuccessor(edge); - } - - return true; - } - } - } - return false; - } - - private static boolean matchWhile(DoStatement stat) { - - // search for an if condition at the entrance of the loop - Statement first = stat.getFirst(); - while(first.type == Statement.TYPE_SEQUENCE) { - first = first.getFirst(); - } - - // found an if statement - if(first.type == Statement.TYPE_IF) { - IfStatement firstif = (IfStatement)first; - - if(firstif.getFirst().getExprents().isEmpty()) { - - if(firstif.iftype == IfStatement.IFTYPE_IF) { - if(firstif.getIfstat()==null) { - StatEdge ifedge = firstif.getIfEdge(); - if(isDirectPath(stat, ifedge.getDestination())) { - // exit condition identified - stat.setLooptype(DoStatement.LOOP_WHILE); - - // negate condition (while header) - IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); - ifexpr.negateIf(); - stat.setConditionExprent(ifexpr.getCondition()); - - // remove edges - firstif.getFirst().removeSuccessor(ifedge); - firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0)); - - if(stat.getAllSuccessorEdges().isEmpty()) { - ifedge.setSource(stat); - if(ifedge.closure == stat) { - ifedge.closure = stat.getParent(); - } - stat.addSuccessor(ifedge); - } - - // remove empty if statement as it is now part of the loop - if(firstif == stat.getFirst()) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList<Exprent>()); - stat.replaceStatement(firstif, bstat); - } else { - // precondition: sequence must contain more than one statement! - Statement sequence = firstif.getParent(); - sequence.getStats().removeWithKey(firstif.id); - sequence.setFirst(sequence.getStats().get(0)); - } - - return true; - } - } else { - StatEdge elseedge = firstif.getAllSuccessorEdges().get(0); - if(isDirectPath(stat, elseedge.getDestination())) { - // exit condition identified - stat.setLooptype(DoStatement.LOOP_WHILE); - - // no need to negate the while condition - stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition()); - - // remove edges - StatEdge ifedge = firstif.getIfEdge(); - firstif.getFirst().removeSuccessor(ifedge); - firstif.removeSuccessor(elseedge); - - if(stat.getAllSuccessorEdges().isEmpty()) { - - elseedge.setSource(stat); - if(elseedge.closure == stat) { - elseedge.closure = stat.getParent(); - } - stat.addSuccessor(elseedge); - } - - if(firstif.getIfstat() == null) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList<Exprent>()); - - ifedge.setSource(bstat); - bstat.addSuccessor(ifedge); - - stat.replaceStatement(firstif, bstat); - } else { - // replace the if statement with its content - first.getParent().replaceStatement(first, firstif.getIfstat()); - - // lift closures - for(StatEdge prededge : elseedge.getDestination().getPredecessorEdges(StatEdge.TYPE_BREAK)) { - if(stat.containsStatementStrict(prededge.closure)) { - stat.addLabeledEdge(prededge); - } - } - - LabelHelper.lowClosures(stat); - } - - return true; - } - } - } - } - } - return false; - } - - public static boolean isDirectPath(Statement stat, Statement endstat) { - - Set<Statement> setStat = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); - if(setStat.isEmpty()) { - Statement parent = stat.getParent(); - if(parent == null) { - return false; - } else { - switch(parent.type) { - case Statement.TYPE_ROOT: - return endstat.type == Statement.TYPE_DUMMYEXIT; - case Statement.TYPE_DO: - return (endstat == parent); - case Statement.TYPE_SWITCH: - SwitchStatement swst = (SwitchStatement)parent; - for(int i=0;i<swst.getCaseStatements().size()-1;i++){ - Statement stt = swst.getCaseStatements().get(i); - if(stt == stat) { - Statement stnext = swst.getCaseStatements().get(i+1); - - if(stnext.getExprents() != null && stnext.getExprents().isEmpty()) { - stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); - } - return (endstat == stnext); - } - } - default: - return isDirectPath(parent, endstat); - } - } - - } else { - return setStat.contains(endstat); - } - } - - private static boolean matchFor(DoStatement stat) { - - Exprent lastDoExprent = null, initDoExprent = null; - Statement lastData = null, preData = null; - - // get last exprent - lastData = getLastDirectData(stat.getFirst()); - if(lastData == null || lastData.getExprents().isEmpty()) { - return false; - } - - List<Exprent> lstExpr = lastData.getExprents(); - lastDoExprent = lstExpr.get(lstExpr.size()-1); - - boolean issingle = false; - if(lstExpr.size() == 1) { // single exprent - if(lastData.getAllPredecessorEdges().size() > 1) { // break edges - issingle = true; - } - } - - boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || - lastDoExprent.type == Exprent.EXPRENT_FUNCTION); - - if(!haslast) { - return false; - } - - boolean hasinit = false; - - // search for an initializing exprent - Statement current = stat; - for(;;){ - Statement parent = current.getParent(); - if(parent == null) { - break; - } - - if(parent.type == Statement.TYPE_SEQUENCE) { - if(current == parent.getFirst()) { - current = parent; - } else { - preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); - preData = getLastDirectData(preData); - if(preData != null && !preData.getExprents().isEmpty()) { - initDoExprent = preData.getExprents().get(preData.getExprents().size()-1); - if(initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) { - hasinit = true; - } - } - break; - } - } else { - break; - } - } - - if((hasinit && haslast) || issingle) { // FIXME: issingle sufficient? - - Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); - set.remove(lastData); - - if(!set.isEmpty()) { - return false; - } - - stat.setLooptype(DoStatement.LOOP_FOR); - if(hasinit) { - stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size()-1)); - } - stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size()-1)); - } - - if(lastData.getExprents().isEmpty()) { - List<StatEdge> lst = lastData.getAllSuccessorEdges(); - if(!lst.isEmpty()) { - lastData.removeSuccessor(lst.get(0)); - } - removeLastEmptyStatement(stat, lastData); - } - - return true; - } - - private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { - - if(stat == dostat.getFirst()) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList<Exprent>()); - dostat.replaceStatement(stat, bstat); - } else { - for(StatEdge edge: stat.getAllPredecessorEdges()) { - edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_CONTINUE); - - stat.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, dostat); - dostat.addPredecessor(edge); - - dostat.addLabeledEdge(edge); - } - - // parent is a sequence statement - stat.getParent().getStats().removeWithKey(stat.id); - } - - } - - private static Statement getLastDirectData(Statement stat) { - - if(stat.getExprents() != null) { - return stat; - } - - switch(stat.type) { - case Statement.TYPE_SEQUENCE: - for(int i=stat.getStats().size()-1;i>=0;i--) { - Statement tmp = getLastDirectData(stat.getStats().get(i)); - if(tmp == null || !tmp.getExprents().isEmpty()) { - return tmp; - } - } - } - return null; - } + public static void enhanceLoops(Statement root) { + + while (enhanceLoopsRec(root)) ; + + SequenceHelper.condenseSequences(root); + } + + private static boolean enhanceLoopsRec(Statement stat) { + + boolean res = false; + + for (Statement st : stat.getStats()) { + if (st.getExprents() == null) { + res |= enhanceLoopsRec(st); + } + } + + if (stat.type == Statement.TYPE_DO) { + res |= enhanceLoop((DoStatement)stat); + } + + return res; + } + + private static boolean enhanceLoop(DoStatement stat) { + + int oldloop = stat.getLooptype(); + + switch (oldloop) { + case DoStatement.LOOP_DO: + + // identify a while loop + if (matchWhile(stat)) { + // identify a for loop - subtype of while + matchFor(stat); + } + else { + // identify a do{}while loop + matchDoWhile(stat); + } + + break; + case DoStatement.LOOP_WHILE: + matchFor(stat); + } + + return (stat.getLooptype() != oldloop); + } + + private static boolean matchDoWhile(DoStatement stat) { + + // search for an if condition at the end of the loop + Statement last = stat.getFirst(); + while (last.type == Statement.TYPE_SEQUENCE) { + last = last.getStats().getLast(); + } + + if (last.type == Statement.TYPE_IF) { + IfStatement lastif = (IfStatement)last; + if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() == null) { + StatEdge ifedge = lastif.getIfEdge(); + StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); + + if ((ifedge.getType() == StatEdge.TYPE_BREAK && elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat + && isDirectPath(stat, ifedge.getDestination())) || + (ifedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.getType() == StatEdge.TYPE_BREAK && ifedge.closure == stat + && isDirectPath(stat, elseedge.getDestination()))) { + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(last); + + if (!set.isEmpty()) { + return false; + } + + + stat.setLooptype(DoStatement.LOOP_DOWHILE); + + IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy(); + if (ifedge.getType() == StatEdge.TYPE_BREAK) { + ifexpr.negateIf(); + } + stat.setConditionExprent(ifexpr.getCondition()); + lastif.getFirst().removeSuccessor(ifedge); + lastif.removeSuccessor(elseedge); + + // remove empty if + if (lastif.getFirst().getExprents().isEmpty()) { + removeLastEmptyStatement(stat, lastif); + } + else { + lastif.setExprents(lastif.getFirst().getExprents()); + + StatEdge newedge = new StatEdge(StatEdge.TYPE_CONTINUE, lastif, stat); + lastif.addSuccessor(newedge); + stat.addLabeledEdge(newedge); + } + + if (stat.getAllSuccessorEdges().isEmpty()) { + StatEdge edge = elseedge.getType() == StatEdge.TYPE_CONTINUE ? ifedge : elseedge; + + edge.setSource(stat); + if (edge.closure == stat) { + edge.closure = stat.getParent(); + } + stat.addSuccessor(edge); + } + + return true; + } + } + } + return false; + } + + private static boolean matchWhile(DoStatement stat) { + + // search for an if condition at the entrance of the loop + Statement first = stat.getFirst(); + while (first.type == Statement.TYPE_SEQUENCE) { + first = first.getFirst(); + } + + // found an if statement + if (first.type == Statement.TYPE_IF) { + IfStatement firstif = (IfStatement)first; + + if (firstif.getFirst().getExprents().isEmpty()) { + + if (firstif.iftype == IfStatement.IFTYPE_IF) { + if (firstif.getIfstat() == null) { + StatEdge ifedge = firstif.getIfEdge(); + if (isDirectPath(stat, ifedge.getDestination())) { + // exit condition identified + stat.setLooptype(DoStatement.LOOP_WHILE); + + // negate condition (while header) + IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); + ifexpr.negateIf(); + stat.setConditionExprent(ifexpr.getCondition()); + + // remove edges + firstif.getFirst().removeSuccessor(ifedge); + firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0)); + + if (stat.getAllSuccessorEdges().isEmpty()) { + ifedge.setSource(stat); + if (ifedge.closure == stat) { + ifedge.closure = stat.getParent(); + } + stat.addSuccessor(ifedge); + } + + // remove empty if statement as it is now part of the loop + if (firstif == stat.getFirst()) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + stat.replaceStatement(firstif, bstat); + } + else { + // precondition: sequence must contain more than one statement! + Statement sequence = firstif.getParent(); + sequence.getStats().removeWithKey(firstif.id); + sequence.setFirst(sequence.getStats().get(0)); + } + + return true; + } + } + else { + StatEdge elseedge = firstif.getAllSuccessorEdges().get(0); + if (isDirectPath(stat, elseedge.getDestination())) { + // exit condition identified + stat.setLooptype(DoStatement.LOOP_WHILE); + + // no need to negate the while condition + stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition()); + + // remove edges + StatEdge ifedge = firstif.getIfEdge(); + firstif.getFirst().removeSuccessor(ifedge); + firstif.removeSuccessor(elseedge); + + if (stat.getAllSuccessorEdges().isEmpty()) { + + elseedge.setSource(stat); + if (elseedge.closure == stat) { + elseedge.closure = stat.getParent(); + } + stat.addSuccessor(elseedge); + } + + if (firstif.getIfstat() == null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + + ifedge.setSource(bstat); + bstat.addSuccessor(ifedge); + + stat.replaceStatement(firstif, bstat); + } + else { + // replace the if statement with its content + first.getParent().replaceStatement(first, firstif.getIfstat()); + + // lift closures + for (StatEdge prededge : elseedge.getDestination().getPredecessorEdges(StatEdge.TYPE_BREAK)) { + if (stat.containsStatementStrict(prededge.closure)) { + stat.addLabeledEdge(prededge); + } + } + + LabelHelper.lowClosures(stat); + } + + return true; + } + } + } + } + } + return false; + } + + public static boolean isDirectPath(Statement stat, Statement endstat) { + + Set<Statement> setStat = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); + if (setStat.isEmpty()) { + Statement parent = stat.getParent(); + if (parent == null) { + return false; + } + else { + switch (parent.type) { + case Statement.TYPE_ROOT: + return endstat.type == Statement.TYPE_DUMMYEXIT; + case Statement.TYPE_DO: + return (endstat == parent); + case Statement.TYPE_SWITCH: + SwitchStatement swst = (SwitchStatement)parent; + for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) { + Statement stt = swst.getCaseStatements().get(i); + if (stt == stat) { + Statement stnext = swst.getCaseStatements().get(i + 1); + + if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) { + stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); + } + return (endstat == stnext); + } + } + default: + return isDirectPath(parent, endstat); + } + } + } + else { + return setStat.contains(endstat); + } + } + + private static boolean matchFor(DoStatement stat) { + + Exprent lastDoExprent = null, initDoExprent = null; + Statement lastData = null, preData = null; + + // get last exprent + lastData = getLastDirectData(stat.getFirst()); + if (lastData == null || lastData.getExprents().isEmpty()) { + return false; + } + + List<Exprent> lstExpr = lastData.getExprents(); + lastDoExprent = lstExpr.get(lstExpr.size() - 1); + + boolean issingle = false; + if (lstExpr.size() == 1) { // single exprent + if (lastData.getAllPredecessorEdges().size() > 1) { // break edges + issingle = true; + } + } + + boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || + lastDoExprent.type == Exprent.EXPRENT_FUNCTION); + + if (!haslast) { + return false; + } + + boolean hasinit = false; + + // search for an initializing exprent + Statement current = stat; + for (; ; ) { + Statement parent = current.getParent(); + if (parent == null) { + break; + } + + if (parent.type == Statement.TYPE_SEQUENCE) { + if (current == parent.getFirst()) { + current = parent; + } + else { + preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); + preData = getLastDirectData(preData); + if (preData != null && !preData.getExprents().isEmpty()) { + initDoExprent = preData.getExprents().get(preData.getExprents().size() - 1); + if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) { + hasinit = true; + } + } + break; + } + } + else { + break; + } + } + + if ((hasinit && haslast) || issingle) { // FIXME: issingle sufficient? + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(lastData); + + if (!set.isEmpty()) { + return false; + } + + stat.setLooptype(DoStatement.LOOP_FOR); + if (hasinit) { + stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size() - 1)); + } + stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1)); + } + + if (lastData.getExprents().isEmpty()) { + List<StatEdge> lst = lastData.getAllSuccessorEdges(); + if (!lst.isEmpty()) { + lastData.removeSuccessor(lst.get(0)); + } + removeLastEmptyStatement(stat, lastData); + } + + return true; + } + + private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { + + if (stat == dostat.getFirst()) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + dostat.replaceStatement(stat, bstat); + } + else { + for (StatEdge edge : stat.getAllPredecessorEdges()) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_CONTINUE); + + stat.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, dostat); + dostat.addPredecessor(edge); + + dostat.addLabeledEdge(edge); + } + + // parent is a sequence statement + stat.getParent().getStats().removeWithKey(stat.id); + } + } + + private static Statement getLastDirectData(Statement stat) { + + if (stat.getExprents() != null) { + return stat; + } + + switch (stat.type) { + case Statement.TYPE_SEQUENCE: + for (int i = stat.getStats().size() - 1; i >= 0; i--) { + Statement tmp = getLastDirectData(stat.getStats().get(i)); + if (tmp == null || !tmp.getExprents().isEmpty()) { + return tmp; + } + } + } + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index be90625..aacb61f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -1,24 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -29,125 +25,130 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatements import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + public class PPandMMHelper { - private boolean exprentReplaced; - - public boolean findPPandMM(RootStatement root) { - - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); - - LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); - stack.add(dgraph.first); - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - - boolean res = false; - - while(!stack.isEmpty()) { - - DirectNode node = stack.removeFirst(); - - if(setVisited.contains(node)) { - continue; - } - setVisited.add(node); - - res |= processExprentList(node.exprents); - - stack.addAll(node.succs); - } - - return res; - } - - private boolean processExprentList(List<Exprent> lst) { - - boolean result = false; - - for(int i=0;i<lst.size();i++) { - Exprent exprent = lst.get(i); - exprentReplaced = false; - - Exprent retexpr = processExprentRecursive(exprent); - if(retexpr != null) { - lst.set(i, retexpr); - - result = true; - i--; // process the same exprent again - } - - result |= exprentReplaced; - } - - return result; - } - - private Exprent processExprentRecursive(Exprent exprent) { - - boolean replaced = true; - while(replaced) { - replaced = false; - - for(Exprent expr: exprent.getAllExprents()) { - Exprent retexpr = processExprentRecursive(expr); - if(retexpr != null) { - exprent.replaceExprent(expr, retexpr); - replaced = true; - exprentReplaced = true; - break; - } - } - } - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)exprent; - - if(as.getRight().type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent func = (FunctionExprent)as.getRight(); - - VarType midlayer = null; - if(func.getFunctype() >= FunctionExprent.FUNCTION_I2L && - func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { - midlayer = func.getSimpleCastType(); - if(func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { - func = (FunctionExprent)func.getLstOperands().get(0); - } else { - return null; - } - } - - if(func.getFunctype() == FunctionExprent.FUNCTION_ADD || - func.getFunctype() == FunctionExprent.FUNCTION_SUB) { - Exprent econd = func.getLstOperands().get(0); - Exprent econst = func.getLstOperands().get(1); - - if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && - func.getFunctype() == FunctionExprent.FUNCTION_ADD) { - econd = econst; - econst = func.getLstOperands().get(0); - } - - if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { - Exprent left = as.getLeft(); - - VarType condtype = econd.getExprType(); - if(left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { - FunctionExprent ret = new FunctionExprent( - func.getFunctype() == FunctionExprent.FUNCTION_ADD?FunctionExprent.FUNCTION_PPI:FunctionExprent.FUNCTION_MMI, - Arrays.asList(new Exprent[]{econd})); - ret.setImplicitType(condtype); - - exprentReplaced = true; - return ret; - } - } - } - } - } - - return null; - } - + private boolean exprentReplaced; + + public boolean findPPandMM(RootStatement root) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + stack.add(dgraph.first); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + boolean res = false; + + while (!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if (setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + res |= processExprentList(node.exprents); + + stack.addAll(node.succs); + } + + return res; + } + + private boolean processExprentList(List<Exprent> lst) { + + boolean result = false; + + for (int i = 0; i < lst.size(); i++) { + Exprent exprent = lst.get(i); + exprentReplaced = false; + + Exprent retexpr = processExprentRecursive(exprent); + if (retexpr != null) { + lst.set(i, retexpr); + + result = true; + i--; // process the same exprent again + } + + result |= exprentReplaced; + } + + return result; + } + + private Exprent processExprentRecursive(Exprent exprent) { + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = processExprentRecursive(expr); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + exprentReplaced = true; + break; + } + } + } + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + + if (as.getRight().type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)as.getRight(); + + VarType midlayer = null; + if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L && + func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + midlayer = func.getSimpleCastType(); + if (func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { + func = (FunctionExprent)func.getLstOperands().get(0); + } + else { + return null; + } + } + + if (func.getFunctype() == FunctionExprent.FUNCTION_ADD || + func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && + func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { + Exprent left = as.getLeft(); + + VarType condtype = econd.getExprType(); + if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { + FunctionExprent ret = new FunctionExprent( + func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, + Arrays.asList(new Exprent[]{econd})); + ret.setImplicitType(condtype); + + exprentReplaced = true; + return ret; + } + } + } + } + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java index 0a0ef45..6dbc887 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java @@ -1,47 +1,49 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.exps.Exprent; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; - public class PrimitiveExprsList { - private List<Exprent> lstExprents = new ArrayList<Exprent>(); - - private ExprentStack stack = new ExprentStack(); - - public PrimitiveExprsList() {} - - public PrimitiveExprsList copyStack() { - PrimitiveExprsList prlst = new PrimitiveExprsList(); - prlst.setStack(stack.clone()); - return prlst; - } - - public List<Exprent> getLstExprents() { - return lstExprents; - } - - public ExprentStack getStack() { - return stack; - } - - public void setStack(ExprentStack stack) { - this.stack = stack; - } + private List<Exprent> lstExprents = new ArrayList<Exprent>(); + + private ExprentStack stack = new ExprentStack(); + + public PrimitiveExprsList() { + } + + public PrimitiveExprsList copyStack() { + PrimitiveExprsList prlst = new PrimitiveExprsList(); + prlst.setStack(stack.clone()); + return prlst; + } + + public List<Exprent> getLstExprents() { + return lstExprents; + } + + public ExprentStack getStack() { + return stack; + } + + public void setStack(ExprentStack stack) { + this.stack = stack; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index e8f1e0a..8dcf9d5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -1,428 +1,432 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + public class SecondaryFunctionsHelper { - private static final int[] funcsnot = new int[] { - FunctionExprent.FUNCTION_NE, - FunctionExprent.FUNCTION_EQ, - FunctionExprent.FUNCTION_GE, - FunctionExprent.FUNCTION_LT, - FunctionExprent.FUNCTION_LE, - FunctionExprent.FUNCTION_GT, - FunctionExprent.FUNCTION_COR, - FunctionExprent.FUNCTION_CADD - }; - - private static final HashMap<Integer, Integer[]> mapNumComparisons = new HashMap<Integer, Integer[]>(); - - static { - mapNumComparisons.put(FunctionExprent.FUNCTION_EQ, new Integer[] {FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT}); - mapNumComparisons.put(FunctionExprent.FUNCTION_NE, new Integer[] {FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE}); - mapNumComparisons.put(FunctionExprent.FUNCTION_GT, new Integer[] {FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null}); - mapNumComparisons.put(FunctionExprent.FUNCTION_GE, new Integer[] {null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT}); - mapNumComparisons.put(FunctionExprent.FUNCTION_LT, new Integer[] {null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE}); - mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[] {FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}); - } - - - public static boolean identifySecondaryFunctions(Statement stat) { - - if(stat.getExprents() == null) { - // if(){;}else{...} -> if(!){...} - if(stat.type == Statement.TYPE_IF) { - IfStatement ifelsestat = (IfStatement)stat; - Statement ifstat = ifelsestat.getIfstat(); - - if(ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null && - ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) { - - // move else to the if position - ifelsestat.getStats().removeWithKey(ifstat.id); - - ifelsestat.iftype = IfStatement.IFTYPE_IF; - ifelsestat.setIfstat(ifelsestat.getElsestat()); - ifelsestat.setElsestat(null); - - if(ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) { - StatEdge endedge = ifstat.getAllSuccessorEdges().get(0); - - ifstat.removeSuccessor(endedge); - endedge.setSource(ifelsestat); - if(endedge.closure != null) { - ifelsestat.getParent().addLabeledEdge(endedge); - } - ifelsestat.addSuccessor(endedge); - } - - ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge()); - - ifelsestat.setIfEdge(ifelsestat.getElseEdge()); - ifelsestat.setElseEdge(null); - - // negate head expression - ifelsestat.setNegated(!ifelsestat.isNegated()); - ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf()); - - return true; - } - } - } - - - boolean replaced = true; - while(replaced) { - replaced = false; - - List<Object> lstObjects = new ArrayList<Object>(stat.getExprents()==null?stat.getSequentialObjects():stat.getExprents()); - - for(int i=0;i<lstObjects.size();i++) { - Object obj = lstObjects.get(i); - - if(obj instanceof Statement) { - if(identifySecondaryFunctions((Statement)obj)) { - replaced = true; - break; - } - } else if(obj instanceof Exprent) { - Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true); - if(retexpr != null) { - if(stat.getExprents()==null) { - // only head expressions can be replaced! - stat.replaceExprent((Exprent)obj, retexpr); - } else { - stat.getExprents().set(i, retexpr); - } - replaced = true; - break; - } - } - } - } - - return false; - } - - - private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) { - - if(exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fexpr = (FunctionExprent)exprent; - - switch(fexpr.getFunctype()) { - case FunctionExprent.FUNCTION_BOOLNOT: - - Exprent retparam = propagateBoolNot(fexpr); - - if(retparam != null) { - return retparam; - } - - break; - case FunctionExprent.FUNCTION_EQ: - case FunctionExprent.FUNCTION_NE: - case FunctionExprent.FUNCTION_GT: - case FunctionExprent.FUNCTION_GE: - case FunctionExprent.FUNCTION_LT: - case FunctionExprent.FUNCTION_LE: - Exprent expr1 = fexpr.getLstOperands().get(0); - Exprent expr2 = fexpr.getLstOperands().get(1); - - if(expr1.type == Exprent.EXPRENT_CONST) { - expr2 = expr1; - expr1 = fexpr.getLstOperands().get(1); - } - - if(expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) { - FunctionExprent funcexpr = (FunctionExprent)expr1; - ConstExprent cexpr = (ConstExprent)expr2; - - int functype = funcexpr.getFunctype(); - if(functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG || - functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG || - functype == FunctionExprent.FUNCTION_DCMPL) { - - int desttype = -1; - - Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype()); - if(destcons != null) { - int index = cexpr.getIntValue()+1; - if(index >= 0 && index <= 2) { - Integer destcon = destcons[index]; - if(destcon != null) { - desttype = destcon.intValue(); - } - } - } - - if(desttype >= 0) { - return new FunctionExprent(desttype, funcexpr.getLstOperands()); - } - } - } - } - } - - - boolean replaced = true; - while(replaced) { - replaced = false; - - for(Exprent expr: exprent.getAllExprents()) { - Exprent retexpr = identifySecondaryFunctions(expr, false); - if(retexpr != null) { - exprent.replaceExprent(expr, retexpr); - replaced = true; - break; - } - } - } - - switch(exprent.type) { - case Exprent.EXPRENT_FUNCTION: - FunctionExprent fexpr = (FunctionExprent)exprent; - List<Exprent> lstOperands = fexpr.getLstOperands(); - - switch(fexpr.getFunctype()) { - case FunctionExprent.FUNCTION_XOR: - for(int i=0;i<2;i++) { - Exprent operand = lstOperands.get(i); - VarType operandtype = operand.getExprType(); - - if(operand.type == Exprent.EXPRENT_CONST && - operandtype.type != CodeConstants.TYPE_BOOLEAN) { - ConstExprent cexpr = (ConstExprent)operand; - long val; - if(operandtype.type == CodeConstants.TYPE_LONG) { - val = ((Long)cexpr.getValue()).longValue(); - } else { - val = ((Integer)cexpr.getValue()).intValue(); - } - - if(val == -1) { - List<Exprent> lstBitNotOperand = new ArrayList<Exprent>(); - lstBitNotOperand.add(lstOperands.get(1-i)); - return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand); - } - } - } - break; - case FunctionExprent.FUNCTION_EQ: - case FunctionExprent.FUNCTION_NE: - if(lstOperands.get(0).getExprType().type == CodeConstants.TYPE_BOOLEAN && - lstOperands.get(1).getExprType().type == CodeConstants.TYPE_BOOLEAN) { - for(int i=0;i<2;i++) { - if(lstOperands.get(i).type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)lstOperands.get(i); - int val = ((Integer)cexpr.getValue()).intValue(); - - if((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) || - (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) { - return lstOperands.get(1-i); - } else { - List<Exprent> lstNotOperand = new ArrayList<Exprent>(); - lstNotOperand.add(lstOperands.get(1-i)); - return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand); - } - } - } - } - break; - case FunctionExprent.FUNCTION_BOOLNOT: - if(lstOperands.get(0).type == Exprent.EXPRENT_CONST) { - int val = ((ConstExprent)lstOperands.get(0)).getIntValue(); - if(val == 0) { - return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1)); - } else { - return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0)); - } - } - break; - case FunctionExprent.FUNCTION_IIF: - Exprent expr1 = lstOperands.get(1); - Exprent expr2 = lstOperands.get(2); - - if(expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr1 = (ConstExprent)expr1; - ConstExprent cexpr2 = (ConstExprent)expr2; - - if(cexpr1.getExprType().type == CodeConstants.TYPE_BOOLEAN && - cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) { - - if(cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) { - return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[] {lstOperands.get(0)})); - } else if(cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) { - return lstOperands.get(0); - } - } - } - break; - case FunctionExprent.FUNCTION_LCMP: - case FunctionExprent.FUNCTION_FCMPL: - case FunctionExprent.FUNCTION_FCMPG: - case FunctionExprent.FUNCTION_DCMPL: - case FunctionExprent.FUNCTION_DCMPG: - int var = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - VarType type = lstOperands.get(0).getExprType(); - VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); - - FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[] { - new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[] {new VarExprent(var, type, processor), - ConstExprent.getZeroConstant(type.type)})), - new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)), - new ConstExprent(VarType.VARTYPE_INT, new Integer(1))})); - - FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[] { - new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB, - Arrays.asList(new Exprent[] {lstOperands.get(0), lstOperands.get(1)}))), - ConstExprent.getZeroConstant(type.type)})); - - processor.setVarType(new VarVersionPaar(var, 0), type); - - return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[] { - head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff})); - } - break; - case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment - AssignmentExprent asexpr = (AssignmentExprent)exprent; - Exprent right = asexpr.getRight(); - Exprent left = asexpr.getLeft(); - - if(right.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent func = (FunctionExprent)right; - - VarType midlayer = null; - if(func.getFunctype() >= FunctionExprent.FUNCTION_I2L && - func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { - right = func.getLstOperands().get(0); - midlayer = func.getSimpleCastType(); - if(right.type == Exprent.EXPRENT_FUNCTION) { - func = (FunctionExprent)right; - } else { - return null; - } - } - - List<Exprent> lstFuncOperands = func.getLstOperands(); - - Exprent cond = null; - - switch(func.getFunctype()) { - case FunctionExprent.FUNCTION_ADD: - case FunctionExprent.FUNCTION_AND: - case FunctionExprent.FUNCTION_OR: - case FunctionExprent.FUNCTION_XOR: - if(left.equals(lstFuncOperands.get(1))) { - cond = lstFuncOperands.get(0); - break; - } - case FunctionExprent.FUNCTION_SUB: - case FunctionExprent.FUNCTION_MUL: - case FunctionExprent.FUNCTION_DIV: - case FunctionExprent.FUNCTION_REM: - case FunctionExprent.FUNCTION_SHL: - case FunctionExprent.FUNCTION_SHR: - case FunctionExprent.FUNCTION_USHR: - if(left.equals(lstFuncOperands.get(0))) { - cond = lstFuncOperands.get(1); - } - } - - if(cond!=null && (midlayer == null || midlayer.equals(cond.getExprType()))) { - asexpr.setRight(cond); - asexpr.setCondtype(func.getFunctype()); - } - } - break; - case Exprent.EXPRENT_INVOCATION: - if(!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once). - Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent); - if(!exprent.equals(retexpr)) { - return retexpr; - } - } - } - - return null; - - } - - public static Exprent propagateBoolNot(Exprent exprent) { - - if(exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fexpr = (FunctionExprent)exprent; - - if(fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) { - - Exprent param = fexpr.getLstOperands().get(0); - - if(param.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fparam = (FunctionExprent)param; - - int ftype = fparam.getFunctype(); - switch(ftype) { - case FunctionExprent.FUNCTION_BOOLNOT: - Exprent newexpr = fparam.getLstOperands().get(0); - Exprent retexpr = propagateBoolNot(newexpr); - return retexpr == null?newexpr:retexpr; - case FunctionExprent.FUNCTION_CADD: - case FunctionExprent.FUNCTION_COR: - List<Exprent> operands = fparam.getLstOperands(); - for(int i=0;i<operands.size();i++) { - Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{operands.get(i)})); - - Exprent retparam = propagateBoolNot(newparam); - operands.set(i, retparam == null?newparam:retparam); - } - case FunctionExprent.FUNCTION_EQ: - case FunctionExprent.FUNCTION_NE: - case FunctionExprent.FUNCTION_LT: - case FunctionExprent.FUNCTION_GE: - case FunctionExprent.FUNCTION_GT: - case FunctionExprent.FUNCTION_LE: - fparam.setFunctype(funcsnot[ftype-FunctionExprent.FUNCTION_EQ]); - return fparam; - } - } - } - } - - return null; - } - + private static final int[] funcsnot = new int[]{ + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_COR, + FunctionExprent.FUNCTION_CADD + }; + + private static final HashMap<Integer, Integer[]> mapNumComparisons = new HashMap<Integer, Integer[]>(); + + static { + mapNumComparisons.put(FunctionExprent.FUNCTION_EQ, + new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_NE, + new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GT, new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GE, new Integer[]{null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LT, new Integer[]{null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}); + } + + + public static boolean identifySecondaryFunctions(Statement stat) { + + if (stat.getExprents() == null) { + // if(){;}else{...} -> if(!){...} + if (stat.type == Statement.TYPE_IF) { + IfStatement ifelsestat = (IfStatement)stat; + Statement ifstat = ifelsestat.getIfstat(); + + if (ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null && + ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) { + + // move else to the if position + ifelsestat.getStats().removeWithKey(ifstat.id); + + ifelsestat.iftype = IfStatement.IFTYPE_IF; + ifelsestat.setIfstat(ifelsestat.getElsestat()); + ifelsestat.setElsestat(null); + + if (ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) { + StatEdge endedge = ifstat.getAllSuccessorEdges().get(0); + + ifstat.removeSuccessor(endedge); + endedge.setSource(ifelsestat); + if (endedge.closure != null) { + ifelsestat.getParent().addLabeledEdge(endedge); + } + ifelsestat.addSuccessor(endedge); + } + + ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge()); + + ifelsestat.setIfEdge(ifelsestat.getElseEdge()); + ifelsestat.setElseEdge(null); + + // negate head expression + ifelsestat.setNegated(!ifelsestat.isNegated()); + ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf()); + + return true; + } + } + } + + + boolean replaced = true; + while (replaced) { + replaced = false; + + List<Object> lstObjects = new ArrayList<Object>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents()); + + for (int i = 0; i < lstObjects.size(); i++) { + Object obj = lstObjects.get(i); + + if (obj instanceof Statement) { + if (identifySecondaryFunctions((Statement)obj)) { + replaced = true; + break; + } + } + else if (obj instanceof Exprent) { + Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true); + if (retexpr != null) { + if (stat.getExprents() == null) { + // only head expressions can be replaced! + stat.replaceExprent((Exprent)obj, retexpr); + } + else { + stat.getExprents().set(i, retexpr); + } + replaced = true; + break; + } + } + } + } + + return false; + } + + + private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + switch (fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_BOOLNOT: + + Exprent retparam = propagateBoolNot(fexpr); + + if (retparam != null) { + return retparam; + } + + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_LE: + Exprent expr1 = fexpr.getLstOperands().get(0); + Exprent expr2 = fexpr.getLstOperands().get(1); + + if (expr1.type == Exprent.EXPRENT_CONST) { + expr2 = expr1; + expr1 = fexpr.getLstOperands().get(1); + } + + if (expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) { + FunctionExprent funcexpr = (FunctionExprent)expr1; + ConstExprent cexpr = (ConstExprent)expr2; + + int functype = funcexpr.getFunctype(); + if (functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG || + functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG || + functype == FunctionExprent.FUNCTION_DCMPL) { + + int desttype = -1; + + Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype()); + if (destcons != null) { + int index = cexpr.getIntValue() + 1; + if (index >= 0 && index <= 2) { + Integer destcon = destcons[index]; + if (destcon != null) { + desttype = destcon.intValue(); + } + } + } + + if (desttype >= 0) { + return new FunctionExprent(desttype, funcexpr.getLstOperands()); + } + } + } + } + } + + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = identifySecondaryFunctions(expr, false); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + switch (exprent.type) { + case Exprent.EXPRENT_FUNCTION: + FunctionExprent fexpr = (FunctionExprent)exprent; + List<Exprent> lstOperands = fexpr.getLstOperands(); + + switch (fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_XOR: + for (int i = 0; i < 2; i++) { + Exprent operand = lstOperands.get(i); + VarType operandtype = operand.getExprType(); + + if (operand.type == Exprent.EXPRENT_CONST && + operandtype.type != CodeConstants.TYPE_BOOLEAN) { + ConstExprent cexpr = (ConstExprent)operand; + long val; + if (operandtype.type == CodeConstants.TYPE_LONG) { + val = ((Long)cexpr.getValue()).longValue(); + } + else { + val = ((Integer)cexpr.getValue()).intValue(); + } + + if (val == -1) { + List<Exprent> lstBitNotOperand = new ArrayList<Exprent>(); + lstBitNotOperand.add(lstOperands.get(1 - i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand); + } + } + } + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + if (lstOperands.get(0).getExprType().type == CodeConstants.TYPE_BOOLEAN && + lstOperands.get(1).getExprType().type == CodeConstants.TYPE_BOOLEAN) { + for (int i = 0; i < 2; i++) { + if (lstOperands.get(i).type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)lstOperands.get(i); + int val = ((Integer)cexpr.getValue()).intValue(); + + if ((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) || + (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) { + return lstOperands.get(1 - i); + } + else { + List<Exprent> lstNotOperand = new ArrayList<Exprent>(); + lstNotOperand.add(lstOperands.get(1 - i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand); + } + } + } + } + break; + case FunctionExprent.FUNCTION_BOOLNOT: + if (lstOperands.get(0).type == Exprent.EXPRENT_CONST) { + int val = ((ConstExprent)lstOperands.get(0)).getIntValue(); + if (val == 0) { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1)); + } + else { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0)); + } + } + break; + case FunctionExprent.FUNCTION_IIF: + Exprent expr1 = lstOperands.get(1); + Exprent expr2 = lstOperands.get(2); + + if (expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr1 = (ConstExprent)expr1; + ConstExprent cexpr2 = (ConstExprent)expr2; + + if (cexpr1.getExprType().type == CodeConstants.TYPE_BOOLEAN && + cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) { + + if (cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) { + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{lstOperands.get(0)})); + } + else if (cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) { + return lstOperands.get(0); + } + } + } + break; + case FunctionExprent.FUNCTION_LCMP: + case FunctionExprent.FUNCTION_FCMPL: + case FunctionExprent.FUNCTION_FCMPG: + case FunctionExprent.FUNCTION_DCMPL: + case FunctionExprent.FUNCTION_DCMPG: + int var = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + VarType type = lstOperands.get(0).getExprType(); + VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); + + FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ + new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[]{new VarExprent(var, type, processor), + ConstExprent.getZeroConstant(type.type)})), + new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)), + new ConstExprent(VarType.VARTYPE_INT, new Integer(1))})); + + FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[]{ + new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB, + Arrays.asList( + new Exprent[]{lstOperands.get(0), + lstOperands.get(1)}))), + ConstExprent.getZeroConstant(type.type)})); + + processor.setVarType(new VarVersionPaar(var, 0), type); + + return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ + head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff})); + } + break; + case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment + AssignmentExprent asexpr = (AssignmentExprent)exprent; + Exprent right = asexpr.getRight(); + Exprent left = asexpr.getLeft(); + + if (right.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)right; + + VarType midlayer = null; + if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L && + func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + right = func.getLstOperands().get(0); + midlayer = func.getSimpleCastType(); + if (right.type == Exprent.EXPRENT_FUNCTION) { + func = (FunctionExprent)right; + } + else { + return null; + } + } + + List<Exprent> lstFuncOperands = func.getLstOperands(); + + Exprent cond = null; + + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_ADD: + case FunctionExprent.FUNCTION_AND: + case FunctionExprent.FUNCTION_OR: + case FunctionExprent.FUNCTION_XOR: + if (left.equals(lstFuncOperands.get(1))) { + cond = lstFuncOperands.get(0); + break; + } + case FunctionExprent.FUNCTION_SUB: + case FunctionExprent.FUNCTION_MUL: + case FunctionExprent.FUNCTION_DIV: + case FunctionExprent.FUNCTION_REM: + case FunctionExprent.FUNCTION_SHL: + case FunctionExprent.FUNCTION_SHR: + case FunctionExprent.FUNCTION_USHR: + if (left.equals(lstFuncOperands.get(0))) { + cond = lstFuncOperands.get(1); + } + } + + if (cond != null && (midlayer == null || midlayer.equals(cond.getExprType()))) { + asexpr.setRight(cond); + asexpr.setCondtype(func.getFunctype()); + } + } + break; + case Exprent.EXPRENT_INVOCATION: + if (!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once). + Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent); + if (!exprent.equals(retexpr)) { + return retexpr; + } + } + } + + return null; + } + + public static Exprent propagateBoolNot(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) { + + Exprent param = fexpr.getLstOperands().get(0); + + if (param.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)param; + + int ftype = fparam.getFunctype(); + switch (ftype) { + case FunctionExprent.FUNCTION_BOOLNOT: + Exprent newexpr = fparam.getLstOperands().get(0); + Exprent retexpr = propagateBoolNot(newexpr); + return retexpr == null ? newexpr : retexpr; + case FunctionExprent.FUNCTION_CADD: + case FunctionExprent.FUNCTION_COR: + List<Exprent> operands = fparam.getLstOperands(); + for (int i = 0; i < operands.size(); i++) { + Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{operands.get(i)})); + + Exprent retparam = propagateBoolNot(newparam); + operands.set(i, retparam == null ? newparam : retparam); + } + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_LE: + fparam.setFunctype(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]); + return fparam; + } + } + } + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java index 69721e8..aa69931 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -1,23 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -26,301 +23,305 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatemen import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + public class SequenceHelper { - public static void condenseSequences(Statement root) { - condenseSequencesRec(root); - } - - private static void condenseSequencesRec(Statement stat) { - - if(stat.type == Statement.TYPE_SEQUENCE) { - - List<Statement> lst = new ArrayList<Statement>(); - lst.addAll(stat.getStats()); - - boolean unfolded = false; - - // unfold blocks - for(int i=0;i<lst.size();i++) { - Statement st = lst.get(i); - if(st.type == Statement.TYPE_SEQUENCE) { - - removeEmptyStatements((SequenceStatement)st); - - if(i == lst.size()-1 || isSequenceDisbandable(st, lst.get(i+1))) { - // move predecessors - Statement first = st.getFirst(); - for(StatEdge edge: st.getAllPredecessorEdges()) { - st.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, first); - first.addPredecessor(edge); - } - - // move successors - Statement last = st.getStats().getLast(); - if(last.getAllSuccessorEdges().isEmpty() && i<lst.size()-1) { - last.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, last, lst.get(i+1))); - } else { - for(StatEdge edge: last.getAllSuccessorEdges()) { - if(i == lst.size()-1) { - if(edge.closure == st) { - stat.addLabeledEdge(edge); - } - } else { - edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); - edge.closure.getLabelEdges().remove(edge); - edge.closure = null; - } - } - } - - for(StatEdge edge : st.getAllSuccessorEdges()) { - st.removeSuccessor(edge); - } - - for(StatEdge edge: new HashSet<StatEdge>(st.getLabelEdges())) { - if(edge.getSource() != last) { - last.addLabeledEdge(edge); - } - } - - lst.remove(i); - lst.addAll(i, st.getStats()); - i--; - - unfolded = true; - } - } - } - - if(unfolded) { - SequenceStatement sequence = new SequenceStatement(lst); - sequence.setAllParent(); - - stat.getParent().replaceStatement(stat, sequence); - - stat = sequence; - } - } - - // sequence consisting of one statement -> disband - if(stat.type == Statement.TYPE_SEQUENCE) { - - removeEmptyStatements((SequenceStatement)stat); - - if(stat.getStats().size() == 1) { - - Statement st = stat.getFirst(); - - boolean ok = st.getAllSuccessorEdges().isEmpty(); - if(!ok) { - StatEdge edge = st.getAllSuccessorEdges().get(0); - - ok = stat.getAllSuccessorEdges().isEmpty(); - if(!ok) { - StatEdge statedge = stat.getAllSuccessorEdges().get(0); - ok = (edge.getDestination() == statedge.getDestination()); - - if(ok) { - st.removeSuccessor(edge); - } - } - } - - if(ok) { - stat.getParent().replaceStatement(stat, st); - stat = st; - } - } - } - - // replace flat statements with synthetic basic blocks - outer: - for(;;) { - for(Statement st: stat.getStats()) { - if((st.getStats().isEmpty() || st.getExprents() != null) && st.type != Statement.TYPE_BASICBLOCK) { - destroyAndFlattenStatement(st); - continue outer; - } - } - break; - } - - // recursion - for(int i=0;i<stat.getStats().size();i++) { - condenseSequencesRec(stat.getStats().get(i)); - } - - } - - private static boolean isSequenceDisbandable(Statement block, Statement next) { - - Statement last = block.getStats().getLast(); - List<StatEdge> lstSuccs = last.getAllSuccessorEdges(); - if(!lstSuccs.isEmpty()) { - if(lstSuccs.get(0).getDestination() != next) { - return false; - } - } - - for(StatEdge edge : next.getPredecessorEdges(StatEdge.TYPE_BREAK)) { - if(last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) { - return false; - } - } - - return true; - } - - private static void removeEmptyStatements(SequenceStatement sequence) { - - if(sequence.getStats().size() <= 1) { - return; - } - - mergeFlatStatements(sequence); - - for(;;) { - - boolean found = false; - - for(Statement st: sequence.getStats()) { - - if(st.getExprents() != null && st.getExprents().isEmpty()) { - - if(st.getAllSuccessorEdges().isEmpty()) { - List<StatEdge> lstBreaks = st.getPredecessorEdges(StatEdge.TYPE_BREAK); - - if(lstBreaks.isEmpty()) { - for(StatEdge edge: st.getAllPredecessorEdges()) { - edge.getSource().removeSuccessor(edge); - } - found = true; - } - } else { - StatEdge sucedge = st.getAllSuccessorEdges().get(0); - if(sucedge.getType() != StatEdge.TYPE_FINALLYEXIT) { - st.removeSuccessor(sucedge); - - for(StatEdge edge: st.getAllPredecessorEdges()) { - if(sucedge.getType()!=StatEdge.TYPE_REGULAR) { - edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, sucedge.getType()); - } - - st.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, sucedge.getDestination()); - sucedge.getDestination().addPredecessor(edge); - - if(sucedge.closure != null) { - sucedge.closure.addLabeledEdge(edge); - } - } - found = true; - } - } - - if(found) { - sequence.getStats().removeWithKey(st.id); - break; - } - } - } - - if(!found) { - break; - } - } - - sequence.setFirst(sequence.getStats().get(0)); - - } - - private static void mergeFlatStatements(SequenceStatement sequence) { - - for(;;) { - - Statement next = null; - Statement current = null; - - boolean found = false; - - for(int i=sequence.getStats().size()-1;i>=0;i--) { - - next = current; - current = sequence.getStats().get(i); - - if(next != null && current.getExprents()!=null && !current.getExprents().isEmpty()) { - if(next.getExprents()!=null) { - next.getExprents().addAll(0, current.getExprents()); - current.getExprents().clear(); - found = true; - } else { - Statement first = getFirstExprentlist(next); - if(first != null) { - first.getExprents().addAll(0, current.getExprents()); - current.getExprents().clear(); - found = true; - } - } - } - } - - if(!found) { - break; - } - } - - } - - private static Statement getFirstExprentlist(Statement stat) { - - if(stat.getExprents() != null) { - return stat; - } - - switch(stat.type) { - case Statement.TYPE_IF: - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - return getFirstExprentlist(stat.getFirst()); - } - - return null; - } - - - public static void destroyAndFlattenStatement(Statement stat) { - - destroyStatementContent(stat, false); - - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - if(stat.getExprents() == null) { - bstat.setExprents(new ArrayList<Exprent>()); - } else { - bstat.setExprents(DecHelper.copyExprentList(stat.getExprents())); - } - - stat.getParent().replaceStatement(stat, bstat); - } - - public static void destroyStatementContent(Statement stat, boolean self) { - - for(Statement st: stat.getStats()) { - destroyStatementContent(st, true); - } - stat.getStats().clear(); - - if(self) { - for(StatEdge edge : stat.getAllSuccessorEdges()) { - stat.removeSuccessor(edge); - } - } - - } - + public static void condenseSequences(Statement root) { + condenseSequencesRec(root); + } + + private static void condenseSequencesRec(Statement stat) { + + if (stat.type == Statement.TYPE_SEQUENCE) { + + List<Statement> lst = new ArrayList<Statement>(); + lst.addAll(stat.getStats()); + + boolean unfolded = false; + + // unfold blocks + for (int i = 0; i < lst.size(); i++) { + Statement st = lst.get(i); + if (st.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)st); + + if (i == lst.size() - 1 || isSequenceDisbandable(st, lst.get(i + 1))) { + // move predecessors + Statement first = st.getFirst(); + for (StatEdge edge : st.getAllPredecessorEdges()) { + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, first); + first.addPredecessor(edge); + } + + // move successors + Statement last = st.getStats().getLast(); + if (last.getAllSuccessorEdges().isEmpty() && i < lst.size() - 1) { + last.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, last, lst.get(i + 1))); + } + else { + for (StatEdge edge : last.getAllSuccessorEdges()) { + if (i == lst.size() - 1) { + if (edge.closure == st) { + stat.addLabeledEdge(edge); + } + } + else { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + edge.closure = null; + } + } + } + + for (StatEdge edge : st.getAllSuccessorEdges()) { + st.removeSuccessor(edge); + } + + for (StatEdge edge : new HashSet<StatEdge>(st.getLabelEdges())) { + if (edge.getSource() != last) { + last.addLabeledEdge(edge); + } + } + + lst.remove(i); + lst.addAll(i, st.getStats()); + i--; + + unfolded = true; + } + } + } + + if (unfolded) { + SequenceStatement sequence = new SequenceStatement(lst); + sequence.setAllParent(); + + stat.getParent().replaceStatement(stat, sequence); + + stat = sequence; + } + } + + // sequence consisting of one statement -> disband + if (stat.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)stat); + + if (stat.getStats().size() == 1) { + + Statement st = stat.getFirst(); + + boolean ok = st.getAllSuccessorEdges().isEmpty(); + if (!ok) { + StatEdge edge = st.getAllSuccessorEdges().get(0); + + ok = stat.getAllSuccessorEdges().isEmpty(); + if (!ok) { + StatEdge statedge = stat.getAllSuccessorEdges().get(0); + ok = (edge.getDestination() == statedge.getDestination()); + + if (ok) { + st.removeSuccessor(edge); + } + } + } + + if (ok) { + stat.getParent().replaceStatement(stat, st); + stat = st; + } + } + } + + // replace flat statements with synthetic basic blocks + outer: + for (; ; ) { + for (Statement st : stat.getStats()) { + if ((st.getStats().isEmpty() || st.getExprents() != null) && st.type != Statement.TYPE_BASICBLOCK) { + destroyAndFlattenStatement(st); + continue outer; + } + } + break; + } + + // recursion + for (int i = 0; i < stat.getStats().size(); i++) { + condenseSequencesRec(stat.getStats().get(i)); + } + } + + private static boolean isSequenceDisbandable(Statement block, Statement next) { + + Statement last = block.getStats().getLast(); + List<StatEdge> lstSuccs = last.getAllSuccessorEdges(); + if (!lstSuccs.isEmpty()) { + if (lstSuccs.get(0).getDestination() != next) { + return false; + } + } + + for (StatEdge edge : next.getPredecessorEdges(StatEdge.TYPE_BREAK)) { + if (last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) { + return false; + } + } + + return true; + } + + private static void removeEmptyStatements(SequenceStatement sequence) { + + if (sequence.getStats().size() <= 1) { + return; + } + + mergeFlatStatements(sequence); + + for (; ; ) { + + boolean found = false; + + for (Statement st : sequence.getStats()) { + + if (st.getExprents() != null && st.getExprents().isEmpty()) { + + if (st.getAllSuccessorEdges().isEmpty()) { + List<StatEdge> lstBreaks = st.getPredecessorEdges(StatEdge.TYPE_BREAK); + + if (lstBreaks.isEmpty()) { + for (StatEdge edge : st.getAllPredecessorEdges()) { + edge.getSource().removeSuccessor(edge); + } + found = true; + } + } + else { + StatEdge sucedge = st.getAllSuccessorEdges().get(0); + if (sucedge.getType() != StatEdge.TYPE_FINALLYEXIT) { + st.removeSuccessor(sucedge); + + for (StatEdge edge : st.getAllPredecessorEdges()) { + if (sucedge.getType() != StatEdge.TYPE_REGULAR) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, sucedge.getType()); + } + + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, sucedge.getDestination()); + sucedge.getDestination().addPredecessor(edge); + + if (sucedge.closure != null) { + sucedge.closure.addLabeledEdge(edge); + } + } + found = true; + } + } + + if (found) { + sequence.getStats().removeWithKey(st.id); + break; + } + } + } + + if (!found) { + break; + } + } + + sequence.setFirst(sequence.getStats().get(0)); + } + + private static void mergeFlatStatements(SequenceStatement sequence) { + + for (; ; ) { + + Statement next = null; + Statement current = null; + + boolean found = false; + + for (int i = sequence.getStats().size() - 1; i >= 0; i--) { + + next = current; + current = sequence.getStats().get(i); + + if (next != null && current.getExprents() != null && !current.getExprents().isEmpty()) { + if (next.getExprents() != null) { + next.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } + else { + Statement first = getFirstExprentlist(next); + if (first != null) { + first.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } + } + } + } + + if (!found) { + break; + } + } + } + + private static Statement getFirstExprentlist(Statement stat) { + + if (stat.getExprents() != null) { + return stat; + } + + switch (stat.type) { + case Statement.TYPE_IF: + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return getFirstExprentlist(stat.getFirst()); + } + + return null; + } + + + public static void destroyAndFlattenStatement(Statement stat) { + + destroyStatementContent(stat, false); + + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + if (stat.getExprents() == null) { + bstat.setExprents(new ArrayList<Exprent>()); + } + else { + bstat.setExprents(DecHelper.copyExprentList(stat.getExprents())); + } + + stat.getParent().replaceStatement(stat, bstat); + } + + public static void destroyStatementContent(Statement stat, boolean self) { + + for (Statement st : stat.getStats()) { + destroyStatementContent(st, true); + } + stat.getStats().clear(); + + if (self) { + for (StatEdge edge : stat.getAllSuccessorEdges()) { + stat.removeSuccessor(edge); + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java index 8f55013..a7d5702 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -1,42 +1,25 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; @@ -45,819 +28,826 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import java.util.*; +import java.util.Map.Entry; + public class SimplifyExprentsHelper { - private boolean firstInvocation; - - public SimplifyExprentsHelper(boolean firstInvocation) { - this.firstInvocation = firstInvocation; - } - - public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) { - - boolean res = false; - - if(stat.getExprents() == null) { - - for(;;) { - - boolean changed = false; - - for(Statement st: stat.getStats()) { - res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl); - - // collapse composed if's - if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) { - break; - } - - // collapse iff ?: statement - if(changed = buildIff(st, ssa)) { - break; - } - } - - res |= changed; - - if(!changed) { - break; - } - } - - } else { - res |= simplifyStackVarsExprents(stat.getExprents(), cl); - } - - return res; - } - - private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) { - - boolean res = false; - - int index = 0; - - while(index < list.size()) { - - Exprent current = list.get(index); - - Exprent ret = isSimpleConstructorInvocation(current); - if(ret != null) { - list.set(index, ret); - res = true; - - continue; - } - - // lambda expression (Java 8) - ret = isLambda(current, cl); - if(ret != null) { - list.set(index, ret); - res = true; - - continue; - } - - // remove monitor exit - if(isMonitorExit(current)) { - list.remove(index); - res = true; - - continue; - } - - // trivial assignment of a stack variable - if(isTrivialStackAssignment(current)) { - list.remove(index); - res = true; - - continue; - } - - if(index == list.size()-1) { - break; - } - - - Exprent next = list.get(index+1); - - - // constructor invocation - if(isConstructorInvocationRemote(list, index)) { - list.remove(index); - res = true; - - continue; - } - - // remove getClass() invocation, which is part of a qualified new - if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GETCLASS_NEW)) { - if(isQualifiedNewGetClass(current, next)) { - list.remove(index); - res = true; - - continue; - } - } - - // direct initialization of an array - int arrcount = isArrayInitializer(list, index); - if(arrcount > 0) { - for(int i=0;i<arrcount;i++) { - list.remove(index+1); - } - res = true; - - continue; - } - - // add array initializer expression - if(addArrayInitializer(current, next)) { - list.remove(index+1); - res = true; - - continue; - } - - // integer ++expr and --expr (except for vars!) - Exprent func = isPPIorMMI(current); - if(func != null) { - list.set(index, func); - res = true; - - continue; - } - - // expr++ and expr-- - if(isIPPorIMM(current, next)) { - list.remove(index+1); - res = true; - - continue; - } - - // assignment on stack - if(isStackAssignement(current, next)) { - list.remove(index+1); - res = true; - - continue; - } - - if(!firstInvocation && isStackAssignement2(current, next)) { - list.remove(index+1); - res = true; - - continue; - } - - index++; - } - - return res; - } - - private static boolean addArrayInitializer(Exprent first, Exprent second) { - - if(first.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)first; - - if(as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { - NewExprent newex = (NewExprent)as.getRight(); - - if(!newex.getLstArrayElements().isEmpty()) { - - VarExprent arrvar = (VarExprent)as.getLeft(); - - if(second.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent aas = (AssignmentExprent)second; - if(aas.getLeft().type == Exprent.EXPRENT_ARRAY) { - ArrayExprent arrex = (ArrayExprent)aas.getLeft(); - if(arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) - && arrex.getIndex().type == Exprent.EXPRENT_CONST) { - - int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); - - if(constvalue < newex.getLstArrayElements().size()) { - Exprent init = newex.getLstArrayElements().get(constvalue); - if(init.type == Exprent.EXPRENT_CONST) { - ConstExprent cinit = (ConstExprent)init; - - VarType arrtype = newex.getNewtype().copy(); - arrtype.decArrayDim(); - - ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); - - if(cinit.equals(defaultval)) { - - Exprent tempexpr = aas.getRight(); - - if(!tempexpr.containsExprent(arrvar)) { - newex.getLstArrayElements().set(constvalue, tempexpr); - - if(tempexpr.type == Exprent.EXPRENT_NEW) { - NewExprent tempnewex = (NewExprent)tempexpr; - int dims = newex.getNewtype().arraydim; - if(dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { - tempnewex.setDirectArrayInit(true); - } - } - - return true; - } - } - } - } - } - } - } - } - } - } - - return false; - } - - - - private static int isArrayInitializer(List<Exprent> list, int index) { - - Exprent current = list.get(index); - if(current.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)current; - - if(as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { - NewExprent newex = (NewExprent)as.getRight(); - - if(newex.getExprType().arraydim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() && - newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) { - - int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue(); - if(size == 0) { - return 0; - } - - VarExprent arrvar = (VarExprent)as.getLeft(); - - HashMap<Integer, Exprent> mapInit = new HashMap<Integer, Exprent>(); - - int i=1; - while(index+i < list.size() && i <= size) { - boolean found = false; - - Exprent expr = list.get(index+i); - if(expr.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent aas = (AssignmentExprent)expr; - if(aas.getLeft().type == Exprent.EXPRENT_ARRAY) { - ArrayExprent arrex = (ArrayExprent)aas.getLeft(); - if(arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) - && arrex.getIndex().type == Exprent.EXPRENT_CONST) { - - int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless... - - if(constvalue < size && !mapInit.containsKey(constvalue)) { - - if(!aas.getRight().containsExprent(arrvar)) { - mapInit.put(constvalue, aas.getRight()); - found = true; - } - } - } - } - } - - if(!found) { - break; - } - - i++; - } - - double fraction = ((double)mapInit.size()) / size; - - if((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || - (size > 7 && fraction >= 0.7)) { - - List<Exprent> lstRet = new ArrayList<Exprent>(); - - VarType arrtype = newex.getNewtype().copy(); - arrtype.decArrayDim(); - - ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); - - for(int j=0;j<size;j++) { - lstRet.add(defaultval.copy()); - } - - int dims = newex.getNewtype().arraydim; - for(Entry<Integer, Exprent> ent: mapInit.entrySet()) { - Exprent tempexpr = ent.getValue(); - lstRet.set(ent.getKey(), tempexpr); - - if(tempexpr.type == Exprent.EXPRENT_NEW) { - NewExprent tempnewex = (NewExprent)tempexpr; - if(dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { - tempnewex.setDirectArrayInit(true); - } - } - } - - newex.setLstArrayElements(lstRet); - - return mapInit.size(); - } - } - } - } - - return 0; - } - - private static boolean isTrivialStackAssignment(Exprent first) { - - if(first.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asf = (AssignmentExprent)first; - - if(asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) { - VarExprent varleft = (VarExprent)asf.getLeft(); - VarExprent varright = (VarExprent)asf.getRight(); - - if(varleft.getIndex() == varright.getIndex() && varleft.isStack() && - varright.isStack()) { - return true; - } - } - } - - return false; - } - - private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation - - if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asf = (AssignmentExprent)first; - AssignmentExprent ass = (AssignmentExprent)second; - - if(asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR && - asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) { - if(ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) { - asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight())); - return true; - } - } - } - - return false; - } - - private static boolean isStackAssignement(Exprent first, Exprent second) { - - if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asf = (AssignmentExprent)first; - AssignmentExprent ass = (AssignmentExprent)second; - - for(;;) { - if(asf.getRight().equals(ass.getRight())) { - if((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) && - (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) { - - if(!ass.getLeft().containsExprent(asf.getLeft())) { - asf.setRight(ass); - return true; - } - } - } - if(asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) { - asf = (AssignmentExprent)asf.getRight(); - } else { - break; - } - } - - } - - return false; - } - - private static Exprent isPPIorMMI(Exprent first) { - - if(first.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)first; - - if(as.getRight().type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent func = (FunctionExprent)as.getRight(); - - if(func.getFunctype() == FunctionExprent.FUNCTION_ADD || - func.getFunctype() == FunctionExprent.FUNCTION_SUB) { - Exprent econd = func.getLstOperands().get(0); - Exprent econst = func.getLstOperands().get(1); - - if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && - func.getFunctype() == FunctionExprent.FUNCTION_ADD) { - econd = econst; - econst = func.getLstOperands().get(0); - } - - if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { - Exprent left = as.getLeft(); - - if(left.type != Exprent.EXPRENT_VAR && left.equals(econd)) { - FunctionExprent ret = new FunctionExprent( - func.getFunctype() == FunctionExprent.FUNCTION_ADD?FunctionExprent.FUNCTION_PPI:FunctionExprent.FUNCTION_MMI, - Arrays.asList(new Exprent[]{econd})); - ret.setImplicitType(VarType.VARTYPE_INT); - return ret; - } - } - } - } - } - - return null; - } - - private static boolean isIPPorIMM(Exprent first, Exprent second) { - - if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) { - AssignmentExprent as = (AssignmentExprent)first; - FunctionExprent in = (FunctionExprent)second; - - if((in.getFunctype() == FunctionExprent.FUNCTION_MMI || in.getFunctype() == FunctionExprent.FUNCTION_PPI) && - in.getLstOperands().get(0).equals(as.getRight())) { - - if(in.getFunctype() == FunctionExprent.FUNCTION_MMI) { - in.setFunctype(FunctionExprent.FUNCTION_IMM); - } else { - in.setFunctype(FunctionExprent.FUNCTION_IPP); - } - as.setRight(in); - - return true; - } - } - - return false; - } - - private static boolean isMonitorExit(Exprent first) { - if(first.type == Exprent.EXPRENT_MONITOR) { - MonitorExprent monexpr = (MonitorExprent)first; - if(monexpr.getMontype() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR - && !((VarExprent)monexpr.getValue()).isStack()) { - return true; - } - } - - return false; - } - - private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) { - - if(first.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)first; - - if(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") && - invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) { - - List<Exprent> lstExprents = second.getAllExprents(); - lstExprents.add(second); - - for(Exprent expr : lstExprents) { - if(expr.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)expr; - if(nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() && - nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) { - - String classname = nexpr.getNewtype().value; - ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); - if(node != null && node.type != ClassNode.CLASS_ROOT) { - return true; - } - } - } - } - - } - } - - return false; - } - -// private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { -// -// Exprent current = list.get(index); -// -// if(current.type == Exprent.EXPRENT_ASSIGNMENT) { -// AssignmentExprent as = (AssignmentExprent)current; -// -// if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { -// -// NewExprent newexpr = (NewExprent)as.getRight(); -// VarType newtype = newexpr.getNewtype(); -// VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); -// -// if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && -// newexpr.getConstructor() == null) { -// -// Set<VarVersionPaar> setChangedVars = new HashSet<VarVersionPaar>(); -// -// for(int i = index + 1; i < list.size(); i++) { -// Exprent remote = list.get(i); -// -// if(remote.type == Exprent.EXPRENT_INVOCATION) { -// InvocationExprent in = (InvocationExprent)remote; -// -// if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR -// && as.getLeft().equals(in.getInstance())) { -// -// Set<VarVersionPaar> setVars = remote.getAllVariables(); -// setVars.remove(leftPaar); -// setVars.retainAll(setChangedVars); -// -// if(setVars.isEmpty()) { -// -// newexpr.setConstructor(in); -// in.setInstance(null); -// -// if(!setChangedVars.isEmpty()) { // some exprents inbetween -// list.add(index+1, as.copy()); -// list.remove(i+1); -// } else { -// list.set(i, as.copy()); -// } -// -// return true; -// } -// } -// } -// -// boolean isTempAssignment = false; -// -// if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution -// AssignmentExprent asremote = (AssignmentExprent)remote; -// if(asremote.getLeft().type == Exprent.EXPRENT_VAR && -// asremote.getRight().type == Exprent.EXPRENT_VAR) { -// setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft())); -// isTempAssignment = true; -// } -// -// // FIXME: needs to be rewritten -// // propagate (var = new X) forward to the <init> invokation and then reduce -// -//// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) { -//// List<Exprent> lstRightExprents = asremote.getRight().getAllExprents(true); -//// lstRightExprents.add(asremote.getRight()); -//// -//// Set<VarVersionPaar> setTempChangedVars = new HashSet<VarVersionPaar>(); -//// boolean isTemp = true; -//// -//// for(Exprent expr : lstRightExprents) { -//// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) { -//// isTemp = false; -//// break; -//// } else if(expr.type == Exprent.EXPRENT_VAR) { -//// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr)); -//// } -//// } -//// -//// if(isTemp) { -//// setChangedVars.addAll(setTempChangedVars); -//// isTempAssignment = true; -//// } -//// } -//// } else if(remote.type == Exprent.EXPRENT_FUNCTION) { -//// FunctionExprent fexpr = (FunctionExprent)remote; -//// if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IPP || fexpr.getFunctype() == FunctionExprent.FUNCTION_IMM -//// || fexpr.getFunctype() == FunctionExprent.FUNCTION_PPI || fexpr.getFunctype() == FunctionExprent.FUNCTION_MMI) { -//// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { -//// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0))); -//// isTempAssignment = true; -//// } -//// } -// } -// -// if(!isTempAssignment) { -// Set<VarVersionPaar> setVars = remote.getAllVariables(); -// if(setVars.contains(leftPaar)) { -// return false; -// } else { -// setChangedVars.addAll(setVars); -// } -// } -// } -// } -// } -// } -// -// return false; -// } - - // propagate (var = new X) forward to the <init> invokation - private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { - - Exprent current = list.get(index); - - if(current.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)current; - - if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { - - NewExprent newexpr = (NewExprent)as.getRight(); - VarType newtype = newexpr.getNewtype(); - VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); - - if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && newexpr.getConstructor() == null) { - - for(int i = index + 1; i < list.size(); i++) { - Exprent remote = list.get(i); - - // <init> invocation - if(remote.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent in = (InvocationExprent)remote; - - if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR && as.getLeft().equals(in.getInstance())) { - - newexpr.setConstructor(in); - in.setInstance(null); - - list.set(i, as.copy()); - - return true; - } - } - - // check for variable in use - Set<VarVersionPaar> setVars = remote.getAllVariables(); - if(setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code - return false; - } - } - } - } - } - - return false; - } - - private static Exprent isLambda(Exprent exprent, StructClass cl) { - - List<Exprent> lst = exprent.getAllExprents(); - for(Exprent expr: lst) { - Exprent ret = isLambda(expr, cl); - if(ret != null) { - exprent.replaceExprent(expr, ret); - } - } - - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent in = (InvocationExprent)exprent; - - if(in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) { - - String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix(); - ClassNode lambda_class = DecompilerContext.getClassprocessor().getMapRootClasses().get(lambda_class_name); - - if(lambda_class != null) { // real lambda class found, replace invocation with an anonymous class - - NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0); - newexp.setConstructor(in); - // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation - // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);) - // in this case instance will hold the corresponding object - - return newexp; - } - } - } - - return null; - } - - - private static Exprent isSimpleConstructorInvocation(Exprent exprent) { - - List<Exprent> lst = exprent.getAllExprents(); - for(Exprent expr: lst) { - Exprent ret = isSimpleConstructorInvocation(expr); - if(ret != null) { - exprent.replaceExprent(expr, ret); - } - } - - if(exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent in = (InvocationExprent)exprent; - if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) { - NewExprent newexp = (NewExprent)in.getInstance(); - newexp.setConstructor(in); - in.setInstance(null); - return newexp; - } - } - - return null; - } - - - private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) { - - if(stat.type == Statement.TYPE_IF && stat.getExprents() == null) { - IfStatement stif = (IfStatement)stat; - if(stif.iftype == IfStatement.IFTYPE_IFELSE) { - Statement ifstat = stif.getIfstat(); - Statement elsestat = stif.getElsestat(); - - if(ifstat.getExprents() != null && ifstat.getExprents().size() == 1 - && elsestat.getExprents()!= null && elsestat.getExprents().size() == 1 - && ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1 - && ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) { - - Exprent ifexpr = ifstat.getExprents().get(0); - Exprent elseexpr = elsestat.getExprents().get(0); - - if(ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent ifas = (AssignmentExprent)ifexpr; - AssignmentExprent elseas = (AssignmentExprent)elseexpr; - - if(ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) { - VarExprent ifvar = (VarExprent)ifas.getLeft(); - VarExprent elsevar = (VarExprent)elseas.getLeft(); - - if(ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) { - - boolean found = false; - - for(Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) { - if(ent.getKey().var == ifvar.getIndex()) { - if(ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) { - found = true; - break; - } - } - } - - if(found) { - List<Exprent> data = new ArrayList<Exprent>(); - data.addAll(stif.getFirst().getExprents()); - - data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, - Arrays.asList(new Exprent[] {((IfExprent)stif.getHeadexprent()).getCondition(), - ifas.getRight(), - elseas.getRight()})))); - stif.setExprents(data); - - if(stif.getAllSuccessorEdges().isEmpty()) { - StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0); - StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination()); - - stif.addSuccessor(edge); - if(ifedge.closure != null) { - ifedge.closure.addLabeledEdge(edge); - } - } - - SequenceHelper.destroyAndFlattenStatement(stif); - - return true; - } - } - } - } else if(ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) { - ExitExprent ifex = (ExitExprent)ifexpr; - ExitExprent elseex = (ExitExprent)elseexpr; - - if(ifex.getExittype() == elseex.getExittype() && ifex.getValue() != null && elseex.getValue() != null && - ifex.getExittype() == ExitExprent.EXIT_RETURN) { - - // throw is dangerous, because of implicit casting to a common superclass - // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work - if(ifex.getExittype() == ExitExprent.EXIT_THROW && - !ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point! - return false; - } - - List<Exprent> data = new ArrayList<Exprent>(); - data.addAll(stif.getFirst().getExprents()); - - data.add(new ExitExprent(ifex.getExittype(), new FunctionExprent(FunctionExprent.FUNCTION_IIF, - Arrays.asList(new Exprent[] {((IfExprent)stif.getHeadexprent()).getCondition(), - ifex.getValue(), - elseex.getValue()})), ifex.getRettype())); - stif.setExprents(data); - - StatEdge retedge = ifstat.getAllSuccessorEdges().get(0); - stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(), retedge.closure==stif?stif.getParent():retedge.closure)); - - SequenceHelper.destroyAndFlattenStatement(stif); - - return true; - } - } - } - } - } - - return false; - } - - + private boolean firstInvocation; + + public SimplifyExprentsHelper(boolean firstInvocation) { + this.firstInvocation = firstInvocation; + } + + public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) { + + boolean res = false; + + if (stat.getExprents() == null) { + + for (; ; ) { + + boolean changed = false; + + for (Statement st : stat.getStats()) { + res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl); + + // collapse composed if's + if (changed = IfHelper.mergeIfs(st, setReorderedIfs)) { + break; + } + + // collapse iff ?: statement + if (changed = buildIff(st, ssa)) { + break; + } + } + + res |= changed; + + if (!changed) { + break; + } + } + } + else { + res |= simplifyStackVarsExprents(stat.getExprents(), cl); + } + + return res; + } + + private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) { + + boolean res = false; + + int index = 0; + + while (index < list.size()) { + + Exprent current = list.get(index); + + Exprent ret = isSimpleConstructorInvocation(current); + if (ret != null) { + list.set(index, ret); + res = true; + + continue; + } + + // lambda expression (Java 8) + ret = isLambda(current, cl); + if (ret != null) { + list.set(index, ret); + res = true; + + continue; + } + + // remove monitor exit + if (isMonitorExit(current)) { + list.remove(index); + res = true; + + continue; + } + + // trivial assignment of a stack variable + if (isTrivialStackAssignment(current)) { + list.remove(index); + res = true; + + continue; + } + + if (index == list.size() - 1) { + break; + } + + + Exprent next = list.get(index + 1); + + + // constructor invocation + if (isConstructorInvocationRemote(list, index)) { + list.remove(index); + res = true; + + continue; + } + + // remove getClass() invocation, which is part of a qualified new + if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GETCLASS_NEW)) { + if (isQualifiedNewGetClass(current, next)) { + list.remove(index); + res = true; + + continue; + } + } + + // direct initialization of an array + int arrcount = isArrayInitializer(list, index); + if (arrcount > 0) { + for (int i = 0; i < arrcount; i++) { + list.remove(index + 1); + } + res = true; + + continue; + } + + // add array initializer expression + if (addArrayInitializer(current, next)) { + list.remove(index + 1); + res = true; + + continue; + } + + // integer ++expr and --expr (except for vars!) + Exprent func = isPPIorMMI(current); + if (func != null) { + list.set(index, func); + res = true; + + continue; + } + + // expr++ and expr-- + if (isIPPorIMM(current, next)) { + list.remove(index + 1); + res = true; + + continue; + } + + // assignment on stack + if (isStackAssignement(current, next)) { + list.remove(index + 1); + res = true; + + continue; + } + + if (!firstInvocation && isStackAssignement2(current, next)) { + list.remove(index + 1); + res = true; + + continue; + } + + index++; + } + + return res; + } + + private static boolean addArrayInitializer(Exprent first, Exprent second) { + + if (first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)first; + + if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { + NewExprent newex = (NewExprent)as.getRight(); + + if (!newex.getLstArrayElements().isEmpty()) { + + VarExprent arrvar = (VarExprent)as.getLeft(); + + if (second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent aas = (AssignmentExprent)second; + if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) { + ArrayExprent arrex = (ArrayExprent)aas.getLeft(); + if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) + && arrex.getIndex().type == Exprent.EXPRENT_CONST) { + + int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); + + if (constvalue < newex.getLstArrayElements().size()) { + Exprent init = newex.getLstArrayElements().get(constvalue); + if (init.type == Exprent.EXPRENT_CONST) { + ConstExprent cinit = (ConstExprent)init; + + VarType arrtype = newex.getNewtype().copy(); + arrtype.decArrayDim(); + + ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + + if (cinit.equals(defaultval)) { + + Exprent tempexpr = aas.getRight(); + + if (!tempexpr.containsExprent(arrvar)) { + newex.getLstArrayElements().set(constvalue, tempexpr); + + if (tempexpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempnewex = (NewExprent)tempexpr; + int dims = newex.getNewtype().arraydim; + if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { + tempnewex.setDirectArrayInit(true); + } + } + + return true; + } + } + } + } + } + } + } + } + } + } + + return false; + } + + + private static int isArrayInitializer(List<Exprent> list, int index) { + + Exprent current = list.get(index); + if (current.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)current; + + if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { + NewExprent newex = (NewExprent)as.getRight(); + + if (newex.getExprType().arraydim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() && + newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) { + + int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue(); + if (size == 0) { + return 0; + } + + VarExprent arrvar = (VarExprent)as.getLeft(); + + HashMap<Integer, Exprent> mapInit = new HashMap<Integer, Exprent>(); + + int i = 1; + while (index + i < list.size() && i <= size) { + boolean found = false; + + Exprent expr = list.get(index + i); + if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent aas = (AssignmentExprent)expr; + if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) { + ArrayExprent arrex = (ArrayExprent)aas.getLeft(); + if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) + && arrex.getIndex().type == Exprent.EXPRENT_CONST) { + + int constvalue = ((ConstExprent)arrex.getIndex()) + .getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless... + + if (constvalue < size && !mapInit.containsKey(constvalue)) { + + if (!aas.getRight().containsExprent(arrvar)) { + mapInit.put(constvalue, aas.getRight()); + found = true; + } + } + } + } + } + + if (!found) { + break; + } + + i++; + } + + double fraction = ((double)mapInit.size()) / size; + + if ((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || + (size > 7 && fraction >= 0.7)) { + + List<Exprent> lstRet = new ArrayList<Exprent>(); + + VarType arrtype = newex.getNewtype().copy(); + arrtype.decArrayDim(); + + ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + + for (int j = 0; j < size; j++) { + lstRet.add(defaultval.copy()); + } + + int dims = newex.getNewtype().arraydim; + for (Entry<Integer, Exprent> ent : mapInit.entrySet()) { + Exprent tempexpr = ent.getValue(); + lstRet.set(ent.getKey(), tempexpr); + + if (tempexpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempnewex = (NewExprent)tempexpr; + if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { + tempnewex.setDirectArrayInit(true); + } + } + } + + newex.setLstArrayElements(lstRet); + + return mapInit.size(); + } + } + } + } + + return 0; + } + + private static boolean isTrivialStackAssignment(Exprent first) { + + if (first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + + if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) { + VarExprent varleft = (VarExprent)asf.getLeft(); + VarExprent varright = (VarExprent)asf.getRight(); + + if (varleft.getIndex() == varright.getIndex() && varleft.isStack() && + varright.isStack()) { + return true; + } + } + } + + return false; + } + + private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation + + if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + AssignmentExprent ass = (AssignmentExprent)second; + + if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR && + asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) { + if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) { + asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight())); + return true; + } + } + } + + return false; + } + + private static boolean isStackAssignement(Exprent first, Exprent second) { + + if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + AssignmentExprent ass = (AssignmentExprent)second; + + for (; ; ) { + if (asf.getRight().equals(ass.getRight())) { + if ((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) && + (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) { + + if (!ass.getLeft().containsExprent(asf.getLeft())) { + asf.setRight(ass); + return true; + } + } + } + if (asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) { + asf = (AssignmentExprent)asf.getRight(); + } + else { + break; + } + } + } + + return false; + } + + private static Exprent isPPIorMMI(Exprent first) { + + if (first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)first; + + if (as.getRight().type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)as.getRight(); + + if (func.getFunctype() == FunctionExprent.FUNCTION_ADD || + func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && + func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { + Exprent left = as.getLeft(); + + if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) { + FunctionExprent ret = new FunctionExprent( + func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, + Arrays.asList(new Exprent[]{econd})); + ret.setImplicitType(VarType.VARTYPE_INT); + return ret; + } + } + } + } + } + + return null; + } + + private static boolean isIPPorIMM(Exprent first, Exprent second) { + + if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) { + AssignmentExprent as = (AssignmentExprent)first; + FunctionExprent in = (FunctionExprent)second; + + if ((in.getFunctype() == FunctionExprent.FUNCTION_MMI || in.getFunctype() == FunctionExprent.FUNCTION_PPI) && + in.getLstOperands().get(0).equals(as.getRight())) { + + if (in.getFunctype() == FunctionExprent.FUNCTION_MMI) { + in.setFunctype(FunctionExprent.FUNCTION_IMM); + } + else { + in.setFunctype(FunctionExprent.FUNCTION_IPP); + } + as.setRight(in); + + return true; + } + } + + return false; + } + + private static boolean isMonitorExit(Exprent first) { + if (first.type == Exprent.EXPRENT_MONITOR) { + MonitorExprent monexpr = (MonitorExprent)first; + if (monexpr.getMontype() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR + && !((VarExprent)monexpr.getValue()).isStack()) { + return true; + } + } + + return false; + } + + private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) { + + if (first.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)first; + + if (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") && + invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) { + + List<Exprent> lstExprents = second.getAllExprents(); + lstExprents.add(second); + + for (Exprent expr : lstExprents) { + if (expr.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)expr; + if (nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() && + nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) { + + String classname = nexpr.getNewtype().value; + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + if (node != null && node.type != ClassNode.CLASS_ROOT) { + return true; + } + } + } + } + } + } + + return false; + } + + // private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { + // + // Exprent current = list.get(index); + // + // if(current.type == Exprent.EXPRENT_ASSIGNMENT) { + // AssignmentExprent as = (AssignmentExprent)current; + // + // if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { + // + // NewExprent newexpr = (NewExprent)as.getRight(); + // VarType newtype = newexpr.getNewtype(); + // VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); + // + // if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && + // newexpr.getConstructor() == null) { + // + // Set<VarVersionPaar> setChangedVars = new HashSet<VarVersionPaar>(); + // + // for(int i = index + 1; i < list.size(); i++) { + // Exprent remote = list.get(i); + // + // if(remote.type == Exprent.EXPRENT_INVOCATION) { + // InvocationExprent in = (InvocationExprent)remote; + // + // if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR + // && as.getLeft().equals(in.getInstance())) { + // + // Set<VarVersionPaar> setVars = remote.getAllVariables(); + // setVars.remove(leftPaar); + // setVars.retainAll(setChangedVars); + // + // if(setVars.isEmpty()) { + // + // newexpr.setConstructor(in); + // in.setInstance(null); + // + // if(!setChangedVars.isEmpty()) { // some exprents inbetween + // list.add(index+1, as.copy()); + // list.remove(i+1); + // } else { + // list.set(i, as.copy()); + // } + // + // return true; + // } + // } + // } + // + // boolean isTempAssignment = false; + // + // if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution + // AssignmentExprent asremote = (AssignmentExprent)remote; + // if(asremote.getLeft().type == Exprent.EXPRENT_VAR && + // asremote.getRight().type == Exprent.EXPRENT_VAR) { + // setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft())); + // isTempAssignment = true; + // } + // + // // FIXME: needs to be rewritten + // // propagate (var = new X) forward to the <init> invokation and then reduce + // + //// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) { + //// List<Exprent> lstRightExprents = asremote.getRight().getAllExprents(true); + //// lstRightExprents.add(asremote.getRight()); + //// + //// Set<VarVersionPaar> setTempChangedVars = new HashSet<VarVersionPaar>(); + //// boolean isTemp = true; + //// + //// for(Exprent expr : lstRightExprents) { + //// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) { + //// isTemp = false; + //// break; + //// } else if(expr.type == Exprent.EXPRENT_VAR) { + //// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr)); + //// } + //// } + //// + //// if(isTemp) { + //// setChangedVars.addAll(setTempChangedVars); + //// isTempAssignment = true; + //// } + //// } + //// } else if(remote.type == Exprent.EXPRENT_FUNCTION) { + //// FunctionExprent fexpr = (FunctionExprent)remote; + //// if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IPP || fexpr.getFunctype() == FunctionExprent.FUNCTION_IMM + //// || fexpr.getFunctype() == FunctionExprent.FUNCTION_PPI || fexpr.getFunctype() == FunctionExprent.FUNCTION_MMI) { + //// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { + //// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0))); + //// isTempAssignment = true; + //// } + //// } + // } + // + // if(!isTempAssignment) { + // Set<VarVersionPaar> setVars = remote.getAllVariables(); + // if(setVars.contains(leftPaar)) { + // return false; + // } else { + // setChangedVars.addAll(setVars); + // } + // } + // } + // } + // } + // } + // + // return false; + // } + + // propagate (var = new X) forward to the <init> invokation + private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { + + Exprent current = list.get(index); + + if (current.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)current; + + if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { + + NewExprent newexpr = (NewExprent)as.getRight(); + VarType newtype = newexpr.getNewtype(); + VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); + + if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && newexpr.getConstructor() == null) { + + for (int i = index + 1; i < list.size(); i++) { + Exprent remote = list.get(i); + + // <init> invocation + if (remote.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)remote; + + if (in.getFunctype() == InvocationExprent.TYP_INIT && + in.getInstance().type == Exprent.EXPRENT_VAR && + as.getLeft().equals(in.getInstance())) { + + newexpr.setConstructor(in); + in.setInstance(null); + + list.set(i, as.copy()); + + return true; + } + } + + // check for variable in use + Set<VarVersionPaar> setVars = remote.getAllVariables(); + if (setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code + return false; + } + } + } + } + } + + return false; + } + + private static Exprent isLambda(Exprent exprent, StructClass cl) { + + List<Exprent> lst = exprent.getAllExprents(); + for (Exprent expr : lst) { + Exprent ret = isLambda(expr, cl); + if (ret != null) { + exprent.replaceExprent(expr, ret); + } + } + + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)exprent; + + if (in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) { + + String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix(); + ClassNode lambda_class = DecompilerContext.getClassprocessor().getMapRootClasses().get(lambda_class_name); + + if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class + + NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0); + newexp.setConstructor(in); + // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation + // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);) + // in this case instance will hold the corresponding object + + return newexp; + } + } + } + + return null; + } + + + private static Exprent isSimpleConstructorInvocation(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(); + for (Exprent expr : lst) { + Exprent ret = isSimpleConstructorInvocation(expr); + if (ret != null) { + exprent.replaceExprent(expr, ret); + } + } + + if (exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)exprent; + if (in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) { + NewExprent newexp = (NewExprent)in.getInstance(); + newexp.setConstructor(in); + in.setInstance(null); + return newexp; + } + } + + return null; + } + + + private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) { + + if (stat.type == Statement.TYPE_IF && stat.getExprents() == null) { + IfStatement stif = (IfStatement)stat; + if (stif.iftype == IfStatement.IFTYPE_IFELSE) { + Statement ifstat = stif.getIfstat(); + Statement elsestat = stif.getElsestat(); + + if (ifstat.getExprents() != null && ifstat.getExprents().size() == 1 + && elsestat.getExprents() != null && elsestat.getExprents().size() == 1 + && ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1 + && ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) { + + Exprent ifexpr = ifstat.getExprents().get(0); + Exprent elseexpr = elsestat.getExprents().get(0); + + if (ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ifas = (AssignmentExprent)ifexpr; + AssignmentExprent elseas = (AssignmentExprent)elseexpr; + + if (ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent ifvar = (VarExprent)ifas.getLeft(); + VarExprent elsevar = (VarExprent)elseas.getLeft(); + + if (ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) { + + boolean found = false; + + for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) { + if (ent.getKey().var == ifvar.getIndex()) { + if (ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) { + found = true; + break; + } + } + } + + if (found) { + List<Exprent> data = new ArrayList<Exprent>(); + data.addAll(stif.getFirst().getExprents()); + + data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, + Arrays.asList(new Exprent[]{ + ((IfExprent)stif.getHeadexprent()).getCondition(), + ifas.getRight(), + elseas.getRight()})))); + stif.setExprents(data); + + if (stif.getAllSuccessorEdges().isEmpty()) { + StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0); + StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination()); + + stif.addSuccessor(edge); + if (ifedge.closure != null) { + ifedge.closure.addLabeledEdge(edge); + } + } + + SequenceHelper.destroyAndFlattenStatement(stif); + + return true; + } + } + } + } + else if (ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ifex = (ExitExprent)ifexpr; + ExitExprent elseex = (ExitExprent)elseexpr; + + if (ifex.getExittype() == elseex.getExittype() && ifex.getValue() != null && elseex.getValue() != null && + ifex.getExittype() == ExitExprent.EXIT_RETURN) { + + // throw is dangerous, because of implicit casting to a common superclass + // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work + if (ifex.getExittype() == ExitExprent.EXIT_THROW && + !ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point! + return false; + } + + List<Exprent> data = new ArrayList<Exprent>(); + data.addAll(stif.getFirst().getExprents()); + + data.add(new ExitExprent(ifex.getExittype(), new FunctionExprent(FunctionExprent.FUNCTION_IIF, + Arrays.asList(new Exprent[]{ + ((IfExprent)stif.getHeadexprent()).getCondition(), + ifex.getValue(), + elseex.getValue()})), ifex.getRettype())); + stif.setExprents(data); + + StatEdge retedge = ifstat.getAllSuccessorEdges().get(0); + stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(), + retedge.closure == stif ? stif.getParent() : retedge.closure)); + + SequenceHelper.destroyAndFlattenStatement(stif); + + return true; + } + } + } + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java index 5dae30c..d0f3f03 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java @@ -1,38 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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 java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; -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.SSAConstructorSparseEx; -import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAUConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; @@ -46,671 +31,705 @@ import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; +import java.util.*; +import java.util.Map.Entry; + public class StackVarsProcessor { - public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) { - - HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); - - SSAUConstructorSparseEx ssau = null; - - for(;;) { - - boolean found = false; - -// System.out.println("--------------- \r\n"+root.toJava()); - - SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); - ssa.splitVariables(root, mt); - -// System.out.println("--------------- \r\n"+root.toJava()); - - - SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null); - while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) { -// System.out.println("--------------- \r\n"+root.toJava()); - found = true; - } - - -// System.out.println("=============== \r\n"+root.toJava()); - - setVersionsToNull(root); - - SequenceHelper.condenseSequences(root); - - ssau = new SSAUConstructorSparseEx(); - ssau.splitVariables(root, mt); - -// try { -// DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); -// } catch(Exception ex) { -// ex.printStackTrace(); -// } - -// System.out.println("++++++++++++++++ \r\n"+root.toJava()); - - - if(iterateStatements(root, ssau)) { - found = true; - } - -// System.out.println("***************** \r\n"+root.toJava()); - - setVersionsToNull(root); - - if(!found) { - break; - } - } - - // remove unused assignments - ssau = new SSAUConstructorSparseEx(); - ssau.splitVariables(root, mt); - -// try { -// DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); -// } catch(Exception ex) { -// ex.printStackTrace(); -// } - - iterateStatements(root, ssau); - -// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - setVersionsToNull(root); - } - - private void setVersionsToNull(Statement stat) { - - if(stat.getExprents() == null) { - for(Object obj: stat.getSequentialObjects()) { - if(obj instanceof Statement) { - setVersionsToNull((Statement)obj); - } else if(obj instanceof Exprent) { - setExprentVersionsToNull((Exprent)obj); - } - } - } else { - for(Exprent exprent: stat.getExprents()) { - setExprentVersionsToNull(exprent); - } - } - } - - private void setExprentVersionsToNull(Exprent exprent) { - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - ((VarExprent)expr).setVersion(0); - } - } - } - - - private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) { - - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); - - boolean res = false; - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); - LinkedList<HashMap<VarVersionPaar, Exprent>> stackMaps = new LinkedList<HashMap<VarVersionPaar, Exprent>>(); - - stack.add(dgraph.first); - stackMaps.add(new HashMap<VarVersionPaar, Exprent>()); - - while(!stack.isEmpty()) { - - DirectNode nd = stack.removeFirst(); - HashMap<VarVersionPaar, Exprent> mapVarValues = stackMaps.removeFirst(); - - if(setVisited.contains(nd)) { - continue; - } - setVisited.add(nd); - - List<List<Exprent>> lstLists = new ArrayList<List<Exprent>>(); - - if(!nd.exprents.isEmpty()) { - lstLists.add(nd.exprents); - } - - if(nd.succs.size() == 1){ - DirectNode ndsucc = nd.succs.get(0); - if(ndsucc.type == DirectNode.NODE_TAIL && !ndsucc.exprents.isEmpty()) { - lstLists.add(nd.succs.get(0).exprents); - nd = ndsucc; - } - } - - for(int i=0;i<lstLists.size();i++) { - List<Exprent> lst = lstLists.get(i); - - int index = 0; - while(index < lst.size()) { - Exprent next = null; - if(index == lst.size()-1) { - if(i<lstLists.size()-1) { - next = lstLists.get(i+1).get(0); - } - } else { - next = lst.get(index+1); - } - - int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa); - - //System.out.println("***************** \r\n"+root.toJava()); - - if(ret[0] >= 0) { - index = ret[0]; - } else { - index++; - } - res |= (ret[1] == 1); - } - } - - for(DirectNode ndx: nd.succs) { - stack.add(ndx); - stackMaps.add(new HashMap<VarVersionPaar, Exprent>(mapVarValues)); - } - - // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty - // change loop type if necessary - if(nd.exprents.isEmpty() && - (nd.type == DirectNode.NODE_INIT || nd.type == DirectNode.NODE_CONDITION || nd.type == DirectNode.NODE_INCREMENT)) { - nd.exprents.add(null); - - if(nd.statement.type == Statement.TYPE_DO) { - DoStatement loop = (DoStatement)nd.statement; - - if(loop.getLooptype() == DoStatement.LOOP_FOR && loop.getInitExprent() == null && loop.getIncExprent() == null) { // "downgrade" loop to 'while' - loop.setLooptype(DoStatement.LOOP_WHILE); - } - } - } - } - - return res; - } - - - private Exprent isReplaceableVar(Exprent exprent, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { - - Exprent dest = null; - - if(exprent.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)exprent; - dest = mapVarValues.get(new VarVersionPaar(var)); - } - - return dest; - } - - private void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) { - - parent.replaceExprent(var, dest); - - // live sets - SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPaar(var)); - HashSet<VarVersionPaar> setVars = getAllVersions(dest); - - for(VarVersionPaar varpaar : setVars) { - VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar); - - for(Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator();itent.hasNext();) { - Entry<Integer, FastSparseSet<Integer>> ent = itent.next(); - - Integer key = ent.getKey(); - - if(!livemap.containsKey(key)) { - itent.remove(); - } else { - FastSparseSet<Integer> set = ent.getValue(); - - set.complement(livemap.get(key)); - if(set.isEmpty()) { - itent.remove(); - } - } - } - } - } - - private int[] iterateExprent(List<Exprent> lstExprents, int index, Exprent next, HashMap<VarVersionPaar, - Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { - - Exprent exprent = lstExprents.get(index); - - int changed = 0; - - for(Exprent expr: exprent.getAllExprents()) { - for(;;) { - Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau); - Exprent retexpr = (Exprent)arr[0]; - changed |= (Boolean)arr[1]?1:0; - - boolean isReplaceable = (Boolean)arr[2]; - if(retexpr != null) { - if(isReplaceable) { - replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); - expr = retexpr; - } else { - exprent.replaceExprent(expr, retexpr); - } - changed = 1; - } - - if(!isReplaceable) { - break; - } - } - } - - // no var on the highest level, so no replacing - - VarExprent left = null; - Exprent right = null; - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)exprent; - if(as.getLeft().type == Exprent.EXPRENT_VAR) { - left = (VarExprent)as.getLeft(); - right = as.getRight(); - } - } - - if(left == null) { - return new int[]{-1, changed}; - } - - VarVersionPaar leftpaar = new VarVersionPaar(left); - - List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); - boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); - - if(!notdom && usedVers.isEmpty()) { - if(left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION || - right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) { - if(right.type == Exprent.EXPRENT_NEW) { - // new Object(); permitted - NewExprent nexpr = (NewExprent)right; - if(nexpr.isAnonymous() || nexpr.getNewtype().arraydim > 0 - || nexpr.getNewtype().type != CodeConstants.TYPE_OBJECT) { - return new int[]{-1, changed}; - } - } - - lstExprents.set(index, right); - return new int[]{index+1, 1}; - } else if(right.type == Exprent.EXPRENT_VAR) { - lstExprents.remove(index); - return new int[]{index, 1}; - } else { - return new int[]{-1, changed}; - } - } - - int useflags = right.getExprentUse(); - - // stack variables only - if(!left.isStack() && - (right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex) - return new int[]{-1, changed}; - } - - if((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size()>1)) { - return new int[]{-1, changed}; - } - - HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); - - boolean isSelfReference = mapVars.containsKey(leftpaar.var); - if(isSelfReference && notdom) { - return new int[]{-1, changed}; - } - - HashSet<VarVersionPaar> setNextVars = next==null?null:getAllVersions(next); - - // FIXME: fix the entire method! - if(right.type != Exprent.EXPRENT_CONST && right.type != Exprent.EXPRENT_VAR && setNextVars!=null && mapVars.containsKey(leftpaar.var)) { - for(VarVersionNode usedvar: usedVers) { - if(!setNextVars.contains(new VarVersionPaar(usedvar.var, usedvar.version))) { - return new int[]{-1, changed}; - } - } - } - - mapVars.remove(leftpaar.var); - - boolean vernotreplaced = false; - boolean verreplaced = false; - - - HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); - - for(VarVersionNode usedvar: usedVers) { - VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); - if(isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && - (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD - || setNextVars==null || setNextVars.contains(usedver))) { - - setTempUsedVers.add(usedver); - verreplaced = true; - } else { - vernotreplaced = true; - } - } - - if(isSelfReference && vernotreplaced) { - return new int[]{-1, changed}; - } else { - for(VarVersionPaar usedver: setTempUsedVers) { - Exprent copy = right.copy(); - if(right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { - ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); - } - - mapVarValues.put(usedver, copy); - } - } - - if(!notdom && !vernotreplaced) { - // remove assignment - lstExprents.remove(index); - return new int[]{index, 1}; - } else if(verreplaced){ - return new int[]{index+1, changed}; - } else { - return new int[]{-1, changed}; - } - } - - private HashSet<VarVersionPaar> getAllVersions(Exprent exprent) { - - HashSet<VarVersionPaar> res = new HashSet<VarVersionPaar>(); - - List<Exprent> listTemp = new ArrayList<Exprent>(exprent.getAllExprents(true)); - listTemp.add(exprent); - - for(Exprent expr: listTemp) { - if(expr.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)expr; - res.add(new VarVersionPaar(var)); - } - } - - return res; - } - - private Object[] iterateChildExprent(Exprent exprent, Exprent parent, Exprent next, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { - - boolean changed = false; - - for(Exprent expr: exprent.getAllExprents()) { - for(;;) { - Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau); - Exprent retexpr = (Exprent)arr[0]; - changed |= (Boolean)arr[1]; - - boolean isReplaceable = (Boolean)arr[2]; - if(retexpr != null) { - if(isReplaceable) { - replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); - expr = retexpr; - } else { - exprent.replaceExprent(expr, retexpr); - } - changed = true; - } - - if(!isReplaceable) { - break; - } - } - } - - Exprent dest = isReplaceableVar(exprent, mapVarValues, ssau); - if(dest != null) { - return new Object[]{dest, true, true}; - } - - - VarExprent left = null; - Exprent right = null; - - if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent as = (AssignmentExprent)exprent; - if(as.getLeft().type == Exprent.EXPRENT_VAR) { - left = (VarExprent)as.getLeft(); - right = as.getRight(); - } - } - - if(left == null) { - return new Object[]{null, changed, false}; - } - - boolean isHeadSynchronized = false; - if(next == null && parent.type == Exprent.EXPRENT_MONITOR) { - MonitorExprent monexpr = (MonitorExprent)parent; - if(monexpr.getMontype() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) { - isHeadSynchronized = true; - } - } - - // stack variable or synchronized head exprent - if(!left.isStack() && !isHeadSynchronized) { - return new Object[]{null, changed, false}; - } - - VarVersionPaar leftpaar = new VarVersionPaar(left); - - List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); - boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); - - if(!notdom && usedVers.isEmpty()) { - return new Object[]{right, changed, false}; - } - - // stack variables only - if(!left.isStack()) { - return new Object[]{null, changed, false}; - } - - int useflags = right.getExprentUse(); - - if((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) { - return new Object[]{null, changed, false}; - } - - HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); - - if(mapVars.containsKey(leftpaar.var) && notdom) { - return new Object[]{null, changed, false}; - } - - - mapVars.remove(leftpaar.var); - - HashSet<VarVersionPaar> setAllowedVars = getAllVersions(parent); - if(next != null) { - setAllowedVars.addAll(getAllVersions(next)); - } - - boolean vernotreplaced = false; - - HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); - - for(VarVersionNode usedvar: usedVers) { - VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); - if(isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && - (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) { - - setTempUsedVers.add(usedver); - } else { - vernotreplaced = true; - } - } - - if(!notdom && !vernotreplaced) { - - for(VarVersionPaar usedver: setTempUsedVers) { - Exprent copy = right.copy(); - if(right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { - ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); - } - - mapVarValues.put(usedver, copy); - } - - // remove assignment - return new Object[]{right, changed, false}; - } - - return new Object[]{null, changed, false}; - } - - private boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPaar var, List<VarVersionNode> res) { - - VarVersionsGraph ssuversions = ssa.getSsuversions(); - VarVersionNode varnode = ssuversions.nodes.getWithKey(var); - - HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); - - HashSet<VarVersionNode> setNotDoms = new HashSet<VarVersionNode>(); - - LinkedList<VarVersionNode> stack = new LinkedList<VarVersionNode>(); - stack.add(varnode); - - while(!stack.isEmpty()) { - - VarVersionNode nd = stack.remove(0); - setVisited.add(nd); - - if(nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FINEXIT)==0) { - res.add(nd); - } - - for(VarVersionEdge edge: nd.succs) { - VarVersionNode succ = edge.dest; - - if(!setVisited.contains(edge.dest)) { - - boolean isDominated = true; - for(VarVersionEdge prededge : succ.preds) { - if(!setVisited.contains(prededge.source)) { - isDominated = false; - break; - } - } - - if(isDominated) { - stack.add(succ); - } else { - setNotDoms.add(succ); - } - } - } - } - - setNotDoms.removeAll(setVisited); - - return !setNotDoms.isEmpty(); - } - - private boolean isVersionToBeReplaced(VarVersionPaar usedvar, HashMap<Integer, HashSet<VarVersionPaar>> mapVars, SSAUConstructorSparseEx ssau, VarVersionPaar leftpaar) { - - VarVersionsGraph ssuversions = ssau.getSsuversions(); - - SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar); - if(mapLiveVars == null) { - // dummy version, predecessor of a phi node - return false; - } - - // compare protected ranges - if(!InterpreterUtil.equalObjects(ssau.getMapVersionFirstRange().get(leftpaar), - ssau.getMapVersionFirstRange().get(usedvar))) { - return false; - } - - for(Entry<Integer, HashSet<VarVersionPaar>> ent: mapVars.entrySet()) { - FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey()); - if(liveverset == null) { - return false; - } - - HashSet<VarVersionNode> domset = new HashSet<VarVersionNode>(); - for(VarVersionPaar verpaar: ent.getValue()) { - domset.add(ssuversions.nodes.getWithKey(verpaar)); - } - - boolean isdom = false; - - for(Integer livever: liveverset) { - VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPaar(ent.getKey().intValue(), livever.intValue())); - - if(ssuversions.isDominatorSet(node, domset)) { - isdom = true; - break; - } - } - - if(!isdom) { - return false; - } - } - - return true; - } - - private HashMap<Integer, HashSet<VarVersionPaar>> getAllVarVersions(VarVersionPaar leftvar, Exprent exprent, SSAUConstructorSparseEx ssau) { - - HashMap<Integer, HashSet<VarVersionPaar>> map = new HashMap<Integer, HashSet<VarVersionPaar>>(); - SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar); - - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent)expr).getIndex(); - if(leftvar.var != varindex) { - if(mapLiveVars.containsKey(varindex)) { - HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); - for(Integer vers: mapLiveVars.get(varindex)) { - verset.add(new VarVersionPaar(varindex, vers.intValue())); - } - map.put(varindex, verset); - } else { - throw new RuntimeException("inkonsistent live map!"); - } - } else { - map.put(varindex, null); - } - } else if(expr.type == Exprent.EXPRENT_FIELD) { - if(ssau.getMapFieldVars().containsKey(expr.id)) { - int varindex = ssau.getMapFieldVars().get(expr.id); - if(mapLiveVars.containsKey(varindex)) { - HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); - for(Integer vers: mapLiveVars.get(varindex)) { - verset.add(new VarVersionPaar(varindex, vers.intValue())); - } - map.put(varindex, verset); - } - } - } - } - - return map; - } - + public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) { + + HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); + + SSAUConstructorSparseEx ssau = null; + + for (; ; ) { + + boolean found = false; + + // System.out.println("--------------- \r\n"+root.toJava()); + + SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); + ssa.splitVariables(root, mt); + + // System.out.println("--------------- \r\n"+root.toJava()); + + + SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null); + while (sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) { + // System.out.println("--------------- \r\n"+root.toJava()); + found = true; + } + + + // System.out.println("=============== \r\n"+root.toJava()); + + setVersionsToNull(root); + + SequenceHelper.condenseSequences(root); + + ssau = new SSAUConstructorSparseEx(); + ssau.splitVariables(root, mt); + + // try { + // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); + // } catch(Exception ex) { + // ex.printStackTrace(); + // } + + // System.out.println("++++++++++++++++ \r\n"+root.toJava()); + + + if (iterateStatements(root, ssau)) { + found = true; + } + + // System.out.println("***************** \r\n"+root.toJava()); + + setVersionsToNull(root); + + if (!found) { + break; + } + } + + // remove unused assignments + ssau = new SSAUConstructorSparseEx(); + ssau.splitVariables(root, mt); + + // try { + // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); + // } catch(Exception ex) { + // ex.printStackTrace(); + // } + + iterateStatements(root, ssau); + + // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + setVersionsToNull(root); + } + + private void setVersionsToNull(Statement stat) { + + if (stat.getExprents() == null) { + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + setVersionsToNull((Statement)obj); + } + else if (obj instanceof Exprent) { + setExprentVersionsToNull((Exprent)obj); + } + } + } + else { + for (Exprent exprent : stat.getExprents()) { + setExprentVersionsToNull(exprent); + } + } + } + + private void setExprentVersionsToNull(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + ((VarExprent)expr).setVersion(0); + } + } + } + + + private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + boolean res = false; + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + LinkedList<HashMap<VarVersionPaar, Exprent>> stackMaps = new LinkedList<HashMap<VarVersionPaar, Exprent>>(); + + stack.add(dgraph.first); + stackMaps.add(new HashMap<VarVersionPaar, Exprent>()); + + while (!stack.isEmpty()) { + + DirectNode nd = stack.removeFirst(); + HashMap<VarVersionPaar, Exprent> mapVarValues = stackMaps.removeFirst(); + + if (setVisited.contains(nd)) { + continue; + } + setVisited.add(nd); + + List<List<Exprent>> lstLists = new ArrayList<List<Exprent>>(); + + if (!nd.exprents.isEmpty()) { + lstLists.add(nd.exprents); + } + + if (nd.succs.size() == 1) { + DirectNode ndsucc = nd.succs.get(0); + if (ndsucc.type == DirectNode.NODE_TAIL && !ndsucc.exprents.isEmpty()) { + lstLists.add(nd.succs.get(0).exprents); + nd = ndsucc; + } + } + + for (int i = 0; i < lstLists.size(); i++) { + List<Exprent> lst = lstLists.get(i); + + int index = 0; + while (index < lst.size()) { + Exprent next = null; + if (index == lst.size() - 1) { + if (i < lstLists.size() - 1) { + next = lstLists.get(i + 1).get(0); + } + } + else { + next = lst.get(index + 1); + } + + int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa); + + //System.out.println("***************** \r\n"+root.toJava()); + + if (ret[0] >= 0) { + index = ret[0]; + } + else { + index++; + } + res |= (ret[1] == 1); + } + } + + for (DirectNode ndx : nd.succs) { + stack.add(ndx); + stackMaps.add(new HashMap<VarVersionPaar, Exprent>(mapVarValues)); + } + + // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty + // change loop type if necessary + if (nd.exprents.isEmpty() && + (nd.type == DirectNode.NODE_INIT || nd.type == DirectNode.NODE_CONDITION || nd.type == DirectNode.NODE_INCREMENT)) { + nd.exprents.add(null); + + if (nd.statement.type == Statement.TYPE_DO) { + DoStatement loop = (DoStatement)nd.statement; + + if (loop.getLooptype() == DoStatement.LOOP_FOR && + loop.getInitExprent() == null && + loop.getIncExprent() == null) { // "downgrade" loop to 'while' + loop.setLooptype(DoStatement.LOOP_WHILE); + } + } + } + } + + return res; + } + + + private Exprent isReplaceableVar(Exprent exprent, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { + + Exprent dest = null; + + if (exprent.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)exprent; + dest = mapVarValues.get(new VarVersionPaar(var)); + } + + return dest; + } + + private void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) { + + parent.replaceExprent(var, dest); + + // live sets + SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPaar(var)); + HashSet<VarVersionPaar> setVars = getAllVersions(dest); + + for (VarVersionPaar varpaar : setVars) { + VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar); + + for (Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator(); itent.hasNext(); ) { + Entry<Integer, FastSparseSet<Integer>> ent = itent.next(); + + Integer key = ent.getKey(); + + if (!livemap.containsKey(key)) { + itent.remove(); + } + else { + FastSparseSet<Integer> set = ent.getValue(); + + set.complement(livemap.get(key)); + if (set.isEmpty()) { + itent.remove(); + } + } + } + } + } + + private int[] iterateExprent(List<Exprent> lstExprents, int index, Exprent next, HashMap<VarVersionPaar, + Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { + + Exprent exprent = lstExprents.get(index); + + int changed = 0; + + for (Exprent expr : exprent.getAllExprents()) { + for (; ; ) { + Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau); + Exprent retexpr = (Exprent)arr[0]; + changed |= (Boolean)arr[1] ? 1 : 0; + + boolean isReplaceable = (Boolean)arr[2]; + if (retexpr != null) { + if (isReplaceable) { + replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); + expr = retexpr; + } + else { + exprent.replaceExprent(expr, retexpr); + } + changed = 1; + } + + if (!isReplaceable) { + break; + } + } + } + + // no var on the highest level, so no replacing + + VarExprent left = null; + Exprent right = null; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + if (as.getLeft().type == Exprent.EXPRENT_VAR) { + left = (VarExprent)as.getLeft(); + right = as.getRight(); + } + } + + if (left == null) { + return new int[]{-1, changed}; + } + + VarVersionPaar leftpaar = new VarVersionPaar(left); + + List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); + boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); + + if (!notdom && usedVers.isEmpty()) { + if (left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION || + right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) { + if (right.type == Exprent.EXPRENT_NEW) { + // new Object(); permitted + NewExprent nexpr = (NewExprent)right; + if (nexpr.isAnonymous() || nexpr.getNewtype().arraydim > 0 + || nexpr.getNewtype().type != CodeConstants.TYPE_OBJECT) { + return new int[]{-1, changed}; + } + } + + lstExprents.set(index, right); + return new int[]{index + 1, 1}; + } + else if (right.type == Exprent.EXPRENT_VAR) { + lstExprents.remove(index); + return new int[]{index, 1}; + } + else { + return new int[]{-1, changed}; + } + } + + int useflags = right.getExprentUse(); + + // stack variables only + if (!left.isStack() && + (right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex) + return new int[]{-1, changed}; + } + + if ((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size() > 1)) { + return new int[]{-1, changed}; + } + + HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); + + boolean isSelfReference = mapVars.containsKey(leftpaar.var); + if (isSelfReference && notdom) { + return new int[]{-1, changed}; + } + + HashSet<VarVersionPaar> setNextVars = next == null ? null : getAllVersions(next); + + // FIXME: fix the entire method! + if (right.type != Exprent.EXPRENT_CONST && + right.type != Exprent.EXPRENT_VAR && + setNextVars != null && + mapVars.containsKey(leftpaar.var)) { + for (VarVersionNode usedvar : usedVers) { + if (!setNextVars.contains(new VarVersionPaar(usedvar.var, usedvar.version))) { + return new int[]{-1, changed}; + } + } + } + + mapVars.remove(leftpaar.var); + + boolean vernotreplaced = false; + boolean verreplaced = false; + + + HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); + + for (VarVersionNode usedvar : usedVers) { + VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && + (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD + || setNextVars == null || setNextVars.contains(usedver))) { + + setTempUsedVers.add(usedver); + verreplaced = true; + } + else { + vernotreplaced = true; + } + } + + if (isSelfReference && vernotreplaced) { + return new int[]{-1, changed}; + } + else { + for (VarVersionPaar usedver : setTempUsedVers) { + Exprent copy = right.copy(); + if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { + ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); + } + + mapVarValues.put(usedver, copy); + } + } + + if (!notdom && !vernotreplaced) { + // remove assignment + lstExprents.remove(index); + return new int[]{index, 1}; + } + else if (verreplaced) { + return new int[]{index + 1, changed}; + } + else { + return new int[]{-1, changed}; + } + } + + private HashSet<VarVersionPaar> getAllVersions(Exprent exprent) { + + HashSet<VarVersionPaar> res = new HashSet<VarVersionPaar>(); + + List<Exprent> listTemp = new ArrayList<Exprent>(exprent.getAllExprents(true)); + listTemp.add(exprent); + + for (Exprent expr : listTemp) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + res.add(new VarVersionPaar(var)); + } + } + + return res; + } + + private Object[] iterateChildExprent(Exprent exprent, + Exprent parent, + Exprent next, + HashMap<VarVersionPaar, Exprent> mapVarValues, + SSAUConstructorSparseEx ssau) { + + boolean changed = false; + + for (Exprent expr : exprent.getAllExprents()) { + for (; ; ) { + Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau); + Exprent retexpr = (Exprent)arr[0]; + changed |= (Boolean)arr[1]; + + boolean isReplaceable = (Boolean)arr[2]; + if (retexpr != null) { + if (isReplaceable) { + replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); + expr = retexpr; + } + else { + exprent.replaceExprent(expr, retexpr); + } + changed = true; + } + + if (!isReplaceable) { + break; + } + } + } + + Exprent dest = isReplaceableVar(exprent, mapVarValues, ssau); + if (dest != null) { + return new Object[]{dest, true, true}; + } + + + VarExprent left = null; + Exprent right = null; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + if (as.getLeft().type == Exprent.EXPRENT_VAR) { + left = (VarExprent)as.getLeft(); + right = as.getRight(); + } + } + + if (left == null) { + return new Object[]{null, changed, false}; + } + + boolean isHeadSynchronized = false; + if (next == null && parent.type == Exprent.EXPRENT_MONITOR) { + MonitorExprent monexpr = (MonitorExprent)parent; + if (monexpr.getMontype() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) { + isHeadSynchronized = true; + } + } + + // stack variable or synchronized head exprent + if (!left.isStack() && !isHeadSynchronized) { + return new Object[]{null, changed, false}; + } + + VarVersionPaar leftpaar = new VarVersionPaar(left); + + List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); + boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); + + if (!notdom && usedVers.isEmpty()) { + return new Object[]{right, changed, false}; + } + + // stack variables only + if (!left.isStack()) { + return new Object[]{null, changed, false}; + } + + int useflags = right.getExprentUse(); + + if ((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) { + return new Object[]{null, changed, false}; + } + + HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); + + if (mapVars.containsKey(leftpaar.var) && notdom) { + return new Object[]{null, changed, false}; + } + + + mapVars.remove(leftpaar.var); + + HashSet<VarVersionPaar> setAllowedVars = getAllVersions(parent); + if (next != null) { + setAllowedVars.addAll(getAllVersions(next)); + } + + boolean vernotreplaced = false; + + HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); + + for (VarVersionNode usedvar : usedVers) { + VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && + (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) { + + setTempUsedVers.add(usedver); + } + else { + vernotreplaced = true; + } + } + + if (!notdom && !vernotreplaced) { + + for (VarVersionPaar usedver : setTempUsedVers) { + Exprent copy = right.copy(); + if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { + ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); + } + + mapVarValues.put(usedver, copy); + } + + // remove assignment + return new Object[]{right, changed, false}; + } + + return new Object[]{null, changed, false}; + } + + private boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPaar var, List<VarVersionNode> res) { + + VarVersionsGraph ssuversions = ssa.getSsuversions(); + VarVersionNode varnode = ssuversions.nodes.getWithKey(var); + + HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); + + HashSet<VarVersionNode> setNotDoms = new HashSet<VarVersionNode>(); + + LinkedList<VarVersionNode> stack = new LinkedList<VarVersionNode>(); + stack.add(varnode); + + while (!stack.isEmpty()) { + + VarVersionNode nd = stack.remove(0); + setVisited.add(nd); + + if (nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FINEXIT) == 0) { + res.add(nd); + } + + for (VarVersionEdge edge : nd.succs) { + VarVersionNode succ = edge.dest; + + if (!setVisited.contains(edge.dest)) { + + boolean isDominated = true; + for (VarVersionEdge prededge : succ.preds) { + if (!setVisited.contains(prededge.source)) { + isDominated = false; + break; + } + } + + if (isDominated) { + stack.add(succ); + } + else { + setNotDoms.add(succ); + } + } + } + } + + setNotDoms.removeAll(setVisited); + + return !setNotDoms.isEmpty(); + } + + private boolean isVersionToBeReplaced(VarVersionPaar usedvar, + HashMap<Integer, HashSet<VarVersionPaar>> mapVars, + SSAUConstructorSparseEx ssau, + VarVersionPaar leftpaar) { + + VarVersionsGraph ssuversions = ssau.getSsuversions(); + + SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar); + if (mapLiveVars == null) { + // dummy version, predecessor of a phi node + return false; + } + + // compare protected ranges + if (!InterpreterUtil.equalObjects(ssau.getMapVersionFirstRange().get(leftpaar), + ssau.getMapVersionFirstRange().get(usedvar))) { + return false; + } + + for (Entry<Integer, HashSet<VarVersionPaar>> ent : mapVars.entrySet()) { + FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey()); + if (liveverset == null) { + return false; + } + + HashSet<VarVersionNode> domset = new HashSet<VarVersionNode>(); + for (VarVersionPaar verpaar : ent.getValue()) { + domset.add(ssuversions.nodes.getWithKey(verpaar)); + } + + boolean isdom = false; + + for (Integer livever : liveverset) { + VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPaar(ent.getKey().intValue(), livever.intValue())); + + if (ssuversions.isDominatorSet(node, domset)) { + isdom = true; + break; + } + } + + if (!isdom) { + return false; + } + } + + return true; + } + + private HashMap<Integer, HashSet<VarVersionPaar>> getAllVarVersions(VarVersionPaar leftvar, + Exprent exprent, + SSAUConstructorSparseEx ssau) { + + HashMap<Integer, HashSet<VarVersionPaar>> map = new HashMap<Integer, HashSet<VarVersionPaar>>(); + SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar); + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)expr).getIndex(); + if (leftvar.var != varindex) { + if (mapLiveVars.containsKey(varindex)) { + HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); + for (Integer vers : mapLiveVars.get(varindex)) { + verset.add(new VarVersionPaar(varindex, vers.intValue())); + } + map.put(varindex, verset); + } + else { + throw new RuntimeException("inkonsistent live map!"); + } + } + else { + map.put(varindex, null); + } + } + else if (expr.type == Exprent.EXPRENT_FIELD) { + if (ssau.getMapFieldVars().containsKey(expr.id)) { + int varindex = ssau.getMapFieldVars().get(expr.id); + if (mapLiveVars.containsKey(varindex)) { + HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); + for (Integer vers : mapLiveVars.get(varindex)) { + verset.add(new VarVersionPaar(varindex, vers.intValue())); + } + map.put(varindex, verset); + } + } + } + } + + return map; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java index b20c2c7..26d427b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java @@ -1,103 +1,104 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.stats.Statement; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; - public class StatEdge { - - public static final int TYPE_ALL = 0xFF; - - public static final int TYPE_REGULAR = 1; - public static final int TYPE_EXCEPTION = 2; - public static final int TYPE_BREAK = 4; - public static final int TYPE_CONTINUE = 8; - public static final int TYPE_FINALLYEXIT = 32; - - public static final int[] TYPES = new int[] { - TYPE_REGULAR, - TYPE_EXCEPTION, - TYPE_BREAK, - TYPE_CONTINUE, - TYPE_FINALLYEXIT - }; - - private int type; - - private Statement source; - - private Statement destination; - - private List<String> exceptions; - - public Statement closure; - - public boolean labeled = true; - - public boolean explicit = true; - - public StatEdge(int type, Statement source, Statement destination, Statement closure) { - this(type, source, destination); - this.closure = closure; - } - - public StatEdge(int type, Statement source, Statement destination) { - this.type = type; - this.source = source; - this.destination = destination; - } - - public StatEdge(Statement source, Statement destination, List<String> exceptions) { - this(TYPE_EXCEPTION, source, destination); - if(exceptions != null) { - this.exceptions = new ArrayList<String>(exceptions); - } - } - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - public Statement getSource() { - return source; - } - - public void setSource(Statement source) { - this.source = source; - } - - public Statement getDestination() { - return destination; - } - - public void setDestination(Statement destination) { - this.destination = destination; - } - - public List<String> getExceptions() { - return this.exceptions; - } - -// public void setException(String exception) { -// this.exception = exception; -// } + + public static final int TYPE_ALL = 0xFF; + + public static final int TYPE_REGULAR = 1; + public static final int TYPE_EXCEPTION = 2; + public static final int TYPE_BREAK = 4; + public static final int TYPE_CONTINUE = 8; + public static final int TYPE_FINALLYEXIT = 32; + + public static final int[] TYPES = new int[]{ + TYPE_REGULAR, + TYPE_EXCEPTION, + TYPE_BREAK, + TYPE_CONTINUE, + TYPE_FINALLYEXIT + }; + + private int type; + + private Statement source; + + private Statement destination; + + private List<String> exceptions; + + public Statement closure; + + public boolean labeled = true; + + public boolean explicit = true; + + public StatEdge(int type, Statement source, Statement destination, Statement closure) { + this(type, source, destination); + this.closure = closure; + } + + public StatEdge(int type, Statement source, Statement destination) { + this.type = type; + this.source = source; + this.destination = destination; + } + + public StatEdge(Statement source, Statement destination, List<String> exceptions) { + this(TYPE_EXCEPTION, source, destination); + if (exceptions != null) { + this.exceptions = new ArrayList<String>(exceptions); + } + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Statement getSource() { + return source; + } + + public void setSource(Statement source) { + this.source = source; + } + + public Statement getDestination() { + return destination; + } + + public void setDestination(Statement destination) { + this.destination = destination; + } + + public List<String> getExceptions() { + return this.exceptions; + } + + // public void setException(String exception) { + // this.exception = exception; + // } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java index a686f77..23f778d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java @@ -1,27 +1,28 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.util.ListStack; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.util.ListStack; - // -------------------------------------------------------------------- // Algorithm // -------------------------------------------------------------------- @@ -62,143 +63,145 @@ import org.jetbrains.java.decompiler.util.ListStack; public class StrongConnectivityHelper { - private ListStack<Statement> lstack; - - private int ncounter; - - private HashSet<Statement> tset; - private HashMap<Statement, Integer> dfsnummap; - private HashMap<Statement, Integer> lowmap; - - private List<List<Statement>> components; - - private HashSet<Statement> setProcessed; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public StrongConnectivityHelper() {} - - public StrongConnectivityHelper(Statement stat) { - findComponents(stat); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public List<List<Statement>> findComponents(Statement stat) { - - components = new ArrayList<List<Statement>>(); - setProcessed = new HashSet<Statement>(); - - visitTree(stat.getFirst()); - - for(Statement st: stat.getStats()) { - if(!setProcessed.contains(st) && st.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) { - visitTree(st); - } - } - - // should not find any more nodes! FIXME: ?? - for(Statement st: stat.getStats()) { - if(!setProcessed.contains(st)) { - visitTree(st); - } - } - - return components; - } - - public static boolean isExitComponent(List<Statement> lst) { - - HashSet<Statement> set = new HashSet<Statement>(); - for(Statement stat : lst) { - set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); - } - set.removeAll(lst); - - return (set.size() == 0); - } - - public static List<Statement> getExitReps(List<List<Statement>> lst) { - - List<Statement> res = new ArrayList<Statement>(); - - for(List<Statement> comp : lst) { - if(isExitComponent(comp)) { - res.add(comp.get(0)); - } - } - - return res; - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void visitTree(Statement stat) { - lstack = new ListStack<Statement>(); - ncounter = 0; - tset = new HashSet<Statement>(); - dfsnummap = new HashMap<Statement, Integer>(); - lowmap = new HashMap<Statement, Integer>(); - - visit(stat); - - setProcessed.addAll(tset); - setProcessed.add(stat); - } - - private void visit(Statement stat) { - - lstack.push(stat); - dfsnummap.put(stat, ncounter); - lowmap.put(stat, ncounter); - ncounter++; - - List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set? - lstSuccs.removeAll(setProcessed); - - for(int i=0;i<lstSuccs.size();i++) { - Statement succ = lstSuccs.get(i); - int secvalue; - - if(tset.contains(succ)) { - secvalue = dfsnummap.get(succ); - } else { - tset.add(succ); - visit(succ); - secvalue = lowmap.get(succ); - } - lowmap.put(stat, Math.min(lowmap.get(stat), secvalue)); - } - - - if(lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) { - List<Statement> lst = new ArrayList<Statement>(); - Statement v; - do { - v = lstack.pop(); - lst.add(v); - } while(v!=stat); - components.add(lst); - } - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public List<List<Statement>> getComponents() { - return components; - } - - public void setComponents(List<List<Statement>> components) { - this.components = components; - } - + private ListStack<Statement> lstack; + + private int ncounter; + + private HashSet<Statement> tset; + private HashMap<Statement, Integer> dfsnummap; + private HashMap<Statement, Integer> lowmap; + + private List<List<Statement>> components; + + private HashSet<Statement> setProcessed; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StrongConnectivityHelper() { + } + + public StrongConnectivityHelper(Statement stat) { + findComponents(stat); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public List<List<Statement>> findComponents(Statement stat) { + + components = new ArrayList<List<Statement>>(); + setProcessed = new HashSet<Statement>(); + + visitTree(stat.getFirst()); + + for (Statement st : stat.getStats()) { + if (!setProcessed.contains(st) && st.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) { + visitTree(st); + } + } + + // should not find any more nodes! FIXME: ?? + for (Statement st : stat.getStats()) { + if (!setProcessed.contains(st)) { + visitTree(st); + } + } + + return components; + } + + public static boolean isExitComponent(List<Statement> lst) { + + HashSet<Statement> set = new HashSet<Statement>(); + for (Statement stat : lst) { + set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); + } + set.removeAll(lst); + + return (set.size() == 0); + } + + public static List<Statement> getExitReps(List<List<Statement>> lst) { + + List<Statement> res = new ArrayList<Statement>(); + + for (List<Statement> comp : lst) { + if (isExitComponent(comp)) { + res.add(comp.get(0)); + } + } + + return res; + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void visitTree(Statement stat) { + lstack = new ListStack<Statement>(); + ncounter = 0; + tset = new HashSet<Statement>(); + dfsnummap = new HashMap<Statement, Integer>(); + lowmap = new HashMap<Statement, Integer>(); + + visit(stat); + + setProcessed.addAll(tset); + setProcessed.add(stat); + } + + private void visit(Statement stat) { + + lstack.push(stat); + dfsnummap.put(stat, ncounter); + lowmap.put(stat, ncounter); + ncounter++; + + List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set? + lstSuccs.removeAll(setProcessed); + + for (int i = 0; i < lstSuccs.size(); i++) { + Statement succ = lstSuccs.get(i); + int secvalue; + + if (tset.contains(succ)) { + secvalue = dfsnummap.get(succ); + } + else { + tset.add(succ); + visit(succ); + secvalue = lowmap.get(succ); + } + lowmap.put(stat, Math.min(lowmap.get(stat), secvalue)); + } + + + if (lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) { + List<Statement> lst = new ArrayList<Statement>(); + Statement v; + do { + v = lstack.pop(); + lst.add(v); + } + while (v != stat); + components.add(lst); + } + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<List<Statement>> getComponents() { + return components; + } + + public void setComponents(List<List<Statement>> components) { + this.components = components; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java index e17a271..b79c864 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java @@ -1,127 +1,128 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.List; + public class DominatorEngine { - private Statement statement; - - private VBStyleCollection<Integer, Integer> colOrderedIDoms = new VBStyleCollection<Integer, Integer>(); - - - public DominatorEngine(Statement statement) { - this.statement = statement; - } - - public void initialize() { - calcIDoms(); - } - - private void orderStatements() { - - for(Statement stat : statement.getReversePostOrderList()) { - colOrderedIDoms.addWithKey(null, stat.id); - } - - } - - private Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection<Integer, Integer> orderedIDoms) { - - if(key1 == null) { - return key2; - } else if(key2 == null) { - return key1; - } - - int index1 = orderedIDoms.getIndexByKey(key1); - int index2 = orderedIDoms.getIndexByKey(key2); - - while(index1 != index2) { - if(index1 > index2) { - key1 = orderedIDoms.getWithKey(key1); - index1 = orderedIDoms.getIndexByKey(key1); - } else { - key2 = orderedIDoms.getWithKey(key2); - index2 = orderedIDoms.getIndexByKey(key2); - } - } - - return key1; - } - - private void calcIDoms() { - - orderStatements(); - - colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id); - - // exclude first statement - List<Integer> lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size()); - - for(;;) { - - boolean changed = false; - - for(Integer id : lstIds) { - - Statement stat = statement.getStats().getWithKey(id); - Integer idom = null; - - for(StatEdge edge : stat.getAllPredecessorEdges()) { - if(colOrderedIDoms.getWithKey(edge.getSource().id) != null) { - idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms); - } - } - - Integer oldidom = colOrderedIDoms.putWithKey(idom, id); - if(!idom.equals(oldidom)) { - changed = true; - } - } - - if(!changed) { - break; - } - } - - } - - public VBStyleCollection<Integer, Integer> getOrderedIDoms() { - return colOrderedIDoms; - } - - public boolean isDominator(Integer node, Integer dom) { - - while(!node.equals(dom)) { - - Integer idom = colOrderedIDoms.getWithKey(node); - - if(idom.equals(node)) { - return false; // root node - } else { - node = idom; - } - } - - return true; - } - + private Statement statement; + + private VBStyleCollection<Integer, Integer> colOrderedIDoms = new VBStyleCollection<Integer, Integer>(); + + + public DominatorEngine(Statement statement) { + this.statement = statement; + } + + public void initialize() { + calcIDoms(); + } + + private void orderStatements() { + + for (Statement stat : statement.getReversePostOrderList()) { + colOrderedIDoms.addWithKey(null, stat.id); + } + } + + private Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection<Integer, Integer> orderedIDoms) { + + if (key1 == null) { + return key2; + } + else if (key2 == null) { + return key1; + } + + int index1 = orderedIDoms.getIndexByKey(key1); + int index2 = orderedIDoms.getIndexByKey(key2); + + while (index1 != index2) { + if (index1 > index2) { + key1 = orderedIDoms.getWithKey(key1); + index1 = orderedIDoms.getIndexByKey(key1); + } + else { + key2 = orderedIDoms.getWithKey(key2); + index2 = orderedIDoms.getIndexByKey(key2); + } + } + + return key1; + } + + private void calcIDoms() { + + orderStatements(); + + colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id); + + // exclude first statement + List<Integer> lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size()); + + for (; ; ) { + + boolean changed = false; + + for (Integer id : lstIds) { + + Statement stat = statement.getStats().getWithKey(id); + Integer idom = null; + + for (StatEdge edge : stat.getAllPredecessorEdges()) { + if (colOrderedIDoms.getWithKey(edge.getSource().id) != null) { + idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms); + } + } + + Integer oldidom = colOrderedIDoms.putWithKey(idom, id); + if (!idom.equals(oldidom)) { + changed = true; + } + } + + if (!changed) { + break; + } + } + } + + public VBStyleCollection<Integer, Integer> getOrderedIDoms() { + return colOrderedIDoms; + } + + public boolean isDominator(Integer node, Integer dom) { + + while (!node.equals(dom)) { + + Integer idom = colOrderedIDoms.getWithKey(node); + + if (idom.equals(node)) { + return false; // root node + } + else { + node = idom; + } + } + + return true; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java index cf9e90c..c11d5bf 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java @@ -1,188 +1,184 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.*; +import java.util.Map.Entry; + public class DominatorTreeExceptionFilter { - private Statement statement; - - // idom, nodes - private Map<Integer, Set<Integer>> mapTreeBranches = new HashMap<Integer, Set<Integer>>(); - - // handler, range nodes - private Map<Integer, Set<Integer>> mapExceptionRanges = new HashMap<Integer, Set<Integer>>(); - - // handler, head dom - private Map<Integer, Integer> mapExceptionDoms = new HashMap<Integer, Integer>(); - - // statement, handler, exit nodes - private Map<Integer, Map<Integer, Integer>> mapExceptionRangeUniqueExit = new HashMap<Integer, Map<Integer, Integer>>(); - - private DominatorEngine domEngine; - - public DominatorTreeExceptionFilter(Statement statement) { - this.statement = statement; - } - - public void initialize() { - - domEngine = new DominatorEngine(statement); - domEngine.initialize(); - - buildDominatorTree(); - - buildExceptionRanges(); - - buildFilter(statement.getFirst().id); - - // free resources - mapTreeBranches.clear(); - mapExceptionRanges.clear(); - - } - - public boolean acceptStatementPair(Integer head, Integer exit) { - - Map<Integer, Integer> filter = mapExceptionRangeUniqueExit.get(head); - for(Entry<Integer, Integer> entry : filter.entrySet()) { - if(!head.equals(mapExceptionDoms.get(entry.getKey()))) { - Integer filterExit = entry.getValue(); - if(filterExit.intValue() == -1 || !filterExit.equals(exit)) { - return false; - } - } - } - - return true; - } - - private void buildDominatorTree() { - - VBStyleCollection<Integer, Integer> orderedIDoms = domEngine.getOrderedIDoms(); - - List<Integer> lstKeys = orderedIDoms.getLstKeys(); - for(int index = lstKeys.size()-1;index>=0;index--) { - Integer key = lstKeys.get(index); - Integer idom = orderedIDoms.get(index); - - Set<Integer> set = mapTreeBranches.get(idom); - if(set == null) { - mapTreeBranches.put(idom, set = new HashSet<Integer>()); - } - set.add(key); - } - - Integer firstid = statement.getFirst().id; - mapTreeBranches.get(firstid).remove(firstid); - } - - private void buildExceptionRanges() { - - for(Statement stat : statement.getStats()) { - List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); - if(!lstPreds.isEmpty()) { - - Set<Integer> set = new HashSet<Integer>(); - - for(Statement st : lstPreds) { - set.add(st.id); - } - - mapExceptionRanges.put(stat.id, set); - } - } - - mapExceptionDoms = buildExceptionDoms(statement.getFirst().id); - } - - private Map<Integer, Integer> buildExceptionDoms(Integer id) { - - Map<Integer, Integer> map = new HashMap<Integer, Integer>(); - - Set<Integer> children = mapTreeBranches.get(id); - if(children != null) { - for(Integer childid : children) { - Map<Integer, Integer> mapChild = buildExceptionDoms(childid); - for(Integer handler : mapChild.keySet()) { - map.put(handler, map.containsKey(handler)?id:mapChild.get(handler)); - } - } - } - - for(Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { - if(entry.getValue().contains(id)) { - map.put(entry.getKey(), id); - } - } - - return map; - } - - - private void buildFilter(Integer id) { - - Map<Integer, Integer> map = new HashMap<Integer, Integer>(); - - Set<Integer> children = mapTreeBranches.get(id); - if(children != null) { - for(Integer childid : children) { - - buildFilter(childid); - - Map<Integer, Integer> mapChild = mapExceptionRangeUniqueExit.get(childid); - - for(Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { - - Integer handler = entry.getKey(); - Set<Integer> range = entry.getValue(); - - if(range.contains(id)) { - - Integer exit = null; - - if(!range.contains(childid)) { - exit = childid; - } else { - // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug? - exit = map.containsKey(handler)?new Integer(-1):mapChild.get(handler); - } - - if(exit != null) { - map.put(handler, exit); - } - } - } - } - } - - mapExceptionRangeUniqueExit.put(id, map); - } - - public DominatorEngine getDomEngine() { - return domEngine; - } - + private Statement statement; + + // idom, nodes + private Map<Integer, Set<Integer>> mapTreeBranches = new HashMap<Integer, Set<Integer>>(); + + // handler, range nodes + private Map<Integer, Set<Integer>> mapExceptionRanges = new HashMap<Integer, Set<Integer>>(); + + // handler, head dom + private Map<Integer, Integer> mapExceptionDoms = new HashMap<Integer, Integer>(); + + // statement, handler, exit nodes + private Map<Integer, Map<Integer, Integer>> mapExceptionRangeUniqueExit = new HashMap<Integer, Map<Integer, Integer>>(); + + private DominatorEngine domEngine; + + public DominatorTreeExceptionFilter(Statement statement) { + this.statement = statement; + } + + public void initialize() { + + domEngine = new DominatorEngine(statement); + domEngine.initialize(); + + buildDominatorTree(); + + buildExceptionRanges(); + + buildFilter(statement.getFirst().id); + + // free resources + mapTreeBranches.clear(); + mapExceptionRanges.clear(); + } + + public boolean acceptStatementPair(Integer head, Integer exit) { + + Map<Integer, Integer> filter = mapExceptionRangeUniqueExit.get(head); + for (Entry<Integer, Integer> entry : filter.entrySet()) { + if (!head.equals(mapExceptionDoms.get(entry.getKey()))) { + Integer filterExit = entry.getValue(); + if (filterExit.intValue() == -1 || !filterExit.equals(exit)) { + return false; + } + } + } + + return true; + } + + private void buildDominatorTree() { + + VBStyleCollection<Integer, Integer> orderedIDoms = domEngine.getOrderedIDoms(); + + List<Integer> lstKeys = orderedIDoms.getLstKeys(); + for (int index = lstKeys.size() - 1; index >= 0; index--) { + Integer key = lstKeys.get(index); + Integer idom = orderedIDoms.get(index); + + Set<Integer> set = mapTreeBranches.get(idom); + if (set == null) { + mapTreeBranches.put(idom, set = new HashSet<Integer>()); + } + set.add(key); + } + + Integer firstid = statement.getFirst().id; + mapTreeBranches.get(firstid).remove(firstid); + } + + private void buildExceptionRanges() { + + for (Statement stat : statement.getStats()) { + List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); + if (!lstPreds.isEmpty()) { + + Set<Integer> set = new HashSet<Integer>(); + + for (Statement st : lstPreds) { + set.add(st.id); + } + + mapExceptionRanges.put(stat.id, set); + } + } + + mapExceptionDoms = buildExceptionDoms(statement.getFirst().id); + } + + private Map<Integer, Integer> buildExceptionDoms(Integer id) { + + Map<Integer, Integer> map = new HashMap<Integer, Integer>(); + + Set<Integer> children = mapTreeBranches.get(id); + if (children != null) { + for (Integer childid : children) { + Map<Integer, Integer> mapChild = buildExceptionDoms(childid); + for (Integer handler : mapChild.keySet()) { + map.put(handler, map.containsKey(handler) ? id : mapChild.get(handler)); + } + } + } + + for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { + if (entry.getValue().contains(id)) { + map.put(entry.getKey(), id); + } + } + + return map; + } + + + private void buildFilter(Integer id) { + + Map<Integer, Integer> map = new HashMap<Integer, Integer>(); + + Set<Integer> children = mapTreeBranches.get(id); + if (children != null) { + for (Integer childid : children) { + + buildFilter(childid); + + Map<Integer, Integer> mapChild = mapExceptionRangeUniqueExit.get(childid); + + for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { + + Integer handler = entry.getKey(); + Set<Integer> range = entry.getValue(); + + if (range.contains(id)) { + + Integer exit = null; + + if (!range.contains(childid)) { + exit = childid; + } + else { + // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug? + exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler); + } + + if (exit != null) { + map.put(handler, exit); + } + } + } + } + } + + mapExceptionRangeUniqueExit.put(id, map); + } + + public DominatorEngine getDomEngine() { + return domEngine; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java index e3093b5..daa898a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java @@ -1,362 +1,355 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.util.FastFixedSetFactory; -import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.util.*; +import java.util.Map.Entry; public class FastExtendedPostdominanceHelper { - private List<Statement> lstReversePostOrderList; - - private HashMap<Integer, FastFixedSet<Integer>> mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); - - private HashMap<Integer, FastFixedSet<Integer>> mapExtPostdominators = new HashMap<Integer, FastFixedSet<Integer>>(); - - private Statement statement; - - private FastFixedSetFactory<Integer> factory; - - public HashMap<Integer, Set<Integer>> getExtendedPostdominators(Statement statement) { - - this.statement = statement; - - HashSet<Integer> set = new HashSet<Integer>(); - for(Statement st : statement.getStats()) { - set.add(st.id); - } - this.factory = new FastFixedSetFactory<Integer>(set); - - lstReversePostOrderList = statement.getReversePostOrderList(); - -// try { -// DotExporter.toDotFile(statement, new File("c:\\Temp\\stat1.dot")); -// } catch (Exception ex) { -// ex.printStackTrace(); -// } - - calcDefaultReachableSets(); - - removeErroneousNodes(); - - DominatorTreeExceptionFilter filter = new DominatorTreeExceptionFilter(statement); - filter.initialize(); - - filterOnExceptionRanges(filter); - - filterOnDominance(filter); - - HashMap<Integer, Set<Integer>> res = new HashMap<Integer, Set<Integer>>(); - for(Entry<Integer, FastFixedSet<Integer>> entry : mapExtPostdominators.entrySet()) { - res.put(entry.getKey(), entry.getValue().toPlainSet()); - } - - return res; - } - - - private void filterOnDominance(DominatorTreeExceptionFilter filter) { - - DominatorEngine engine = filter.getDomEngine(); - - for(Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { - - FastFixedSet<Integer> setPostdoms = mapExtPostdominators.get(head); - - LinkedList<Statement> stack = new LinkedList<Statement>(); - LinkedList<FastFixedSet<Integer>> stackPath = new LinkedList<FastFixedSet<Integer>>(); - - stack.add(statement.getStats().getWithKey(head)); - stackPath.add(factory.spawnEmptySet()); - - Set<Statement> setVisited = new HashSet<Statement>(); - - while(!stack.isEmpty()) { - - Statement stat = stack.removeFirst(); - FastFixedSet<Integer> path = stackPath.removeFirst(); - - if(setPostdoms.contains(stat.id)) { - path.add(stat.id); - } - - if(path.contains(setPostdoms)) { - continue; - } - - setVisited.add(stat); - - int domflag = 0; - - for(Iterator<Integer> it = setPostdoms.iterator();it.hasNext();) { - Integer post = it.next(); - - if(!path.contains(post)) { - if(domflag == 0) { - domflag = engine.isDominator(stat.id, head)?2:1; - } - - if(domflag == 1) { // not a dominator - it.remove(); - } - } - } - - for(StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_REGULAR)) { - if(!setVisited.contains(edge.getDestination())) { - stack.add(edge.getDestination()); - stackPath.add(path.getCopy()); - } - } - } - - if(setPostdoms.isEmpty()) { - mapExtPostdominators.remove(head); - } - - } - } - - - private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) { - - - for(Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { - - FastFixedSet<Integer> set = mapExtPostdominators.get(head); - for(Iterator<Integer> it = set.iterator();it.hasNext();) { - if(!filter.acceptStatementPair(head, it.next())) { - it.remove(); - } - } - if(set.isEmpty()) { - mapExtPostdominators.remove(head); - } - } - - } - - - private void removeErroneousNodes() { - - mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); - - calcReachabilitySuppPoints(StatEdge.TYPE_REGULAR); - - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { - - Integer nodeid = node.id; - - FastFixedSet<Integer> setReachability = mapSets.get(nodeid); - List<FastFixedSet<Integer>> lstPredSets = new ArrayList<FastFixedSet<Integer>>(); - - for(StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { - FastFixedSet<Integer> setPred = mapSets.get(prededge.getSource().id); - if(setPred == null) { - setPred = mapSupportPoints.get(prededge.getSource().id); - } - - // setPred cannot be empty as it is a reachability set - lstPredSets.add(setPred); - } - - for(Integer id : setReachability.toPlainSet()) { - - FastFixedSet<Integer> setReachabilityCopy = setReachability.getCopy(); - - FastFixedSet<Integer> setIntersection = factory.spawnEmptySet(); - boolean isIntersectionInitialized = false; - - for(FastFixedSet<Integer> predset : lstPredSets) { - if(predset.contains(id)) { - if(!isIntersectionInitialized) { - setIntersection.union(predset); - isIntersectionInitialized = true; - } else { - setIntersection.intersection(predset); - } - } - } - - if(nodeid != id.intValue()) { - setIntersection.add(nodeid); - } else { - setIntersection.remove(nodeid); - } - - setReachabilityCopy.complement(setIntersection); - - mapExtPostdominators.get(id).complement(setReachabilityCopy); - } - - return false; - } - }, StatEdge.TYPE_REGULAR); - - // exception handlers cannot be postdominator nodes - // TODO: replace with a standard set? - FastFixedSet<Integer> setHandlers = factory.spawnEmptySet(); - boolean handlerfound = false; - - for(Statement stat : statement.getStats()) { - if(stat.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty() && - !stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { // exception handler - setHandlers.add(stat.id); - handlerfound = true; - } - } - - if(handlerfound) { - for(FastFixedSet<Integer> set : mapExtPostdominators.values()) { - set.complement(setHandlers); - } - } - } - - - private void calcDefaultReachableSets() { - - int edgetype = StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION; - - calcReachabilitySuppPoints(edgetype); - - for(Statement stat : statement.getStats()) { - mapExtPostdominators.put(stat.id, factory.spawnEmptySet()); - } - - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { - - Integer nodeid = node.id; - FastFixedSet<Integer> setReachability = mapSets.get(nodeid); - - for(Integer id : setReachability.toPlainSet()) { - mapExtPostdominators.get(id).add(nodeid); - } - - return false; - } - }, edgetype); - - } - - - private void calcReachabilitySuppPoints(final int edgetype) { - - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { - - // consider to be a support point - for(StatEdge sucedge : node.getAllSuccessorEdges()) { - if((sucedge.getType() & edgetype) != 0) { - if(mapSets.containsKey(sucedge.getDestination().id)) { - FastFixedSet<Integer> setReachability = mapSets.get(node.id); - - if(!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) { - mapSupportPoints.put(node.id, setReachability); - return true; - } - } - } - } - - return false; - } - }, edgetype); - - } - - private void iterateReachability(IReachabilityAction action, int edgetype) { - - for(;;) { - - boolean iterate = false; - - HashMap<Integer, FastFixedSet<Integer>> mapSets = new HashMap<Integer, FastFixedSet<Integer>>(); - - for(Statement stat : lstReversePostOrderList) { - - FastFixedSet<Integer> set = factory.spawnEmptySet(); - set.add(stat.id); - - for(StatEdge prededge : stat.getAllPredecessorEdges()) { - if((prededge.getType() & edgetype) != 0) { - Statement pred = prededge.getSource(); - - FastFixedSet<Integer> setPred = mapSets.get(pred.id); - if(setPred == null) { - setPred = mapSupportPoints.get(pred.id); - } - - if(setPred != null) { - set.union(setPred); - } - } - } - - mapSets.put(stat.id, set); - - if(action != null) { - iterate |= action.action(stat, mapSets); - } - - // remove reachability information of fully processed nodes (saves memory) - for(StatEdge prededge : stat.getAllPredecessorEdges()) { - if((prededge.getType() & edgetype) != 0) { - Statement pred = prededge.getSource(); - - if(mapSets.containsKey(pred.id)) { - boolean remstat = true; - for(StatEdge sucedge : pred.getAllSuccessorEdges()) { - if((sucedge.getType() & edgetype) != 0) { - if(!mapSets.containsKey(sucedge.getDestination().id)) { - remstat = false; - break; - } - } - } - - if(remstat) { - mapSets.put(pred.id, null); - } - } - } - } - } - - if(!iterate) { - break; - } - } - } - - - private interface IReachabilityAction { - public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets); - } + private List<Statement> lstReversePostOrderList; + + private HashMap<Integer, FastFixedSet<Integer>> mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); + + private HashMap<Integer, FastFixedSet<Integer>> mapExtPostdominators = new HashMap<Integer, FastFixedSet<Integer>>(); + + private Statement statement; + + private FastFixedSetFactory<Integer> factory; + + public HashMap<Integer, Set<Integer>> getExtendedPostdominators(Statement statement) { + + this.statement = statement; + + HashSet<Integer> set = new HashSet<Integer>(); + for (Statement st : statement.getStats()) { + set.add(st.id); + } + this.factory = new FastFixedSetFactory<Integer>(set); + + lstReversePostOrderList = statement.getReversePostOrderList(); + + // try { + // DotExporter.toDotFile(statement, new File("c:\\Temp\\stat1.dot")); + // } catch (Exception ex) { + // ex.printStackTrace(); + // } + + calcDefaultReachableSets(); + + removeErroneousNodes(); + + DominatorTreeExceptionFilter filter = new DominatorTreeExceptionFilter(statement); + filter.initialize(); + + filterOnExceptionRanges(filter); + + filterOnDominance(filter); + + HashMap<Integer, Set<Integer>> res = new HashMap<Integer, Set<Integer>>(); + for (Entry<Integer, FastFixedSet<Integer>> entry : mapExtPostdominators.entrySet()) { + res.put(entry.getKey(), entry.getValue().toPlainSet()); + } + + return res; + } + + + private void filterOnDominance(DominatorTreeExceptionFilter filter) { + + DominatorEngine engine = filter.getDomEngine(); + + for (Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { + + FastFixedSet<Integer> setPostdoms = mapExtPostdominators.get(head); + + LinkedList<Statement> stack = new LinkedList<Statement>(); + LinkedList<FastFixedSet<Integer>> stackPath = new LinkedList<FastFixedSet<Integer>>(); + + stack.add(statement.getStats().getWithKey(head)); + stackPath.add(factory.spawnEmptySet()); + + Set<Statement> setVisited = new HashSet<Statement>(); + + while (!stack.isEmpty()) { + + Statement stat = stack.removeFirst(); + FastFixedSet<Integer> path = stackPath.removeFirst(); + + if (setPostdoms.contains(stat.id)) { + path.add(stat.id); + } + + if (path.contains(setPostdoms)) { + continue; + } + + setVisited.add(stat); + + int domflag = 0; + + for (Iterator<Integer> it = setPostdoms.iterator(); it.hasNext(); ) { + Integer post = it.next(); + + if (!path.contains(post)) { + if (domflag == 0) { + domflag = engine.isDominator(stat.id, head) ? 2 : 1; + } + + if (domflag == 1) { // not a dominator + it.remove(); + } + } + } + + for (StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_REGULAR)) { + if (!setVisited.contains(edge.getDestination())) { + stack.add(edge.getDestination()); + stackPath.add(path.getCopy()); + } + } + } + + if (setPostdoms.isEmpty()) { + mapExtPostdominators.remove(head); + } + } + } + + + private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) { + + + for (Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { + + FastFixedSet<Integer> set = mapExtPostdominators.get(head); + for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) { + if (!filter.acceptStatementPair(head, it.next())) { + it.remove(); + } + } + if (set.isEmpty()) { + mapExtPostdominators.remove(head); + } + } + } + + + private void removeErroneousNodes() { + + mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); + + calcReachabilitySuppPoints(StatEdge.TYPE_REGULAR); + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + Integer nodeid = node.id; + + FastFixedSet<Integer> setReachability = mapSets.get(nodeid); + List<FastFixedSet<Integer>> lstPredSets = new ArrayList<FastFixedSet<Integer>>(); + + for (StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + FastFixedSet<Integer> setPred = mapSets.get(prededge.getSource().id); + if (setPred == null) { + setPred = mapSupportPoints.get(prededge.getSource().id); + } + + // setPred cannot be empty as it is a reachability set + lstPredSets.add(setPred); + } + + for (Integer id : setReachability.toPlainSet()) { + + FastFixedSet<Integer> setReachabilityCopy = setReachability.getCopy(); + + FastFixedSet<Integer> setIntersection = factory.spawnEmptySet(); + boolean isIntersectionInitialized = false; + + for (FastFixedSet<Integer> predset : lstPredSets) { + if (predset.contains(id)) { + if (!isIntersectionInitialized) { + setIntersection.union(predset); + isIntersectionInitialized = true; + } + else { + setIntersection.intersection(predset); + } + } + } + + if (nodeid != id.intValue()) { + setIntersection.add(nodeid); + } + else { + setIntersection.remove(nodeid); + } + + setReachabilityCopy.complement(setIntersection); + + mapExtPostdominators.get(id).complement(setReachabilityCopy); + } + + return false; + } + }, StatEdge.TYPE_REGULAR); + + // exception handlers cannot be postdominator nodes + // TODO: replace with a standard set? + FastFixedSet<Integer> setHandlers = factory.spawnEmptySet(); + boolean handlerfound = false; + + for (Statement stat : statement.getStats()) { + if (stat.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty() && + !stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { // exception handler + setHandlers.add(stat.id); + handlerfound = true; + } + } + + if (handlerfound) { + for (FastFixedSet<Integer> set : mapExtPostdominators.values()) { + set.complement(setHandlers); + } + } + } + + + private void calcDefaultReachableSets() { + + int edgetype = StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION; + + calcReachabilitySuppPoints(edgetype); + + for (Statement stat : statement.getStats()) { + mapExtPostdominators.put(stat.id, factory.spawnEmptySet()); + } + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + Integer nodeid = node.id; + FastFixedSet<Integer> setReachability = mapSets.get(nodeid); + + for (Integer id : setReachability.toPlainSet()) { + mapExtPostdominators.get(id).add(nodeid); + } + + return false; + } + }, edgetype); + } + + + private void calcReachabilitySuppPoints(final int edgetype) { + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + // consider to be a support point + for (StatEdge sucedge : node.getAllSuccessorEdges()) { + if ((sucedge.getType() & edgetype) != 0) { + if (mapSets.containsKey(sucedge.getDestination().id)) { + FastFixedSet<Integer> setReachability = mapSets.get(node.id); + + if (!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) { + mapSupportPoints.put(node.id, setReachability); + return true; + } + } + } + } + + return false; + } + }, edgetype); + } + + private void iterateReachability(IReachabilityAction action, int edgetype) { + + for (; ; ) { + + boolean iterate = false; + + HashMap<Integer, FastFixedSet<Integer>> mapSets = new HashMap<Integer, FastFixedSet<Integer>>(); + + for (Statement stat : lstReversePostOrderList) { + + FastFixedSet<Integer> set = factory.spawnEmptySet(); + set.add(stat.id); + + for (StatEdge prededge : stat.getAllPredecessorEdges()) { + if ((prededge.getType() & edgetype) != 0) { + Statement pred = prededge.getSource(); + + FastFixedSet<Integer> setPred = mapSets.get(pred.id); + if (setPred == null) { + setPred = mapSupportPoints.get(pred.id); + } + + if (setPred != null) { + set.union(setPred); + } + } + } + + mapSets.put(stat.id, set); + + if (action != null) { + iterate |= action.action(stat, mapSets); + } + + // remove reachability information of fully processed nodes (saves memory) + for (StatEdge prededge : stat.getAllPredecessorEdges()) { + if ((prededge.getType() & edgetype) != 0) { + Statement pred = prededge.getSource(); + + if (mapSets.containsKey(pred.id)) { + boolean remstat = true; + for (StatEdge sucedge : pred.getAllSuccessorEdges()) { + if ((sucedge.getType() & edgetype) != 0) { + if (!mapSets.containsKey(sucedge.getDestination().id)) { + remstat = false; + break; + } + } + } + + if (remstat) { + mapSets.put(pred.id, null); + } + } + } + } + } + + if (!iterate) { + break; + } + } + } + + + private interface IReachabilityAction { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java index ed35202..2eca3b7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java @@ -1,150 +1,152 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + import java.util.List; import java.util.Set; -import org.jetbrains.java.decompiler.util.VBStyleCollection; - public class GenericDominatorEngine { - private IGraph graph; - - private VBStyleCollection<IGraphNode, IGraphNode> colOrderedIDoms = new VBStyleCollection<IGraphNode, IGraphNode>(); - - private Set<? extends IGraphNode> setRoots; - - public GenericDominatorEngine(IGraph graph) { - this.graph = graph; - } - - public void initialize() { - calcIDoms(); - } - - private void orderNodes() { - - setRoots = graph.getRoots(); - - for(IGraphNode node : graph.getReversePostOrderList()) { - colOrderedIDoms.addWithKey(null, node); - } - - } - - private IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection<IGraphNode, IGraphNode> orderedIDoms) { - - IGraphNode nodeOld; - - if(node1 == null) { - return node2; - } else if(node2 == null) { - return node1; - } - - int index1 = orderedIDoms.getIndexByKey(node1); - int index2 = orderedIDoms.getIndexByKey(node2); - - while(index1 != index2) { - if(index1 > index2) { - nodeOld = node1; - node1 = orderedIDoms.getWithKey(node1); - - if(nodeOld == node1) { // no idom - root or merging point - return null; - } - - index1 = orderedIDoms.getIndexByKey(node1); - } else { - nodeOld = node2; - node2 = orderedIDoms.getWithKey(node2); - - if(nodeOld == node2) { // no idom - root or merging point - return null; - } - - index2 = orderedIDoms.getIndexByKey(node2); - } - } - - return node1; - } - - private void calcIDoms() { - - orderNodes(); - - List<IGraphNode> lstNodes = colOrderedIDoms.getLstKeys(); - - for(;;) { - - boolean changed = false; - - for(IGraphNode node : lstNodes) { - - IGraphNode idom = null; - - if(!setRoots.contains(node)) { - for(IGraphNode pred : node.getPredecessors()) { - if(colOrderedIDoms.getWithKey(pred) != null) { - idom = getCommonIDom(idom, pred, colOrderedIDoms); - if(idom == null) { - break; // no idom found: merging point of two trees - } - } - } - } - - if(idom == null) { - idom = node; - } - - IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node); - if(!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time - changed = true; - } - } - - if(!changed) { - break; - } - } - - } - - public VBStyleCollection<IGraphNode, IGraphNode> getOrderedIDoms() { - return colOrderedIDoms; - } - - public boolean isDominator(IGraphNode node, IGraphNode dom) { - - while(!node.equals(dom)) { - - IGraphNode idom = colOrderedIDoms.getWithKey(node); - - if(idom == node) { - return false; // root node or merging point - } else if(idom == null) { - throw new RuntimeException("Inconsistent idom sequence discovered!"); - } else { - node = idom; - } - } - - return true; - } - + private IGraph graph; + + private VBStyleCollection<IGraphNode, IGraphNode> colOrderedIDoms = new VBStyleCollection<IGraphNode, IGraphNode>(); + + private Set<? extends IGraphNode> setRoots; + + public GenericDominatorEngine(IGraph graph) { + this.graph = graph; + } + + public void initialize() { + calcIDoms(); + } + + private void orderNodes() { + + setRoots = graph.getRoots(); + + for (IGraphNode node : graph.getReversePostOrderList()) { + colOrderedIDoms.addWithKey(null, node); + } + } + + private IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection<IGraphNode, IGraphNode> orderedIDoms) { + + IGraphNode nodeOld; + + if (node1 == null) { + return node2; + } + else if (node2 == null) { + return node1; + } + + int index1 = orderedIDoms.getIndexByKey(node1); + int index2 = orderedIDoms.getIndexByKey(node2); + + while (index1 != index2) { + if (index1 > index2) { + nodeOld = node1; + node1 = orderedIDoms.getWithKey(node1); + + if (nodeOld == node1) { // no idom - root or merging point + return null; + } + + index1 = orderedIDoms.getIndexByKey(node1); + } + else { + nodeOld = node2; + node2 = orderedIDoms.getWithKey(node2); + + if (nodeOld == node2) { // no idom - root or merging point + return null; + } + + index2 = orderedIDoms.getIndexByKey(node2); + } + } + + return node1; + } + + private void calcIDoms() { + + orderNodes(); + + List<IGraphNode> lstNodes = colOrderedIDoms.getLstKeys(); + + for (; ; ) { + + boolean changed = false; + + for (IGraphNode node : lstNodes) { + + IGraphNode idom = null; + + if (!setRoots.contains(node)) { + for (IGraphNode pred : node.getPredecessors()) { + if (colOrderedIDoms.getWithKey(pred) != null) { + idom = getCommonIDom(idom, pred, colOrderedIDoms); + if (idom == null) { + break; // no idom found: merging point of two trees + } + } + } + } + + if (idom == null) { + idom = node; + } + + IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node); + if (!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time + changed = true; + } + } + + if (!changed) { + break; + } + } + } + + public VBStyleCollection<IGraphNode, IGraphNode> getOrderedIDoms() { + return colOrderedIDoms; + } + + public boolean isDominator(IGraphNode node, IGraphNode dom) { + + while (!node.equals(dom)) { + + IGraphNode idom = colOrderedIDoms.getWithKey(node); + + if (idom == node) { + return false; // root node or merging point + } + else if (idom == null) { + throw new RuntimeException("Inconsistent idom sequence discovered!"); + } + else { + node = idom; + } + } + + return true; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java index 8b72a4f..9179f9c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; import java.util.List; @@ -19,8 +20,7 @@ import java.util.Set; public interface IGraph { - public List<? extends IGraphNode> getReversePostOrderList(); - - public Set<? extends IGraphNode> getRoots(); - + public List<? extends IGraphNode> getReversePostOrderList(); + + public Set<? extends IGraphNode> getRoots(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java index d2d1e0b..191b974 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java @@ -1,23 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.decompose; import java.util.List; public interface IGraphNode { - public List<? extends IGraphNode> getPredecessors(); - + public List<? extends IGraphNode> getPredecessors(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index d0f7243..b015230 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -1,28 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.deobfuscator; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; @@ -35,290 +27,294 @@ import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.*; +import java.util.Map.Entry; + public class ExceptionDeobfuscator { - public static void restorePopRanges(ControlFlowGraph graph) { - - List<Object[]> lstRanges = new ArrayList<Object[]>(); - - // aggregate ranges - for(ExceptionRangeCFG range : graph.getExceptions()) { - boolean found = false; - for(Object[] arr : lstRanges) { - if(arr[0] == range.getHandler() && InterpreterUtil.equalObjects(range.getUniqueExceptionsString(),arr[1])) { - ((HashSet<BasicBlock>)arr[2]).addAll(range.getProtectedRange()); - found = true; - break; - } - } - - if(!found) { - // doesn't matter, which range chosen - lstRanges.add(new Object[] {range.getHandler(), range.getUniqueExceptionsString(), new HashSet<BasicBlock>(range.getProtectedRange()), range}); - } - } - - // process aggregated ranges - for(Object[] range : lstRanges) { - - if(range[1] != null) { - - BasicBlock handler = (BasicBlock)range[0]; - InstructionSequence seq = handler.getSeq(); - - Instruction firstinstr = null; - if(seq.length() > 0) { - firstinstr = seq.getInstr(0); - - if(firstinstr.opcode == CodeConstants.opc_pop || - firstinstr.opcode == CodeConstants.opc_astore) { - HashSet<BasicBlock> setrange = new HashSet<BasicBlock>((HashSet<BasicBlock>)range[2]); - - for(Object[] range_super : lstRanges) { // finally or strict superset - - if(range != range_super) { - - HashSet<BasicBlock> setrange_super = new HashSet<BasicBlock>((HashSet<BasicBlock>)range_super[2]); - - if(!setrange.contains(range_super[0]) && !setrange_super.contains(handler) - && (range_super[1] == null || setrange_super.containsAll(setrange))) { - - if(range_super[1] == null) { - setrange_super.retainAll(setrange); - } else { - setrange_super.removeAll(setrange); - } - - if(!setrange_super.isEmpty()) { - - BasicBlock newblock = handler; - - // split the handler - if(seq.length() > 1) { - newblock = new BasicBlock(++graph.last_id); - InstructionSequence newseq = new SimpleInstructionSequence(); - newseq.addInstruction(firstinstr.clone() , -1); - - newblock.setSeq(newseq); - graph.getBlocks().addWithKey(newblock, newblock.id); - - - List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); - lstTemp.addAll(handler.getPreds()); - lstTemp.addAll(handler.getPredExceptions()); - - // replace predecessors - for(BasicBlock pred: lstTemp) { - pred.replaceSuccessor(handler, newblock); - } - - // replace handler - for(ExceptionRangeCFG range_ext: graph.getExceptions()) { - if(range_ext.getHandler() == handler) { - range_ext.setHandler(newblock); - } else if(range_ext.getProtectedRange().contains(handler)) { - newblock.addSuccessorException(range_ext.getHandler()); - range_ext.getProtectedRange().add(newblock); - } - } - - newblock.addSuccessor(handler); - if(graph.getFirst() == handler) { - graph.setFirst(newblock); - } - - // remove the first pop in the handler - seq.removeInstruction(0); - } - - - newblock.addSuccessorException((BasicBlock)range_super[0]); - ((ExceptionRangeCFG)range_super[3]).getProtectedRange().add(newblock); - - handler = ((ExceptionRangeCFG)range[3]).getHandler(); - seq = handler.getSeq(); - } - } - } - } - } - } - } - } - } - - public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) { - - HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); - - for(ExceptionRangeCFG range : graph.getExceptions()) { - BasicBlock handler = range.getHandler(); - - if(setVisited.contains(handler)) { - continue; - } - setVisited.add(handler); - - BasicBlock emptyblock = new BasicBlock(++graph.last_id); - graph.getBlocks().addWithKey(emptyblock, emptyblock.id); - - List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); - // only exception predecessors considered - lstTemp.addAll(handler.getPredExceptions()); - - // replace predecessors - for(BasicBlock pred: lstTemp) { - pred.replaceSuccessor(handler, emptyblock); - } - - // replace handler - for(ExceptionRangeCFG range_ext: graph.getExceptions()) { - if(range_ext.getHandler() == handler) { - range_ext.setHandler(emptyblock); - } else if(range_ext.getProtectedRange().contains(handler)) { - emptyblock.addSuccessorException(range_ext.getHandler()); - range_ext.getProtectedRange().add(emptyblock); - } - } - - emptyblock.addSuccessor(handler); - if(graph.getFirst() == handler) { - graph.setFirst(emptyblock); - } - } - } - - public static void removeEmptyRanges(ControlFlowGraph graph) { - - List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); - for(int i=lstRanges.size()-1;i>=0;i--) { - ExceptionRangeCFG range = lstRanges.get(i); - - boolean isEmpty = true; - for(BasicBlock block : range.getProtectedRange()) { - if(!block.getSeq().isEmpty()) { - isEmpty = false; - break; - } - } - - if(isEmpty) { - for(BasicBlock block : range.getProtectedRange()) { - block.removeSuccessorException(range.getHandler()); - } - - lstRanges.remove(i); - } - } - - } - - public static void removeCircularRanges(final ControlFlowGraph graph) { - - GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { - public List<? extends IGraphNode> getReversePostOrderList() { - return graph.getReversePostOrder(); - } - - public Set<? extends IGraphNode> getRoots() { - return new HashSet<IGraphNode>(Arrays.asList(new IGraphNode[]{graph.getFirst()})); - } - }); - - engine.initialize(); - - List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); - for(int i=lstRanges.size()-1;i>=0;i--) { - ExceptionRangeCFG range = lstRanges.get(i); - - BasicBlock handler = range.getHandler(); - List<BasicBlock> rangeList = range.getProtectedRange(); - - if(rangeList.contains(handler)) { // TODO: better removing strategy - - List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine); - - if(lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) { - for(BasicBlock block : lstRemBlocks) { - block.removeSuccessorException(handler); - rangeList.remove(block); - } - } - - if(rangeList.isEmpty()) { - lstRanges.remove(i); - } - } - } - - } - - private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) { - - List<BasicBlock> lstRes = new ArrayList<BasicBlock>(); - - LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); - HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); - - BasicBlock handler = range.getHandler(); - stack.addFirst(handler); - - while(!stack.isEmpty()) { - BasicBlock block = stack.removeFirst(); - - setVisited.add(block); - - if(range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) { - lstRes.add(block); - - List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); - lstSuccs.addAll(block.getSuccExceptions()); - - for(BasicBlock succ : lstSuccs) { - if(!setVisited.contains(succ)) { - stack.add(succ); - } - } - } - } - - return lstRes; - } - - - public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) { - - BasicBlock first = graph.getFirst(); - - HashMap<BasicBlock, HashSet<BasicBlock>> mapRanges = new HashMap<BasicBlock, HashSet<BasicBlock>>(); - for(ExceptionRangeCFG range : graph.getExceptions()) { - HashSet<BasicBlock> set = mapRanges.get(range.getHandler()); - if(set == null) { - mapRanges.put(range.getHandler(), set = new HashSet<BasicBlock>()); - } - set.addAll(range.getProtectedRange()); - - } - - for(Entry<BasicBlock, HashSet<BasicBlock>> ent : mapRanges.entrySet()) { - HashSet<BasicBlock> setEntries = new HashSet<BasicBlock>(); - - for(BasicBlock block : ent.getValue()) { - HashSet<BasicBlock> setTemp = new HashSet<BasicBlock>(block.getPreds()); - setTemp.removeAll(ent.getValue()); - - if(!setTemp.isEmpty()) { - setEntries.add(block); - } - } - - if(!setEntries.isEmpty()) { - if(setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) { - return true; - } - } - } - - return false; - } + public static void restorePopRanges(ControlFlowGraph graph) { + + List<Object[]> lstRanges = new ArrayList<Object[]>(); + + // aggregate ranges + for (ExceptionRangeCFG range : graph.getExceptions()) { + boolean found = false; + for (Object[] arr : lstRanges) { + if (arr[0] == range.getHandler() && InterpreterUtil.equalObjects(range.getUniqueExceptionsString(), arr[1])) { + ((HashSet<BasicBlock>)arr[2]).addAll(range.getProtectedRange()); + found = true; + break; + } + } + + if (!found) { + // doesn't matter, which range chosen + lstRanges.add( + new Object[]{range.getHandler(), range.getUniqueExceptionsString(), new HashSet<BasicBlock>(range.getProtectedRange()), range}); + } + } + + // process aggregated ranges + for (Object[] range : lstRanges) { + + if (range[1] != null) { + + BasicBlock handler = (BasicBlock)range[0]; + InstructionSequence seq = handler.getSeq(); + + Instruction firstinstr = null; + if (seq.length() > 0) { + firstinstr = seq.getInstr(0); + + if (firstinstr.opcode == CodeConstants.opc_pop || + firstinstr.opcode == CodeConstants.opc_astore) { + HashSet<BasicBlock> setrange = new HashSet<BasicBlock>((HashSet<BasicBlock>)range[2]); + + for (Object[] range_super : lstRanges) { // finally or strict superset + + if (range != range_super) { + + HashSet<BasicBlock> setrange_super = new HashSet<BasicBlock>((HashSet<BasicBlock>)range_super[2]); + + if (!setrange.contains(range_super[0]) && !setrange_super.contains(handler) + && (range_super[1] == null || setrange_super.containsAll(setrange))) { + + if (range_super[1] == null) { + setrange_super.retainAll(setrange); + } + else { + setrange_super.removeAll(setrange); + } + + if (!setrange_super.isEmpty()) { + + BasicBlock newblock = handler; + + // split the handler + if (seq.length() > 1) { + newblock = new BasicBlock(++graph.last_id); + InstructionSequence newseq = new SimpleInstructionSequence(); + newseq.addInstruction(firstinstr.clone(), -1); + + newblock.setSeq(newseq); + graph.getBlocks().addWithKey(newblock, newblock.id); + + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + lstTemp.addAll(handler.getPreds()); + lstTemp.addAll(handler.getPredExceptions()); + + // replace predecessors + for (BasicBlock pred : lstTemp) { + pred.replaceSuccessor(handler, newblock); + } + + // replace handler + for (ExceptionRangeCFG range_ext : graph.getExceptions()) { + if (range_ext.getHandler() == handler) { + range_ext.setHandler(newblock); + } + else if (range_ext.getProtectedRange().contains(handler)) { + newblock.addSuccessorException(range_ext.getHandler()); + range_ext.getProtectedRange().add(newblock); + } + } + + newblock.addSuccessor(handler); + if (graph.getFirst() == handler) { + graph.setFirst(newblock); + } + + // remove the first pop in the handler + seq.removeInstruction(0); + } + + + newblock.addSuccessorException((BasicBlock)range_super[0]); + ((ExceptionRangeCFG)range_super[3]).getProtectedRange().add(newblock); + + handler = ((ExceptionRangeCFG)range[3]).getHandler(); + seq = handler.getSeq(); + } + } + } + } + } + } + } + } + } + + public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) { + + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + for (ExceptionRangeCFG range : graph.getExceptions()) { + BasicBlock handler = range.getHandler(); + + if (setVisited.contains(handler)) { + continue; + } + setVisited.add(handler); + + BasicBlock emptyblock = new BasicBlock(++graph.last_id); + graph.getBlocks().addWithKey(emptyblock, emptyblock.id); + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + // only exception predecessors considered + lstTemp.addAll(handler.getPredExceptions()); + + // replace predecessors + for (BasicBlock pred : lstTemp) { + pred.replaceSuccessor(handler, emptyblock); + } + + // replace handler + for (ExceptionRangeCFG range_ext : graph.getExceptions()) { + if (range_ext.getHandler() == handler) { + range_ext.setHandler(emptyblock); + } + else if (range_ext.getProtectedRange().contains(handler)) { + emptyblock.addSuccessorException(range_ext.getHandler()); + range_ext.getProtectedRange().add(emptyblock); + } + } + + emptyblock.addSuccessor(handler); + if (graph.getFirst() == handler) { + graph.setFirst(emptyblock); + } + } + } + + public static void removeEmptyRanges(ControlFlowGraph graph) { + + List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for (int i = lstRanges.size() - 1; i >= 0; i--) { + ExceptionRangeCFG range = lstRanges.get(i); + + boolean isEmpty = true; + for (BasicBlock block : range.getProtectedRange()) { + if (!block.getSeq().isEmpty()) { + isEmpty = false; + break; + } + } + + if (isEmpty) { + for (BasicBlock block : range.getProtectedRange()) { + block.removeSuccessorException(range.getHandler()); + } + + lstRanges.remove(i); + } + } + } + + public static void removeCircularRanges(final ControlFlowGraph graph) { + + GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { + public List<? extends IGraphNode> getReversePostOrderList() { + return graph.getReversePostOrder(); + } + + public Set<? extends IGraphNode> getRoots() { + return new HashSet<IGraphNode>(Arrays.asList(new IGraphNode[]{graph.getFirst()})); + } + }); + + engine.initialize(); + + List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for (int i = lstRanges.size() - 1; i >= 0; i--) { + ExceptionRangeCFG range = lstRanges.get(i); + + BasicBlock handler = range.getHandler(); + List<BasicBlock> rangeList = range.getProtectedRange(); + + if (rangeList.contains(handler)) { // TODO: better removing strategy + + List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine); + + if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) { + for (BasicBlock block : lstRemBlocks) { + block.removeSuccessorException(handler); + rangeList.remove(block); + } + } + + if (rangeList.isEmpty()) { + lstRanges.remove(i); + } + } + } + } + + private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) { + + List<BasicBlock> lstRes = new ArrayList<BasicBlock>(); + + LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + BasicBlock handler = range.getHandler(); + stack.addFirst(handler); + + while (!stack.isEmpty()) { + BasicBlock block = stack.removeFirst(); + + setVisited.add(block); + + if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) { + lstRes.add(block); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); + lstSuccs.addAll(block.getSuccExceptions()); + + for (BasicBlock succ : lstSuccs) { + if (!setVisited.contains(succ)) { + stack.add(succ); + } + } + } + } + + return lstRes; + } + + + public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) { + + BasicBlock first = graph.getFirst(); + + HashMap<BasicBlock, HashSet<BasicBlock>> mapRanges = new HashMap<BasicBlock, HashSet<BasicBlock>>(); + for (ExceptionRangeCFG range : graph.getExceptions()) { + HashSet<BasicBlock> set = mapRanges.get(range.getHandler()); + if (set == null) { + mapRanges.put(range.getHandler(), set = new HashSet<BasicBlock>()); + } + set.addAll(range.getProtectedRange()); + } + + for (Entry<BasicBlock, HashSet<BasicBlock>> ent : mapRanges.entrySet()) { + HashSet<BasicBlock> setEntries = new HashSet<BasicBlock>(); + + for (BasicBlock block : ent.getValue()) { + HashSet<BasicBlock> setTemp = new HashSet<BasicBlock>(block.getPreds()); + setTemp.removeAll(ent.getValue()); + + if (!setTemp.isEmpty()) { + setEntries.add(block); + } + } + + if (!setEntries.isEmpty()) { + if (setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) { + return true; + } + } + } + + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java index 250a25e..11f9483 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java @@ -1,238 +1,245 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.deobfuscator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + public class IrreducibleCFGDeobfuscator { - - - public static boolean isStatementIrreducible(Statement statement) { - - class Node { - public Integer id; - public Set<Node> preds = new HashSet<Node>(); - public Set<Node> succs = new HashSet<Node>(); - - public Node(Integer id) {this.id = id;} - } - - HashMap<Integer, Node> mapNodes = new HashMap<Integer, Node>(); - - // checking exceptions and creating nodes - for(Statement stat : statement.getStats()) { - if(!stat.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { - return false; - } - - mapNodes.put(stat.id, new Node(stat.id)); - } - - // connecting nodes - for(Statement stat : statement.getStats()) { - Node node = mapNodes.get(stat.id); - - for(Statement succ : stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)) { - Node nodeSucc = mapNodes.get(succ.id); - - node.succs.add(nodeSucc); - nodeSucc.preds.add(node); - } - } - - // transforming and reducing the graph - for(;;) { - int ttype = 0; - Node node = null; - - for(Node nd : mapNodes.values()) { - if(nd.succs.contains(nd)) { // T1 - ttype = 1; - } else if(nd.preds.size() == 1) { // T2 - ttype = 2; - } - - if(ttype != 0) { - node = nd; - break; - } - } - - if(node != null) { - if(ttype == 1) { - node.succs.remove(node); - node.preds.remove(node); - } else { - Node pred = node.preds.iterator().next(); - - pred.succs.addAll(node.succs); - pred.succs.remove(node); - - for(Node succ : node.succs) { - succ.preds.remove(node); - succ.preds.add(pred); - } - - mapNodes.remove(node.id); - } - } else { // no transformation applicable - return mapNodes.size() > 1; // reducible iff one node remains - } - } - - } - - - - - private static Statement getCandidateForSplitting(Statement statement) { - - Statement candidateForSplitting = null; - int sizeCandidateForSplitting = Integer.MAX_VALUE; - int succsCandidateForSplitting = Integer.MAX_VALUE; - - for(Statement stat : statement.getStats()) { - - Set<Statement> setPreds = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); - - if(setPreds.size() > 1) { - int succCount = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD).size(); - if(succCount <= succsCandidateForSplitting) { - int size = getStatementSize(stat)*(setPreds.size()-1); - - if(succCount < succsCandidateForSplitting || - size < sizeCandidateForSplitting) { - candidateForSplitting = stat; - sizeCandidateForSplitting = size; - succsCandidateForSplitting = succCount; - } - } - } - } - - return candidateForSplitting; - } - - public static boolean splitIrreducibleNode(Statement statement) { - - Statement splitnode = getCandidateForSplitting(statement); - if(splitnode == null) { - return false; - } - - StatEdge enteredge = splitnode.getPredecessorEdges(StatEdge.TYPE_REGULAR).iterator().next(); - - // copy the smallest statement - Statement splitcopy = copyStatement(splitnode, null, new HashMap<Statement, Statement>()); - initCopiedStatement(splitcopy); - - // insert the copy - splitcopy.setParent(statement); - statement.getStats().addWithKey(splitcopy, splitcopy.id); - - // switch input edges - for(StatEdge prededge : splitnode.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { - if(prededge.getSource() == enteredge.getSource() || - prededge.closure == enteredge.getSource()) { - splitnode.removePredecessor(prededge); - prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, splitcopy); - splitcopy.addPredecessor(prededge); - } - } - - // connect successors - for(StatEdge succ : splitnode.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { - splitcopy.addSuccessor(new StatEdge(succ.getType(), splitcopy, succ.getDestination(), succ.closure)); - } - - return true; - } - - private static int getStatementSize(Statement statement) { - - int res = 0; - - if(statement.type == Statement.TYPE_BASICBLOCK) { - res = ((BasicBlockStatement)statement).getBlock().getSeq().length(); - } else { - for(Statement stat: statement.getStats()) { - res+=getStatementSize(stat); - } - } - - return res; - } - - private static Statement copyStatement(Statement from, Statement to, HashMap<Statement, Statement> mapAltToCopies) { - - if(to == null) { - // first outer invocation - to = from.getSimpleCopy(); - mapAltToCopies.put(from, to); - } - - // copy statements - for(Statement st : from.getStats()) { - Statement stcopy = st.getSimpleCopy(); - - to.getStats().addWithKey(stcopy, stcopy.id); - mapAltToCopies.put(st, stcopy); - } - - // copy edges - for(int i=0;i<from.getStats().size();i++) { - Statement stold = from.getStats().get(i); - Statement stnew = to.getStats().get(i); - - for(StatEdge edgeold : stold.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { - // type cannot be TYPE_EXCEPTION (checked in isIrreducibleTriangle) - StatEdge edgenew = new StatEdge(edgeold.getType(), stnew, - mapAltToCopies.containsKey(edgeold.getDestination())?mapAltToCopies.get(edgeold.getDestination()):edgeold.getDestination(), - mapAltToCopies.containsKey(edgeold.closure)?mapAltToCopies.get(edgeold.closure):edgeold.closure); - - stnew.addSuccessor(edgenew); - } - } - - // recurse statements - for(int i=0;i<from.getStats().size();i++) { - Statement stold = from.getStats().get(i); - Statement stnew = to.getStats().get(i); - - copyStatement(stold, stnew, mapAltToCopies); - } - - return to; - } - - private static void initCopiedStatement(Statement statement) { - - statement.initSimpleCopy(); - statement.setCopied(true); - - for(Statement st : statement.getStats()) { - st.setParent(statement); - initCopiedStatement(st); - } - } - + + + public static boolean isStatementIrreducible(Statement statement) { + + class Node { + public Integer id; + public Set<Node> preds = new HashSet<Node>(); + public Set<Node> succs = new HashSet<Node>(); + + public Node(Integer id) { + this.id = id; + } + } + + HashMap<Integer, Node> mapNodes = new HashMap<Integer, Node>(); + + // checking exceptions and creating nodes + for (Statement stat : statement.getStats()) { + if (!stat.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { + return false; + } + + mapNodes.put(stat.id, new Node(stat.id)); + } + + // connecting nodes + for (Statement stat : statement.getStats()) { + Node node = mapNodes.get(stat.id); + + for (Statement succ : stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)) { + Node nodeSucc = mapNodes.get(succ.id); + + node.succs.add(nodeSucc); + nodeSucc.preds.add(node); + } + } + + // transforming and reducing the graph + for (; ; ) { + int ttype = 0; + Node node = null; + + for (Node nd : mapNodes.values()) { + if (nd.succs.contains(nd)) { // T1 + ttype = 1; + } + else if (nd.preds.size() == 1) { // T2 + ttype = 2; + } + + if (ttype != 0) { + node = nd; + break; + } + } + + if (node != null) { + if (ttype == 1) { + node.succs.remove(node); + node.preds.remove(node); + } + else { + Node pred = node.preds.iterator().next(); + + pred.succs.addAll(node.succs); + pred.succs.remove(node); + + for (Node succ : node.succs) { + succ.preds.remove(node); + succ.preds.add(pred); + } + + mapNodes.remove(node.id); + } + } + else { // no transformation applicable + return mapNodes.size() > 1; // reducible iff one node remains + } + } + } + + + private static Statement getCandidateForSplitting(Statement statement) { + + Statement candidateForSplitting = null; + int sizeCandidateForSplitting = Integer.MAX_VALUE; + int succsCandidateForSplitting = Integer.MAX_VALUE; + + for (Statement stat : statement.getStats()) { + + Set<Statement> setPreds = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + + if (setPreds.size() > 1) { + int succCount = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD).size(); + if (succCount <= succsCandidateForSplitting) { + int size = getStatementSize(stat) * (setPreds.size() - 1); + + if (succCount < succsCandidateForSplitting || + size < sizeCandidateForSplitting) { + candidateForSplitting = stat; + sizeCandidateForSplitting = size; + succsCandidateForSplitting = succCount; + } + } + } + } + + return candidateForSplitting; + } + + public static boolean splitIrreducibleNode(Statement statement) { + + Statement splitnode = getCandidateForSplitting(statement); + if (splitnode == null) { + return false; + } + + StatEdge enteredge = splitnode.getPredecessorEdges(StatEdge.TYPE_REGULAR).iterator().next(); + + // copy the smallest statement + Statement splitcopy = copyStatement(splitnode, null, new HashMap<Statement, Statement>()); + initCopiedStatement(splitcopy); + + // insert the copy + splitcopy.setParent(statement); + statement.getStats().addWithKey(splitcopy, splitcopy.id); + + // switch input edges + for (StatEdge prededge : splitnode.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + if (prededge.getSource() == enteredge.getSource() || + prededge.closure == enteredge.getSource()) { + splitnode.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, splitcopy); + splitcopy.addPredecessor(prededge); + } + } + + // connect successors + for (StatEdge succ : splitnode.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + splitcopy.addSuccessor(new StatEdge(succ.getType(), splitcopy, succ.getDestination(), succ.closure)); + } + + return true; + } + + private static int getStatementSize(Statement statement) { + + int res = 0; + + if (statement.type == Statement.TYPE_BASICBLOCK) { + res = ((BasicBlockStatement)statement).getBlock().getSeq().length(); + } + else { + for (Statement stat : statement.getStats()) { + res += getStatementSize(stat); + } + } + + return res; + } + + private static Statement copyStatement(Statement from, Statement to, HashMap<Statement, Statement> mapAltToCopies) { + + if (to == null) { + // first outer invocation + to = from.getSimpleCopy(); + mapAltToCopies.put(from, to); + } + + // copy statements + for (Statement st : from.getStats()) { + Statement stcopy = st.getSimpleCopy(); + + to.getStats().addWithKey(stcopy, stcopy.id); + mapAltToCopies.put(st, stcopy); + } + + // copy edges + for (int i = 0; i < from.getStats().size(); i++) { + Statement stold = from.getStats().get(i); + Statement stnew = to.getStats().get(i); + + for (StatEdge edgeold : stold.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + // type cannot be TYPE_EXCEPTION (checked in isIrreducibleTriangle) + StatEdge edgenew = new StatEdge(edgeold.getType(), stnew, + mapAltToCopies.containsKey(edgeold.getDestination()) + ? mapAltToCopies.get(edgeold.getDestination()) + : edgeold.getDestination(), + mapAltToCopies.containsKey(edgeold.closure) + ? mapAltToCopies.get(edgeold.closure) + : edgeold.closure); + + stnew.addSuccessor(edgenew); + } + } + + // recurse statements + for (int i = 0; i < from.getStats().size(); i++) { + Statement stold = from.getStats().get(i); + Statement stnew = to.getStats().get(i); + + copyStatement(stold, stnew, mapAltToCopies); + } + + return to; + } + + private static void initCopiedStatement(Statement statement) { + + statement.initSimpleCopy(); + statement.setCopied(true); + + for (Statement st : statement.getStats()) { + st.setParent(statement); + initCopiedStatement(st); + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java index 2945913..b919548 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java @@ -1,111 +1,114 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.List; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.List; + public class AnnotationExprent extends Exprent { - - public static final int ANNOTATION_NORMAL = 1; - public static final int ANNOTATION_MARKER = 2; - public static final int ANNOTATION_SINGLE_ELEMENT = 3; - - - private String classname; - - private List<String> parnames; - - private List<Exprent> parvalues; - - { - this.type = EXPRENT_ANNOTATION; - } - - public AnnotationExprent(String classname, List<String> parnames, List<Exprent> parvalues) { - this.classname = classname; - this.parnames = parnames; - this.parvalues = parvalues; - } - - public String toJava(int indent) { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuilder buffer = new StringBuilder(); - String indstr = InterpreterUtil.getIndentString(indent); - - buffer.append(indstr); - buffer.append("@"); - buffer.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); - - if(!parnames.isEmpty()) { - buffer.append("("); - if(parnames.size() == 1 && "value".equals(parnames.get(0))) { - buffer.append(parvalues.get(0).toJava(indent+1)); - } else { - String indstr1 = InterpreterUtil.getIndentString(indent+1); - - for(int i=0;i<parnames.size();i++) { - buffer.append(new_line_separator+indstr1); - buffer.append(parnames.get(i)); - buffer.append(" = "); - buffer.append(parvalues.get(i).toJava(indent+2)); - - if(i<parnames.size()-1) { - buffer.append(","); - } - } - buffer.append(new_line_separator+indstr); - } - - buffer.append(")"); - } - - return buffer.toString(); - } - - public int getAnnotationType() { - - if(parnames.isEmpty()) { - return ANNOTATION_MARKER; - } else { - if(parnames.size() == 1 && "value".equals(parnames.get(0))) { - return ANNOTATION_SINGLE_ELEMENT; - } else { - return ANNOTATION_NORMAL; - } - } - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof AnnotationExprent)) return false; + + public static final int ANNOTATION_NORMAL = 1; + public static final int ANNOTATION_MARKER = 2; + public static final int ANNOTATION_SINGLE_ELEMENT = 3; + + + private String classname; + + private List<String> parnames; + + private List<Exprent> parvalues; + + { + this.type = EXPRENT_ANNOTATION; + } + + public AnnotationExprent(String classname, List<String> parnames, List<Exprent> parvalues) { + this.classname = classname; + this.parnames = parnames; + this.parvalues = parvalues; + } + + public String toJava(int indent) { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuilder buffer = new StringBuilder(); + String indstr = InterpreterUtil.getIndentString(indent); + + buffer.append(indstr); + buffer.append("@"); + buffer.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + + if (!parnames.isEmpty()) { + buffer.append("("); + if (parnames.size() == 1 && "value".equals(parnames.get(0))) { + buffer.append(parvalues.get(0).toJava(indent + 1)); + } + else { + String indstr1 = InterpreterUtil.getIndentString(indent + 1); + + for (int i = 0; i < parnames.size(); i++) { + buffer.append(new_line_separator + indstr1); + buffer.append(parnames.get(i)); + buffer.append(" = "); + buffer.append(parvalues.get(i).toJava(indent + 2)); + + if (i < parnames.size() - 1) { + buffer.append(","); + } + } + buffer.append(new_line_separator + indstr); + } + + buffer.append(")"); + } + + return buffer.toString(); + } + + public int getAnnotationType() { + + if (parnames.isEmpty()) { + return ANNOTATION_MARKER; + } + else { + if (parnames.size() == 1 && "value".equals(parnames.get(0))) { + return ANNOTATION_SINGLE_ELEMENT; + } + else { + return ANNOTATION_NORMAL; + } + } + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof AnnotationExprent)) return false; AnnotationExprent ann = (AnnotationExprent)o; return classname.equals(ann.classname) && - InterpreterUtil.equalLists(parnames, ann.parnames) && - InterpreterUtil.equalLists(parvalues, ann.parvalues); + InterpreterUtil.equalLists(parnames, ann.parnames) && + InterpreterUtil.equalLists(parvalues, ann.parvalues); } - public String getClassname() { - return classname; - } - + public String getClassname() { + return classname; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java index 2f262bd..e5ead9d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java @@ -1,125 +1,126 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class ArrayExprent extends Exprent { - private Exprent array; - - private Exprent index; - - private VarType hardtype; - - { - this.type = EXPRENT_ARRAY; - } - - public ArrayExprent(Exprent array, Exprent index, VarType hardtype) { - this.array = array; - this.index = index; - this.hardtype = hardtype; - } - - public Exprent copy() { - return new ArrayExprent(array.copy(), index.copy(), hardtype); - } - - public VarType getExprType() { - VarType exprType = array.getExprType().copy(); - if(exprType.equals(VarType.VARTYPE_NULL)) { - exprType = hardtype.copy(); - } else { - exprType.decArrayDim(); - } - - return exprType; - } - - public int getExprentUse() { - return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES; - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR); - result.addMaxTypeExprent(index, VarType.VARTYPE_INT); - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.add(array); - lst.add(index); - return lst; - } - - - public String toJava(int indent) { - String res = array.toJava(indent); - - if(array.getPrecedence() > getPrecedence()) { // array precedence equals 0 - res = "("+res+")"; - } - - VarType arrtype = array.getExprType(); - if(arrtype.arraydim == 0) { - VarType objarr = VarType.VARTYPE_OBJECT.copy(); - objarr.arraydim = 1; // type family does not change - - res = "(("+ExprProcessor.getCastTypeName(objarr)+")"+res+")"; - } - - return res+"["+index.toJava(indent)+"]"; - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof ArrayExprent)) return false; + private Exprent array; + + private Exprent index; + + private VarType hardtype; + + { + this.type = EXPRENT_ARRAY; + } + + public ArrayExprent(Exprent array, Exprent index, VarType hardtype) { + this.array = array; + this.index = index; + this.hardtype = hardtype; + } + + public Exprent copy() { + return new ArrayExprent(array.copy(), index.copy(), hardtype); + } + + public VarType getExprType() { + VarType exprType = array.getExprType().copy(); + if (exprType.equals(VarType.VARTYPE_NULL)) { + exprType = hardtype.copy(); + } + else { + exprType.decArrayDim(); + } + + return exprType; + } + + public int getExprentUse() { + return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES; + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(index, VarType.VARTYPE_INT); + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(array); + lst.add(index); + return lst; + } + + + public String toJava(int indent) { + String res = array.toJava(indent); + + if (array.getPrecedence() > getPrecedence()) { // array precedence equals 0 + res = "(" + res + ")"; + } + + VarType arrtype = array.getExprType(); + if (arrtype.arraydim == 0) { + VarType objarr = VarType.VARTYPE_OBJECT.copy(); + objarr.arraydim = 1; // type family does not change + + res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")"; + } + + return res + "[" + index.toJava(indent) + "]"; + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof ArrayExprent)) return false; ArrayExprent arr = (ArrayExprent)o; return InterpreterUtil.equalObjects(array, arr.getArray()) && - InterpreterUtil.equalObjects(index, arr.getIndex()); + InterpreterUtil.equalObjects(index, arr.getIndex()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == array) { + array = newexpr; + } + + if (oldexpr == index) { + index = newexpr; + } } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == array) { - array = newexpr; - } - - if(oldexpr == index) { - index = newexpr; - } - } - - public Exprent getArray() { - return array; - } - - public Exprent getIndex() { - return index; - } - + public Exprent getArray() { + return array; + } + + public Exprent getIndex() { + return index; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java index 03ead80..0983dad 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java @@ -1,51 +1,51 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; import java.util.List; public class AssertExprent extends Exprent { - - private List<Exprent> parameters; - - { - this.type = EXPRENT_ASSERT; - } - - public AssertExprent(List<Exprent> parameters) { - this.parameters = parameters; - } - - public String toJava(int indent) { - - StringBuilder buffer = new StringBuilder(); - - buffer.append("assert "); - - if(parameters.get(0) == null) { - buffer.append("false"); - } else { - buffer.append(parameters.get(0).toJava(indent)); - } - if(parameters.size() > 1) { - buffer.append(" : "); - buffer.append(parameters.get(1).toJava(indent)); - } - - return buffer.toString(); - } - + private List<Exprent> parameters; + + { + this.type = EXPRENT_ASSERT; + } + + public AssertExprent(List<Exprent> parameters) { + this.parameters = parameters; + } + + public String toJava(int indent) { + + StringBuilder buffer = new StringBuilder(); + + buffer.append("assert "); + + if (parameters.get(0) == null) { + buffer.append("false"); + } + else { + buffer.append(parameters.get(0).toJava(indent)); + } + if (parameters.size() > 1) { + buffer.append(" : "); + buffer.append(parameters.get(1).toJava(indent)); + } + + return buffer.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java index 9c233d4..e8b0b82 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java @@ -1,25 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.StructClass; @@ -27,169 +25,177 @@ import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class AssignmentExprent extends Exprent { - public static final int CONDITION_NONE = -1; - - private static final String[] funceq = new String[] { - " += ", // FUNCTION_ADD - " -= ", // FUNCTION_SUB - " *= ", // FUNCTION_MUL - " /= ", // FUNCTION_DIV - " &= ", // FUNCTION_AND - " |= ", // FUNCTION_OR - " ^= ", // FUNCTION_XOR - " %= ", // FUNCTION_REM - " <<= ", // FUNCTION_SHL - " >>= ", // FUNCTION_SHR - " >>>= " // FUNCTION_USHR - }; - - - private Exprent left; - - private Exprent right; - - private int condtype = CONDITION_NONE; - - { - this.type = EXPRENT_ASSIGNMENT; - } - - - public AssignmentExprent(Exprent left, Exprent right) { - this.left = left; - this.right = right; - } - - - public VarType getExprType() { - return left.getExprType(); - } - - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - VarType typeleft = left.getExprType(); - VarType typeright = right.getExprType(); - - if(typeleft.type_family > typeright.type_family) { - result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeleft.type_family)); - } else if(typeleft.type_family < typeright.type_family) { - result.addMinTypeExprent(left, typeright); - } else { - result.addMinTypeExprent(left, VarType.getCommonSupertype(typeleft, typeright)); - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.add(left); - lst.add(right); - return lst; - } - - public Exprent copy() { - return new AssignmentExprent(left.copy(), right.copy()); - } - - public int getPrecedence() { - return 13; - } - - public String toJava(int indent) { - - VarType leftType = left.getExprType(); - VarType rightType = right.getExprType(); - - String res = right.toJava(indent); - - if(condtype == CONDITION_NONE && !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) { - if(right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { - res = "("+res+")"; - } - - res = "("+ExprProcessor.getCastTypeName(leftType)+")"+res; - } - - StringBuilder buffer = new StringBuilder(); - - boolean finstat_init = false; - if(left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it - FieldExprent field = (FieldExprent)left; - if(field.isStatic()) { - ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); - if(node != null) { - StructClass cl = node.classStruct; - StructField fd = cl.getField(field.getName(), field.getDescriptor().descriptorString); - - if(fd != null && (fd.access_flags & CodeConstants.ACC_FINAL) != 0) { - finstat_init = true; - } - } - } - } - - if(finstat_init) { - buffer.append(((FieldExprent)left).getName()); - } else { - buffer.append(left.toJava(indent)); - } - - buffer.append(condtype == CONDITION_NONE ? " = " : funceq[condtype]).append(res); - - return buffer.toString(); - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof AssignmentExprent)) return false; + public static final int CONDITION_NONE = -1; + + private static final String[] funceq = new String[]{ + " += ", // FUNCTION_ADD + " -= ", // FUNCTION_SUB + " *= ", // FUNCTION_MUL + " /= ", // FUNCTION_DIV + " &= ", // FUNCTION_AND + " |= ", // FUNCTION_OR + " ^= ", // FUNCTION_XOR + " %= ", // FUNCTION_REM + " <<= ", // FUNCTION_SHL + " >>= ", // FUNCTION_SHR + " >>>= " // FUNCTION_USHR + }; + + + private Exprent left; + + private Exprent right; + + private int condtype = CONDITION_NONE; + + { + this.type = EXPRENT_ASSIGNMENT; + } + + + public AssignmentExprent(Exprent left, Exprent right) { + this.left = left; + this.right = right; + } + + + public VarType getExprType() { + return left.getExprType(); + } + + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + VarType typeleft = left.getExprType(); + VarType typeright = right.getExprType(); + + if (typeleft.type_family > typeright.type_family) { + result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeleft.type_family)); + } + else if (typeleft.type_family < typeright.type_family) { + result.addMinTypeExprent(left, typeright); + } + else { + result.addMinTypeExprent(left, VarType.getCommonSupertype(typeleft, typeright)); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(left); + lst.add(right); + return lst; + } + + public Exprent copy() { + return new AssignmentExprent(left.copy(), right.copy()); + } + + public int getPrecedence() { + return 13; + } + + public String toJava(int indent) { + + VarType leftType = left.getExprType(); + VarType rightType = right.getExprType(); + + String res = right.toJava(indent); + + if (condtype == CONDITION_NONE && + !leftType.isSuperset(rightType) && + (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) { + if (right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { + res = "(" + res + ")"; + } + + res = "(" + ExprProcessor.getCastTypeName(leftType) + ")" + res; + } + + StringBuilder buffer = new StringBuilder(); + + boolean finstat_init = false; + if (left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it + FieldExprent field = (FieldExprent)left; + if (field.isStatic()) { + ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); + if (node != null) { + StructClass cl = node.classStruct; + StructField fd = cl.getField(field.getName(), field.getDescriptor().descriptorString); + + if (fd != null && (fd.access_flags & CodeConstants.ACC_FINAL) != 0) { + finstat_init = true; + } + } + } + } + + if (finstat_init) { + buffer.append(((FieldExprent)left).getName()); + } + else { + buffer.append(left.toJava(indent)); + } + + buffer.append(condtype == CONDITION_NONE ? " = " : funceq[condtype]).append(res); + + return buffer.toString(); + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof AssignmentExprent)) return false; AssignmentExprent as = (AssignmentExprent)o; return InterpreterUtil.equalObjects(left, as.getLeft()) && - InterpreterUtil.equalObjects(right, as.getRight()) && - condtype == as.getCondtype(); - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == left) { - left = newexpr; - } - - if(oldexpr == right) { - right = newexpr; - } - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Exprent getLeft() { - return left; - } - - public void setLeft(Exprent left) { - this.left = left; - } - - public Exprent getRight() { - return right; - } - - public void setRight(Exprent right) { - this.right = right; - } - - public int getCondtype() { - return condtype; - } - - public void setCondtype(int condtype) { - this.condtype = condtype; - } + InterpreterUtil.equalObjects(right, as.getRight()) && + condtype == as.getCondtype(); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == left) { + left = newexpr; + } + + if (oldexpr == right) { + right = newexpr; + } + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Exprent getLeft() { + return left; + } + + public void setLeft(Exprent left) { + this.left = left; + } + + public Exprent getRight() { + return right; + } + + public void setRight(Exprent right) { + this.right = right; + } + + public int getCondtype() { + return condtype; + } + + public void setCondtype(int condtype) { + this.condtype = condtype; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 10fa1d4..ae1747d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -1,23 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; @@ -26,344 +23,380 @@ import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + public class ConstExprent extends Exprent { - private static final HashMap<Integer, String> escapes = new HashMap<Integer, String>(); - - static { - escapes.put(new Integer(0x8), "\\b"); /* \u0008: backspace BS */ - escapes.put(new Integer(0x9), "\\t"); /* \u0009: horizontal tab HT */ - escapes.put(new Integer(0xA), "\\n"); /* \u000a: linefeed LF */ - escapes.put(new Integer(0xC), "\\f"); /* \u000c: form feed FF */ - escapes.put(new Integer(0xD), "\\r"); /* \u000d: carriage return CR */ - escapes.put(new Integer(0x22), "\\\""); /* \u0022: double quote " */ - escapes.put(new Integer(0x27), "\\\'"); /* \u0027: single quote ' */ - escapes.put(new Integer(0x5C), "\\\\"); /* \u005c: backslash \ */ - } - - - private VarType consttype; - - private Object value; - - private boolean boolPermitted; - - { - this.type = EXPRENT_CONST; - } - - public ConstExprent(int val, boolean boolPermitted) { - - this.boolPermitted = boolPermitted; - if(boolPermitted) { - consttype = VarType.VARTYPE_BOOLEAN; - if(val != 0 && val != 1) { - consttype = consttype.copy(); - consttype.convinfo |= VarType.FALSEBOOLEAN; - } - } else { - if(0 <= val && val <= 127) { - consttype = VarType.VARTYPE_BYTECHAR; - } else if(-128 <= val && val <= 127) { - consttype = VarType.VARTYPE_BYTE; - } else if(0 <= val && val <= 32767) { - consttype = VarType.VARTYPE_SHORTCHAR; - } else if(-32768 <= val && val <= 32767) { - consttype = VarType.VARTYPE_SHORT; - } else if(0 <= val && val <= 0xFFFF) { - consttype = VarType.VARTYPE_CHAR; - } else { - consttype = VarType.VARTYPE_INT; - } - } - value = new Integer(val); - } - - public ConstExprent(VarType consttype, Object value) { - this.consttype = consttype; - this.value = value; - } - - public Exprent copy() { - return new ConstExprent(consttype, value); - } - - public VarType getExprType() { - return consttype; - } - - public int getExprentUse() { - return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; - } - - public List<Exprent> getAllExprents() { - return new ArrayList<Exprent>(); - } - - public String toJava(int indent) { + private static final HashMap<Integer, String> escapes = new HashMap<Integer, String>(); + + static { + escapes.put(new Integer(0x8), "\\b"); /* \u0008: backspace BS */ + escapes.put(new Integer(0x9), "\\t"); /* \u0009: horizontal tab HT */ + escapes.put(new Integer(0xA), "\\n"); /* \u000a: linefeed LF */ + escapes.put(new Integer(0xC), "\\f"); /* \u000c: form feed FF */ + escapes.put(new Integer(0xD), "\\r"); /* \u000d: carriage return CR */ + escapes.put(new Integer(0x22), "\\\""); /* \u0022: double quote " */ + escapes.put(new Integer(0x27), "\\\'"); /* \u0027: single quote ' */ + escapes.put(new Integer(0x5C), "\\\\"); /* \u005c: backslash \ */ + } + + + private VarType consttype; + + private Object value; + + private boolean boolPermitted; + + { + this.type = EXPRENT_CONST; + } + + public ConstExprent(int val, boolean boolPermitted) { + + this.boolPermitted = boolPermitted; + if (boolPermitted) { + consttype = VarType.VARTYPE_BOOLEAN; + if (val != 0 && val != 1) { + consttype = consttype.copy(); + consttype.convinfo |= VarType.FALSEBOOLEAN; + } + } + else { + if (0 <= val && val <= 127) { + consttype = VarType.VARTYPE_BYTECHAR; + } + else if (-128 <= val && val <= 127) { + consttype = VarType.VARTYPE_BYTE; + } + else if (0 <= val && val <= 32767) { + consttype = VarType.VARTYPE_SHORTCHAR; + } + else if (-32768 <= val && val <= 32767) { + consttype = VarType.VARTYPE_SHORT; + } + else if (0 <= val && val <= 0xFFFF) { + consttype = VarType.VARTYPE_CHAR; + } + else { + consttype = VarType.VARTYPE_INT; + } + } + value = new Integer(val); + } + + public ConstExprent(VarType consttype, Object value) { + this.consttype = consttype; + this.value = value; + } + + public Exprent copy() { + return new ConstExprent(consttype, value); + } + + public VarType getExprType() { + return consttype; + } + + public int getExprentUse() { + return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + } + + public List<Exprent> getAllExprents() { + return new ArrayList<Exprent>(); + } + + public String toJava(int indent) { boolean literal = DecompilerContext.getOption(IFernflowerPreferences.LITERALS_AS_IS); boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS); - if(consttype.type != CodeConstants.TYPE_NULL && value == null) { - return ExprProcessor.getCastTypeName(consttype); - } else { - switch(consttype.type) { - case CodeConstants.TYPE_BOOLEAN: - return new Boolean(((Integer)value).intValue() != 0).toString(); - case CodeConstants.TYPE_CHAR: - Integer val = (Integer)value; - String ret = escapes.get(val); - if(ret == null) { - char c = (char)val.intValue(); - if(c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { - ret = String.valueOf(c); - } else { - ret = InterpreterUtil.charToUnicodeLiteral(c); - } - } - return "\'"+ret+"\'"; - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_BYTECHAR: - case CodeConstants.TYPE_SHORT: - case CodeConstants.TYPE_SHORTCHAR: - case CodeConstants.TYPE_INT: - int ival = ((Integer)value).intValue(); - - String intfield; - if(literal) { - return value.toString(); - } else if(ival == Integer.MAX_VALUE) { - intfield = "MAX_VALUE"; - } else if(ival == Integer.MIN_VALUE) { - intfield = "MIN_VALUE"; - } else { - return value.toString(); - } - return new FieldExprent(intfield, "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR).toJava(0); - case CodeConstants.TYPE_LONG: - long lval = ((Long)value).longValue(); - - String longfield; - if(literal) { - return value.toString()+"L"; - } else if(lval == Long.MAX_VALUE) { - longfield = "MAX_VALUE"; - } else if(lval == Long.MIN_VALUE) { - longfield = "MIN_VALUE"; - } else { - return value.toString()+"L"; - } - return new FieldExprent(longfield, "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR).toJava(0); - case CodeConstants.TYPE_DOUBLE: - double dval = ((Double)value).doubleValue(); - - String doublefield; - if(literal) { - if(Double.isNaN(dval)) { - return "0.0D / 0.0"; - } else if(dval == Double.POSITIVE_INFINITY) { - return "1.0D / 0.0"; - } else if(dval == Double.NEGATIVE_INFINITY) { - return "-1.0D / 0.0"; - } else { + if (consttype.type != CodeConstants.TYPE_NULL && value == null) { + return ExprProcessor.getCastTypeName(consttype); + } + else { + switch (consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + return new Boolean(((Integer)value).intValue() != 0).toString(); + case CodeConstants.TYPE_CHAR: + Integer val = (Integer)value; + String ret = escapes.get(val); + if (ret == null) { + char c = (char)val.intValue(); + if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { + ret = String.valueOf(c); + } + else { + ret = InterpreterUtil.charToUnicodeLiteral(c); + } + } + return "\'" + ret + "\'"; + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + int ival = ((Integer)value).intValue(); + + String intfield; + if (literal) { + return value.toString(); + } + else if (ival == Integer.MAX_VALUE) { + intfield = "MAX_VALUE"; + } + else if (ival == Integer.MIN_VALUE) { + intfield = "MIN_VALUE"; + } + else { + return value.toString(); + } + return new FieldExprent(intfield, "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_LONG: + long lval = ((Long)value).longValue(); + + String longfield; + if (literal) { + return value.toString() + "L"; + } + else if (lval == Long.MAX_VALUE) { + longfield = "MAX_VALUE"; + } + else if (lval == Long.MIN_VALUE) { + longfield = "MIN_VALUE"; + } + else { + return value.toString() + "L"; + } + return new FieldExprent(longfield, "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_DOUBLE: + double dval = ((Double)value).doubleValue(); + + String doublefield; + if (literal) { + if (Double.isNaN(dval)) { + return "0.0D / 0.0"; + } + else if (dval == Double.POSITIVE_INFINITY) { + return "1.0D / 0.0"; + } + else if (dval == Double.NEGATIVE_INFINITY) { + return "-1.0D / 0.0"; + } + else { + return value.toString() + "D"; + } + } + else if (Double.isNaN(dval)) { + doublefield = "NaN"; + } + else if (dval == Double.POSITIVE_INFINITY) { + doublefield = "POSITIVE_INFINITY"; + } + else if (dval == Double.NEGATIVE_INFINITY) { + doublefield = "NEGATIVE_INFINITY"; + } + else if (dval == Double.MAX_VALUE) { + doublefield = "MAX_VALUE"; + } + else if (dval == Double.MIN_VALUE) { + doublefield = "MIN_VALUE"; + } + else { return value.toString() + "D"; } - } else if(Double.isNaN(dval)) { - doublefield = "NaN"; - } else if(dval == Double.POSITIVE_INFINITY) { - doublefield = "POSITIVE_INFINITY"; - } else if(dval == Double.NEGATIVE_INFINITY) { - doublefield = "NEGATIVE_INFINITY"; - } else if(dval == Double.MAX_VALUE) { - doublefield = "MAX_VALUE"; - } else if(dval == Double.MIN_VALUE) { - doublefield = "MIN_VALUE"; - } else { - return value.toString()+"D"; - } - return new FieldExprent(doublefield, "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR).toJava(0); - case CodeConstants.TYPE_FLOAT: - float fval = ((Float)value).floatValue(); - - String floatfield; - if(literal) { - if(Double.isNaN(fval)) { - return "0.0F / 0.0"; - } else if(fval == Double.POSITIVE_INFINITY) { - return "1.0F / 0.0"; - } else if(fval == Double.NEGATIVE_INFINITY) { - return "-1.0F / 0.0"; - } else { + return new FieldExprent(doublefield, "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_FLOAT: + float fval = ((Float)value).floatValue(); + + String floatfield; + if (literal) { + if (Double.isNaN(fval)) { + return "0.0F / 0.0"; + } + else if (fval == Double.POSITIVE_INFINITY) { + return "1.0F / 0.0"; + } + else if (fval == Double.NEGATIVE_INFINITY) { + return "-1.0F / 0.0"; + } + else { + return value.toString() + "F"; + } + } + else if (Float.isNaN(fval)) { + floatfield = "NaN"; + } + else if (fval == Float.POSITIVE_INFINITY) { + floatfield = "POSITIVE_INFINITY"; + } + else if (fval == Float.NEGATIVE_INFINITY) { + floatfield = "NEGATIVE_INFINITY"; + } + else if (fval == Float.MAX_VALUE) { + floatfield = "MAX_VALUE"; + } + else if (fval == Float.MIN_VALUE) { + floatfield = "MIN_VALUE"; + } + else { return value.toString() + "F"; } - } else if(Float.isNaN(fval)) { - floatfield = "NaN"; - } else if(fval == Float.POSITIVE_INFINITY) { - floatfield = "POSITIVE_INFINITY"; - } else if(fval == Float.NEGATIVE_INFINITY) { - floatfield = "NEGATIVE_INFINITY"; - } else if(fval == Float.MAX_VALUE) { - floatfield = "MAX_VALUE"; - } else if(fval == Float.MIN_VALUE) { - floatfield = "MIN_VALUE"; - } else { - return value.toString()+"F"; - } - return new FieldExprent(floatfield, "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR).toJava(0); - case CodeConstants.TYPE_NULL: - return "null"; - case CodeConstants.TYPE_OBJECT: - if(consttype.equals(VarType.VARTYPE_STRING)) { - return "\""+convertStringToJava(value.toString(), ascii)+"\""; - } else if(consttype.equals(VarType.VARTYPE_CLASS)) { - String strval = value.toString(); - - VarType classtype; - if(strval.startsWith("[")) { // array of simple type - classtype = new VarType(strval, false); - } else { // class - classtype = new VarType(strval, true); - } - - return ExprProcessor.getCastTypeName(classtype)+".class"; - } - } - } - - throw new RuntimeException("invalid constant type"); - } - - private String convertStringToJava(String value, boolean ascii) { - char[] arr = value.toCharArray(); - StringBuilder buffer = new StringBuilder(arr.length); - - for(char c: arr){ - switch(c) { - case '\\': // u005c: backslash \ - buffer.append("\\\\"); - break; - case 0x8: // "\\\\b"); // u0008: backspace BS - buffer.append("\\b"); - break; - case 0x9: //"\\\\t"); // u0009: horizontal tab HT - buffer.append("\\t"); - break; - case 0xA: //"\\\\n"); // u000a: linefeed LF - buffer.append("\\n"); - break; - case 0xC: //"\\\\f"); // u000c: form feed FF - buffer.append("\\f"); - break; - case 0xD: //"\\\\r"); // u000d: carriage return CR - buffer.append("\\r"); - break; - case 0x22: //"\\\\\""); // u0022: double quote " - buffer.append("\\\""); - break; - case 0x27: //"\\\\'"); // u0027: single quote ' - buffer.append("\\\'"); - break; - default: - if(c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { - buffer.append(c); - } else { - buffer.append(InterpreterUtil.charToUnicodeLiteral(c)); - } - } - } - - return buffer.toString(); - } - - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof ConstExprent)) return false; + return new FieldExprent(floatfield, "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_NULL: + return "null"; + case CodeConstants.TYPE_OBJECT: + if (consttype.equals(VarType.VARTYPE_STRING)) { + return "\"" + convertStringToJava(value.toString(), ascii) + "\""; + } + else if (consttype.equals(VarType.VARTYPE_CLASS)) { + String strval = value.toString(); + + VarType classtype; + if (strval.startsWith("[")) { // array of simple type + classtype = new VarType(strval, false); + } + else { // class + classtype = new VarType(strval, true); + } + + return ExprProcessor.getCastTypeName(classtype) + ".class"; + } + } + } + + throw new RuntimeException("invalid constant type"); + } + + private String convertStringToJava(String value, boolean ascii) { + char[] arr = value.toCharArray(); + StringBuilder buffer = new StringBuilder(arr.length); + + for (char c : arr) { + switch (c) { + case '\\': // u005c: backslash \ + buffer.append("\\\\"); + break; + case 0x8: // "\\\\b"); // u0008: backspace BS + buffer.append("\\b"); + break; + case 0x9: //"\\\\t"); // u0009: horizontal tab HT + buffer.append("\\t"); + break; + case 0xA: //"\\\\n"); // u000a: linefeed LF + buffer.append("\\n"); + break; + case 0xC: //"\\\\f"); // u000c: form feed FF + buffer.append("\\f"); + break; + case 0xD: //"\\\\r"); // u000d: carriage return CR + buffer.append("\\r"); + break; + case 0x22: //"\\\\\""); // u0022: double quote " + buffer.append("\\\""); + break; + case 0x27: //"\\\\'"); // u0027: single quote ' + buffer.append("\\\'"); + break; + default: + if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { + buffer.append(c); + } + else { + buffer.append(InterpreterUtil.charToUnicodeLiteral(c)); + } + } + } + + return buffer.toString(); + } + + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof ConstExprent)) return false; ConstExprent cn = (ConstExprent)o; return InterpreterUtil.equalObjects(consttype, cn.getConsttype()) && - InterpreterUtil.equalObjects(value, cn.getValue()); + InterpreterUtil.equalObjects(value, cn.getValue()); + } + + public boolean hasBooleanValue() { + + switch (consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + Integer ival = (Integer)value; + return ival.intValue() == 0 || + (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && ival.intValue() == 1); + } + + return false; + } + + public boolean hasValueOne() { + + switch (consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + return ((Integer)value).intValue() == 1; + case CodeConstants.TYPE_LONG: + return ((Long)value).intValue() == 1; + case CodeConstants.TYPE_DOUBLE: + return ((Double)value).intValue() == 1; + case CodeConstants.TYPE_FLOAT: + return ((Float)value).intValue() == 1; + } + + return false; + } + + public static ConstExprent getZeroConstant(int type) { + + switch (type) { + case CodeConstants.TYPE_INT: + return new ConstExprent(VarType.VARTYPE_INT, new Integer(0)); + case CodeConstants.TYPE_LONG: + return new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); + case CodeConstants.TYPE_DOUBLE: + return new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); + case CodeConstants.TYPE_FLOAT: + return new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); + } + + throw new RuntimeException("Invalid argument!"); + } + + public VarType getConsttype() { + return consttype; + } + + public void setConsttype(VarType consttype) { + this.consttype = consttype; + } + + public Object getValue() { + return value; + } + + public int getIntValue() { + return ((Integer)value).intValue(); + } + + public boolean isBoolPermitted() { + return boolPermitted; + } + + public void setBoolPermitted(boolean boolPermitted) { + this.boolPermitted = boolPermitted; } - - public boolean hasBooleanValue() { - - switch(consttype.type) { - case CodeConstants.TYPE_BOOLEAN: - case CodeConstants.TYPE_CHAR: - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_BYTECHAR: - case CodeConstants.TYPE_SHORT: - case CodeConstants.TYPE_SHORTCHAR: - case CodeConstants.TYPE_INT: - Integer ival = (Integer)value; - return ival.intValue() == 0 || - (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && ival.intValue() == 1); - } - - return false; - } - - public boolean hasValueOne() { - - switch(consttype.type) { - case CodeConstants.TYPE_BOOLEAN: - case CodeConstants.TYPE_CHAR: - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_BYTECHAR: - case CodeConstants.TYPE_SHORT: - case CodeConstants.TYPE_SHORTCHAR: - case CodeConstants.TYPE_INT: - return ((Integer)value).intValue() == 1; - case CodeConstants.TYPE_LONG: - return ((Long)value).intValue() == 1; - case CodeConstants.TYPE_DOUBLE: - return ((Double)value).intValue() == 1; - case CodeConstants.TYPE_FLOAT: - return ((Float)value).intValue() == 1; - } - - return false; - } - - public static ConstExprent getZeroConstant(int type) { - - switch(type) { - case CodeConstants.TYPE_INT: - return new ConstExprent(VarType.VARTYPE_INT, new Integer(0)); - case CodeConstants.TYPE_LONG: - return new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); - case CodeConstants.TYPE_DOUBLE: - return new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); - case CodeConstants.TYPE_FLOAT: - return new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); - } - - throw new RuntimeException("Invalid argument!"); - } - - public VarType getConsttype() { - return consttype; - } - - public void setConsttype(VarType consttype) { - this.consttype = consttype; - } - - public Object getValue() { - return value; - } - - public int getIntValue() { - return ((Integer)value).intValue(); - } - - public boolean isBoolPermitted() { - return boolPermitted; - } - - public void setBoolPermitted(boolean boolPermitted) { - this.boolPermitted = boolPermitted; - } - - - } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java index ceaae30..e61388d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -1,25 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; @@ -27,123 +25,127 @@ import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class ExitExprent extends Exprent { - - public static final int EXIT_RETURN = 0; - public static final int EXIT_THROW = 1; - - // return or throw statement - private int exittype; - - private Exprent value; - - private VarType rettype; - - { - this.type = EXPRENT_EXIT; - } - - public ExitExprent(int exittype, Exprent value, VarType rettype) { - this.exittype = exittype; - this.value = value; - this.rettype = rettype; - } - - public Exprent copy() { - return new ExitExprent(exittype, value==null?null:value.copy(), rettype); - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - if(exittype == EXIT_RETURN && rettype.type!=CodeConstants.TYPE_VOID) { - result.addMinTypeExprent(value, VarType.getMinTypeInFamily(rettype.type_family)); - result.addMaxTypeExprent(value, rettype); - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - if(value != null) { - lst.add(value); - } - return lst; - } - - public String toJava(int indent) { - if(exittype == EXIT_RETURN) { - StringBuilder buffer = new StringBuilder(); - - if(rettype.type!=CodeConstants.TYPE_VOID) { - buffer.append(" "); - ExprProcessor.getCastedExprent(value, rettype, buffer, indent, false); - } - - return "return"+buffer.toString(); - } else { - - MethodWrapper meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); - - if(meth != null && node != null) { - StructExceptionsAttribute attr = (StructExceptionsAttribute)meth.methodStruct.getAttributes().getWithKey("Exceptions"); - - if(attr != null) { - String classname = null; - - for(int i=0;i<attr.getThrowsExceptions().size();i++) { - String excclassname = attr.getExcClassname(i, node.classStruct.getPool()); - if("java/lang/Throwable".equals(excclassname)) { - classname = excclassname; - break; - } else if("java/lang/Exception".equals(excclassname)) { - classname = excclassname; - } - } - - if(classname != null) { - VarType exctype = new VarType(classname, true); - - StringBuilder buffer = new StringBuilder(); - ExprProcessor.getCastedExprent(value, exctype, buffer, indent, false); - - return "throw "+buffer.toString(); - } - } - } - - return "throw "+value.toJava(indent); - } - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof ExitExprent)) return false; + + public static final int EXIT_RETURN = 0; + public static final int EXIT_THROW = 1; + + // return or throw statement + private int exittype; + + private Exprent value; + + private VarType rettype; + + { + this.type = EXPRENT_EXIT; + } + + public ExitExprent(int exittype, Exprent value, VarType rettype) { + this.exittype = exittype; + this.value = value; + this.rettype = rettype; + } + + public Exprent copy() { + return new ExitExprent(exittype, value == null ? null : value.copy(), rettype); + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + if (exittype == EXIT_RETURN && rettype.type != CodeConstants.TYPE_VOID) { + result.addMinTypeExprent(value, VarType.getMinTypeInFamily(rettype.type_family)); + result.addMaxTypeExprent(value, rettype); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if (value != null) { + lst.add(value); + } + return lst; + } + + public String toJava(int indent) { + if (exittype == EXIT_RETURN) { + StringBuilder buffer = new StringBuilder(); + + if (rettype.type != CodeConstants.TYPE_VOID) { + buffer.append(" "); + ExprProcessor.getCastedExprent(value, rettype, buffer, indent, false); + } + + return "return" + buffer.toString(); + } + else { + + MethodWrapper meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); + + if (meth != null && node != null) { + StructExceptionsAttribute attr = (StructExceptionsAttribute)meth.methodStruct.getAttributes().getWithKey("Exceptions"); + + if (attr != null) { + String classname = null; + + for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { + String excclassname = attr.getExcClassname(i, node.classStruct.getPool()); + if ("java/lang/Throwable".equals(excclassname)) { + classname = excclassname; + break; + } + else if ("java/lang/Exception".equals(excclassname)) { + classname = excclassname; + } + } + + if (classname != null) { + VarType exctype = new VarType(classname, true); + + StringBuilder buffer = new StringBuilder(); + ExprProcessor.getCastedExprent(value, exctype, buffer, indent, false); + + return "throw " + buffer.toString(); + } + } + } + + return "throw " + value.toJava(indent); + } + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof ExitExprent)) return false; ExitExprent et = (ExitExprent)o; - return exittype==et.getExittype() && - InterpreterUtil.equalObjects(value, et.getValue()); + return exittype == et.getExittype() && + InterpreterUtil.equalObjects(value, et.getValue()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == value) { + value = newexpr; + } } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == value) { - value = newexpr; - } - } - - public int getExittype() { - return exittype; - } - - public Exprent getValue() { - return value; - } - - public VarType getRettype() { - return rettype; - } + public int getExittype() { + return exittype; + } + + public Exprent getValue() { + return value; + } + + public VarType getRettype() { + return rettype; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index 6e15170..63b3a5f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -1,134 +1,133 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class Exprent { - - public static final int MULTIPLE_USES = 1; - public static final int SIDE_EFFECTS_FREE = 2; - public static final int BOTH_FLAGS = 3; - - - public static final int EXPRENT_ARRAY = 1; - public static final int EXPRENT_ASSIGNMENT = 2; - public static final int EXPRENT_CONST = 3; - public static final int EXPRENT_EXIT = 4; - public static final int EXPRENT_FIELD = 5; - public static final int EXPRENT_FUNCTION = 6; - public static final int EXPRENT_IF = 7; - public static final int EXPRENT_INVOCATION = 8; - public static final int EXPRENT_MONITOR = 9; - public static final int EXPRENT_NEW = 10; - public static final int EXPRENT_SWITCH = 11; - public static final int EXPRENT_VAR = 12; - public static final int EXPRENT_ANNOTATION = 13; - public static final int EXPRENT_ASSERT = 14; - - public int type; - - public int id; - - { - // set exprent id - id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.EXPRENT_COUNTER); - } - - public int getPrecedence() { - return 0; // the highest precedence - } - - public VarType getExprType() { - return VarType.VARTYPE_VOID; - } - - public int getExprentUse() { - return 0; - } - - public CheckTypesResult checkExprTypeBounds() { - return new CheckTypesResult(); - } - - public boolean containsExprent(Exprent exprent) { - - List<Exprent> listTemp = new ArrayList<Exprent>(getAllExprents(true)); - listTemp.add(this); - - for(Exprent lstexpr : listTemp) { - if(lstexpr.equals(exprent)) { - return true; - } - } - - return false; - } - - public List<Exprent> getAllExprents(boolean recursive) { - List<Exprent> lst = getAllExprents(); - - if(recursive) { - for(int i=lst.size()-1;i>=0;i--) { - lst.addAll(lst.get(i).getAllExprents(true)); - } - } - - return lst; - } - - public Set<VarVersionPaar> getAllVariables() { - - HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); - - List<Exprent> lstAllExprents = getAllExprents(true); - lstAllExprents.add(this); - - for(Exprent expr : lstAllExprents) { - if(expr.type == Exprent.EXPRENT_VAR) { - set.add(new VarVersionPaar((VarExprent)expr)); - } - } - - return set; - } - - public List<Exprent> getAllExprents() { - throw new RuntimeException("not implemented"); - } - - public Exprent copy() { - throw new RuntimeException("not implemented"); - } - - public String toJava(int indent) { - throw new RuntimeException("not implemented"); - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) {} - - + + public static final int MULTIPLE_USES = 1; + public static final int SIDE_EFFECTS_FREE = 2; + public static final int BOTH_FLAGS = 3; + + + public static final int EXPRENT_ARRAY = 1; + public static final int EXPRENT_ASSIGNMENT = 2; + public static final int EXPRENT_CONST = 3; + public static final int EXPRENT_EXIT = 4; + public static final int EXPRENT_FIELD = 5; + public static final int EXPRENT_FUNCTION = 6; + public static final int EXPRENT_IF = 7; + public static final int EXPRENT_INVOCATION = 8; + public static final int EXPRENT_MONITOR = 9; + public static final int EXPRENT_NEW = 10; + public static final int EXPRENT_SWITCH = 11; + public static final int EXPRENT_VAR = 12; + public static final int EXPRENT_ANNOTATION = 13; + public static final int EXPRENT_ASSERT = 14; + + public int type; + + public int id; + + { + // set exprent id + id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.EXPRENT_COUNTER); + } + + public int getPrecedence() { + return 0; // the highest precedence + } + + public VarType getExprType() { + return VarType.VARTYPE_VOID; + } + + public int getExprentUse() { + return 0; + } + + public CheckTypesResult checkExprTypeBounds() { + return new CheckTypesResult(); + } + + public boolean containsExprent(Exprent exprent) { + + List<Exprent> listTemp = new ArrayList<Exprent>(getAllExprents(true)); + listTemp.add(this); + + for (Exprent lstexpr : listTemp) { + if (lstexpr.equals(exprent)) { + return true; + } + } + + return false; + } + + public List<Exprent> getAllExprents(boolean recursive) { + List<Exprent> lst = getAllExprents(); + + if (recursive) { + for (int i = lst.size() - 1; i >= 0; i--) { + lst.addAll(lst.get(i).getAllExprents(true)); + } + } + + return lst; + } + + public Set<VarVersionPaar> getAllVariables() { + + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); + + List<Exprent> lstAllExprents = getAllExprents(true); + lstAllExprents.add(this); + + for (Exprent expr : lstAllExprents) { + if (expr.type == Exprent.EXPRENT_VAR) { + set.add(new VarVersionPaar((VarExprent)expr)); + } + } + + return set; + } + + public List<Exprent> getAllExprents() { + throw new RuntimeException("not implemented"); + } + + public Exprent copy() { + throw new RuntimeException("not implemented"); + } + + public String toJava(int indent) { + throw new RuntimeException("not implemented"); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index 235ff6f..652f61f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -1,25 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; @@ -29,167 +27,174 @@ import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class FieldExprent extends Exprent { - private String name; - - private String classname; - - private boolean isStatic; - - private Exprent instance; - - private FieldDescriptor descriptor; - - { - this.type = EXPRENT_FIELD; - } - - public FieldExprent(LinkConstant cn, Exprent instance) { - - this.instance = instance; - - if(instance == null) { - isStatic = true; - } - - classname = cn.classname; - name = cn.elementname; - descriptor = FieldDescriptor.parseDescriptor(cn.descriptor); - } - - public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor) { - this.name = name; - this.classname = classname; - this.isStatic = isStatic; - this.instance = instance; - this.descriptor = descriptor; - } - - public VarType getExprType() { - return descriptor.type; - } - - public int getExprentUse() { - if(instance == null) { - return Exprent.MULTIPLE_USES; - } else { - return instance.getExprentUse() & Exprent.MULTIPLE_USES; - } - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - if(instance != null) { - lst.add(instance); - } - return lst; - } - - public Exprent copy() { - return new FieldExprent(name, classname, isStatic, instance==null?null:instance.copy(), descriptor); - } - - public String toJava(int indent) { - StringBuffer buf = new StringBuffer(); - - - if(isStatic) { - ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - if(node == null || !classname.equals(node.classStruct.qualifiedName)) { - buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); - buf.append("."); - } - } else { - - String super_qualifier = null; - - if(instance != null && instance.type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)instance; - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - - if(current_meth != null) { // FIXME: remove - String this_classname = current_meth.varproc.getThisvars().get(varpaar); - - if(this_classname != null) { - if(!classname.equals(this_classname)) { // TODO: direct comparison to the super class? - super_qualifier = this_classname; - } - } - } - } - - if(super_qualifier != null) { - StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; - - if(!super_qualifier.equals(current_class.qualifiedName)) { - buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); - buf.append("."); - } - buf.append("super"); - } else { - StringBuilder buff = new StringBuilder(); - boolean casted = ExprProcessor.getCastedExprent(instance, new VarType(CodeConstants.TYPE_OBJECT, 0, classname), buff, indent, true); - String res = buff.toString(); - - if(casted || instance.getPrecedence() > getPrecedence()) { - res = "("+res+")"; - } - - buf.append(res); - } - - if(buf.toString().equals(VarExprent.VAR_NAMELESS_ENCLOSURE)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way. - buf.setLength(0); - } else { - buf.append("."); - } - } - - buf.append(name); - - return buf.toString(); - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FieldExprent)) return false; + private String name; + + private String classname; + + private boolean isStatic; + + private Exprent instance; + + private FieldDescriptor descriptor; + + { + this.type = EXPRENT_FIELD; + } + + public FieldExprent(LinkConstant cn, Exprent instance) { + + this.instance = instance; + + if (instance == null) { + isStatic = true; + } + + classname = cn.classname; + name = cn.elementname; + descriptor = FieldDescriptor.parseDescriptor(cn.descriptor); + } + + public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor) { + this.name = name; + this.classname = classname; + this.isStatic = isStatic; + this.instance = instance; + this.descriptor = descriptor; + } + + public VarType getExprType() { + return descriptor.type; + } + + public int getExprentUse() { + if (instance == null) { + return Exprent.MULTIPLE_USES; + } + else { + return instance.getExprentUse() & Exprent.MULTIPLE_USES; + } + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if (instance != null) { + lst.add(instance); + } + return lst; + } + + public Exprent copy() { + return new FieldExprent(name, classname, isStatic, instance == null ? null : instance.copy(), descriptor); + } + + public String toJava(int indent) { + StringBuffer buf = new StringBuffer(); + + + if (isStatic) { + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + if (node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + buf.append("."); + } + } + else { + + String super_qualifier = null; + + if (instance != null && instance.type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)instance; + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + + if (current_meth != null) { // FIXME: remove + String this_classname = current_meth.varproc.getThisvars().get(varpaar); + + if (this_classname != null) { + if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class? + super_qualifier = this_classname; + } + } + } + } + + if (super_qualifier != null) { + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + + if (!super_qualifier.equals(current_class.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); + buf.append("."); + } + buf.append("super"); + } + else { + StringBuilder buff = new StringBuilder(); + boolean casted = ExprProcessor.getCastedExprent(instance, new VarType(CodeConstants.TYPE_OBJECT, 0, classname), buff, indent, true); + String res = buff.toString(); + + if (casted || instance.getPrecedence() > getPrecedence()) { + res = "(" + res + ")"; + } + + buf.append(res); + } + + if (buf.toString().equals( + VarExprent.VAR_NAMELESS_ENCLOSURE)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way. + buf.setLength(0); + } + else { + buf.append("."); + } + } + + buf.append(name); + + return buf.toString(); + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FieldExprent)) return false; FieldExprent ft = (FieldExprent)o; return InterpreterUtil.equalObjects(name, ft.getName()) && - InterpreterUtil.equalObjects(classname, ft.getClassname()) && - isStatic == ft.isStatic() && - InterpreterUtil.equalObjects(instance, ft.getInstance()) && - InterpreterUtil.equalObjects(descriptor, ft.getDescriptor()); + InterpreterUtil.equalObjects(classname, ft.getClassname()) && + isStatic == ft.isStatic() && + InterpreterUtil.equalObjects(instance, ft.getInstance()) && + InterpreterUtil.equalObjects(descriptor, ft.getDescriptor()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == instance) { - instance = newexpr; - } - } - - public String getClassname() { - return classname; - } - - public FieldDescriptor getDescriptor() { - return descriptor; - } - - public Exprent getInstance() { - return instance; - } - - public boolean isStatic() { - return isStatic; - } - - public String getName() { - return name; - } - + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == instance) { + instance = newexpr; + } + } + + public String getClassname() { + return classname; + } + + public FieldDescriptor getDescriptor() { + return descriptor; + } + + public Exprent getInstance() { + return instance; + } + + public boolean isStatic() { + return isStatic; + } + + public String getName() { + return name; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java index e4c942f..305fd59 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java @@ -1,24 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; @@ -26,560 +22,597 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; public class FunctionExprent extends Exprent { - public static final int FUNCTION_ADD = 0; - public static final int FUNCTION_SUB = 1; - public static final int FUNCTION_MUL = 2; - public static final int FUNCTION_DIV = 3; - - public static final int FUNCTION_AND = 4; - public static final int FUNCTION_OR = 5; - public static final int FUNCTION_XOR = 6; - - public static final int FUNCTION_REM = 7; - - public static final int FUNCTION_SHL = 8; - public static final int FUNCTION_SHR = 9; - public static final int FUNCTION_USHR = 10; - - public static final int FUNCTION_BITNOT = 11; - public static final int FUNCTION_BOOLNOT = 12; - public static final int FUNCTION_NEG = 13; - - public final static int FUNCTION_I2L = 14; - public final static int FUNCTION_I2F = 15; - public final static int FUNCTION_I2D = 16; - public final static int FUNCTION_L2I = 17; - public final static int FUNCTION_L2F = 18; - public final static int FUNCTION_L2D = 19; - public final static int FUNCTION_F2I = 20; - public final static int FUNCTION_F2L = 21; - public final static int FUNCTION_F2D = 22; - public final static int FUNCTION_D2I = 23; - public final static int FUNCTION_D2L = 24; - public final static int FUNCTION_D2F = 25; - public final static int FUNCTION_I2B = 26; - public final static int FUNCTION_I2C = 27; - public final static int FUNCTION_I2S = 28; - - public final static int FUNCTION_CAST = 29; - public final static int FUNCTION_INSTANCEOF = 30; - - public final static int FUNCTION_ARRAYLENGTH = 31; - - public final static int FUNCTION_IMM = 32; - public final static int FUNCTION_MMI = 33; - - public final static int FUNCTION_IPP = 34; - public final static int FUNCTION_PPI = 35; - - public final static int FUNCTION_IIF = 36; - - public final static int FUNCTION_LCMP = 37; - public final static int FUNCTION_FCMPL = 38; - public final static int FUNCTION_FCMPG = 39; - public final static int FUNCTION_DCMPL = 40; - public final static int FUNCTION_DCMPG = 41; - - public static final int FUNCTION_EQ = 42; - public static final int FUNCTION_NE = 43; - public static final int FUNCTION_LT = 44; - public static final int FUNCTION_GE = 45; - public static final int FUNCTION_GT = 46; - public static final int FUNCTION_LE = 47; - - public static final int FUNCTION_CADD = 48; - public static final int FUNCTION_COR = 49; - - public static final int FUNCTION_STRCONCAT = 50; - - private static final VarType[] types = new VarType[] { - VarType.VARTYPE_LONG, - VarType.VARTYPE_FLOAT, - VarType.VARTYPE_DOUBLE, - VarType.VARTYPE_INT, - VarType.VARTYPE_FLOAT, - VarType.VARTYPE_DOUBLE, - VarType.VARTYPE_INT, - VarType.VARTYPE_LONG, - VarType.VARTYPE_DOUBLE, - VarType.VARTYPE_INT, - VarType.VARTYPE_LONG, - VarType.VARTYPE_FLOAT, - VarType.VARTYPE_BYTE, - VarType.VARTYPE_CHAR, - VarType.VARTYPE_SHORT - }; - - private static final String[] operators = new String[] { - " + ", - " - ", - " * ", - " / ", - " & ", - " | ", - " ^ ", - " % ", - " << ", - " >> ", - " >>> ", - " == ", - " != ", - " < ", - " >= ", - " > ", - " <= ", - " && ", - " || ", - " + " - }; - - private static final int[] precedence = new int[] { - 3, // FUNCTION_ADD - 3, // FUNCTION_SUB - 2, // FUNCTION_MUL - 2, // FUNCTION_DIV - 7, // FUNCTION_AND - 9, // FUNCTION_OR - 8, // FUNCTION_XOR - 2, // FUNCTION_REM - 4, // FUNCTION_SHL - 4, // FUNCTION_SHR - 4, // FUNCTION_USHR - 1, // FUNCTION_BITNOT - 1, // FUNCTION_BOOLNOT - 1, // FUNCTION_NEG - 1, // FUNCTION_I2L - 1, // FUNCTION_I2F - 1, // FUNCTION_I2D - 1, // FUNCTION_L2I - 1, // FUNCTION_L2F - 1, // FUNCTION_L2D - 1, // FUNCTION_F2I - 1, // FUNCTION_F2L - 1, // FUNCTION_F2D - 1, // FUNCTION_D2I - 1, // FUNCTION_D2L - 1, // FUNCTION_D2F - 1, // FUNCTION_I2B - 1, // FUNCTION_I2C - 1, // FUNCTION_I2S - 1, // FUNCTION_CAST - 6, // FUNCTION_INSTANCEOF - 0, // FUNCTION_ARRAYLENGTH - 1, // FUNCTION_IMM - 1, // FUNCTION_MMI - 1, // FUNCTION_IPP - 1, // FUNCTION_PPI - 12, // FUNCTION_IFF - -1, // FUNCTION_LCMP - -1, // FUNCTION_FCMPL - -1, // FUNCTION_FCMPG - -1, // FUNCTION_DCMPL - -1, // FUNCTION_DCMPG - 6, // FUNCTION_EQ = 41; - 6, // FUNCTION_NE = 42; - 5, // FUNCTION_LT = 43; - 5, // FUNCTION_GE = 44; - 5, // FUNCTION_GT = 45; - 5, // FUNCTION_LE = 46; - 10, // FUNCTION_CADD = 47; - 11, // FUNCTION_COR = 48; - 3 // FUNCTION_STRCONCAT = 49; - }; - - private static final HashSet<Integer> associativity = new HashSet<Integer>(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, - FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT})); - - private int functype; - - private VarType implicitType; - - private List<Exprent> lstOperands = new ArrayList<Exprent>(); - - { - this.type = EXPRENT_FUNCTION; - } - - public FunctionExprent(int functype, ListStack<Exprent> stack) { - this.functype = functype; - if(functype>=FUNCTION_BITNOT && functype<=FUNCTION_PPI && functype!=FUNCTION_CAST - && functype!=FUNCTION_INSTANCEOF) { - lstOperands.add(stack.pop()); - } else if(functype == FUNCTION_IIF) { - throw new RuntimeException("no direct instantiation possible"); - } else { - Exprent expr = stack.pop(); - lstOperands.add(stack.pop()); - lstOperands.add(expr); - } - } - - public FunctionExprent(int functype, List<Exprent> operands) { - this.functype = functype; - this.lstOperands = operands; - } - - public VarType getExprType() { - VarType exprType = null; - - if(functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI - || functype == FUNCTION_IMM || functype == FUNCTION_MMI) { - - VarType type1 = lstOperands.get(0).getExprType(); - VarType type2 = null; - if(lstOperands.size() > 1) { - type2 = lstOperands.get(1).getExprType(); - } - - switch(functype) { - case FUNCTION_IMM: - case FUNCTION_MMI: - case FUNCTION_IPP: - case FUNCTION_PPI: - exprType = implicitType; - break; - case FUNCTION_BOOLNOT: - exprType = VarType.VARTYPE_BOOLEAN; - break; - case FUNCTION_SHL: - case FUNCTION_SHR: - case FUNCTION_USHR: - case FUNCTION_BITNOT: - case FUNCTION_NEG: - exprType = getMaxVarType(new VarType[]{type1}); - break; - case FUNCTION_ADD: - case FUNCTION_SUB: - case FUNCTION_MUL: - case FUNCTION_DIV: - case FUNCTION_REM: - exprType = getMaxVarType(new VarType[]{type1, type2}); - break; - case FUNCTION_AND: - case FUNCTION_OR: - case FUNCTION_XOR: - if(type1.type == CodeConstants.TYPE_BOOLEAN & type2.type == CodeConstants.TYPE_BOOLEAN) { - exprType = VarType.VARTYPE_BOOLEAN; - } else { - exprType = getMaxVarType(new VarType[]{type1, type2}); - } - } - } else if(functype == FUNCTION_CAST){ - exprType = lstOperands.get(1).getExprType(); - } else if(functype == FUNCTION_IIF){ - Exprent param1 = lstOperands.get(1); - Exprent param2 = lstOperands.get(2); - VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType()); - - if(param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST && - supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) { - exprType = VarType.VARTYPE_INT; - } else { - exprType = supertype; - } - } else if(functype == FUNCTION_STRCONCAT){ - exprType = VarType.VARTYPE_STRING; - } else if(functype >= FUNCTION_EQ){ - exprType = VarType.VARTYPE_BOOLEAN; - } else if(functype == FUNCTION_INSTANCEOF) { - exprType = VarType.VARTYPE_BOOLEAN; - } else if(functype >= FUNCTION_ARRAYLENGTH) { - exprType = VarType.VARTYPE_INT; - } else { - exprType = types[functype - FUNCTION_I2L]; - } - - return exprType; - } - - public int getExprentUse() { - - if(functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) { - return 0; - } else { - int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; - for(Exprent expr: lstOperands) { - ret &= expr.getExprentUse(); - } - return ret; - } - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - Exprent param1 = lstOperands.get(0); - VarType type1 = param1.getExprType(); - Exprent param2 = null; - VarType type2 = null; - - if(lstOperands.size() > 1) { - param2 = lstOperands.get(1); - type2 = param2.getExprType(); - } - - switch(functype) { - case FUNCTION_IIF: - VarType supertype = getExprType(); - if(supertype == null) { - supertype = getExprType(); - } - result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN); - result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family)); - result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family)); - break; - case FUNCTION_I2L: - case FUNCTION_I2F: - case FUNCTION_I2D: - case FUNCTION_I2B: - case FUNCTION_I2C: - case FUNCTION_I2S: - result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); - result.addMaxTypeExprent(param1, VarType.VARTYPE_INT); - break; - case FUNCTION_IMM: - case FUNCTION_IPP: - case FUNCTION_MMI: - case FUNCTION_PPI: - result.addMinTypeExprent(param1, implicitType); - result.addMaxTypeExprent(param1, implicitType); - break; - case FUNCTION_ADD: - case FUNCTION_SUB: - case FUNCTION_MUL: - case FUNCTION_DIV: - case FUNCTION_REM: - case FUNCTION_SHL: - case FUNCTION_SHR: - case FUNCTION_USHR: - case FUNCTION_LT: - case FUNCTION_GE: - case FUNCTION_GT: - case FUNCTION_LE: - result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); - case FUNCTION_BITNOT: - // case FUNCTION_BOOLNOT: - case FUNCTION_NEG: - result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); - break; - case FUNCTION_AND: - case FUNCTION_OR: - case FUNCTION_XOR: - case FUNCTION_EQ: - case FUNCTION_NE: - { - if(type1.type == CodeConstants.TYPE_BOOLEAN) { - - if(type2.isStrictSuperset(type1)) { - - result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); - - } else { // both are booleans - - boolean param1_false_boolean = type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()); - boolean param2_false_boolean = type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue()); - - if(param1_false_boolean || param2_false_boolean) { - result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); - result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); - } - } - - } else if(type2.type == CodeConstants.TYPE_BOOLEAN) { - - if(type1.isStrictSuperset(type2)) { - result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); - } - } - } - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.addAll(lstOperands); - return lst; - } - - public Exprent copy() { - List<Exprent> lst = new ArrayList<Exprent>(); - for(Exprent expr: lstOperands) { - lst.add(expr.copy()); - } - FunctionExprent func = new FunctionExprent(functype, lst); - func.setImplicitType(implicitType); - - return func; - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FunctionExprent)) return false; + public static final int FUNCTION_ADD = 0; + public static final int FUNCTION_SUB = 1; + public static final int FUNCTION_MUL = 2; + public static final int FUNCTION_DIV = 3; + + public static final int FUNCTION_AND = 4; + public static final int FUNCTION_OR = 5; + public static final int FUNCTION_XOR = 6; + + public static final int FUNCTION_REM = 7; + + public static final int FUNCTION_SHL = 8; + public static final int FUNCTION_SHR = 9; + public static final int FUNCTION_USHR = 10; + + public static final int FUNCTION_BITNOT = 11; + public static final int FUNCTION_BOOLNOT = 12; + public static final int FUNCTION_NEG = 13; + + public final static int FUNCTION_I2L = 14; + public final static int FUNCTION_I2F = 15; + public final static int FUNCTION_I2D = 16; + public final static int FUNCTION_L2I = 17; + public final static int FUNCTION_L2F = 18; + public final static int FUNCTION_L2D = 19; + public final static int FUNCTION_F2I = 20; + public final static int FUNCTION_F2L = 21; + public final static int FUNCTION_F2D = 22; + public final static int FUNCTION_D2I = 23; + public final static int FUNCTION_D2L = 24; + public final static int FUNCTION_D2F = 25; + public final static int FUNCTION_I2B = 26; + public final static int FUNCTION_I2C = 27; + public final static int FUNCTION_I2S = 28; + + public final static int FUNCTION_CAST = 29; + public final static int FUNCTION_INSTANCEOF = 30; + + public final static int FUNCTION_ARRAYLENGTH = 31; + + public final static int FUNCTION_IMM = 32; + public final static int FUNCTION_MMI = 33; + + public final static int FUNCTION_IPP = 34; + public final static int FUNCTION_PPI = 35; + + public final static int FUNCTION_IIF = 36; + + public final static int FUNCTION_LCMP = 37; + public final static int FUNCTION_FCMPL = 38; + public final static int FUNCTION_FCMPG = 39; + public final static int FUNCTION_DCMPL = 40; + public final static int FUNCTION_DCMPG = 41; + + public static final int FUNCTION_EQ = 42; + public static final int FUNCTION_NE = 43; + public static final int FUNCTION_LT = 44; + public static final int FUNCTION_GE = 45; + public static final int FUNCTION_GT = 46; + public static final int FUNCTION_LE = 47; + + public static final int FUNCTION_CADD = 48; + public static final int FUNCTION_COR = 49; + + public static final int FUNCTION_STRCONCAT = 50; + + private static final VarType[] types = new VarType[]{ + VarType.VARTYPE_LONG, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_LONG, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_LONG, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_BYTE, + VarType.VARTYPE_CHAR, + VarType.VARTYPE_SHORT + }; + + private static final String[] operators = new String[]{ + " + ", + " - ", + " * ", + " / ", + " & ", + " | ", + " ^ ", + " % ", + " << ", + " >> ", + " >>> ", + " == ", + " != ", + " < ", + " >= ", + " > ", + " <= ", + " && ", + " || ", + " + " + }; + + private static final int[] precedence = new int[]{ + 3, // FUNCTION_ADD + 3, // FUNCTION_SUB + 2, // FUNCTION_MUL + 2, // FUNCTION_DIV + 7, // FUNCTION_AND + 9, // FUNCTION_OR + 8, // FUNCTION_XOR + 2, // FUNCTION_REM + 4, // FUNCTION_SHL + 4, // FUNCTION_SHR + 4, // FUNCTION_USHR + 1, // FUNCTION_BITNOT + 1, // FUNCTION_BOOLNOT + 1, // FUNCTION_NEG + 1, // FUNCTION_I2L + 1, // FUNCTION_I2F + 1, // FUNCTION_I2D + 1, // FUNCTION_L2I + 1, // FUNCTION_L2F + 1, // FUNCTION_L2D + 1, // FUNCTION_F2I + 1, // FUNCTION_F2L + 1, // FUNCTION_F2D + 1, // FUNCTION_D2I + 1, // FUNCTION_D2L + 1, // FUNCTION_D2F + 1, // FUNCTION_I2B + 1, // FUNCTION_I2C + 1, // FUNCTION_I2S + 1, // FUNCTION_CAST + 6, // FUNCTION_INSTANCEOF + 0, // FUNCTION_ARRAYLENGTH + 1, // FUNCTION_IMM + 1, // FUNCTION_MMI + 1, // FUNCTION_IPP + 1, // FUNCTION_PPI + 12, // FUNCTION_IFF + -1, // FUNCTION_LCMP + -1, // FUNCTION_FCMPL + -1, // FUNCTION_FCMPG + -1, // FUNCTION_DCMPL + -1, // FUNCTION_DCMPG + 6, // FUNCTION_EQ = 41; + 6, // FUNCTION_NE = 42; + 5, // FUNCTION_LT = 43; + 5, // FUNCTION_GE = 44; + 5, // FUNCTION_GT = 45; + 5, // FUNCTION_LE = 46; + 10, // FUNCTION_CADD = 47; + 11, // FUNCTION_COR = 48; + 3 // FUNCTION_STRCONCAT = 49; + }; + + private static final HashSet<Integer> associativity = + new HashSet<Integer>(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, + FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT})); + + private int functype; + + private VarType implicitType; + + private List<Exprent> lstOperands = new ArrayList<Exprent>(); + + { + this.type = EXPRENT_FUNCTION; + } + + public FunctionExprent(int functype, ListStack<Exprent> stack) { + this.functype = functype; + if (functype >= FUNCTION_BITNOT && functype <= FUNCTION_PPI && functype != FUNCTION_CAST + && functype != FUNCTION_INSTANCEOF) { + lstOperands.add(stack.pop()); + } + else if (functype == FUNCTION_IIF) { + throw new RuntimeException("no direct instantiation possible"); + } + else { + Exprent expr = stack.pop(); + lstOperands.add(stack.pop()); + lstOperands.add(expr); + } + } + + public FunctionExprent(int functype, List<Exprent> operands) { + this.functype = functype; + this.lstOperands = operands; + } + + public VarType getExprType() { + VarType exprType = null; + + if (functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI + || functype == FUNCTION_IMM || functype == FUNCTION_MMI) { + + VarType type1 = lstOperands.get(0).getExprType(); + VarType type2 = null; + if (lstOperands.size() > 1) { + type2 = lstOperands.get(1).getExprType(); + } + + switch (functype) { + case FUNCTION_IMM: + case FUNCTION_MMI: + case FUNCTION_IPP: + case FUNCTION_PPI: + exprType = implicitType; + break; + case FUNCTION_BOOLNOT: + exprType = VarType.VARTYPE_BOOLEAN; + break; + case FUNCTION_SHL: + case FUNCTION_SHR: + case FUNCTION_USHR: + case FUNCTION_BITNOT: + case FUNCTION_NEG: + exprType = getMaxVarType(new VarType[]{type1}); + break; + case FUNCTION_ADD: + case FUNCTION_SUB: + case FUNCTION_MUL: + case FUNCTION_DIV: + case FUNCTION_REM: + exprType = getMaxVarType(new VarType[]{type1, type2}); + break; + case FUNCTION_AND: + case FUNCTION_OR: + case FUNCTION_XOR: + if (type1.type == CodeConstants.TYPE_BOOLEAN & type2.type == CodeConstants.TYPE_BOOLEAN) { + exprType = VarType.VARTYPE_BOOLEAN; + } + else { + exprType = getMaxVarType(new VarType[]{type1, type2}); + } + } + } + else if (functype == FUNCTION_CAST) { + exprType = lstOperands.get(1).getExprType(); + } + else if (functype == FUNCTION_IIF) { + Exprent param1 = lstOperands.get(1); + Exprent param2 = lstOperands.get(2); + VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType()); + + if (param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST && + supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) { + exprType = VarType.VARTYPE_INT; + } + else { + exprType = supertype; + } + } + else if (functype == FUNCTION_STRCONCAT) { + exprType = VarType.VARTYPE_STRING; + } + else if (functype >= FUNCTION_EQ) { + exprType = VarType.VARTYPE_BOOLEAN; + } + else if (functype == FUNCTION_INSTANCEOF) { + exprType = VarType.VARTYPE_BOOLEAN; + } + else if (functype >= FUNCTION_ARRAYLENGTH) { + exprType = VarType.VARTYPE_INT; + } + else { + exprType = types[functype - FUNCTION_I2L]; + } + + return exprType; + } + + public int getExprentUse() { + + if (functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) { + return 0; + } + else { + int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + for (Exprent expr : lstOperands) { + ret &= expr.getExprentUse(); + } + return ret; + } + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + Exprent param1 = lstOperands.get(0); + VarType type1 = param1.getExprType(); + Exprent param2 = null; + VarType type2 = null; + + if (lstOperands.size() > 1) { + param2 = lstOperands.get(1); + type2 = param2.getExprType(); + } + + switch (functype) { + case FUNCTION_IIF: + VarType supertype = getExprType(); + if (supertype == null) { + supertype = getExprType(); + } + result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN); + result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family)); + result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family)); + break; + case FUNCTION_I2L: + case FUNCTION_I2F: + case FUNCTION_I2D: + case FUNCTION_I2B: + case FUNCTION_I2C: + case FUNCTION_I2S: + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(param1, VarType.VARTYPE_INT); + break; + case FUNCTION_IMM: + case FUNCTION_IPP: + case FUNCTION_MMI: + case FUNCTION_PPI: + result.addMinTypeExprent(param1, implicitType); + result.addMaxTypeExprent(param1, implicitType); + break; + case FUNCTION_ADD: + case FUNCTION_SUB: + case FUNCTION_MUL: + case FUNCTION_DIV: + case FUNCTION_REM: + case FUNCTION_SHL: + case FUNCTION_SHR: + case FUNCTION_USHR: + case FUNCTION_LT: + case FUNCTION_GE: + case FUNCTION_GT: + case FUNCTION_LE: + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + case FUNCTION_BITNOT: + // case FUNCTION_BOOLNOT: + case FUNCTION_NEG: + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + break; + case FUNCTION_AND: + case FUNCTION_OR: + case FUNCTION_XOR: + case FUNCTION_EQ: + case FUNCTION_NE: { + if (type1.type == CodeConstants.TYPE_BOOLEAN) { + + if (type2.isStrictSuperset(type1)) { + + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + } + else { // both are booleans + + boolean param1_false_boolean = + type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()); + boolean param2_false_boolean = + type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue()); + + if (param1_false_boolean || param2_false_boolean) { + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + } + } + } + else if (type2.type == CodeConstants.TYPE_BOOLEAN) { + + if (type1.isStrictSuperset(type2)) { + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + } + } + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.addAll(lstOperands); + return lst; + } + + public Exprent copy() { + List<Exprent> lst = new ArrayList<Exprent>(); + for (Exprent expr : lstOperands) { + lst.add(expr.copy()); + } + FunctionExprent func = new FunctionExprent(functype, lst); + func.setImplicitType(implicitType); + + return func; + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FunctionExprent)) return false; FunctionExprent fe = (FunctionExprent)o; - return functype==fe.getFunctype() && - InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant + return functype == fe.getFunctype() && + InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + for (int i = 0; i < lstOperands.size(); i++) { + if (oldexpr == lstOperands.get(i)) { + lstOperands.set(i, newexpr); + } + } + } + + public String toJava(int indent) { + + if (functype <= FUNCTION_USHR) { + return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype] + + wrapOperandString(lstOperands.get(1), true, indent); + } + + if (functype >= FUNCTION_EQ) { + return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype - FUNCTION_EQ + 11] + + wrapOperandString(lstOperands.get(1), true, indent); + } + + switch (functype) { + case FUNCTION_BITNOT: + return "~" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_BOOLNOT: + return "!" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_NEG: + return "-" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_CAST: + return "(" + lstOperands.get(1).toJava(indent) + ")" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_ARRAYLENGTH: + Exprent arr = lstOperands.get(0); + + String res = wrapOperandString(arr, false, indent); + if (arr.getExprType().arraydim == 0) { + VarType objarr = VarType.VARTYPE_OBJECT.copy(); + objarr.arraydim = 1; // type family does not change + + res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")"; + } + return res + ".length"; + case FUNCTION_IIF: + return wrapOperandString(lstOperands.get(0), true, indent) + "?" + wrapOperandString(lstOperands.get(1), true, indent) + ":" + + wrapOperandString(lstOperands.get(2), true, indent); + case FUNCTION_IPP: + return wrapOperandString(lstOperands.get(0), true, indent) + "++"; + case FUNCTION_PPI: + return "++" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_IMM: + return wrapOperandString(lstOperands.get(0), true, indent) + "--"; + case FUNCTION_MMI: + return "--" + wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_INSTANCEOF: + return wrapOperandString(lstOperands.get(0), true, indent) + " instanceof " + wrapOperandString(lstOperands.get(1), true, indent); + case FUNCTION_LCMP: // shouldn't appear in the final code + return "__lcmp__(" + + wrapOperandString(lstOperands.get(0), true, indent) + + "," + + wrapOperandString(lstOperands.get(1), true, indent) + + ")"; + case FUNCTION_FCMPL: // shouldn't appear in the final code + return "__fcmpl__(" + + wrapOperandString(lstOperands.get(0), true, indent) + + "," + + wrapOperandString(lstOperands.get(1), true, indent) + + ")"; + case FUNCTION_FCMPG: // shouldn't appear in the final code + return "__fcmpg__(" + + wrapOperandString(lstOperands.get(0), true, indent) + + "," + + wrapOperandString(lstOperands.get(1), true, indent) + + ")"; + case FUNCTION_DCMPL: // shouldn't appear in the final code + return "__dcmpl__(" + + wrapOperandString(lstOperands.get(0), true, indent) + + "," + + wrapOperandString(lstOperands.get(1), true, indent) + + ")"; + case FUNCTION_DCMPG: // shouldn't appear in the final code + return "__dcmpg__(" + + wrapOperandString(lstOperands.get(0), true, indent) + + "," + + wrapOperandString(lstOperands.get(1), true, indent) + + ")"; + } + + if (functype <= FUNCTION_I2S) { + return "(" + ExprProcessor.getTypeName(types[functype - FUNCTION_I2L]) + ")" + wrapOperandString(lstOperands.get(0), true, indent); + } + + // return "<unknown function>"; + throw new RuntimeException("invalid function"); + } + + public int getPrecedence() { + return getPrecedence(functype); + } + + public static int getPrecedence(int func) { + return precedence[func]; + } + + public VarType getSimpleCastType() { + return types[functype - FUNCTION_I2L]; + } + + private String wrapOperandString(Exprent expr, boolean eq, int indent) { + + int myprec = getPrecedence(); + int exprprec = expr.getPrecedence(); + + boolean parentheses = exprprec > myprec; + if (!parentheses && eq) { + parentheses = (exprprec == myprec); + if (parentheses) { + if (expr.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)expr).getFunctype() == functype) { + parentheses = !associativity.contains(functype); + } + } + } + + String res = expr.toJava(indent); + + if (parentheses) { + res = "(" + res + ")"; + } + + return res; + } + + private VarType getMaxVarType(VarType[] arr) { + + int[] types = new int[]{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; + VarType[] vartypes = new VarType[]{VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; + + for (int i = 0; i < types.length; i++) { + for (int j = 0; j < arr.length; j++) { + if (arr[j].type == types[i]) { + return vartypes[i]; + } + } + } + + return VarType.VARTYPE_INT; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public int getFunctype() { + return functype; + } + + public void setFunctype(int functype) { + this.functype = functype; + } + + public List<Exprent> getLstOperands() { + return lstOperands; + } + + public void setLstOperands(List<Exprent> lstOperands) { + this.lstOperands = lstOperands; + } + + public VarType getImplicitType() { + return implicitType; + } + + public void setImplicitType(VarType implicitType) { + this.implicitType = implicitType; } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - for(int i=0;i<lstOperands.size();i++) { - if(oldexpr == lstOperands.get(i)) { - lstOperands.set(i, newexpr); - } - } - } - - public String toJava(int indent) { - - if(functype <= FUNCTION_USHR) { - return wrapOperandString(lstOperands.get(0), false, indent)+operators[functype]+ - wrapOperandString(lstOperands.get(1), true, indent); - } - - if(functype >= FUNCTION_EQ) { - return wrapOperandString(lstOperands.get(0), false, indent)+operators[functype-FUNCTION_EQ+11]+ - wrapOperandString(lstOperands.get(1), true, indent); - } - - switch(functype) { - case FUNCTION_BITNOT: - return "~"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_BOOLNOT: - return "!"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_NEG: - return "-"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_CAST: - return "("+lstOperands.get(1).toJava(indent)+")"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_ARRAYLENGTH: - Exprent arr = lstOperands.get(0); - - String res = wrapOperandString(arr, false, indent); - if(arr.getExprType().arraydim == 0) { - VarType objarr = VarType.VARTYPE_OBJECT.copy(); - objarr.arraydim = 1; // type family does not change - - res = "(("+ExprProcessor.getCastTypeName(objarr)+")"+res+")"; - } - return res+".length"; - case FUNCTION_IIF: - return wrapOperandString(lstOperands.get(0), true, indent)+"?"+wrapOperandString(lstOperands.get(1), true, indent)+":"+ - wrapOperandString(lstOperands.get(2), true, indent); - case FUNCTION_IPP: - return wrapOperandString(lstOperands.get(0), true, indent)+"++"; - case FUNCTION_PPI: - return "++"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_IMM: - return wrapOperandString(lstOperands.get(0), true, indent)+"--"; - case FUNCTION_MMI: - return "--"+wrapOperandString(lstOperands.get(0), true, indent); - case FUNCTION_INSTANCEOF: - return wrapOperandString(lstOperands.get(0), true, indent)+" instanceof "+wrapOperandString(lstOperands.get(1), true, indent); - case FUNCTION_LCMP: // shouldn't appear in the final code - return "__lcmp__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; - case FUNCTION_FCMPL: // shouldn't appear in the final code - return "__fcmpl__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; - case FUNCTION_FCMPG: // shouldn't appear in the final code - return "__fcmpg__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; - case FUNCTION_DCMPL: // shouldn't appear in the final code - return "__dcmpl__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; - case FUNCTION_DCMPG: // shouldn't appear in the final code - return "__dcmpg__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; - } - - if(functype <= FUNCTION_I2S) { - return "("+ExprProcessor.getTypeName(types[functype - FUNCTION_I2L])+")"+wrapOperandString(lstOperands.get(0), true, indent); - } - -// return "<unknown function>"; - throw new RuntimeException("invalid function"); - } - - public int getPrecedence() { - return getPrecedence(functype); - } - - public static int getPrecedence(int func) { - return precedence[func]; - } - - public VarType getSimpleCastType() { - return types[functype - FUNCTION_I2L]; - } - - private String wrapOperandString(Exprent expr, boolean eq, int indent) { - - int myprec = getPrecedence(); - int exprprec = expr.getPrecedence(); - - boolean parentheses = exprprec > myprec; - if(!parentheses && eq) { - parentheses = (exprprec == myprec); - if(parentheses) { - if(expr.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)expr).getFunctype() == functype) { - parentheses = !associativity.contains(functype); - } - } - } - - String res = expr.toJava(indent); - - if(parentheses) { - res = "("+res+")"; - } - - return res; - } - - private VarType getMaxVarType(VarType[] arr) { - - int[] types = new int[] {CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; - VarType[] vartypes = new VarType[] {VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; - - for(int i=0;i<types.length;i++) { - for(int j=0;j<arr.length;j++) { - if(arr[j].type == types[i]) { - return vartypes[i]; - } - } - } - - return VarType.VARTYPE_INT; - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public int getFunctype() { - return functype; - } - - public void setFunctype(int functype) { - this.functype = functype; - } - - public List<Exprent> getLstOperands() { - return lstOperands; - } - - public void setLstOperands(List<Exprent> lstOperands) { - this.lstOperands = lstOperands; - } - - public VarType getImplicitType() { - return implicitType; - } - - public void setImplicitType(VarType implicitType) { - this.implicitType = implicitType; - } - } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java index 15e39ba..030fe9a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java @@ -1,146 +1,149 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class IfExprent extends Exprent { - - public static final int IF_EQ = 0; - public static final int IF_NE = 1; - public static final int IF_LT = 2; - public static final int IF_GE = 3; - public static final int IF_GT = 4; - public static final int IF_LE = 5; - - public static final int IF_NULL = 6; - public static final int IF_NONNULL = 7; - - public static final int IF_ICMPEQ = 8; - public static final int IF_ICMPNE = 9; - public static final int IF_ICMPLT = 10; - public static final int IF_ICMPGE = 11; - public static final int IF_ICMPGT = 12; - public static final int IF_ICMPLE = 13; - public static final int IF_ACMPEQ = 14; - public static final int IF_ACMPNE = 15; - - public static final int IF_CAND = 16; - public static final int IF_COR = 17; - - public static final int IF_NOT = 18; - public static final int IF_VALUE = 19; - - private static final int[] functypes = new int[] { - FunctionExprent.FUNCTION_EQ, - FunctionExprent.FUNCTION_NE, - FunctionExprent.FUNCTION_LT, - FunctionExprent.FUNCTION_GE, - FunctionExprent.FUNCTION_GT, - FunctionExprent.FUNCTION_LE, - FunctionExprent.FUNCTION_EQ, - FunctionExprent.FUNCTION_NE, - FunctionExprent.FUNCTION_EQ, - FunctionExprent.FUNCTION_NE, - FunctionExprent.FUNCTION_LT, - FunctionExprent.FUNCTION_GE, - FunctionExprent.FUNCTION_GT, - FunctionExprent.FUNCTION_LE, - FunctionExprent.FUNCTION_EQ, - FunctionExprent.FUNCTION_NE, - FunctionExprent.FUNCTION_CADD, - FunctionExprent.FUNCTION_COR, - FunctionExprent.FUNCTION_BOOLNOT, - -1 - }; - - private Exprent condition; - - { - this.type = EXPRENT_IF; - } - - public IfExprent(int iftype, ListStack<Exprent> stack) { - - if(iftype <= IF_LE) { - stack.push(new ConstExprent(0, true)); - } else if(iftype <= IF_NONNULL) { - stack.push(new ConstExprent(VarType.VARTYPE_NULL, null)); - } - - if(iftype == IF_VALUE) { - condition = stack.pop(); - } else { - condition = new FunctionExprent(functypes[iftype], stack); - } - } - - private IfExprent(Exprent condition) { - this.condition = condition; - } - - public Exprent copy() { - return new IfExprent(condition.copy()); - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.add(condition); - return lst; - } - - public String toJava(int indent) { - StringBuffer buf = new StringBuffer("if("); - buf.append(condition.toJava(indent)); - buf.append(")"); - - return buf.toString(); - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof IfExprent)) return false; + + public static final int IF_EQ = 0; + public static final int IF_NE = 1; + public static final int IF_LT = 2; + public static final int IF_GE = 3; + public static final int IF_GT = 4; + public static final int IF_LE = 5; + + public static final int IF_NULL = 6; + public static final int IF_NONNULL = 7; + + public static final int IF_ICMPEQ = 8; + public static final int IF_ICMPNE = 9; + public static final int IF_ICMPLT = 10; + public static final int IF_ICMPGE = 11; + public static final int IF_ICMPGT = 12; + public static final int IF_ICMPLE = 13; + public static final int IF_ACMPEQ = 14; + public static final int IF_ACMPNE = 15; + + public static final int IF_CAND = 16; + public static final int IF_COR = 17; + + public static final int IF_NOT = 18; + public static final int IF_VALUE = 19; + + private static final int[] functypes = new int[]{ + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_CADD, + FunctionExprent.FUNCTION_COR, + FunctionExprent.FUNCTION_BOOLNOT, + -1 + }; + + private Exprent condition; + + { + this.type = EXPRENT_IF; + } + + public IfExprent(int iftype, ListStack<Exprent> stack) { + + if (iftype <= IF_LE) { + stack.push(new ConstExprent(0, true)); + } + else if (iftype <= IF_NONNULL) { + stack.push(new ConstExprent(VarType.VARTYPE_NULL, null)); + } + + if (iftype == IF_VALUE) { + condition = stack.pop(); + } + else { + condition = new FunctionExprent(functypes[iftype], stack); + } + } + + private IfExprent(Exprent condition) { + this.condition = condition; + } + + public Exprent copy() { + return new IfExprent(condition.copy()); + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(condition); + return lst; + } + + public String toJava(int indent) { + StringBuffer buf = new StringBuffer("if("); + buf.append(condition.toJava(indent)); + buf.append(")"); + + return buf.toString(); + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof IfExprent)) return false; IfExprent ie = (IfExprent)o; return InterpreterUtil.equalObjects(condition, ie.getCondition()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == condition) { - condition = newexpr; - } - } - - public IfExprent negateIf() { - condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{condition})); - return this; - } - - public Exprent getCondition() { - return condition; - } - - public void setCondition(Exprent condition) { - this.condition = condition; - } + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == condition) { + condition = newexpr; + } + } + + public IfExprent negateIf() { + condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{condition})); + return this; + } + + public Exprent getCondition() { + return condition; + } + + public void setCondition(Exprent condition) { + this.condition = condition; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 835e96e..c19052d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -1,21 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.*; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -33,472 +32,485 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import java.util.*; + public class InvocationExprent extends Exprent { - public static final int INVOKE_SPECIAL = 1; - public static final int INVOKE_VIRTUAL = 2; - public static final int INVOKE_STATIC = 3; - public static final int INVOKE_INTERFACE = 4; - public static final int INVOKE_DYNAMIC = 5; - - public static final int TYP_GENERAL = 1; - public static final int TYP_INIT = 2; - public static final int TYP_CLINIT = 3; - - public static final int CONSTRUCTOR_NOT = 0; - public static final int CONSTRUCTOR_THIS = 1; - public static final int CONSTRUCTOR_SUPER = 2; - - private String name; - - private String classname; - - private boolean isStatic; - - private int functype = TYP_GENERAL; - - private Exprent instance; - - private MethodDescriptor descriptor; - - private String stringDescriptor; - - private String invoke_dynamic_classsuffix; - - private int invocationTyp = INVOKE_VIRTUAL; - - private List<Exprent> lstParameters = new ArrayList<Exprent>(); - - { - this.type = EXPRENT_INVOCATION; - } - - public InvocationExprent() {} - - public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamic_invokation_type) { - - name = cn.elementname; - classname = cn.classname; - - switch(opcode) { - case CodeConstants.opc_invokestatic: - invocationTyp = INVOKE_STATIC; - break; - case CodeConstants.opc_invokespecial: - invocationTyp = INVOKE_SPECIAL; - break; - case CodeConstants.opc_invokevirtual: - invocationTyp = INVOKE_VIRTUAL; - break; - case CodeConstants.opc_invokeinterface: - invocationTyp = INVOKE_INTERFACE; - break; - case CodeConstants.opc_invokedynamic: - invocationTyp = INVOKE_DYNAMIC; - - classname = "java/lang/Class"; // dummy class name - invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; - - } - - if("<init>".equals(name)) { - functype = TYP_INIT; - }else if("<clinit>".equals(name)) { - functype = TYP_CLINIT; - } - - stringDescriptor = cn.descriptor; - descriptor = MethodDescriptor.parseDescriptor(cn.descriptor); - - for(int i=0;i<descriptor.params.length;i++) { - lstParameters.add(0, stack.pop()); - } - - if(opcode == CodeConstants.opc_invokedynamic) { - if(dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) { - isStatic = true; - } else { - instance = lstParameters.get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method. - } - } else if(opcode == CodeConstants.opc_invokestatic) { - isStatic = true; - } else { - instance = stack.pop(); - } - } - - private InvocationExprent(InvocationExprent expr) { - name = expr.getName(); - classname = expr.getClassname(); - isStatic = expr.isStatic(); - functype = expr.getFunctype(); - instance = expr.getInstance(); - if(instance != null) { - instance = instance.copy(); - } - invocationTyp = expr.getInvocationTyp(); - stringDescriptor = expr.getStringDescriptor(); - descriptor = expr.getDescriptor(); - lstParameters = new ArrayList<Exprent>(expr.getLstParameters()); - for(int i=0;i<lstParameters.size();i++) { - lstParameters.set(i, lstParameters.get(i).copy()); - } - } - - - public VarType getExprType() { - return descriptor.ret; - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - for(int i=0;i<lstParameters.size();i++) { - Exprent parameter = lstParameters.get(i); - - VarType leftType = descriptor.params[i]; - - result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family)); - result.addMaxTypeExprent(parameter, leftType); - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - if(instance != null) { - lst.add(instance); - } - lst.addAll(lstParameters); - return lst; - } - - - public Exprent copy() { - return new InvocationExprent(this); - } - - public String toJava(int indent) { - StringBuilder buf = new StringBuilder(""); - - String super_qualifier = null; - boolean isInstanceThis = false; - - if(invocationTyp == INVOKE_DYNAMIC) { -// ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); -// -// if(node != null) { -// ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix); -// if(lambda_node != null) { -// -// String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType); -// -// StringWriter strwriter = new StringWriter(); -// BufferedWriter bufstrwriter = new BufferedWriter(strwriter); -// -// ClassWriter clwriter = new ClassWriter(); -// -// try { -// bufstrwriter.write("new " + typename + "() {"); -// bufstrwriter.newLine(); -// -// -// -// bufstrwriter.flush(); -// } catch(IOException ex) { -// throw new RuntimeException(ex); -// } -// -// buf.append(strwriter.toString()); -// -// } -// } - - } else if(isStatic) { - - ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); - if(node == null || !classname.equals(node.classStruct.qualifiedName)) { - buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); - } - - } else { - - if(instance != null && instance.type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)instance; - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - VarProcessor vproc = instvar.getProcessor(); - if(vproc == null) { - MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - if(current_meth != null) { - vproc = current_meth.varproc; - } - } - - String this_classname = null; - if(vproc != null) { - this_classname = vproc.getThisvars().get(varpaar); - } - - if(this_classname != null) { - isInstanceThis = true; - - if(invocationTyp == INVOKE_SPECIAL) { - if(!classname.equals(this_classname)) { // TODO: direct comparison to the super class? - super_qualifier = this_classname; - } - } - } - } - - if(functype == TYP_GENERAL){ - if(super_qualifier != null) { - StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; - - if(!super_qualifier.equals(current_class.qualifiedName)) { - buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); - buf.append("."); - } - buf.append("super"); - } else { - String res = instance.toJava(indent); - - VarType rightType = instance.getExprType(); - VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname); - - if(rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) { - buf.append("(("+ExprProcessor.getCastTypeName(leftType)+")"); - - if(instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { - res = "("+res+")"; - } - buf.append(res+")"); - } else if(instance.getPrecedence() > getPrecedence()) { - buf.append("("+res+")"); - } else { - buf.append(res); - } - } - } - } - - switch(functype) { - case TYP_GENERAL: - if(VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { - buf = new StringBuilder(""); - } - - if(buf.length() > 0) { - buf.append("."); - } - - buf.append(name); - if(invocationTyp == INVOKE_DYNAMIC) { - buf.append("<invokedynamic>"); - } - buf.append("("); - - break; - case TYP_CLINIT: - throw new RuntimeException("Explicite invocation of <clinit>"); - case TYP_INIT: - if(super_qualifier != null) { - buf.append("super("); - } else if(isInstanceThis) { - buf.append("this("); - } else { - buf.append(instance.toJava(indent)); - buf.append(".<init>("); -// throw new RuntimeException("Unrecognized invocation of <init>"); // FIXME: activate - } - } - - List<VarVersionPaar> sigFields = null; + public static final int INVOKE_SPECIAL = 1; + public static final int INVOKE_VIRTUAL = 2; + public static final int INVOKE_STATIC = 3; + public static final int INVOKE_INTERFACE = 4; + public static final int INVOKE_DYNAMIC = 5; + + public static final int TYP_GENERAL = 1; + public static final int TYP_INIT = 2; + public static final int TYP_CLINIT = 3; + + public static final int CONSTRUCTOR_NOT = 0; + public static final int CONSTRUCTOR_THIS = 1; + public static final int CONSTRUCTOR_SUPER = 2; + + private String name; + + private String classname; + + private boolean isStatic; + + private int functype = TYP_GENERAL; + + private Exprent instance; + + private MethodDescriptor descriptor; + + private String stringDescriptor; + + private String invoke_dynamic_classsuffix; + + private int invocationTyp = INVOKE_VIRTUAL; + + private List<Exprent> lstParameters = new ArrayList<Exprent>(); + + { + this.type = EXPRENT_INVOCATION; + } + + public InvocationExprent() { + } + + public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamic_invokation_type) { + + name = cn.elementname; + classname = cn.classname; + + switch (opcode) { + case CodeConstants.opc_invokestatic: + invocationTyp = INVOKE_STATIC; + break; + case CodeConstants.opc_invokespecial: + invocationTyp = INVOKE_SPECIAL; + break; + case CodeConstants.opc_invokevirtual: + invocationTyp = INVOKE_VIRTUAL; + break; + case CodeConstants.opc_invokeinterface: + invocationTyp = INVOKE_INTERFACE; + break; + case CodeConstants.opc_invokedynamic: + invocationTyp = INVOKE_DYNAMIC; + + classname = "java/lang/Class"; // dummy class name + invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; + } + + if ("<init>".equals(name)) { + functype = TYP_INIT; + } + else if ("<clinit>".equals(name)) { + functype = TYP_CLINIT; + } + + stringDescriptor = cn.descriptor; + descriptor = MethodDescriptor.parseDescriptor(cn.descriptor); + + for (int i = 0; i < descriptor.params.length; i++) { + lstParameters.add(0, stack.pop()); + } + + if (opcode == CodeConstants.opc_invokedynamic) { + if (dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) { + isStatic = true; + } + else { + instance = lstParameters + .get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method. + } + } + else if (opcode == CodeConstants.opc_invokestatic) { + isStatic = true; + } + else { + instance = stack.pop(); + } + } + + private InvocationExprent(InvocationExprent expr) { + name = expr.getName(); + classname = expr.getClassname(); + isStatic = expr.isStatic(); + functype = expr.getFunctype(); + instance = expr.getInstance(); + if (instance != null) { + instance = instance.copy(); + } + invocationTyp = expr.getInvocationTyp(); + stringDescriptor = expr.getStringDescriptor(); + descriptor = expr.getDescriptor(); + lstParameters = new ArrayList<Exprent>(expr.getLstParameters()); + for (int i = 0; i < lstParameters.size(); i++) { + lstParameters.set(i, lstParameters.get(i).copy()); + } + } + + + public VarType getExprType() { + return descriptor.ret; + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + for (int i = 0; i < lstParameters.size(); i++) { + Exprent parameter = lstParameters.get(i); + + VarType leftType = descriptor.params[i]; + + result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMaxTypeExprent(parameter, leftType); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if (instance != null) { + lst.add(instance); + } + lst.addAll(lstParameters); + return lst; + } + + + public Exprent copy() { + return new InvocationExprent(this); + } + + public String toJava(int indent) { + StringBuilder buf = new StringBuilder(""); + + String super_qualifier = null; + boolean isInstanceThis = false; + + if (invocationTyp == INVOKE_DYNAMIC) { + // ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + // + // if(node != null) { + // ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix); + // if(lambda_node != null) { + // + // String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType); + // + // StringWriter strwriter = new StringWriter(); + // BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + // + // ClassWriter clwriter = new ClassWriter(); + // + // try { + // bufstrwriter.write("new " + typename + "() {"); + // bufstrwriter.newLine(); + // + // + // + // bufstrwriter.flush(); + // } catch(IOException ex) { + // throw new RuntimeException(ex); + // } + // + // buf.append(strwriter.toString()); + // + // } + // } + + } + else if (isStatic) { + + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + if (node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + } + } + else { + + if (instance != null && instance.type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)instance; + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + VarProcessor vproc = instvar.getProcessor(); + if (vproc == null) { + MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + if (current_meth != null) { + vproc = current_meth.varproc; + } + } + + String this_classname = null; + if (vproc != null) { + this_classname = vproc.getThisvars().get(varpaar); + } + + if (this_classname != null) { + isInstanceThis = true; + + if (invocationTyp == INVOKE_SPECIAL) { + if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class? + super_qualifier = this_classname; + } + } + } + } + + if (functype == TYP_GENERAL) { + if (super_qualifier != null) { + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + + if (!super_qualifier.equals(current_class.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); + buf.append("."); + } + buf.append("super"); + } + else { + String res = instance.toJava(indent); + + VarType rightType = instance.getExprType(); + VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname); + + if (rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) { + buf.append("((" + ExprProcessor.getCastTypeName(leftType) + ")"); + + if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { + res = "(" + res + ")"; + } + buf.append(res + ")"); + } + else if (instance.getPrecedence() > getPrecedence()) { + buf.append("(" + res + ")"); + } + else { + buf.append(res); + } + } + } + } + + switch (functype) { + case TYP_GENERAL: + if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { + buf = new StringBuilder(""); + } + + if (buf.length() > 0) { + buf.append("."); + } + + buf.append(name); + if (invocationTyp == INVOKE_DYNAMIC) { + buf.append("<invokedynamic>"); + } + buf.append("("); + + break; + case TYP_CLINIT: + throw new RuntimeException("Explicite invocation of <clinit>"); + case TYP_INIT: + if (super_qualifier != null) { + buf.append("super("); + } + else if (isInstanceThis) { + buf.append("this("); + } + else { + buf.append(instance.toJava(indent)); + buf.append(".<init>("); + // throw new RuntimeException("Unrecognized invocation of <init>"); // FIXME: activate + } + } + + List<VarVersionPaar> sigFields = null; boolean isEnum = false; - if(functype == TYP_INIT) { - ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); - - if(newnode != null) { // own class - if(newnode.wrapper != null) { - sigFields = newnode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields; - } else { - if(newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class - sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); - } - } + if (functype == TYP_INIT) { + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + + if (newnode != null) { // own class + if (newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields; + } + else { + if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } isEnum = (newnode.classStruct.access_flags & CodeConstants.ACC_ENUM) != 0 && - DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); - } - } - - Set<Integer> setAmbiguousParameters = getAmbiguousParameters(); - - boolean firstpar = true; + DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + } + } + + Set<Integer> setAmbiguousParameters = getAmbiguousParameters(); + + boolean firstpar = true; int start = isEnum ? 2 : 0; - for(int i=start;i<lstParameters.size();i++) { - if(sigFields == null || sigFields.get(i) == null) { - if(!firstpar) { - buf.append(", "); - } - - StringBuilder buff = new StringBuilder(); - ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i)); - - buf.append(buff); - firstpar = false; - } - } - buf.append(")"); - - return buf.toString(); - } - - private Set<Integer> getAmbiguousParameters() { - - Set<Integer> ret = new HashSet<Integer>(); - - StructClass cstr = DecompilerContext.getStructcontext().getClass(classname); - if(cstr != null) { - List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>(); - for(StructMethod meth : cstr.getMethods()) { - if(name.equals(meth.getName())) { - MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor()); - if(md.params.length == descriptor.params.length) { - boolean equals = true; - for(int i=0;i<md.params.length;i++) { - if(md.params[i].type_family != descriptor.params[i].type_family) { - equals = false; - break; - } - } - - if(equals) { - lstMethods.add(md); - } - } - } - } - - if(lstMethods.size() > 1) { - for(int i=0;i<descriptor.params.length;i++) { - VarType partype = descriptor.params[i]; - - for(MethodDescriptor md : lstMethods) { - if(!partype.equals(md.params[i])) { - ret.add(i); - break; - } - } - } - } - } - - return ret; - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof InvocationExprent)) return false; + for (int i = start; i < lstParameters.size(); i++) { + if (sigFields == null || sigFields.get(i) == null) { + if (!firstpar) { + buf.append(", "); + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i)); + + buf.append(buff); + firstpar = false; + } + } + buf.append(")"); + + return buf.toString(); + } + + private Set<Integer> getAmbiguousParameters() { + + Set<Integer> ret = new HashSet<Integer>(); + + StructClass cstr = DecompilerContext.getStructcontext().getClass(classname); + if (cstr != null) { + List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>(); + for (StructMethod meth : cstr.getMethods()) { + if (name.equals(meth.getName())) { + MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor()); + if (md.params.length == descriptor.params.length) { + boolean equals = true; + for (int i = 0; i < md.params.length; i++) { + if (md.params[i].type_family != descriptor.params[i].type_family) { + equals = false; + break; + } + } + + if (equals) { + lstMethods.add(md); + } + } + } + } + + if (lstMethods.size() > 1) { + for (int i = 0; i < descriptor.params.length; i++) { + VarType partype = descriptor.params[i]; + + for (MethodDescriptor md : lstMethods) { + if (!partype.equals(md.params[i])) { + ret.add(i); + break; + } + } + } + } + } + + return ret; + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof InvocationExprent)) return false; InvocationExprent it = (InvocationExprent)o; return InterpreterUtil.equalObjects(name, it.getName()) && - InterpreterUtil.equalObjects(classname, it.getClassname()) && - isStatic == it.isStatic() && - InterpreterUtil.equalObjects(instance, it.getInstance()) && - InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) && - functype == it.getFunctype() && - InterpreterUtil.equalLists(lstParameters, it.getLstParameters()); + InterpreterUtil.equalObjects(classname, it.getClassname()) && + isStatic == it.isStatic() && + InterpreterUtil.equalObjects(instance, it.getInstance()) && + InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) && + functype == it.getFunctype() && + InterpreterUtil.equalLists(lstParameters, it.getLstParameters()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == instance) { + instance = newexpr; + } + + for (int i = 0; i < lstParameters.size(); i++) { + if (oldexpr == lstParameters.get(i)) { + lstParameters.set(i, newexpr); + } + } + } + + public List<Exprent> getLstParameters() { + return lstParameters; + } + + public void setLstParameters(List<Exprent> lstParameters) { + this.lstParameters = lstParameters; + } + + public MethodDescriptor getDescriptor() { + return descriptor; + } + + public void setDescriptor(MethodDescriptor descriptor) { + this.descriptor = descriptor; + } + + public String getClassname() { + return classname; + } + + public void setClassname(String classname) { + this.classname = classname; + } + + public int getFunctype() { + return functype; + } + + public void setFunctype(int functype) { + this.functype = functype; + } + + public Exprent getInstance() { + return instance; + } + + public void setInstance(Exprent instance) { + this.instance = instance; + } + + public boolean isStatic() { + return isStatic; + } + + public void setStatic(boolean isStatic) { + this.isStatic = isStatic; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStringDescriptor() { + return stringDescriptor; + } + + public void setStringDescriptor(String stringDescriptor) { + this.stringDescriptor = stringDescriptor; + } + + public int getInvocationTyp() { + return invocationTyp; + } + + public void setInvocationTyp(int invocationTyp) { + this.invocationTyp = invocationTyp; + } + + public String getInvokeDynamicClassSuffix() { + return invoke_dynamic_classsuffix; } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == instance) { - instance = newexpr; - } - - for(int i=0;i<lstParameters.size();i++) { - if(oldexpr == lstParameters.get(i)) { - lstParameters.set(i, newexpr); - } - } - } - - public List<Exprent> getLstParameters() { - return lstParameters; - } - - public void setLstParameters(List<Exprent> lstParameters) { - this.lstParameters = lstParameters; - } - - public MethodDescriptor getDescriptor() { - return descriptor; - } - - public void setDescriptor(MethodDescriptor descriptor) { - this.descriptor = descriptor; - } - - public String getClassname() { - return classname; - } - - public void setClassname(String classname) { - this.classname = classname; - } - - public int getFunctype() { - return functype; - } - - public void setFunctype(int functype) { - this.functype = functype; - } - - public Exprent getInstance() { - return instance; - } - - public void setInstance(Exprent instance) { - this.instance = instance; - } - - public boolean isStatic() { - return isStatic; - } - - public void setStatic(boolean isStatic) { - this.isStatic = isStatic; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getStringDescriptor() { - return stringDescriptor; - } - - public void setStringDescriptor(String stringDescriptor) { - this.stringDescriptor = stringDescriptor; - } - - public int getInvocationTyp() { - return invocationTyp; - } - - public void setInvocationTyp(int invocationTyp) { - this.invocationTyp = invocationTyp; - } - - public String getInvokeDynamicClassSuffix() { - return invoke_dynamic_classsuffix; - } - } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java index d231af0..9a1945e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java @@ -1,82 +1,83 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.util.InterpreterUtil; - public class MonitorExprent extends Exprent { - public static final int MONITOR_ENTER = 0; - public static final int MONITOR_EXIT = 1; - - private int montype; - - private Exprent value; - - { - this.type = EXPRENT_MONITOR; - } - - public MonitorExprent(int montype, Exprent value) { - this.montype = montype; - this.value = value; - } - - public Exprent copy() { - return new MonitorExprent(montype, value.copy()); - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.add(value); - return lst; - } - - public String toJava(int indent) { - if(montype == MONITOR_ENTER) { - return "synchronized("+value.toJava(indent)+")"; - } else { - return ""; - } - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof MonitorExprent)) return false; + public static final int MONITOR_ENTER = 0; + public static final int MONITOR_EXIT = 1; + + private int montype; + + private Exprent value; + + { + this.type = EXPRENT_MONITOR; + } + + public MonitorExprent(int montype, Exprent value) { + this.montype = montype; + this.value = value; + } + + public Exprent copy() { + return new MonitorExprent(montype, value.copy()); + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(value); + return lst; + } + + public String toJava(int indent) { + if (montype == MONITOR_ENTER) { + return "synchronized(" + value.toJava(indent) + ")"; + } + else { + return ""; + } + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof MonitorExprent)) return false; MonitorExprent me = (MonitorExprent)o; return montype == me.getMontype() && - InterpreterUtil.equalObjects(value, me.getValue()); + InterpreterUtil.equalObjects(value, me.getValue()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == value) { - value = newexpr; - } - } - - public int getMontype() { - return montype; - } - - public Exprent getValue() { - return value; - } - + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == value) { + value = newexpr; + } + } + + public int getMontype() { + return montype; + } + + public Exprent getValue() { + return value; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index 0d1814a..f71df1a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -1,30 +1,24 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassWriter; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; @@ -34,479 +28,496 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + public class NewExprent extends Exprent { - private InvocationExprent constructor; - - private VarType newtype; - - private List<Exprent> lstDims = new ArrayList<Exprent>(); - - private List<Exprent> lstArrayElements = new ArrayList<Exprent>(); - - private boolean directArrayInit; - - private boolean anonymous; - - private boolean lambda; - - private boolean enumconst; - - { - this.type = EXPRENT_NEW; - } - - public NewExprent(VarType newtype, ListStack<Exprent> stack, int arraydim) { - this.newtype = newtype; - for(int i=0;i<arraydim;i++) { - lstDims.add(0, stack.pop()); - } - - setAnonymous(); - } - - public NewExprent(VarType newtype, List<Exprent> lstDims) { - this.newtype = newtype; - this.lstDims = lstDims; - - setAnonymous(); - } - - private void setAnonymous() { - - anonymous = false; - lambda = false; - - if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) { - ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); - - if(node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) { - anonymous = true; - - if(node.type == ClassNode.CLASS_LAMBDA) { - lambda = true; - } - } - } - } - - public VarType getExprType() { - - if(anonymous) { - ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); - - return node.anonimousClassType; - } else { - return newtype; - } - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - if(newtype.arraydim != 0) { - for(Exprent dim: lstDims) { - result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR); - result.addMaxTypeExprent(dim, VarType.VARTYPE_INT); - } - - if(newtype.arraydim == 1) { - - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - - for(Exprent element: lstArrayElements) { - result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.type_family)); - result.addMaxTypeExprent(element, leftType); - } - } - } else { - if(constructor != null) { - return constructor.checkExprTypeBounds(); - } - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - if(newtype.arraydim == 0) { - if(constructor != null) { - Exprent constructor_instance = constructor.getInstance(); - - if(constructor_instance != null) { // should be true only for a lambda expression with a virtual content method - lst.add(constructor_instance); - } - - lst.addAll(constructor.getLstParameters()); - } - } else { - lst.addAll(lstDims); - lst.addAll(lstArrayElements); - } - - return lst; - } - - public Exprent copy() { - List<Exprent> lst = new ArrayList<Exprent>(); - for(Exprent expr: lstDims) { - lst.add(expr.copy()); - } - - NewExprent ret = new NewExprent(newtype, lst); - ret.setConstructor(constructor==null?null:(InvocationExprent)constructor.copy()); - ret.setLstArrayElements(lstArrayElements); - ret.setDirectArrayInit(directArrayInit); - ret.setAnonymous(anonymous); - ret.setEnumconst(enumconst); - return ret; - } - - public int getPrecedence() { - return 1; // precedence of new - } - - public String toJava(int indent) { - StringBuilder buf = new StringBuilder(); - - if (anonymous) { - - ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); - - buf.append("("); - - if (!lambda && constructor != null) { - - InvocationExprent invsuper = child.superInvocation; - - ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(invsuper.getClassname()); - - List<VarVersionPaar> sigFields = null; - if (newnode != null) { // own class - if (newnode.wrapper != null) { - sigFields = newnode.wrapper.getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields; - } else { - if(newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && - !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance - sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(constructor.getLstParameters().size(), (VarVersionPaar)null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); - } - } - } - - boolean firstpar = true; - int start = 0, end = invsuper.getLstParameters().size(); - if (enumconst) { - start += 2; - end -= 1; - } - for(int i = start; i < end; i++) { - if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { - buf.append(", "); - } - - Exprent param = invsuper.getLstParameters().get(i); - if (param.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent) param).getIndex(); - if (varindex > 0 && varindex <= constructor.getLstParameters().size()) { - param = constructor.getLstParameters().get(varindex - 1); - } - } - - StringBuilder buff = new StringBuilder(); - ExprProcessor.getCastedExprent(param, invsuper.getDescriptor().params[i], buff, indent, true); - - buf.append(buff); - firstpar = false; - } - } - - } - - if (!enumconst) { - String enclosing = null; - if (!lambda && constructor != null) { - enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent); - } - - String typename = ExprProcessor.getCastTypeName(child.anonimousClassType); - - if (enclosing != null) { - ClassNode anonimousNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(child.anonimousClassType.value); - if (anonimousNode != null) { - typename = anonimousNode.simpleName; - } else { - typename = typename.substring(typename.lastIndexOf('.') + 1); - } - } - buf.insert(0, "new " + typename); - - if (enclosing != null) { - buf.insert(0, enclosing + "."); - } - } - - buf.append(")"); - - if (enumconst && buf.length() == 2) { - buf.setLength(0); - } - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - ClassWriter clwriter = new ClassWriter(); - try { - if (lambda) { - clwriter.classLambdaToJava(child, bufstrwriter, (constructor == null ? null : constructor.getInstance()), indent); - } else { - clwriter.classToJava(child, bufstrwriter, indent); - } - bufstrwriter.flush(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - - if (lambda && !DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { - buf.setLength(0); // remove the usual 'new <class>()', it will - // be replaced with lambda style '() ->' - } - - buf.append(strwriter.toString()); - - } else if (directArrayInit) { - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - - buf.append("{"); - for(int i = 0; i < lstArrayElements.size(); i++) { - if (i > 0) { - buf.append(", "); - } - StringBuilder buff = new StringBuilder(); - ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); - - buf.append(buff); - } - buf.append("}"); - } else { - if (newtype.arraydim == 0) { - - if (constructor != null) { - - List<Exprent> lstParameters = constructor.getLstParameters(); - - ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(constructor.getClassname()); - - List<VarVersionPaar> sigFields = null; - if (newnode != null) { // own class - if (newnode.wrapper != null) { - sigFields = newnode.wrapper.getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields; - } else { - if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && - !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance - sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar) null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); - } - } - } - - int start = enumconst ? 2 : 0; - if (!enumconst || start < lstParameters.size()) { - buf.append("("); - - boolean firstpar = true; - for(int i = start; i < lstParameters.size(); i++) { - if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { - buf.append(", "); - } - - StringBuilder buff = new StringBuilder(); - ExprProcessor.getCastedExprent(lstParameters.get(i), constructor.getDescriptor().params[i], buff, indent, true); - - buf.append(buff); - firstpar = false; - } - } - buf.append(")"); - } - } - - if (!enumconst) { - String enclosing = null; - if (constructor != null) { - enclosing = getQualifiedNewInstance(newtype.value, constructor.getLstParameters(), indent); - } - - String typename = ExprProcessor.getTypeName(newtype); - - if (enclosing != null) { - ClassNode newNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); - if (newNode != null) { - typename = newNode.simpleName; - } else { - typename = typename.substring(typename.lastIndexOf('.') + 1); - } - } - buf.insert(0, "new " + typename); - - if (enclosing != null) { - buf.insert(0, enclosing + "."); - } - } - - } else { - buf.append("new ").append(ExprProcessor.getTypeName(newtype)); - - if (lstArrayElements.isEmpty()) { - for(int i = 0; i < newtype.arraydim; i++) { - buf.append("[").append(i < lstDims.size() ? lstDims.get(i).toJava(indent) : "").append("]"); - } - } else { - for(int i = 0; i < newtype.arraydim; i++) { - buf.append("[]"); - } - - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - - buf.append("{"); - for(int i = 0; i < lstArrayElements.size(); i++) { - if (i > 0) { - buf.append(", "); - } - StringBuilder buff = new StringBuilder(); - ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); - - buf.append(buff); - } - buf.append("}"); - } - } - } - return buf.toString(); - } - - private String getQualifiedNewInstance(String classname, List<Exprent> lstParams, int indent) { - - ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); - - if(node != null && node.type != ClassNode.CLASS_ROOT && (node.access & CodeConstants.ACC_STATIC) == 0) { - if(!lstParams.isEmpty()) { - Exprent enclosing = lstParams.get(0); - - boolean isQualifiedNew = false; - - if(enclosing.type == Exprent.EXPRENT_VAR) { - VarExprent varEnclosing = (VarExprent)enclosing; - - StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; - String this_classname = varEnclosing.getProcessor().getThisvars().get(new VarVersionPaar(varEnclosing)); - - if(!current_class.qualifiedName.equals(this_classname)) { - isQualifiedNew = true; - } - } else { - isQualifiedNew = true; - } - - if(isQualifiedNew) { - return enclosing.toJava(indent); - } - } - } - - return null; - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof NewExprent)) return false; + private InvocationExprent constructor; + + private VarType newtype; + + private List<Exprent> lstDims = new ArrayList<Exprent>(); + + private List<Exprent> lstArrayElements = new ArrayList<Exprent>(); + + private boolean directArrayInit; + + private boolean anonymous; + + private boolean lambda; + + private boolean enumconst; + + { + this.type = EXPRENT_NEW; + } + + public NewExprent(VarType newtype, ListStack<Exprent> stack, int arraydim) { + this.newtype = newtype; + for (int i = 0; i < arraydim; i++) { + lstDims.add(0, stack.pop()); + } + + setAnonymous(); + } + + public NewExprent(VarType newtype, List<Exprent> lstDims) { + this.newtype = newtype; + this.lstDims = lstDims; + + setAnonymous(); + } + + private void setAnonymous() { + + anonymous = false; + lambda = false; + + if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) { + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + if (node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) { + anonymous = true; + + if (node.type == ClassNode.CLASS_LAMBDA) { + lambda = true; + } + } + } + } + + public VarType getExprType() { + + if (anonymous) { + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + return node.anonimousClassType; + } + else { + return newtype; + } + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + if (newtype.arraydim != 0) { + for (Exprent dim : lstDims) { + result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(dim, VarType.VARTYPE_INT); + } + + if (newtype.arraydim == 1) { + + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + for (Exprent element : lstArrayElements) { + result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMaxTypeExprent(element, leftType); + } + } + } + else { + if (constructor != null) { + return constructor.checkExprTypeBounds(); + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if (newtype.arraydim == 0) { + if (constructor != null) { + Exprent constructor_instance = constructor.getInstance(); + + if (constructor_instance != null) { // should be true only for a lambda expression with a virtual content method + lst.add(constructor_instance); + } + + lst.addAll(constructor.getLstParameters()); + } + } + else { + lst.addAll(lstDims); + lst.addAll(lstArrayElements); + } + + return lst; + } + + public Exprent copy() { + List<Exprent> lst = new ArrayList<Exprent>(); + for (Exprent expr : lstDims) { + lst.add(expr.copy()); + } + + NewExprent ret = new NewExprent(newtype, lst); + ret.setConstructor(constructor == null ? null : (InvocationExprent)constructor.copy()); + ret.setLstArrayElements(lstArrayElements); + ret.setDirectArrayInit(directArrayInit); + ret.setAnonymous(anonymous); + ret.setEnumconst(enumconst); + return ret; + } + + public int getPrecedence() { + return 1; // precedence of new + } + + public String toJava(int indent) { + StringBuilder buf = new StringBuilder(); + + if (anonymous) { + + ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + buf.append("("); + + if (!lambda && constructor != null) { + + InvocationExprent invsuper = child.superInvocation; + + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(invsuper.getClassname()); + + List<VarVersionPaar> sigFields = null; + if (newnode != null) { // own class + if (newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields; + } + else { + if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && + !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(constructor.getLstParameters().size(), (VarVersionPaar)null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } + } + + boolean firstpar = true; + int start = 0, end = invsuper.getLstParameters().size(); + if (enumconst) { + start += 2; + end -= 1; + } + for (int i = start; i < end; i++) { + if (sigFields == null || sigFields.get(i) == null) { + if (!firstpar) { + buf.append(", "); + } + + Exprent param = invsuper.getLstParameters().get(i); + if (param.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)param).getIndex(); + if (varindex > 0 && varindex <= constructor.getLstParameters().size()) { + param = constructor.getLstParameters().get(varindex - 1); + } + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(param, invsuper.getDescriptor().params[i], buff, indent, true); + + buf.append(buff); + firstpar = false; + } + } + } + + if (!enumconst) { + String enclosing = null; + if (!lambda && constructor != null) { + enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent); + } + + String typename = ExprProcessor.getCastTypeName(child.anonimousClassType); + + if (enclosing != null) { + ClassNode anonimousNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(child.anonimousClassType.value); + if (anonimousNode != null) { + typename = anonimousNode.simpleName; + } + else { + typename = typename.substring(typename.lastIndexOf('.') + 1); + } + } + buf.insert(0, "new " + typename); + + if (enclosing != null) { + buf.insert(0, enclosing + "."); + } + } + + buf.append(")"); + + if (enumconst && buf.length() == 2) { + buf.setLength(0); + } + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + ClassWriter clwriter = new ClassWriter(); + try { + if (lambda) { + clwriter.classLambdaToJava(child, bufstrwriter, (constructor == null ? null : constructor.getInstance()), indent); + } + else { + clwriter.classToJava(child, bufstrwriter, indent); + } + bufstrwriter.flush(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + + if (lambda && !DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { + buf.setLength(0); // remove the usual 'new <class>()', it will + // be replaced with lambda style '() ->' + } + + buf.append(strwriter.toString()); + } + else if (directArrayInit) { + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + buf.append("{"); + for (int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); + + buf.append(buff); + } + buf.append("}"); + } + else { + if (newtype.arraydim == 0) { + + if (constructor != null) { + + List<Exprent> lstParameters = constructor.getLstParameters(); + + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(constructor.getClassname()); + + List<VarVersionPaar> sigFields = null; + if (newnode != null) { // own class + if (newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields; + } + else { + if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && + !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } + } + + int start = enumconst ? 2 : 0; + if (!enumconst || start < lstParameters.size()) { + buf.append("("); + + boolean firstpar = true; + for (int i = start; i < lstParameters.size(); i++) { + if (sigFields == null || sigFields.get(i) == null) { + if (!firstpar) { + buf.append(", "); + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstParameters.get(i), constructor.getDescriptor().params[i], buff, indent, true); + + buf.append(buff); + firstpar = false; + } + } + buf.append(")"); + } + } + + if (!enumconst) { + String enclosing = null; + if (constructor != null) { + enclosing = getQualifiedNewInstance(newtype.value, constructor.getLstParameters(), indent); + } + + String typename = ExprProcessor.getTypeName(newtype); + + if (enclosing != null) { + ClassNode newNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + if (newNode != null) { + typename = newNode.simpleName; + } + else { + typename = typename.substring(typename.lastIndexOf('.') + 1); + } + } + buf.insert(0, "new " + typename); + + if (enclosing != null) { + buf.insert(0, enclosing + "."); + } + } + } + else { + buf.append("new ").append(ExprProcessor.getTypeName(newtype)); + + if (lstArrayElements.isEmpty()) { + for (int i = 0; i < newtype.arraydim; i++) { + buf.append("[").append(i < lstDims.size() ? lstDims.get(i).toJava(indent) : "").append("]"); + } + } + else { + for (int i = 0; i < newtype.arraydim; i++) { + buf.append("[]"); + } + + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + buf.append("{"); + for (int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); + + buf.append(buff); + } + buf.append("}"); + } + } + } + return buf.toString(); + } + + private String getQualifiedNewInstance(String classname, List<Exprent> lstParams, int indent) { + + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + + if (node != null && node.type != ClassNode.CLASS_ROOT && (node.access & CodeConstants.ACC_STATIC) == 0) { + if (!lstParams.isEmpty()) { + Exprent enclosing = lstParams.get(0); + + boolean isQualifiedNew = false; + + if (enclosing.type == Exprent.EXPRENT_VAR) { + VarExprent varEnclosing = (VarExprent)enclosing; + + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + String this_classname = varEnclosing.getProcessor().getThisvars().get(new VarVersionPaar(varEnclosing)); + + if (!current_class.qualifiedName.equals(this_classname)) { + isQualifiedNew = true; + } + } + else { + isQualifiedNew = true; + } + + if (isQualifiedNew) { + return enclosing.toJava(indent); + } + } + } + + return null; + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof NewExprent)) return false; NewExprent ne = (NewExprent)o; return InterpreterUtil.equalObjects(newtype, ne.getNewtype()) && - InterpreterUtil.equalLists(lstDims, ne.getLstDims()) && - InterpreterUtil.equalObjects(constructor, ne.getConstructor()) && - directArrayInit == ne.directArrayInit && - InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements()); + InterpreterUtil.equalLists(lstDims, ne.getLstDims()) && + InterpreterUtil.equalObjects(constructor, ne.getConstructor()) && + directArrayInit == ne.directArrayInit && + InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements()); } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == constructor) { - constructor = (InvocationExprent)newexpr; - } - - if(constructor != null) { - constructor.replaceExprent(oldexpr, newexpr); - } - - for(int i=0;i<lstDims.size();i++) { - if(oldexpr == lstDims.get(i)) { - lstDims.set(i, newexpr); - } - } - - for(int i=0;i<lstArrayElements.size();i++) { - if(oldexpr == lstArrayElements.get(i)) { - lstArrayElements.set(i, newexpr); - } - } - } - - public InvocationExprent getConstructor() { - return constructor; - } - - public void setConstructor(InvocationExprent constructor) { - this.constructor = constructor; - } - - public List<Exprent> getLstDims() { - return lstDims; - } - - public VarType getNewtype() { - return newtype; - } - - public List<Exprent> getLstArrayElements() { - return lstArrayElements; - } - - public void setLstArrayElements(List<Exprent> lstArrayElements) { - this.lstArrayElements = lstArrayElements; - } - - public boolean isDirectArrayInit() { - return directArrayInit; - } - - public void setDirectArrayInit(boolean directArrayInit) { - this.directArrayInit = directArrayInit; - } - - public boolean isLambda() { - return lambda; - } - - public boolean isAnonymous() { - return anonymous; - } - - public void setAnonymous(boolean anonymous) { - this.anonymous = anonymous; - } - - public boolean isEnumconst() { - return enumconst; - } - - public void setEnumconst(boolean enumconst) { - this.enumconst = enumconst; - } + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == constructor) { + constructor = (InvocationExprent)newexpr; + } + + if (constructor != null) { + constructor.replaceExprent(oldexpr, newexpr); + } + + for (int i = 0; i < lstDims.size(); i++) { + if (oldexpr == lstDims.get(i)) { + lstDims.set(i, newexpr); + } + } + + for (int i = 0; i < lstArrayElements.size(); i++) { + if (oldexpr == lstArrayElements.get(i)) { + lstArrayElements.set(i, newexpr); + } + } + } + + public InvocationExprent getConstructor() { + return constructor; + } + + public void setConstructor(InvocationExprent constructor) { + this.constructor = constructor; + } + + public List<Exprent> getLstDims() { + return lstDims; + } + + public VarType getNewtype() { + return newtype; + } + + public List<Exprent> getLstArrayElements() { + return lstArrayElements; + } + + public void setLstArrayElements(List<Exprent> lstArrayElements) { + this.lstArrayElements = lstArrayElements; + } + + public boolean isDirectArrayInit() { + return directArrayInit; + } + + public void setDirectArrayInit(boolean directArrayInit) { + this.directArrayInit = directArrayInit; + } + + public boolean isLambda() { + return lambda; + } + + public boolean isAnonymous() { + return anonymous; + } + + public void setAnonymous(boolean anonymous) { + this.anonymous = anonymous; + } + + public boolean isEnumconst() { + return enumconst; + } + + public void setEnumconst(boolean enumconst) { + this.enumconst = enumconst; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java index 13bd3b4..36bead2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java @@ -1,113 +1,114 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class SwitchExprent extends Exprent { - private Exprent value; - - private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); - - { - this.type = EXPRENT_SWITCH; - } - - public SwitchExprent(Exprent value) { - this.value = value; - } - - public Exprent copy() { - SwitchExprent swexpr = new SwitchExprent(value.copy()); - - List<List<ConstExprent>> lstCaseValues = new ArrayList<List<ConstExprent>>(); - for(List<ConstExprent> lst: caseValues) { - lstCaseValues.add(new ArrayList<ConstExprent>(lst)); - } - swexpr.setCaseValues(lstCaseValues); - - return swexpr; - } - - public VarType getExprType() { - return value.getExprType(); - } - - public CheckTypesResult checkExprTypeBounds() { - CheckTypesResult result = new CheckTypesResult(); - - result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR); - result.addMaxTypeExprent(value, VarType.VARTYPE_INT); - - VarType valtype = value.getExprType(); - for(List<ConstExprent> lst: caseValues) { - for(ConstExprent expr: lst) { - if(expr != null) { - VarType casetype = expr.getExprType(); - if(!casetype.equals(valtype)) { - valtype = VarType.getCommonSupertype(casetype, valtype); - result.addMinTypeExprent(value, valtype); - } - } - } - } - - return result; - } - - public List<Exprent> getAllExprents() { - List<Exprent> lst = new ArrayList<Exprent>(); - lst.add(value); - return lst; - } - - public String toJava(int indent) { - return "switch("+value.toJava(indent)+")"; - } - - public boolean equals(Object o) { - if(o == this) { - return true; - } - - if(o == null || !(o instanceof SwitchExprent)) { - return false; - } - - SwitchExprent sw = (SwitchExprent) o; - return InterpreterUtil.equalObjects(value, sw.getValue()); - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(oldexpr == value) { - value = newexpr; - } - } - - public Exprent getValue() { - return value; - } - - public void setCaseValues(List<List<ConstExprent>> caseValues) { - this.caseValues = caseValues; - } + private Exprent value; + + private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); + + { + this.type = EXPRENT_SWITCH; + } + + public SwitchExprent(Exprent value) { + this.value = value; + } + + public Exprent copy() { + SwitchExprent swexpr = new SwitchExprent(value.copy()); + + List<List<ConstExprent>> lstCaseValues = new ArrayList<List<ConstExprent>>(); + for (List<ConstExprent> lst : caseValues) { + lstCaseValues.add(new ArrayList<ConstExprent>(lst)); + } + swexpr.setCaseValues(lstCaseValues); + + return swexpr; + } + + public VarType getExprType() { + return value.getExprType(); + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(value, VarType.VARTYPE_INT); + + VarType valtype = value.getExprType(); + for (List<ConstExprent> lst : caseValues) { + for (ConstExprent expr : lst) { + if (expr != null) { + VarType casetype = expr.getExprType(); + if (!casetype.equals(valtype)) { + valtype = VarType.getCommonSupertype(casetype, valtype); + result.addMinTypeExprent(value, valtype); + } + } + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(value); + return lst; + } + + public String toJava(int indent) { + return "switch(" + value.toJava(indent) + ")"; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o == null || !(o instanceof SwitchExprent)) { + return false; + } + + SwitchExprent sw = (SwitchExprent)o; + return InterpreterUtil.equalObjects(value, sw.getValue()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (oldexpr == value) { + value = newexpr; + } + } + + public Exprent getValue() { + return value; + } + + public void setCaseValues(List<List<ConstExprent>> caseValues) { + this.caseValues = caseValues; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index c2356ca..cd75f1f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -1,29 +1,24 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.exps; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassWriter; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; @@ -31,168 +26,177 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + public class VarExprent extends Exprent { - public static final int STACK_BASE = 10000; - - public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>"; - - private int index; - - private VarType vartype; - - private boolean definition = false;; - - private VarProcessor processor; - - private int version = 0; - - private boolean classdef = false; - - private boolean stack = false;; - - { - this.type = EXPRENT_VAR; - } - - public VarExprent(int index, VarType vartype, VarProcessor processor) { - this.index = index; - this.vartype = vartype; - this.processor = processor; - } - - public VarType getExprType() { - return getVartype(); - } - - public int getExprentUse() { - return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; - } - - public List<Exprent> getAllExprents() { - return new ArrayList<Exprent>(); - } - - public Exprent copy() { - VarExprent var = new VarExprent(index, getVartype(), processor); - var.setDefinition(definition); - var.setVersion(version); - var.setClassdef(classdef); - var.setStack(stack); - return var; - } - - public String toJava(int indent) { - - if(classdef) { - - ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(vartype.value); - - StringWriter strwriter = new StringWriter(); - BufferedWriter bufstrwriter = new BufferedWriter(strwriter); - - ClassWriter clwriter = new ClassWriter(); - try { - clwriter.classToJava(child, bufstrwriter, indent); - bufstrwriter.flush(); - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - return strwriter.toString(); - - } else { - String name = null; - if(processor != null) { - name = processor.getVarName(new VarVersionPaar(index, version)); - } - - StringBuilder buf = new StringBuilder(); - - if(definition) { - if(processor != null && processor.getVarFinal(new VarVersionPaar(index, version)) == VarTypeProcessor.VAR_FINALEXPLICIT) { - buf.append("final "); - } - buf.append(ExprProcessor.getCastTypeName(getVartype())+" "); - } - buf.append(name==null?("var"+index+(version==0?"":"_"+version)):name); - - return buf.toString(); - } - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof VarExprent)) return false; + public static final int STACK_BASE = 10000; + + public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>"; + + private int index; + + private VarType vartype; + + private boolean definition = false; + ; + + private VarProcessor processor; + + private int version = 0; + + private boolean classdef = false; + + private boolean stack = false; + ; + + { + this.type = EXPRENT_VAR; + } + + public VarExprent(int index, VarType vartype, VarProcessor processor) { + this.index = index; + this.vartype = vartype; + this.processor = processor; + } + + public VarType getExprType() { + return getVartype(); + } + + public int getExprentUse() { + return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + } + + public List<Exprent> getAllExprents() { + return new ArrayList<Exprent>(); + } + + public Exprent copy() { + VarExprent var = new VarExprent(index, getVartype(), processor); + var.setDefinition(definition); + var.setVersion(version); + var.setClassdef(classdef); + var.setStack(stack); + return var; + } + + public String toJava(int indent) { + + if (classdef) { + + ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(vartype.value); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + ClassWriter clwriter = new ClassWriter(); + try { + clwriter.classToJava(child, bufstrwriter, indent); + bufstrwriter.flush(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + + return strwriter.toString(); + } + else { + String name = null; + if (processor != null) { + name = processor.getVarName(new VarVersionPaar(index, version)); + } + + StringBuilder buf = new StringBuilder(); + + if (definition) { + if (processor != null && processor.getVarFinal(new VarVersionPaar(index, version)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + buf.append("final "); + } + buf.append(ExprProcessor.getCastTypeName(getVartype()) + " "); + } + buf.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name); + + return buf.toString(); + } + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarExprent)) return false; VarExprent ve = (VarExprent)o; return index == ve.getIndex() && - version == ve.getVersion() && - InterpreterUtil.equalObjects(getVartype(), ve.getVartype()); // FIXME: vartype comparison redundant? - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public VarType getVartype() { - VarType vt = null; - if(processor != null) { - vt = processor.getVarType(new VarVersionPaar(index, version)); - } - - if(vt == null || (vartype != null && vartype.type != CodeConstants.TYPE_UNKNOWN)) { - vt = vartype; - } - - return vt==null?VarType.VARTYPE_UNKNOWN:vt; - } - - public void setVartype(VarType vartype) { - this.vartype = vartype; - } - - public boolean isDefinition() { - return definition; - } - - public void setDefinition(boolean definition) { - this.definition = definition; - } - - public VarProcessor getProcessor() { - return processor; - } - - public void setProcessor(VarProcessor processor) { - this.processor = processor; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public boolean isClassdef() { - return classdef; - } - - public void setClassdef(boolean classdef) { - this.classdef = classdef; - } - - public boolean isStack() { - return stack; - } - - public void setStack(boolean stack) { - this.stack = stack; - } + version == ve.getVersion() && + InterpreterUtil.equalObjects(getVartype(), ve.getVartype()); // FIXME: vartype comparison redundant? + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public VarType getVartype() { + VarType vt = null; + if (processor != null) { + vt = processor.getVarType(new VarVersionPaar(index, version)); + } + + if (vt == null || (vartype != null && vartype.type != CodeConstants.TYPE_UNKNOWN)) { + vt = vartype; + } + + return vt == null ? VarType.VARTYPE_UNKNOWN : vt; + } + + public void setVartype(VarType vartype) { + this.vartype = vartype; + } + + public boolean isDefinition() { + return definition; + } + + public void setDefinition(boolean definition) { + this.definition = definition; + } + + public VarProcessor getProcessor() { + return processor; + } + + public void setProcessor(VarProcessor processor) { + this.processor = processor; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public boolean isClassdef() { + return classdef; + } + + public void setClassdef(boolean classdef) { + this.classdef = classdef; + } + + public boolean isStack() { + return stack; + } + + public void setStack(boolean stack) { + this.stack = stack; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java index b35f801..1fc568f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java @@ -1,136 +1,136 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.sforms; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; -import org.jetbrains.java.decompiler.util.VBStyleCollection; - public class DirectGraph { - public VBStyleCollection<DirectNode, String> nodes = new VBStyleCollection<DirectNode, String>(); - - public DirectNode first; - - // exit, [source, destination] - public HashMap<String, List<FinallyPathWrapper>> mapShortRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); - - // exit, [source, destination] - public HashMap<String, List<FinallyPathWrapper>> mapLongRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); - - // negative if branches (recorded for handling of && and ||) - public HashMap<String, String> mapNegIfBranch = new HashMap<String, String>(); - - // nodes, that are exception exits of a finally block with monitor variable - public HashMap<String, String> mapFinallyMonitorExceptionPathExits = new HashMap<String, String>(); - - public void sortReversePostOrder() { - LinkedList<DirectNode> res = new LinkedList<DirectNode>(); - addToReversePostOrderListIterative(first, res); - - nodes.clear(); - for(DirectNode node: res) { - nodes.addWithKey(node, node.id); - } - } - - private void addToReversePostOrderListIterative(DirectNode root, List<DirectNode> lst) { - - LinkedList<DirectNode> stackNode = new LinkedList<DirectNode>(); - LinkedList<Integer> stackIndex = new LinkedList<Integer>(); - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - - stackNode.add(root); - stackIndex.add(0); - - while(!stackNode.isEmpty()) { - - DirectNode node = stackNode.getLast(); - int index = stackIndex.removeLast(); - - setVisited.add(node); - - for(;index<node.succs.size();index++) { - DirectNode succ = node.succs.get(index); - - if(!setVisited.contains(succ)) { - stackIndex.add(index+1); - - stackNode.add(succ); - stackIndex.add(0); - - break; - } - } - - if(index == node.succs.size()) { - lst.add(0, node); - - stackNode.removeLast(); - } - } - - } - - - public boolean iterateExprents(ExprentIterator iter) { - - LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); - stack.add(first); - - HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); - - while(!stack.isEmpty()) { - - DirectNode node = stack.removeFirst(); - - if(setVisited.contains(node)) { - continue; - } - setVisited.add(node); - - for(int i=0;i<node.exprents.size();i++) { - int res = iter.processExprent(node.exprents.get(i)); - - if(res == 1) { - return false; - } - - if(res == 2) { - node.exprents.remove(i); - i--; - } - } - - stack.addAll(node.succs); - } - - return true; - } - - public interface ExprentIterator { - // 0 - success, do nothing - // 1 - cancel iteration - // 2 - success, delete exprent - public int processExprent(Exprent exprent); - } + public VBStyleCollection<DirectNode, String> nodes = new VBStyleCollection<DirectNode, String>(); + + public DirectNode first; + + // exit, [source, destination] + public HashMap<String, List<FinallyPathWrapper>> mapShortRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); + + // exit, [source, destination] + public HashMap<String, List<FinallyPathWrapper>> mapLongRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); + + // negative if branches (recorded for handling of && and ||) + public HashMap<String, String> mapNegIfBranch = new HashMap<String, String>(); + + // nodes, that are exception exits of a finally block with monitor variable + public HashMap<String, String> mapFinallyMonitorExceptionPathExits = new HashMap<String, String>(); + + public void sortReversePostOrder() { + LinkedList<DirectNode> res = new LinkedList<DirectNode>(); + addToReversePostOrderListIterative(first, res); + + nodes.clear(); + for (DirectNode node : res) { + nodes.addWithKey(node, node.id); + } + } + + private void addToReversePostOrderListIterative(DirectNode root, List<DirectNode> lst) { + + LinkedList<DirectNode> stackNode = new LinkedList<DirectNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + stackNode.add(root); + stackIndex.add(0); + + while (!stackNode.isEmpty()) { + + DirectNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + for (; index < node.succs.size(); index++) { + DirectNode succ = node.succs.get(index); + + if (!setVisited.contains(succ)) { + stackIndex.add(index + 1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if (index == node.succs.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + } + + + public boolean iterateExprents(ExprentIterator iter) { + + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + stack.add(first); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + while (!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if (setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + for (int i = 0; i < node.exprents.size(); i++) { + int res = iter.processExprent(node.exprents.get(i)); + + if (res == 1) { + return false; + } + + if (res == 2) { + node.exprents.remove(i); + i--; + } + } + + stack.addAll(node.succs); + } + + return true; + } + + public interface ExprentIterator { + // 0 - success, do nothing + // 1 - cancel iteration + // 2 - success, delete exprent + public int processExprent(Exprent exprent); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java index ebad3eb..73303ec 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java @@ -1,68 +1,67 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.sforms; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import java.util.ArrayList; +import java.util.List; + public class DirectNode { - - public static final int NODE_DIRECT = 1; - public static final int NODE_TAIL = 2; - public static final int NODE_INIT = 3; - public static final int NODE_CONDITION = 4; - public static final int NODE_INCREMENT = 5; - public static final int NODE_TRY = 6; - - public int type; - - public String id; - - public BasicBlockStatement block; - - public Statement statement; - - public List<Exprent> exprents = new ArrayList<Exprent>(); - - public List<DirectNode> succs = new ArrayList<DirectNode>(); - - public List<DirectNode> preds = new ArrayList<DirectNode>(); - - public DirectNode(int type, Statement statement, String id) { - this.type = type; - this.statement = statement; - this.id = id; - } - - public DirectNode(int type, Statement statement, BasicBlockStatement block) { - this.type = type; - this.statement = statement; - - this.id = block.id.toString(); - this.block = block; - } - - @Override - public String toString() { - return id; - } - - + + public static final int NODE_DIRECT = 1; + public static final int NODE_TAIL = 2; + public static final int NODE_INIT = 3; + public static final int NODE_CONDITION = 4; + public static final int NODE_INCREMENT = 5; + public static final int NODE_TRY = 6; + + public int type; + + public String id; + + public BasicBlockStatement block; + + public Statement statement; + + public List<Exprent> exprents = new ArrayList<Exprent>(); + + public List<DirectNode> succs = new ArrayList<DirectNode>(); + + public List<DirectNode> preds = new ArrayList<DirectNode>(); + + public DirectNode(int type, Statement statement, String id) { + this.type = type; + this.statement = statement; + this.id = id; + } + + public DirectNode(int type, Statement statement, BasicBlockStatement block) { + this.type = type; + this.statement = statement; + + this.id = block.id.toString(); + this.block = block; + } + + @Override + public String toString() { + return id; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java index 9388073..1eb79e9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -1,562 +1,575 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.sforms; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; + +import java.util.*; +import java.util.Map.Entry; public class FlattenStatementsHelper { - // statement.id, node.id(direct), node.id(continue) - private HashMap<Integer, String[]> mapDestinationNodes = new HashMap<Integer, String[]>(); - - // node.id(source), statement.id(destination), edge type - private List<Edge> listEdges = new ArrayList<Edge>(); - - // node.id(exit), [node.id(source), statement.id(destination)] - public HashMap<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<String, List<String[]>>(); - - // node.id(exit), [node.id(source), statement.id(destination)] - public HashMap<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<String, List<String[]>>(); - - // positive if branches - public HashMap<String, String> mapPosIfBranch = new HashMap<String, String>(); - - - private DirectGraph graph; - - private RootStatement root; - - public DirectGraph buildDirectGraph(RootStatement root) { - - this.root = root; - - graph = new DirectGraph(); - - flattenStatement(); - - // dummy exit node - Statement dummyexit = root.getDummyExit(); - DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString()); - node.exprents = new ArrayList<Exprent>(); - graph.nodes.addWithKey(node, node.id); - mapDestinationNodes.put(dummyexit.id, new String[] {node.id, null}); - - setEdges(); - - graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]); - graph.sortReversePostOrder(); - - return graph; - } - - private void flattenStatement() { - - class StatementStackEntry { - public Statement statement; - public LinkedList<StackEntry> stackFinally; - public List<Exprent> tailExprents; - - public int statementIndex; - public int edgeIndex; - public List<StatEdge> succEdges; - - public StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) { - this.statement = statement; - this.stackFinally = stackFinally; - this.tailExprents = tailExprents; - } - } - - LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<StatementStackEntry>(); - - lstStackStatements.add(new StatementStackEntry(root, new LinkedList<StackEntry>(), null)); - - mainloop: - while(!lstStackStatements.isEmpty()) { - - StatementStackEntry statEntry = lstStackStatements.removeFirst(); - - Statement stat = statEntry.statement; - LinkedList<StackEntry> stackFinally = statEntry.stackFinally; - int statementBreakIndex = statEntry.statementIndex; - - DirectNode node = null, nd = null; - - List<StatEdge> lstSuccEdges = new ArrayList<StatEdge>(); - DirectNode sourcenode = null; - - if(statEntry.succEdges == null) { - - switch(stat.type) { - case Statement.TYPE_BASICBLOCK: - node = new DirectNode(DirectNode.NODE_DIRECT, stat, (BasicBlockStatement)stat); - if(stat.getExprents() != null) { - node.exprents = stat.getExprents(); - } - graph.nodes.putWithKey(node, node.id); - mapDestinationNodes.put(stat.id, new String[] {node.id, null}); - - lstSuccEdges.addAll(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)); - sourcenode = node; - - List<Exprent> tailExprentList = statEntry.tailExprents; - - if(tailExprentList != null) { - DirectNode tail = new DirectNode(DirectNode.NODE_TAIL, stat, stat.id+"_tail"); - tail.exprents = tailExprentList; - graph.nodes.putWithKey(tail, tail.id); - - mapDestinationNodes.put(-stat.id, new String[] {tail.id, null}); - listEdges.add(new Edge(node.id, -stat.id, StatEdge.TYPE_REGULAR)); - - sourcenode = tail; - } - - // 'if' statement: record positive branch - if(stat.getLastBasicType() == Statement.LASTBASICTYPE_IF) { - mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id.toString()); - } - - break; - case Statement.TYPE_CATCHALL: - case Statement.TYPE_TRYCATCH: - DirectNode firstnd = new DirectNode(DirectNode.NODE_TRY, stat, stat.id+"_try"); - - mapDestinationNodes.put(stat.id, new String[] {firstnd.id, null}); - graph.nodes.putWithKey(firstnd, firstnd.id); - - LinkedList<StatementStackEntry> lst = new LinkedList<StatementStackEntry>(); - - for(Statement st: stat.getStats()) { - listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR)); - - LinkedList<StackEntry> stack = stackFinally; - if(stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) { - stack = new LinkedList<StackEntry>(stackFinally); - - if(st == stat.getFirst()) { // catch head - stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE)); - } else { // handler - stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, StatEdge.TYPE_BREAK, - root.getDummyExit(), st, st, firstnd, firstnd, true)); - } - } - lst.add(new StatementStackEntry(st, stack, null)); - } - - lstStackStatements.addAll(0, lst); - break; - case Statement.TYPE_DO: - if(statementBreakIndex == 0) { - statEntry.statementIndex = 1; - lstStackStatements.addFirst(statEntry); - lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null)); - - continue mainloop; - } - - nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); - - DoStatement dostat = (DoStatement)stat; - int looptype = dostat.getLooptype(); - - if(looptype == DoStatement.LOOP_DO) { - mapDestinationNodes.put(stat.id, new String[] {nd.id, nd.id}); - break; - } - - lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge - - switch(looptype) { - case DoStatement.LOOP_WHILE: - case DoStatement.LOOP_DOWHILE: - node = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id+"_cond"); - node.exprents = dostat.getConditionExprentList(); - graph.nodes.putWithKey(node, node.id); - - listEdges.add(new Edge(node.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); - - if(looptype == DoStatement.LOOP_WHILE) { - mapDestinationNodes.put(stat.id, new String[] {node.id, node.id}); - } else { - mapDestinationNodes.put(stat.id, new String[] {nd.id, node.id}); - - boolean found = false; - for(Edge edge: listEdges) { - if(edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { - found = true; - break; - } - } - if(!found) { - listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); - } - } - sourcenode = node; - break; - case DoStatement.LOOP_FOR: - DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id+"_init"); - if(dostat.getInitExprent() != null) { - nodeinit.exprents = dostat.getInitExprentList(); - } - graph.nodes.putWithKey(nodeinit, nodeinit.id); - - DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id+"_cond"); - nodecond.exprents = dostat.getConditionExprentList(); - graph.nodes.putWithKey(nodecond, nodecond.id); - - DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id+"_inc"); - nodeinc.exprents = dostat.getIncExprentList(); - graph.nodes.putWithKey(nodeinc, nodeinc.id); - - mapDestinationNodes.put(stat.id, new String[] {nodeinit.id, nodeinc.id}); - mapDestinationNodes.put(-stat.id, new String[] {nodecond.id, null}); - - listEdges.add(new Edge(nodecond.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); - listEdges.add(new Edge(nodeinit.id, -stat.id, StatEdge.TYPE_REGULAR)); - listEdges.add(new Edge(nodeinc.id, -stat.id, StatEdge.TYPE_REGULAR)); - - boolean found = false; - for(Edge edge: listEdges) { - if(edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { - found = true; - break; - } - } - if(!found) { - listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); - } - - sourcenode = nodecond; - } - break; - case Statement.TYPE_SYNCRONIZED: - case Statement.TYPE_SWITCH: - case Statement.TYPE_IF: - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_ROOT: - int statsize = stat.getStats().size(); - if(stat.type == Statement.TYPE_SYNCRONIZED) { - statsize = 2; // exclude the handler if synchronized - } - - if(statementBreakIndex <= statsize) { - List<Exprent> tailexprlst = null; - - switch(stat.type) { - case Statement.TYPE_SYNCRONIZED: - tailexprlst = ((SynchronizedStatement)stat).getHeadexprentList(); - break; - case Statement.TYPE_SWITCH: - tailexprlst = ((SwitchStatement)stat).getHeadexprentList(); - break; - case Statement.TYPE_IF: - tailexprlst = ((IfStatement)stat).getHeadexprentList(); - } - - for(int i=statementBreakIndex; i < statsize; i++) { - statEntry.statementIndex = i+1; - lstStackStatements.addFirst(statEntry); - lstStackStatements.addFirst( - new StatementStackEntry(stat.getStats().get(i), stackFinally, - (i==0 && tailexprlst != null && tailexprlst.get(0)!=null)?tailexprlst:null)); - - continue mainloop; - } - - node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); - mapDestinationNodes.put(stat.id, new String[] {node.id, null}); - - if(stat.type == Statement.TYPE_IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) { - lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge - sourcenode = tailexprlst.get(0)==null?node:graph.nodes.getWithKey(node.id+"_tail"); - } - } - } - } - - // no successor edges - if(sourcenode != null) { - - if(statEntry.succEdges != null) { - lstSuccEdges = statEntry.succEdges; - } - - for(int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) { - - StatEdge edge = lstSuccEdges.get(edgeindex); - - LinkedList<StackEntry> stack = new LinkedList<StackEntry>(stackFinally); - - int edgetype = edge.getType(); - Statement destination = edge.getDestination(); - - DirectNode finallyShortRangeSource = sourcenode; - DirectNode finallyLongRangeSource = sourcenode; - Statement finallyShortRangeEntry = null; - Statement finallyLongRangeEntry = null; - - boolean isFinallyMonitorExceptionPath = false; - - boolean isFinallyExit = false; - - for(;;) { - - StackEntry entry = null; - if(!stack.isEmpty()) { - entry = stack.getLast(); - } - - boolean created = true; - - if(entry == null) { - saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); - } else { - - CatchAllStatement catchall = entry.catchstatement; - - if(entry.state) { // finally handler statement - if(edgetype == StatEdge.TYPE_FINALLYEXIT) { - - stack.removeLast(); - destination = entry.destination; - edgetype = entry.edgetype; - - finallyShortRangeSource = entry.finallyShortRangeSource; - finallyLongRangeSource = entry.finallyLongRangeSource; - finallyShortRangeEntry = entry.finallyShortRangeEntry; - finallyLongRangeEntry = entry.finallyLongRangeEntry; - - isFinallyExit = true; - isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath; - - created = false; - } else { - if(!catchall.containsStatementStrict(destination)) { - stack.removeLast(); - created = false; - } else { - saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); - } - } - } else { // finally protected try statement - if(!catchall.containsStatementStrict(destination)) { - saveEdge(sourcenode, catchall.getHandler(), StatEdge.TYPE_REGULAR, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); - - stack.removeLast(); - stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(), finallyLongRangeEntry==null?catchall.getHandler():finallyLongRangeEntry, - sourcenode, finallyLongRangeSource, false)); - - statEntry.edgeIndex = edgeindex+1; - statEntry.succEdges = lstSuccEdges; - lstStackStatements.addFirst(statEntry); - lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null)); - - continue mainloop; - } else { - saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); - } - } - } - - if(created) { - break; - } - } - } - } - - } - - } - - private void saveEdge(DirectNode sourcenode, Statement destination, int edgetype, DirectNode finallyShortRangeSource, - DirectNode finallyLongRangeSource, Statement finallyShortRangeEntry, Statement finallyLongRangeEntry, boolean isFinallyMonitorExceptionPath) { - - if(edgetype != StatEdge.TYPE_FINALLYEXIT) { - listEdges.add(new Edge(sourcenode.id, destination.id, edgetype)); - } - - if(finallyShortRangeSource != null) { - - boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE); - - List<String[]> lst = mapShortRangeFinallyPathIds.get(sourcenode.id); - if(lst == null) { - mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); - } - lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), isFinallyMonitorExceptionPath?"1":null, isContinueEdge?"1":null}); - - lst = mapLongRangeFinallyPathIds.get(sourcenode.id); - if(lst == null) { - mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); - } - lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(), isContinueEdge?"1":null}); - } - - } - - private void setEdges() { - - for(Edge edge : listEdges) { - - String sourceid = edge.sourceid; - Integer statid = edge.statid; - - DirectNode source = graph.nodes.getWithKey(sourceid); - - DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype==StatEdge.TYPE_CONTINUE?1:0]); - - if(!source.succs.contains(dest)) { - source.succs.add(dest); - } - - if(!dest.preds.contains(source)) { - dest.preds.add(source); - } - - if(mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) { - graph.mapNegIfBranch.put(sourceid, dest.id); - } - } - - for(int i=0;i<2;i++) { - for(Entry<String, List<String[]>> ent : (i==0?mapShortRangeFinallyPathIds:mapLongRangeFinallyPathIds).entrySet()) { - - List<FinallyPathWrapper> newLst = new ArrayList<FinallyPathWrapper>(); - - List<String[]> lst = ent.getValue(); - for(String[] arr : lst) { - - boolean isContinueEdge = arr[i==0?4:3] != null; - - DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge?1:0]); - DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]); - - newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id)); - - if(i==0 && arr[3] != null) { - graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id); - } - } - - if(!newLst.isEmpty()) { - (i==0?graph.mapShortRangeFinallyPaths:graph.mapLongRangeFinallyPaths).put(ent.getKey(), - new ArrayList<FinallyPathWrapper>(new HashSet<FinallyPathWrapper>(newLst))); - } - } - } - - } - - public HashMap<Integer, String[]> getMapDestinationNodes() { - return mapDestinationNodes; - } - - public static class FinallyPathWrapper { - public String source; - public String destination; - public String entry; - - private FinallyPathWrapper(String source, String destination, String entry) { - this.source = source; - this.destination = destination; - this.entry = entry; - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FinallyPathWrapper)) return false; + // statement.id, node.id(direct), node.id(continue) + private HashMap<Integer, String[]> mapDestinationNodes = new HashMap<Integer, String[]>(); + + // node.id(source), statement.id(destination), edge type + private List<Edge> listEdges = new ArrayList<Edge>(); + + // node.id(exit), [node.id(source), statement.id(destination)] + public HashMap<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<String, List<String[]>>(); + + // node.id(exit), [node.id(source), statement.id(destination)] + public HashMap<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<String, List<String[]>>(); + + // positive if branches + public HashMap<String, String> mapPosIfBranch = new HashMap<String, String>(); + + + private DirectGraph graph; + + private RootStatement root; + + public DirectGraph buildDirectGraph(RootStatement root) { + + this.root = root; + + graph = new DirectGraph(); + + flattenStatement(); + + // dummy exit node + Statement dummyexit = root.getDummyExit(); + DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString()); + node.exprents = new ArrayList<Exprent>(); + graph.nodes.addWithKey(node, node.id); + mapDestinationNodes.put(dummyexit.id, new String[]{node.id, null}); + + setEdges(); + + graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]); + graph.sortReversePostOrder(); + + return graph; + } + + private void flattenStatement() { + + class StatementStackEntry { + public Statement statement; + public LinkedList<StackEntry> stackFinally; + public List<Exprent> tailExprents; + + public int statementIndex; + public int edgeIndex; + public List<StatEdge> succEdges; + + public StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) { + this.statement = statement; + this.stackFinally = stackFinally; + this.tailExprents = tailExprents; + } + } + + LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<StatementStackEntry>(); + + lstStackStatements.add(new StatementStackEntry(root, new LinkedList<StackEntry>(), null)); + + mainloop: + while (!lstStackStatements.isEmpty()) { + + StatementStackEntry statEntry = lstStackStatements.removeFirst(); + + Statement stat = statEntry.statement; + LinkedList<StackEntry> stackFinally = statEntry.stackFinally; + int statementBreakIndex = statEntry.statementIndex; + + DirectNode node = null, nd = null; + + List<StatEdge> lstSuccEdges = new ArrayList<StatEdge>(); + DirectNode sourcenode = null; + + if (statEntry.succEdges == null) { + + switch (stat.type) { + case Statement.TYPE_BASICBLOCK: + node = new DirectNode(DirectNode.NODE_DIRECT, stat, (BasicBlockStatement)stat); + if (stat.getExprents() != null) { + node.exprents = stat.getExprents(); + } + graph.nodes.putWithKey(node, node.id); + mapDestinationNodes.put(stat.id, new String[]{node.id, null}); + + lstSuccEdges.addAll(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)); + sourcenode = node; + + List<Exprent> tailExprentList = statEntry.tailExprents; + + if (tailExprentList != null) { + DirectNode tail = new DirectNode(DirectNode.NODE_TAIL, stat, stat.id + "_tail"); + tail.exprents = tailExprentList; + graph.nodes.putWithKey(tail, tail.id); + + mapDestinationNodes.put(-stat.id, new String[]{tail.id, null}); + listEdges.add(new Edge(node.id, -stat.id, StatEdge.TYPE_REGULAR)); + + sourcenode = tail; + } + + // 'if' statement: record positive branch + if (stat.getLastBasicType() == Statement.LASTBASICTYPE_IF) { + mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id.toString()); + } + + break; + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + DirectNode firstnd = new DirectNode(DirectNode.NODE_TRY, stat, stat.id + "_try"); + + mapDestinationNodes.put(stat.id, new String[]{firstnd.id, null}); + graph.nodes.putWithKey(firstnd, firstnd.id); + + LinkedList<StatementStackEntry> lst = new LinkedList<StatementStackEntry>(); + + for (Statement st : stat.getStats()) { + listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR)); + + LinkedList<StackEntry> stack = stackFinally; + if (stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) { + stack = new LinkedList<StackEntry>(stackFinally); + + if (st == stat.getFirst()) { // catch head + stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE)); + } + else { // handler + stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, StatEdge.TYPE_BREAK, + root.getDummyExit(), st, st, firstnd, firstnd, true)); + } + } + lst.add(new StatementStackEntry(st, stack, null)); + } + + lstStackStatements.addAll(0, lst); + break; + case Statement.TYPE_DO: + if (statementBreakIndex == 0) { + statEntry.statementIndex = 1; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null)); + + continue mainloop; + } + + nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); + + DoStatement dostat = (DoStatement)stat; + int looptype = dostat.getLooptype(); + + if (looptype == DoStatement.LOOP_DO) { + mapDestinationNodes.put(stat.id, new String[]{nd.id, nd.id}); + break; + } + + lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge + + switch (looptype) { + case DoStatement.LOOP_WHILE: + case DoStatement.LOOP_DOWHILE: + node = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond"); + node.exprents = dostat.getConditionExprentList(); + graph.nodes.putWithKey(node, node.id); + + listEdges.add(new Edge(node.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); + + if (looptype == DoStatement.LOOP_WHILE) { + mapDestinationNodes.put(stat.id, new String[]{node.id, node.id}); + } + else { + mapDestinationNodes.put(stat.id, new String[]{nd.id, node.id}); + + boolean found = false; + for (Edge edge : listEdges) { + if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { + found = true; + break; + } + } + if (!found) { + listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); + } + } + sourcenode = node; + break; + case DoStatement.LOOP_FOR: + DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id + "_init"); + if (dostat.getInitExprent() != null) { + nodeinit.exprents = dostat.getInitExprentList(); + } + graph.nodes.putWithKey(nodeinit, nodeinit.id); + + DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond"); + nodecond.exprents = dostat.getConditionExprentList(); + graph.nodes.putWithKey(nodecond, nodecond.id); + + DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id + "_inc"); + nodeinc.exprents = dostat.getIncExprentList(); + graph.nodes.putWithKey(nodeinc, nodeinc.id); + + mapDestinationNodes.put(stat.id, new String[]{nodeinit.id, nodeinc.id}); + mapDestinationNodes.put(-stat.id, new String[]{nodecond.id, null}); + + listEdges.add(new Edge(nodecond.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); + listEdges.add(new Edge(nodeinit.id, -stat.id, StatEdge.TYPE_REGULAR)); + listEdges.add(new Edge(nodeinc.id, -stat.id, StatEdge.TYPE_REGULAR)); + + boolean found = false; + for (Edge edge : listEdges) { + if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { + found = true; + break; + } + } + if (!found) { + listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); + } + + sourcenode = nodecond; + } + break; + case Statement.TYPE_SYNCRONIZED: + case Statement.TYPE_SWITCH: + case Statement.TYPE_IF: + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_ROOT: + int statsize = stat.getStats().size(); + if (stat.type == Statement.TYPE_SYNCRONIZED) { + statsize = 2; // exclude the handler if synchronized + } + + if (statementBreakIndex <= statsize) { + List<Exprent> tailexprlst = null; + + switch (stat.type) { + case Statement.TYPE_SYNCRONIZED: + tailexprlst = ((SynchronizedStatement)stat).getHeadexprentList(); + break; + case Statement.TYPE_SWITCH: + tailexprlst = ((SwitchStatement)stat).getHeadexprentList(); + break; + case Statement.TYPE_IF: + tailexprlst = ((IfStatement)stat).getHeadexprentList(); + } + + for (int i = statementBreakIndex; i < statsize; i++) { + statEntry.statementIndex = i + 1; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst( + new StatementStackEntry(stat.getStats().get(i), stackFinally, + (i == 0 && tailexprlst != null && tailexprlst.get(0) != null) ? tailexprlst : null)); + + continue mainloop; + } + + node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); + mapDestinationNodes.put(stat.id, new String[]{node.id, null}); + + if (stat.type == Statement.TYPE_IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) { + lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge + sourcenode = tailexprlst.get(0) == null ? node : graph.nodes.getWithKey(node.id + "_tail"); + } + } + } + } + + // no successor edges + if (sourcenode != null) { + + if (statEntry.succEdges != null) { + lstSuccEdges = statEntry.succEdges; + } + + for (int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) { + + StatEdge edge = lstSuccEdges.get(edgeindex); + + LinkedList<StackEntry> stack = new LinkedList<StackEntry>(stackFinally); + + int edgetype = edge.getType(); + Statement destination = edge.getDestination(); + + DirectNode finallyShortRangeSource = sourcenode; + DirectNode finallyLongRangeSource = sourcenode; + Statement finallyShortRangeEntry = null; + Statement finallyLongRangeEntry = null; + + boolean isFinallyMonitorExceptionPath = false; + + boolean isFinallyExit = false; + + for (; ; ) { + + StackEntry entry = null; + if (!stack.isEmpty()) { + entry = stack.getLast(); + } + + boolean created = true; + + if (entry == null) { + saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource, + finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } + else { + + CatchAllStatement catchall = entry.catchstatement; + + if (entry.state) { // finally handler statement + if (edgetype == StatEdge.TYPE_FINALLYEXIT) { + + stack.removeLast(); + destination = entry.destination; + edgetype = entry.edgetype; + + finallyShortRangeSource = entry.finallyShortRangeSource; + finallyLongRangeSource = entry.finallyLongRangeSource; + finallyShortRangeEntry = entry.finallyShortRangeEntry; + finallyLongRangeEntry = entry.finallyLongRangeEntry; + + isFinallyExit = true; + isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath; + + created = false; + } + else { + if (!catchall.containsStatementStrict(destination)) { + stack.removeLast(); + created = false; + } + else { + saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource, + finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } + } + } + else { // finally protected try statement + if (!catchall.containsStatementStrict(destination)) { + saveEdge(sourcenode, catchall.getHandler(), StatEdge.TYPE_REGULAR, isFinallyExit ? finallyShortRangeSource : null, + finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + + stack.removeLast(); + stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(), + finallyLongRangeEntry == null ? catchall.getHandler() : finallyLongRangeEntry, + sourcenode, finallyLongRangeSource, false)); + + statEntry.edgeIndex = edgeindex + 1; + statEntry.succEdges = lstSuccEdges; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null)); + + continue mainloop; + } + else { + saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource, + finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } + } + } + + if (created) { + break; + } + } + } + } + } + } + + private void saveEdge(DirectNode sourcenode, + Statement destination, + int edgetype, + DirectNode finallyShortRangeSource, + DirectNode finallyLongRangeSource, + Statement finallyShortRangeEntry, + Statement finallyLongRangeEntry, + boolean isFinallyMonitorExceptionPath) { + + if (edgetype != StatEdge.TYPE_FINALLYEXIT) { + listEdges.add(new Edge(sourcenode.id, destination.id, edgetype)); + } + + if (finallyShortRangeSource != null) { + + boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE); + + List<String[]> lst = mapShortRangeFinallyPathIds.get(sourcenode.id); + if (lst == null) { + mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); + } + lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), + isFinallyMonitorExceptionPath ? "1" : null, isContinueEdge ? "1" : null}); + + lst = mapLongRangeFinallyPathIds.get(sourcenode.id); + if (lst == null) { + mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); + } + lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(), + isContinueEdge ? "1" : null}); + } + } + + private void setEdges() { + + for (Edge edge : listEdges) { + + String sourceid = edge.sourceid; + Integer statid = edge.statid; + + DirectNode source = graph.nodes.getWithKey(sourceid); + + DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype == StatEdge.TYPE_CONTINUE ? 1 : 0]); + + if (!source.succs.contains(dest)) { + source.succs.add(dest); + } + + if (!dest.preds.contains(source)) { + dest.preds.add(source); + } + + if (mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) { + graph.mapNegIfBranch.put(sourceid, dest.id); + } + } + + for (int i = 0; i < 2; i++) { + for (Entry<String, List<String[]>> ent : (i == 0 ? mapShortRangeFinallyPathIds : mapLongRangeFinallyPathIds).entrySet()) { + + List<FinallyPathWrapper> newLst = new ArrayList<FinallyPathWrapper>(); + + List<String[]> lst = ent.getValue(); + for (String[] arr : lst) { + + boolean isContinueEdge = arr[i == 0 ? 4 : 3] != null; + + DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge ? 1 : 0]); + DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]); + + newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id)); + + if (i == 0 && arr[3] != null) { + graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id); + } + } + + if (!newLst.isEmpty()) { + (i == 0 ? graph.mapShortRangeFinallyPaths : graph.mapLongRangeFinallyPaths).put(ent.getKey(), + new ArrayList<FinallyPathWrapper>( + new HashSet<FinallyPathWrapper>(newLst))); + } + } + } + } + + public HashMap<Integer, String[]> getMapDestinationNodes() { + return mapDestinationNodes; + } + + public static class FinallyPathWrapper { + public String source; + public String destination; + public String entry; + + private FinallyPathWrapper(String source, String destination, String entry) { + this.source = source; + this.destination = destination; + this.entry = entry; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FinallyPathWrapper)) return false; FinallyPathWrapper fpw = (FinallyPathWrapper)o; - return (source+":"+destination+":"+entry).equals(fpw.source+":"+fpw.destination+":"+fpw.entry); + return (source + ":" + destination + ":" + entry).equals(fpw.source + ":" + fpw.destination + ":" + fpw.entry); } - @Override - public int hashCode() { - return (source+":"+destination+":"+entry).hashCode(); - } + @Override + public int hashCode() { + return (source + ":" + destination + ":" + entry).hashCode(); + } @Override - public String toString() { - return source + "->(" + entry + ")->" + destination; - } - } - - - private static class StackEntry { - - public CatchAllStatement catchstatement; - public boolean state; - public int edgetype; - public boolean isFinallyExceptionPath; - - public Statement destination; - public Statement finallyShortRangeEntry; - public Statement finallyLongRangeEntry; - public DirectNode finallyShortRangeSource; - public DirectNode finallyLongRangeSource; - - public StackEntry(CatchAllStatement catchstatement, boolean state, int edgetype, Statement destination, - Statement finallyShortRangeEntry, Statement finallyLongRangeEntry, DirectNode finallyShortRangeSource, DirectNode finallyLongRangeSource, boolean isFinallyExceptionPath) { - - this.catchstatement=catchstatement; - this.state=state; - this.edgetype=edgetype; - this.isFinallyExceptionPath = isFinallyExceptionPath; - - this.destination=destination; - this.finallyShortRangeEntry=finallyShortRangeEntry; - this.finallyLongRangeEntry=finallyLongRangeEntry; - this.finallyShortRangeSource=finallyShortRangeSource; - this.finallyLongRangeSource=finallyLongRangeSource; - } - - public StackEntry(CatchAllStatement catchstatement, boolean state) { - this(catchstatement, state, -1, null, null, null, null, null, false); - } - - } - - private static class Edge { - public String sourceid; - public Integer statid; - public int edgetype; - - public Edge(String sourceid, Integer statid, int edgetype) { - this.sourceid = sourceid; - this.statid = statid; - this.edgetype = edgetype; - } - } + public String toString() { + return source + "->(" + entry + ")->" + destination; + } + } + + + private static class StackEntry { + + public CatchAllStatement catchstatement; + public boolean state; + public int edgetype; + public boolean isFinallyExceptionPath; + + public Statement destination; + public Statement finallyShortRangeEntry; + public Statement finallyLongRangeEntry; + public DirectNode finallyShortRangeSource; + public DirectNode finallyLongRangeSource; + + public StackEntry(CatchAllStatement catchstatement, + boolean state, + int edgetype, + Statement destination, + Statement finallyShortRangeEntry, + Statement finallyLongRangeEntry, + DirectNode finallyShortRangeSource, + DirectNode finallyLongRangeSource, + boolean isFinallyExceptionPath) { + + this.catchstatement = catchstatement; + this.state = state; + this.edgetype = edgetype; + this.isFinallyExceptionPath = isFinallyExceptionPath; + + this.destination = destination; + this.finallyShortRangeEntry = finallyShortRangeEntry; + this.finallyLongRangeEntry = finallyLongRangeEntry; + this.finallyShortRangeSource = finallyShortRangeSource; + this.finallyLongRangeSource = finallyLongRangeSource; + } + + public StackEntry(CatchAllStatement catchstatement, boolean state) { + this(catchstatement, state, -1, null, null, null, null, null, false); + } + } + + private static class Edge { + public String sourceid; + public Integer statid; + public int edgetype; + + public Edge(String sourceid, Integer statid, int edgetype) { + this.sourceid = sourceid; + this.statid = statid; + this.edgetype = edgetype; + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java index e21f38a..5a6cb9d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java @@ -1,26 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.sforms; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -39,476 +33,498 @@ import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; -public class SSAConstructorSparseEx { - - // node id, var, version - private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); - - // node id, var, version (direct branch) - private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); - - // node id, var, version (negative branch) - private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); - - // node id, var, version - private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); - - // (var, version), version - private HashMap<VarVersionPaar, FastSparseSet<Integer>> phi = new HashMap<VarVersionPaar, FastSparseSet<Integer>>(); - - // var, version - private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); - - private List<VarVersionPaar> startVars = new ArrayList<VarVersionPaar>(); - - // set factory - private FastSparseSetFactory<Integer> factory; - - public void splitVariables(RootStatement root, StructMethod mt) { - - 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();} - - HashSet<Integer> setInit = new HashSet<Integer>(); - for(int i = 0; i < 64; i++) { - setInit.add(i); - } - factory = new FastSparseSetFactory<Integer>(setInit); - - SFormsFastMapDirect firstmap = createFirstMap(mt); - extraVarVersions.put(dgraph.first.id, firstmap); - - setCatchMaps(root, dgraph, flatthelper); - - HashSet<String> updated = new HashSet<String>(); - do { - // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - ssaStatements(dgraph, updated); - // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - } while (!updated.isEmpty()); - } - - private void ssaStatements(DirectGraph dgraph, HashSet<String> updated) { - - // try { - // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); - // } catch(Exception ex) {ex.printStackTrace();} - - for(DirectNode node : dgraph.nodes) { - -// if (node.id.endsWith("_inc")) { -// System.out.println(); -// -// try { -// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); -// } catch (Exception ex) { -// ex.printStackTrace(); -// } -// } - - updated.remove(node.id); - mergeInVarMaps(node, dgraph); - - SFormsFastMapDirect varmap = inVarVersions.get(node.id); - varmap = new SFormsFastMapDirect(varmap); - - SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] { varmap, null }; - - if (node.exprents != null) { - for(Exprent expr : node.exprents) { - processExprent(expr, varmaparr); - } - } - - if (varmaparr[1] == null) { - varmaparr[1] = varmaparr[0]; - } - - boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) - || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); - - if (this_updated) { - outVarVersions.put(node.id, varmaparr[0]); - if (dgraph.mapNegIfBranch.containsKey(node.id)) { - outNegVarVersions.put(node.id, varmaparr[1]); - } - - for(DirectNode nd : node.succs) { - updated.add(nd.id); - } - } - } - - } - - private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) { - - if (expr == null) { - return; - } - - VarExprent varassign = null; - boolean finished = false; - - switch (expr.type) { - case Exprent.EXPRENT_ASSIGNMENT: - AssignmentExprent assexpr = (AssignmentExprent) expr; - if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { - Exprent dest = assexpr.getLeft(); - if (dest.type == Exprent.EXPRENT_VAR) { - varassign = (VarExprent) dest; - } - } - break; - case Exprent.EXPRENT_FUNCTION: - FunctionExprent func = (FunctionExprent) expr; - switch (func.getFunctype()) { - case FunctionExprent.FUNCTION_IIF: - processExprent(func.getLstOperands().get(0), varmaparr); - - SFormsFastMapDirect varmapFalse; - if (varmaparr[1] == null) { - varmapFalse = new SFormsFastMapDirect(varmaparr[0]); - } else { - varmapFalse = varmaparr[1]; - varmaparr[1] = null; - } - - processExprent(func.getLstOperands().get(1), varmaparr); - - SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] { varmapFalse, null }; - processExprent(func.getLstOperands().get(2), varmaparrNeg); - - mergeMaps(varmaparr[0], varmaparrNeg[0]); - varmaparr[1] = null; - - finished = true; - break; - case FunctionExprent.FUNCTION_CADD: - processExprent(func.getLstOperands().get(0), varmaparr); - - SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[0]), null }; - - processExprent(func.getLstOperands().get(1), varmaparrAnd); - - // false map - varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]); - // true map - varmaparr[0] = varmaparrAnd[0]; - - finished = true; - break; - case FunctionExprent.FUNCTION_COR: - processExprent(func.getLstOperands().get(0), varmaparr); - - SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null }; - - processExprent(func.getLstOperands().get(1), varmaparrOr); - - // false map - varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; - // true map - varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); - - finished = true; - } - } - - if (finished) { - return; - } - - List<Exprent> lst = expr.getAllExprents(); - lst.remove(varassign); - - for(Exprent ex : lst) { - processExprent(ex, varmaparr); - } - - SFormsFastMapDirect varmap = varmaparr[0]; - - if (varassign != null) { - - Integer varindex = varassign.getIndex(); - - if (varassign.getVersion() == 0) { - // get next version - Integer nextver = getNextFreeVersion(varindex); - - // set version - varassign.setVersion(nextver); - - setCurrentVar(varmap, varindex, nextver); - } else { - setCurrentVar(varmap, varindex, varassign.getVersion()); - } - - } else if (expr.type == Exprent.EXPRENT_VAR) { - - VarExprent vardest = (VarExprent) expr; - Integer varindex = vardest.getIndex(); - FastSparseSet<Integer> vers = varmap.get(varindex); - - int cardinality = vers.getCardinality(); - if (cardinality == 1) { // == 1 - // set version - Integer it = vers.iterator().next(); - vardest.setVersion(it.intValue()); - } else if (cardinality == 2) { // size > 1 - Integer current_vers = vardest.getVersion(); - - VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers); - if (current_vers != 0 && phi.containsKey(currpaar)) { - setCurrentVar(varmap, varindex, current_vers); - // update phi node - phi.get(currpaar).union(vers); - } else { - // increase version - Integer nextver = getNextFreeVersion(varindex); - // set version - vardest.setVersion(nextver); - - setCurrentVar(varmap, varindex, nextver); - // create new phi node - phi.put(new VarVersionPaar(varindex, nextver), vers); - - } - } // 0 means uninitialized variable, which is impossible - } - } - - private Integer getNextFreeVersion(Integer var) { - Integer nextver = lastversion.get(var); - if (nextver == null) { - nextver = new Integer(1); - } else { - nextver = new Integer(nextver.intValue() + 1); - } - lastversion.put(var, nextver); - return nextver; - } - - private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { - - SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - - for(DirectNode pred : node.preds) { - SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); - if (mapNew.isEmpty()) { - mapNew = mapOut.getCopy(); - } else { - mergeMaps(mapNew, mapOut); - } - } - - if (extraVarVersions.containsKey(node.id)) { - SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); - if (mapNew.isEmpty()) { - mapNew = mapExtra.getCopy(); - } else { - mergeMaps(mapNew, mapExtra); - } - } - - inVarVersions.put(node.id, mapNew); - } - - private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { - - SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - - if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { - if (outNegVarVersions.containsKey(predid)) { - mapNew = outNegVarVersions.get(predid).getCopy(); - } - } else if (outVarVersions.containsKey(predid)) { - mapNew = outVarVersions.get(predid).getCopy(); - } - - boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); - - if (isFinallyExit && !mapNew.isEmpty()) { - - SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); - - SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); - - String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); - boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); - - HashSet<String> setLongPathWrapper = new HashSet<String>(); - for(FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) { - setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); - } - - for(FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { - SFormsFastMapDirect map; - - boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); - - if (recFinally) { - // recursion - map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); - } else { - if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { - map = outNegVarVersions.get(finwrap.source); - } else { - map = outVarVersions.get(finwrap.source); - } - } - - // false path? - boolean isFalsePath = true; - - if (recFinally) { - isFalsePath = !finwrap.destination.equals(nodeid); - } else { - isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source); - } - - if (isFalsePath) { - mapNewTemp.complement(map); - } else { - if (mapTrueSource.isEmpty()) { - if (map != null) { - mapTrueSource = map.getCopy(); - } - } else { - mergeMaps(mapTrueSource, map); - } - } - } - - if (isExceptionMonitorExit) { - - mapNew = mapTrueSource; - - } else { +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; - mapNewTemp.union(mapTrueSource); +public class SSAConstructorSparseEx { - SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid); - if (oldInMap != null) { - mapNewTemp.union(oldInMap); - } - - mapNew.intersection(mapNewTemp); - } - } - - return mapNew; - } + // node id, var, version + private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); - private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { + // node id, var, version (direct branch) + private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); - if (map2 != null && !map2.isEmpty()) { - mapTo.union(map2); - } + // node id, var, version (negative branch) + private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); - return mapTo; - } + // node id, var, version + private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); - private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { + // (var, version), version + private HashMap<VarVersionPaar, FastSparseSet<Integer>> phi = new HashMap<VarVersionPaar, FastSparseSet<Integer>>(); - if (map1 == null) { - return map2 == null; - } else if (map2 == null) { - return false; - } + // var, version + private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); - if (map1.size() != map2.size()) { - return false; - } + private List<VarVersionPaar> startVars = new ArrayList<VarVersionPaar>(); - for(Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) { - if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { - return false; - } - } + // set factory + private FastSparseSetFactory<Integer> factory; - return true; - } + public void splitVariables(RootStatement root, StructMethod mt) { - private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { - FastSparseSet<Integer> set = factory.spawnEmptySet(); - set.add(vers); - varmap.put(var, set); - } - - private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { - - SFormsFastMapDirect map; - - switch (stat.type) { - case Statement.TYPE_CATCHALL: - case Statement.TYPE_TRYCATCH: - - List<VarExprent> lstVars; - if (stat.type == Statement.TYPE_CATCHALL) { - lstVars = ((CatchAllStatement) stat).getVars(); - } else { - lstVars = ((CatchStatement) stat).getVars(); - } - - for(int i = 1; i < stat.getStats().size(); i++) { - int varindex = lstVars.get(i - 1).getIndex(); - int version = getNextFreeVersion(varindex); // == 1 + 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();} + + HashSet<Integer> setInit = new HashSet<Integer>(); + for (int i = 0; i < 64; i++) { + setInit.add(i); + } + factory = new FastSparseSetFactory<Integer>(setInit); + + SFormsFastMapDirect firstmap = createFirstMap(mt); + extraVarVersions.put(dgraph.first.id, firstmap); + + setCatchMaps(root, dgraph, flatthelper); + + HashSet<String> updated = new HashSet<String>(); + do { + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + ssaStatements(dgraph, updated); + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + } + while (!updated.isEmpty()); + } + + private void ssaStatements(DirectGraph dgraph, HashSet<String> updated) { - map = new SFormsFastMapDirect(); - setCurrentVar(map, varindex, version); + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} - extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); - startVars.add(new VarVersionPaar(varindex, version)); - } - } + for (DirectNode node : dgraph.nodes) { - for(Statement st : stat.getStats()) { - setCatchMaps(st, dgraph, flatthelper); - } - } + // if (node.id.endsWith("_inc")) { + // System.out.println(); + // + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); + // } catch (Exception ex) { + // ex.printStackTrace(); + // } + // } - private SFormsFastMapDirect createFirstMap(StructMethod mt) { + updated.remove(node.id); + mergeInVarMaps(node, dgraph); - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + SFormsFastMapDirect varmap = inVarVersions.get(node.id); + varmap = new SFormsFastMapDirect(varmap); - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null}; - int paramcount = md.params.length + (thisvar ? 1 : 0); + if (node.exprents != null) { + for (Exprent expr : node.exprents) { + processExprent(expr, varmaparr); + } + } - int varindex = 0; - SFormsFastMapDirect map = new SFormsFastMapDirect(); - for(int i = 0; i < paramcount; i++) { - int version = getNextFreeVersion(varindex); // == 1 + if (varmaparr[1] == null) { + varmaparr[1] = varmaparr[0]; + } - FastSparseSet<Integer> set = factory.spawnEmptySet(); - set.add(version); - map.put(varindex, set); - startVars.add(new VarVersionPaar(varindex, version)); + boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) + || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); - if (thisvar) { - if (i == 0) { - varindex++; - } else { - varindex += md.params[i - 1].stack_size; - } - } else { - varindex += md.params[i].stack_size; - } - } + if (this_updated) { + outVarVersions.put(node.id, varmaparr[0]); + if (dgraph.mapNegIfBranch.containsKey(node.id)) { + outNegVarVersions.put(node.id, varmaparr[1]); + } - return map; - } + for (DirectNode nd : node.succs) { + updated.add(nd.id); + } + } + } + } - public HashMap<VarVersionPaar, FastSparseSet<Integer>> getPhi() { - return phi; - } + private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) { - public List<VarVersionPaar> getStartVars() { - return startVars; - } + if (expr == null) { + return; + } + + VarExprent varassign = null; + boolean finished = false; + + switch (expr.type) { + case Exprent.EXPRENT_ASSIGNMENT: + AssignmentExprent assexpr = (AssignmentExprent)expr; + if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + Exprent dest = assexpr.getLeft(); + if (dest.type == Exprent.EXPRENT_VAR) { + varassign = (VarExprent)dest; + } + } + break; + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)expr; + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_IIF: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect varmapFalse; + if (varmaparr[1] == null) { + varmapFalse = new SFormsFastMapDirect(varmaparr[0]); + } + else { + varmapFalse = varmaparr[1]; + varmaparr[1] = null; + } + + processExprent(func.getLstOperands().get(1), varmaparr); + + SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null}; + processExprent(func.getLstOperands().get(2), varmaparrNeg); + + mergeMaps(varmaparr[0], varmaparrNeg[0]); + varmaparr[1] = null; + + finished = true; + break; + case FunctionExprent.FUNCTION_CADD: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrAnd); + + // false map + varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]); + // true map + varmaparr[0] = varmaparrAnd[0]; + + finished = true; + break; + case FunctionExprent.FUNCTION_COR: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect[] varmaparrOr = + new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrOr); + + // false map + varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; + // true map + varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); + + finished = true; + } + } + + if (finished) { + return; + } + + List<Exprent> lst = expr.getAllExprents(); + lst.remove(varassign); + + for (Exprent ex : lst) { + processExprent(ex, varmaparr); + } + + SFormsFastMapDirect varmap = varmaparr[0]; + + if (varassign != null) { + + Integer varindex = varassign.getIndex(); + + if (varassign.getVersion() == 0) { + // get next version + Integer nextver = getNextFreeVersion(varindex); + + // set version + varassign.setVersion(nextver); + + setCurrentVar(varmap, varindex, nextver); + } + else { + setCurrentVar(varmap, varindex, varassign.getVersion()); + } + } + else if (expr.type == Exprent.EXPRENT_VAR) { + + VarExprent vardest = (VarExprent)expr; + Integer varindex = vardest.getIndex(); + FastSparseSet<Integer> vers = varmap.get(varindex); + + int cardinality = vers.getCardinality(); + if (cardinality == 1) { // == 1 + // set version + Integer it = vers.iterator().next(); + vardest.setVersion(it.intValue()); + } + else if (cardinality == 2) { // size > 1 + Integer current_vers = vardest.getVersion(); + + VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers); + if (current_vers != 0 && phi.containsKey(currpaar)) { + setCurrentVar(varmap, varindex, current_vers); + // update phi node + phi.get(currpaar).union(vers); + } + else { + // increase version + Integer nextver = getNextFreeVersion(varindex); + // set version + vardest.setVersion(nextver); + + setCurrentVar(varmap, varindex, nextver); + // create new phi node + phi.put(new VarVersionPaar(varindex, nextver), vers); + } + } // 0 means uninitialized variable, which is impossible + } + } + + private Integer getNextFreeVersion(Integer var) { + Integer nextver = lastversion.get(var); + if (nextver == null) { + nextver = new Integer(1); + } + else { + nextver = new Integer(nextver.intValue() + 1); + } + lastversion.put(var, nextver); + return nextver; + } + + private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + for (DirectNode pred : node.preds) { + SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); + if (mapNew.isEmpty()) { + mapNew = mapOut.getCopy(); + } + else { + mergeMaps(mapNew, mapOut); + } + } + + if (extraVarVersions.containsKey(node.id)) { + SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); + if (mapNew.isEmpty()) { + mapNew = mapExtra.getCopy(); + } + else { + mergeMaps(mapNew, mapExtra); + } + } + + inVarVersions.put(node.id, mapNew); + } + + private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { + if (outNegVarVersions.containsKey(predid)) { + mapNew = outNegVarVersions.get(predid).getCopy(); + } + } + else if (outVarVersions.containsKey(predid)) { + mapNew = outVarVersions.get(predid).getCopy(); + } + + boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); + + if (isFinallyExit && !mapNew.isEmpty()) { + + SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); + + SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); + + String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); + boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); + + HashSet<String> setLongPathWrapper = new HashSet<String>(); + for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) { + setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); + } + + for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { + SFormsFastMapDirect map; + + boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); + + if (recFinally) { + // recursion + map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); + } + else { + if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { + map = outNegVarVersions.get(finwrap.source); + } + else { + map = outVarVersions.get(finwrap.source); + } + } + + // false path? + boolean isFalsePath = true; + + if (recFinally) { + isFalsePath = !finwrap.destination.equals(nodeid); + } + else { + isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source); + } + + if (isFalsePath) { + mapNewTemp.complement(map); + } + else { + if (mapTrueSource.isEmpty()) { + if (map != null) { + mapTrueSource = map.getCopy(); + } + } + else { + mergeMaps(mapTrueSource, map); + } + } + } + + if (isExceptionMonitorExit) { + + mapNew = mapTrueSource; + } + else { + + mapNewTemp.union(mapTrueSource); + + SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid); + if (oldInMap != null) { + mapNewTemp.union(oldInMap); + } + + mapNew.intersection(mapNewTemp); + } + } + + return mapNew; + } + + private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { + + if (map2 != null && !map2.isEmpty()) { + mapTo.union(map2); + } + + return mapTo; + } + + private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { + + if (map1 == null) { + return map2 == null; + } + else if (map2 == null) { + return false; + } + + if (map1.size() != map2.size()) { + return false; + } + + for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) { + if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { + return false; + } + } + + return true; + } + + private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(vers); + varmap.put(var, set); + } + + private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { + + SFormsFastMapDirect map; + + switch (stat.type) { + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + + List<VarExprent> lstVars; + if (stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)stat).getVars(); + } + else { + lstVars = ((CatchStatement)stat).getVars(); + } + + for (int i = 1; i < stat.getStats().size(); i++) { + int varindex = lstVars.get(i - 1).getIndex(); + int version = getNextFreeVersion(varindex); // == 1 + + map = new SFormsFastMapDirect(); + setCurrentVar(map, varindex, version); + + extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); + startVars.add(new VarVersionPaar(varindex, version)); + } + } + + for (Statement st : stat.getStats()) { + setCatchMaps(st, dgraph, flatthelper); + } + } + + private SFormsFastMapDirect createFirstMap(StructMethod mt) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = md.params.length + (thisvar ? 1 : 0); + int varindex = 0; + SFormsFastMapDirect map = new SFormsFastMapDirect(); + for (int i = 0; i < paramcount; i++) { + int version = getNextFreeVersion(varindex); // == 1 + + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(version); + map.put(varindex, set); + startVars.add(new VarVersionPaar(varindex, version)); + + if (thisvar) { + if (i == 0) { + varindex++; + } + else { + varindex += md.params[i - 1].stack_size; + } + } + else { + varindex += md.params[i].stack_size; + } + } + + return map; + } + + public HashMap<VarVersionPaar, FastSparseSet<Integer>> getPhi() { + return phi; + } + + public List<VarVersionPaar> getStartVars() { + return startVars; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java index a59780f..596da31 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java @@ -1,37 +1,24 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.sforms; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; @@ -39,795 +26,820 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.FastSparseSetFactory; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; import org.jetbrains.java.decompiler.util.VBStyleCollection; -import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; public class SSAUConstructorSparseEx { - - // node id, var, version - private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); - //private HashMap<String, HashMap<Integer, FastSet<Integer>>> inVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); - - // node id, var, version (direct branch) - private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); - //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); - - // node id, var, version (negative branch) - private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); - //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outNegVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); - - // node id, var, version - private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); - //private HashMap<String, HashMap<Integer, FastSet<Integer>>> extraVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); - - // (var, version), version - private HashMap<VarVersionPaar, HashSet<Integer>> phi = new HashMap<VarVersionPaar, HashSet<Integer>>(); - - // var, version - private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); - - // version, protected ranges (catch, finally) - private HashMap<VarVersionPaar, Integer> mapVersionFirstRange = new HashMap<VarVersionPaar, Integer>(); - - // version, version - private HashMap<VarVersionPaar, VarVersionPaar> phantomppnodes = new HashMap<VarVersionPaar, VarVersionPaar>(); // ++ and -- - - // node.id, version, version - private HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>> phantomexitnodes = new HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>>(); // finally exits - - // versions memory dependencies - private VarVersionsGraph ssuversions = new VarVersionsGraph(); - - // field access vars (exprent id, var id) - private HashMap<Integer, Integer> mapFieldVars = new HashMap<Integer, Integer>(); - - // field access counter - private int fieldvarcounter = -1; - - // set factory - private FastSparseSetFactory<Integer> factory; - - public void splitVariables(RootStatement root, StructMethod mt) { - - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); - - HashSet<Integer> setInit = new HashSet<Integer>(); - for(int i=0;i<64;i++) { - setInit.add(i); - } - factory = new FastSparseSetFactory<Integer>(setInit); - - extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root)); - - setCatchMaps(root, dgraph, flatthelper); - -// try { -// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); -// } catch(Exception ex) {ex.printStackTrace();} - - HashSet<String> updated = new HashSet<String>(); - do { -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - ssaStatements(dgraph, updated, false); -// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); - } while(!updated.isEmpty()); - - - ssaStatements(dgraph, updated, true); - - ssuversions.initDominators(); - } - - private void ssaStatements(DirectGraph dgraph, HashSet<String> updated, boolean calcLiveVars) { - - for(DirectNode node: dgraph.nodes) { - - updated.remove(node.id); - mergeInVarMaps(node, dgraph); - - SFormsFastMapDirect varmap = new SFormsFastMapDirect(inVarVersions.get(node.id)); - - SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] {varmap, null}; - - if(node.exprents != null) { - for(Exprent expr: node.exprents) { - processExprent(expr, varmaparr, node.statement, calcLiveVars); - } - } - - if(varmaparr[1] == null) { - varmaparr[1] = varmaparr[0]; - } - - // quick solution: 'dummy' field variables should not cross basic block borders (otherwise problems e.g. with finally loops - usage without assignment in a loop) - // For the full solution consider adding a dummy assignment at the entry point of the method - boolean allow_field_propagation = node.succs.isEmpty() || (node.succs.size() == 1 && node.succs.get(0).preds.size() == 1); - - if(!allow_field_propagation && varmaparr[0] != null) { - varmaparr[0].removeAllFields(); - varmaparr[1].removeAllFields(); - } - - boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) - || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); - - if(this_updated) { - - outVarVersions.put(node.id, varmaparr[0]); - if(dgraph.mapNegIfBranch.containsKey(node.id)) { - outNegVarVersions.put(node.id, varmaparr[1]); - } - - for(DirectNode nd: node.succs) { - updated.add(nd.id); - } - } - } - - } - - - private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, boolean calcLiveVars) { - - if(expr == null) { - return; - } - - - VarExprent varassign = null; - boolean finished = false; - - switch(expr.type) { - case Exprent.EXPRENT_ASSIGNMENT: - AssignmentExprent assexpr = (AssignmentExprent)expr; - if(assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { - Exprent dest = assexpr.getLeft(); - if(dest.type == Exprent.EXPRENT_VAR) { - varassign = (VarExprent)dest; - } - } - break; - case Exprent.EXPRENT_FUNCTION: - FunctionExprent func = (FunctionExprent)expr; - switch(func.getFunctype()) { - case FunctionExprent.FUNCTION_IIF: - processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); - - SFormsFastMapDirect varmapFalse; - if(varmaparr[1] == null) { - varmapFalse = new SFormsFastMapDirect(varmaparr[0]); - } else { - varmapFalse = varmaparr[1]; - varmaparr[1] = null; - } - - processExprent(func.getLstOperands().get(1), varmaparr, stat, calcLiveVars); - - SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] {varmapFalse, null}; - processExprent(func.getLstOperands().get(2), varmaparrNeg, stat, calcLiveVars); - - mergeMaps(varmaparr[0], varmaparrNeg[0]); - varmaparr[1] = null; - - finished = true; - break; - case FunctionExprent.FUNCTION_CADD: - processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); - - SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[0]), null}; - - processExprent(func.getLstOperands().get(1), varmaparrAnd, stat, calcLiveVars); - - // false map - varmaparr[1] = mergeMaps(varmaparr[varmaparr[1]==null?0:1], varmaparrAnd[varmaparrAnd[1]==null?0:1]); - // true map - varmaparr[0] = varmaparrAnd[0]; - - finished = true; - break; - case FunctionExprent.FUNCTION_COR: - processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); - - SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[varmaparr[1]==null?0:1]), null}; - - processExprent(func.getLstOperands().get(1), varmaparrOr, stat, calcLiveVars); - - // false map - varmaparr[1] = varmaparrOr[varmaparrOr[1]==null?0:1]; - // true map - varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); - - finished = true; - } - } - - if(!finished) { - List<Exprent> lst = expr.getAllExprents(); - lst.remove(varassign); - - for(Exprent ex: lst) { - processExprent(ex, varmaparr, stat, calcLiveVars); - } - } - - - SFormsFastMapDirect varmap = varmaparr[0]; - - // field access - if(expr.type == Exprent.EXPRENT_FIELD) { - - int index; - if(mapFieldVars.containsKey(expr.id)) { - index = mapFieldVars.get(expr.id); - } else { - index = fieldvarcounter--; - mapFieldVars.put(expr.id, index); - - // ssu graph - ssuversions.createNode(new VarVersionPaar(index, 1)); - } - - setCurrentVar(varmap, index, 1); - - } else if(expr.type == Exprent.EXPRENT_INVOCATION || - (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || - (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || - expr.type == Exprent.EXPRENT_FUNCTION) { - - boolean ismmpp = true; - - if(expr.type == Exprent.EXPRENT_FUNCTION) { - - ismmpp = false; - - FunctionExprent fexpr = (FunctionExprent)expr; - if(fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { - if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { - ismmpp = true; - } - } - } - - if(ismmpp) { - varmap.removeAllFields(); - } - } - - - if(varassign != null) { - - Integer varindex = varassign.getIndex(); - - if(varassign.getVersion() == 0) { - // get next version - Integer nextver = getNextFreeVersion(varindex, stat); - - // set version - varassign.setVersion(nextver); - - // ssu graph - ssuversions.createNode(new VarVersionPaar(varindex, nextver)); - - setCurrentVar(varmap, varindex, nextver); - } else { - if(calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex.intValue(), varassign.getVersion()), varmap); - } - setCurrentVar(varmap, varindex, varassign.getVersion()); - } - } else if(expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function - FunctionExprent func = (FunctionExprent)expr; - - switch(func.getFunctype()) { - case FunctionExprent.FUNCTION_IMM: - case FunctionExprent.FUNCTION_MMI: - case FunctionExprent.FUNCTION_IPP: - case FunctionExprent.FUNCTION_PPI: - - if(func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)func.getLstOperands().get(0); - Integer varindex = var.getIndex(); - VarVersionPaar varpaar = new VarVersionPaar(varindex.intValue(), var.getVersion()); - - // ssu graph - VarVersionPaar phantomver = phantomppnodes.get(varpaar); - if(phantomver == null) { - // get next version - Integer nextver = getNextFreeVersion(varindex, null); - phantomver = new VarVersionPaar(varindex, nextver); - //ssuversions.createOrGetNode(phantomver); - ssuversions.createNode(phantomver); - - VarVersionNode vernode = ssuversions.nodes.getWithKey(varpaar); - - FastSparseSet<Integer> vers = factory.spawnEmptySet(); - if(vernode.preds.size() == 1) { - vers.add(vernode.preds.iterator().next().source.version); - } else { - for(VarVersionEdge edge: vernode.preds) { - vers.add(edge.source.preds.iterator().next().source.version); - } - } - vers.add(nextver); - createOrUpdatePhiNode(varpaar, vers, stat); - phantomppnodes.put(varpaar, phantomver); - } - if(calcLiveVars) { - varMapToGraph(varpaar, varmap); - } - setCurrentVar(varmap, varindex.intValue(), var.getVersion()); - } - - } - } else if(expr.type == Exprent.EXPRENT_VAR) { - - VarExprent vardest = (VarExprent)expr; - - Integer varindex = vardest.getIndex(); - Integer current_vers = vardest.getVersion(); - - FastSparseSet<Integer> vers = varmap.get(varindex); - - int cardinality = vers.getCardinality(); - if(cardinality == 1) { // size == 1 - if(current_vers.intValue() != 0) { - if(calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); - } - setCurrentVar(varmap, varindex, current_vers); - } else { - // split last version - Integer usever = getNextFreeVersion(varindex, stat); - - // set version - vardest.setVersion(usever); - setCurrentVar(varmap, varindex, usever); - - // ssu graph - Integer lastver = vers.iterator().next(); - VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(varindex, lastver)); - VarVersionNode usenode = ssuversions.createNode(new VarVersionPaar(varindex, usever)); - VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode); - prenode.addSuccessor(edge); - usenode.addPredecessor(edge); - } - } else if(cardinality == 2) { // size > 1 - - if(current_vers.intValue() != 0) { - if(calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); - } - setCurrentVar(varmap, varindex, current_vers); - } else { - // split version - Integer usever = getNextFreeVersion(varindex, stat); - // set version - vardest.setVersion(usever); - - // ssu node - ssuversions.createNode(new VarVersionPaar(varindex, usever)); - - setCurrentVar(varmap, varindex, usever); - - current_vers = usever; - } - - createOrUpdatePhiNode(new VarVersionPaar(varindex, current_vers), vers, stat); - - } // vers.size() == 0 means uninitialized variable, which is impossible - } - - - } - - private void createOrUpdatePhiNode(VarVersionPaar phivar, FastSparseSet<Integer> vers, Statement stat) { - - FastSparseSet<Integer> versCopy = vers.getCopy(); - HashSet<Integer> phiVers = new HashSet<Integer>(); - - // take into account the corresponding mm/pp node if existing - int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1; - - // ssu graph - VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar); - List<VarVersionEdge> lstPreds = new ArrayList<VarVersionEdge>(phinode.preds); - if(lstPreds.size() == 1) { - // not yet a phi node - VarVersionEdge edge = lstPreds.get(0); - edge.source.removeSuccessor(edge); - phinode.removePredecessor(edge); - } else { - for(VarVersionEdge edge: lstPreds) { - int verssrc = edge.source.preds.iterator().next().source.version; - if(!vers.contains(verssrc) && verssrc != ppvers) { - edge.source.removeSuccessor(edge); - phinode.removePredecessor(edge); - } else { - versCopy.remove(verssrc); - phiVers.add(verssrc); - } - } - } - - List<VarVersionNode> colnodes = new ArrayList<VarVersionNode>(); - List<VarVersionPaar> colpaars = new ArrayList<VarVersionPaar>(); - - for(Integer ver: versCopy) { - - VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(phivar.var, ver.intValue())); - - Integer tempver = getNextFreeVersion(phivar.var, stat); - - VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver.intValue()); - - colnodes.add(tempnode); - colpaars.add(new VarVersionPaar(phivar.var, tempver.intValue())); - - VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode); - - prenode.addSuccessor(edge); - tempnode.addPredecessor(edge); - - - edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, tempnode, phinode); - tempnode.addSuccessor(edge); - phinode.addPredecessor(edge); - - phiVers.add(tempver); - - } - - ssuversions.addNodes(colnodes, colpaars); - - // update phi node - phi.put(phivar, phiVers); - } - - private void varMapToGraph(VarVersionPaar varpaar, SFormsFastMapDirect varmap) { - - VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = ssuversions.nodes; - - VarVersionNode node = nodes.getWithKey(varpaar); - - node.live = new SFormsFastMapDirect(varmap); - } - - private Integer getNextFreeVersion(Integer var, Statement stat) { - - Integer nextver = lastversion.get(var); - - if(nextver==null) { - nextver = new Integer(1); - } else { - nextver = new Integer(nextver.intValue()+1); - } - lastversion.put(var, nextver); - - // save the first protected range, containing current statement - if(stat != null) { // null iff phantom version - Integer firstRangeId = getFirstProtectedRange(stat); - if(firstRangeId != null) { - mapVersionFirstRange.put(new VarVersionPaar(var, nextver), firstRangeId); - } - } - - return nextver; - } - - private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { - - - SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - - for(DirectNode pred: node.preds) { - SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); - if(mapNew.isEmpty()) { - mapNew = mapOut.getCopy(); - } else { - mergeMaps(mapNew, mapOut); - } - } - - if(extraVarVersions.containsKey(node.id)) { - SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); - if(mapNew.isEmpty()) { - mapNew = mapExtra.getCopy(); - } else { - mergeMaps(mapNew, mapExtra); - } - } - - inVarVersions.put(node.id, mapNew); - - } - - private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { - - SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); - - boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); - - if(nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { - if(outNegVarVersions.containsKey(predid)) { - mapNew = outNegVarVersions.get(predid).getCopy(); - } - } else if(outVarVersions.containsKey(predid)) { - mapNew = outVarVersions.get(predid).getCopy(); - } - - if(isFinallyExit) { - - SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); - - SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); - - String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); - boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); - - HashSet<String> setLongPathWrapper = new HashSet<String>(); - for(List<FinallyPathWrapper> lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) { - for(FinallyPathWrapper finwraplong : lstwrapper) { - setLongPathWrapper.add(finwraplong.destination+"##"+finwraplong.source); - } - } - - for(FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { - SFormsFastMapDirect map; - - boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); - - if(recFinally) { - // recursion - map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); - } else { - if(finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { - map = outNegVarVersions.get(finwrap.source); - } else { - map = outVarVersions.get(finwrap.source); - } - } - - // false path? - boolean isFalsePath = true; - - if(recFinally) { - isFalsePath = !finwrap.destination.equals(nodeid); - } else { - isFalsePath = !setLongPathWrapper.contains(destid+"##"+finwrap.source); - } - - if(isFalsePath) { - mapNewTemp.complement(map); - } else { - if(mapTrueSource.isEmpty()) { - if(map != null) { - mapTrueSource = map.getCopy(); - } - } else { - mergeMaps(mapTrueSource, map); - } - } - - } - - if(isExceptionMonitorExit) { - - mapNew = mapTrueSource; - - } else { - - mapNewTemp.union(mapTrueSource); - mapNew.intersection(mapNewTemp); - - if(!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for?? - - // replace phi versions with corresponding phantom ones - HashMap<VarVersionPaar, VarVersionPaar> mapPhantom = phantomexitnodes.get(predid); - if(mapPhantom == null) { - mapPhantom = new HashMap<VarVersionPaar, VarVersionPaar>(); - } - - SFormsFastMapDirect mapExitVar = mapNew.getCopy(); - mapExitVar.complement(mapTrueSource); - - for(Entry<Integer, FastSparseSet<Integer>> ent : mapExitVar.entryList()) { - for(Integer version : ent.getValue()) { - - Integer varindex = ent.getKey(); - VarVersionPaar exitvar = new VarVersionPaar(varindex, version); - FastSparseSet<Integer> newSet = mapNew.get(varindex); - - // remove the actual exit version - newSet.remove(version); - - // get or create phantom version - VarVersionPaar phantomvar = mapPhantom.get(exitvar); - if(phantomvar == null) { - Integer newversion = getNextFreeVersion(exitvar.var, null); - phantomvar = new VarVersionPaar(exitvar.var, newversion.intValue()); - - VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar); - VarVersionNode phantomnode = ssuversions.createNode(phantomvar); - phantomnode.flags |= VarVersionNode.FLAG_PHANTOM_FINEXIT; - - VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_PHANTOM, exitnode, phantomnode); - exitnode.addSuccessor(edge); - phantomnode.addPredecessor(edge); - - mapPhantom.put(exitvar, phantomvar); - } - - // add phantom version - newSet.add(phantomvar.version); - } - } - - if(!mapPhantom.isEmpty()) { - phantomexitnodes.put(predid, mapPhantom); - } - } - } - - } - - return mapNew; - } - - private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { - - if(map2 != null && !map2.isEmpty()) { - mapTo.union(map2); - } - - return mapTo; - } - - private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { - - if(map1 == null) { - return map2 == null; - } else if (map2 == null) { - return false; - } - - if(map1.size() != map2.size()) { - return false; - } - - for(Entry<Integer, FastSparseSet<Integer>> ent2: map2.entryList()) { - if(!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { - return false; - } - } - - return true; - } - - - private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { - FastSparseSet<Integer> set = factory.spawnEmptySet(); - set.add(vers); - varmap.put(var, set); - } - - private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { - - SFormsFastMapDirect map; - - switch(stat.type) { - case Statement.TYPE_CATCHALL: - case Statement.TYPE_TRYCATCH: - - List<VarExprent> lstVars; - if(stat.type == Statement.TYPE_CATCHALL) { - lstVars = ((CatchAllStatement)stat).getVars(); - } else { - lstVars = ((CatchStatement)stat).getVars(); - } - - for(int i=1;i<stat.getStats().size();i++) { - int varindex = lstVars.get(i-1).getIndex(); - int version = getNextFreeVersion(varindex, stat); // == 1 - - map = new SFormsFastMapDirect(); - setCurrentVar(map, varindex, version); - - extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); - //ssuversions.createOrGetNode(new VarVersionPaar(varindex, version)); - ssuversions.createNode(new VarVersionPaar(varindex, version)); - } - } - - for(Statement st: stat.getStats()) { - setCatchMaps(st, dgraph, flatthelper); - } - } - - private SFormsFastMapDirect createFirstMap(StructMethod mt, RootStatement root) { - - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - int paramcount = md.params.length + (thisvar?1:0); - - int varindex = 0; - SFormsFastMapDirect map = new SFormsFastMapDirect(); - for(int i=0;i<paramcount;i++) { - int version = getNextFreeVersion(varindex, root); // == 1 - - FastSparseSet<Integer> set = factory.spawnEmptySet(); - set.add(version); - map.put(varindex, set); - ssuversions.createNode(new VarVersionPaar(varindex, version)); - - if(thisvar) { - if(i==0) { - varindex++; - } else { - varindex+=md.params[i-1].stack_size; - } - } else { - varindex+=md.params[i].stack_size; - } - } - - return map; - } - - private Integer getFirstProtectedRange(Statement stat) { - - for(;;) { - Statement parent = stat.getParent(); - - if(parent == null) { - break; - } - - if(parent.type == Statement.TYPE_CATCHALL || - parent.type == Statement.TYPE_TRYCATCH) { - if(parent.getFirst() == stat) { - return parent.id; - } - } else if(parent.type == Statement.TYPE_SYNCRONIZED) { - if(((SynchronizedStatement)parent).getBody() == stat) { - return parent.id; - } - } - - stat = parent; - } - - return null; - } - - public HashMap<VarVersionPaar, HashSet<Integer>> getPhi() { - return phi; - } - - public VarVersionsGraph getSsuversions() { - return ssuversions; - } - - public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPaar varpaar) { - - - VarVersionNode node = ssuversions.nodes.getWithKey(varpaar); - if(node != null) { - return node.live; - } - - return null; - } - - public HashMap<VarVersionPaar, Integer> getMapVersionFirstRange() { - return mapVersionFirstRange; - } - - public HashMap<Integer, Integer> getMapFieldVars() { - return mapFieldVars; - } + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> inVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version (direct branch) + private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version (negative branch) + private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outNegVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> extraVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // (var, version), version + private HashMap<VarVersionPaar, HashSet<Integer>> phi = new HashMap<VarVersionPaar, HashSet<Integer>>(); + + // var, version + private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); + + // version, protected ranges (catch, finally) + private HashMap<VarVersionPaar, Integer> mapVersionFirstRange = new HashMap<VarVersionPaar, Integer>(); + + // version, version + private HashMap<VarVersionPaar, VarVersionPaar> phantomppnodes = new HashMap<VarVersionPaar, VarVersionPaar>(); // ++ and -- + + // node.id, version, version + private HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>> phantomexitnodes = + new HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>>(); // finally exits + + // versions memory dependencies + private VarVersionsGraph ssuversions = new VarVersionsGraph(); + + // field access vars (exprent id, var id) + private HashMap<Integer, Integer> mapFieldVars = new HashMap<Integer, Integer>(); + + // field access counter + private int fieldvarcounter = -1; + + // set factory + private FastSparseSetFactory<Integer> factory; + + public void splitVariables(RootStatement root, StructMethod mt) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + HashSet<Integer> setInit = new HashSet<Integer>(); + for (int i = 0; i < 64; i++) { + setInit.add(i); + } + factory = new FastSparseSetFactory<Integer>(setInit); + + extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root)); + + setCatchMaps(root, dgraph, flatthelper); + + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + HashSet<String> updated = new HashSet<String>(); + do { + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + ssaStatements(dgraph, updated, false); + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + } + while (!updated.isEmpty()); + + + ssaStatements(dgraph, updated, true); + + ssuversions.initDominators(); + } + + private void ssaStatements(DirectGraph dgraph, HashSet<String> updated, boolean calcLiveVars) { + + for (DirectNode node : dgraph.nodes) { + + updated.remove(node.id); + mergeInVarMaps(node, dgraph); + + SFormsFastMapDirect varmap = new SFormsFastMapDirect(inVarVersions.get(node.id)); + + SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null}; + + if (node.exprents != null) { + for (Exprent expr : node.exprents) { + processExprent(expr, varmaparr, node.statement, calcLiveVars); + } + } + + if (varmaparr[1] == null) { + varmaparr[1] = varmaparr[0]; + } + + // quick solution: 'dummy' field variables should not cross basic block borders (otherwise problems e.g. with finally loops - usage without assignment in a loop) + // For the full solution consider adding a dummy assignment at the entry point of the method + boolean allow_field_propagation = node.succs.isEmpty() || (node.succs.size() == 1 && node.succs.get(0).preds.size() == 1); + + if (!allow_field_propagation && varmaparr[0] != null) { + varmaparr[0].removeAllFields(); + varmaparr[1].removeAllFields(); + } + + boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) + || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); + + if (this_updated) { + + outVarVersions.put(node.id, varmaparr[0]); + if (dgraph.mapNegIfBranch.containsKey(node.id)) { + outNegVarVersions.put(node.id, varmaparr[1]); + } + + for (DirectNode nd : node.succs) { + updated.add(nd.id); + } + } + } + } + + + private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, boolean calcLiveVars) { + + if (expr == null) { + return; + } + + + VarExprent varassign = null; + boolean finished = false; + + switch (expr.type) { + case Exprent.EXPRENT_ASSIGNMENT: + AssignmentExprent assexpr = (AssignmentExprent)expr; + if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + Exprent dest = assexpr.getLeft(); + if (dest.type == Exprent.EXPRENT_VAR) { + varassign = (VarExprent)dest; + } + } + break; + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)expr; + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_IIF: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect varmapFalse; + if (varmaparr[1] == null) { + varmapFalse = new SFormsFastMapDirect(varmaparr[0]); + } + else { + varmapFalse = varmaparr[1]; + varmaparr[1] = null; + } + + processExprent(func.getLstOperands().get(1), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null}; + processExprent(func.getLstOperands().get(2), varmaparrNeg, stat, calcLiveVars); + + mergeMaps(varmaparr[0], varmaparrNeg[0]); + varmaparr[1] = null; + + finished = true; + break; + case FunctionExprent.FUNCTION_CADD: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrAnd, stat, calcLiveVars); + + // false map + varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]); + // true map + varmaparr[0] = varmaparrAnd[0]; + + finished = true; + break; + case FunctionExprent.FUNCTION_COR: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrOr = + new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrOr, stat, calcLiveVars); + + // false map + varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; + // true map + varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); + + finished = true; + } + } + + if (!finished) { + List<Exprent> lst = expr.getAllExprents(); + lst.remove(varassign); + + for (Exprent ex : lst) { + processExprent(ex, varmaparr, stat, calcLiveVars); + } + } + + + SFormsFastMapDirect varmap = varmaparr[0]; + + // field access + if (expr.type == Exprent.EXPRENT_FIELD) { + + int index; + if (mapFieldVars.containsKey(expr.id)) { + index = mapFieldVars.get(expr.id); + } + else { + index = fieldvarcounter--; + mapFieldVars.put(expr.id, index); + + // ssu graph + ssuversions.createNode(new VarVersionPaar(index, 1)); + } + + setCurrentVar(varmap, index, 1); + } + else if (expr.type == Exprent.EXPRENT_INVOCATION || + (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || + (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || + expr.type == Exprent.EXPRENT_FUNCTION) { + + boolean ismmpp = true; + + if (expr.type == Exprent.EXPRENT_FUNCTION) { + + ismmpp = false; + + FunctionExprent fexpr = (FunctionExprent)expr; + if (fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { + if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + ismmpp = true; + } + } + } + + if (ismmpp) { + varmap.removeAllFields(); + } + } + + + if (varassign != null) { + + Integer varindex = varassign.getIndex(); + + if (varassign.getVersion() == 0) { + // get next version + Integer nextver = getNextFreeVersion(varindex, stat); + + // set version + varassign.setVersion(nextver); + + // ssu graph + ssuversions.createNode(new VarVersionPaar(varindex, nextver)); + + setCurrentVar(varmap, varindex, nextver); + } + else { + if (calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex.intValue(), varassign.getVersion()), varmap); + } + setCurrentVar(varmap, varindex, varassign.getVersion()); + } + } + else if (expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function + FunctionExprent func = (FunctionExprent)expr; + + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_IMM: + case FunctionExprent.FUNCTION_MMI: + case FunctionExprent.FUNCTION_IPP: + case FunctionExprent.FUNCTION_PPI: + + if (func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)func.getLstOperands().get(0); + Integer varindex = var.getIndex(); + VarVersionPaar varpaar = new VarVersionPaar(varindex.intValue(), var.getVersion()); + + // ssu graph + VarVersionPaar phantomver = phantomppnodes.get(varpaar); + if (phantomver == null) { + // get next version + Integer nextver = getNextFreeVersion(varindex, null); + phantomver = new VarVersionPaar(varindex, nextver); + //ssuversions.createOrGetNode(phantomver); + ssuversions.createNode(phantomver); + + VarVersionNode vernode = ssuversions.nodes.getWithKey(varpaar); + + FastSparseSet<Integer> vers = factory.spawnEmptySet(); + if (vernode.preds.size() == 1) { + vers.add(vernode.preds.iterator().next().source.version); + } + else { + for (VarVersionEdge edge : vernode.preds) { + vers.add(edge.source.preds.iterator().next().source.version); + } + } + vers.add(nextver); + createOrUpdatePhiNode(varpaar, vers, stat); + phantomppnodes.put(varpaar, phantomver); + } + if (calcLiveVars) { + varMapToGraph(varpaar, varmap); + } + setCurrentVar(varmap, varindex.intValue(), var.getVersion()); + } + } + } + else if (expr.type == Exprent.EXPRENT_VAR) { + + VarExprent vardest = (VarExprent)expr; + + Integer varindex = vardest.getIndex(); + Integer current_vers = vardest.getVersion(); + + FastSparseSet<Integer> vers = varmap.get(varindex); + + int cardinality = vers.getCardinality(); + if (cardinality == 1) { // size == 1 + if (current_vers.intValue() != 0) { + if (calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + } + setCurrentVar(varmap, varindex, current_vers); + } + else { + // split last version + Integer usever = getNextFreeVersion(varindex, stat); + + // set version + vardest.setVersion(usever); + setCurrentVar(varmap, varindex, usever); + + // ssu graph + Integer lastver = vers.iterator().next(); + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(varindex, lastver)); + VarVersionNode usenode = ssuversions.createNode(new VarVersionPaar(varindex, usever)); + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode); + prenode.addSuccessor(edge); + usenode.addPredecessor(edge); + } + } + else if (cardinality == 2) { // size > 1 + + if (current_vers.intValue() != 0) { + if (calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + } + setCurrentVar(varmap, varindex, current_vers); + } + else { + // split version + Integer usever = getNextFreeVersion(varindex, stat); + // set version + vardest.setVersion(usever); + + // ssu node + ssuversions.createNode(new VarVersionPaar(varindex, usever)); + + setCurrentVar(varmap, varindex, usever); + + current_vers = usever; + } + + createOrUpdatePhiNode(new VarVersionPaar(varindex, current_vers), vers, stat); + } // vers.size() == 0 means uninitialized variable, which is impossible + } + } + + private void createOrUpdatePhiNode(VarVersionPaar phivar, FastSparseSet<Integer> vers, Statement stat) { + + FastSparseSet<Integer> versCopy = vers.getCopy(); + HashSet<Integer> phiVers = new HashSet<Integer>(); + + // take into account the corresponding mm/pp node if existing + int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1; + + // ssu graph + VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar); + List<VarVersionEdge> lstPreds = new ArrayList<VarVersionEdge>(phinode.preds); + if (lstPreds.size() == 1) { + // not yet a phi node + VarVersionEdge edge = lstPreds.get(0); + edge.source.removeSuccessor(edge); + phinode.removePredecessor(edge); + } + else { + for (VarVersionEdge edge : lstPreds) { + int verssrc = edge.source.preds.iterator().next().source.version; + if (!vers.contains(verssrc) && verssrc != ppvers) { + edge.source.removeSuccessor(edge); + phinode.removePredecessor(edge); + } + else { + versCopy.remove(verssrc); + phiVers.add(verssrc); + } + } + } + + List<VarVersionNode> colnodes = new ArrayList<VarVersionNode>(); + List<VarVersionPaar> colpaars = new ArrayList<VarVersionPaar>(); + + for (Integer ver : versCopy) { + + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(phivar.var, ver.intValue())); + + Integer tempver = getNextFreeVersion(phivar.var, stat); + + VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver.intValue()); + + colnodes.add(tempnode); + colpaars.add(new VarVersionPaar(phivar.var, tempver.intValue())); + + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode); + + prenode.addSuccessor(edge); + tempnode.addPredecessor(edge); + + + edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, tempnode, phinode); + tempnode.addSuccessor(edge); + phinode.addPredecessor(edge); + + phiVers.add(tempver); + } + + ssuversions.addNodes(colnodes, colpaars); + + // update phi node + phi.put(phivar, phiVers); + } + + private void varMapToGraph(VarVersionPaar varpaar, SFormsFastMapDirect varmap) { + + VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = ssuversions.nodes; + + VarVersionNode node = nodes.getWithKey(varpaar); + + node.live = new SFormsFastMapDirect(varmap); + } + + private Integer getNextFreeVersion(Integer var, Statement stat) { + + Integer nextver = lastversion.get(var); + + if (nextver == null) { + nextver = new Integer(1); + } + else { + nextver = new Integer(nextver.intValue() + 1); + } + lastversion.put(var, nextver); + + // save the first protected range, containing current statement + if (stat != null) { // null iff phantom version + Integer firstRangeId = getFirstProtectedRange(stat); + if (firstRangeId != null) { + mapVersionFirstRange.put(new VarVersionPaar(var, nextver), firstRangeId); + } + } + + return nextver; + } + + private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { + + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + for (DirectNode pred : node.preds) { + SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); + if (mapNew.isEmpty()) { + mapNew = mapOut.getCopy(); + } + else { + mergeMaps(mapNew, mapOut); + } + } + + if (extraVarVersions.containsKey(node.id)) { + SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); + if (mapNew.isEmpty()) { + mapNew = mapExtra.getCopy(); + } + else { + mergeMaps(mapNew, mapExtra); + } + } + + inVarVersions.put(node.id, mapNew); + } + + private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); + + if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { + if (outNegVarVersions.containsKey(predid)) { + mapNew = outNegVarVersions.get(predid).getCopy(); + } + } + else if (outVarVersions.containsKey(predid)) { + mapNew = outVarVersions.get(predid).getCopy(); + } + + if (isFinallyExit) { + + SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); + + SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); + + String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); + boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); + + HashSet<String> setLongPathWrapper = new HashSet<String>(); + for (List<FinallyPathWrapper> lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) { + for (FinallyPathWrapper finwraplong : lstwrapper) { + setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); + } + } + + for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { + SFormsFastMapDirect map; + + boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); + + if (recFinally) { + // recursion + map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); + } + else { + if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { + map = outNegVarVersions.get(finwrap.source); + } + else { + map = outVarVersions.get(finwrap.source); + } + } + + // false path? + boolean isFalsePath = true; + + if (recFinally) { + isFalsePath = !finwrap.destination.equals(nodeid); + } + else { + isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source); + } + + if (isFalsePath) { + mapNewTemp.complement(map); + } + else { + if (mapTrueSource.isEmpty()) { + if (map != null) { + mapTrueSource = map.getCopy(); + } + } + else { + mergeMaps(mapTrueSource, map); + } + } + } + + if (isExceptionMonitorExit) { + + mapNew = mapTrueSource; + } + else { + + mapNewTemp.union(mapTrueSource); + mapNew.intersection(mapNewTemp); + + if (!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for?? + + // replace phi versions with corresponding phantom ones + HashMap<VarVersionPaar, VarVersionPaar> mapPhantom = phantomexitnodes.get(predid); + if (mapPhantom == null) { + mapPhantom = new HashMap<VarVersionPaar, VarVersionPaar>(); + } + + SFormsFastMapDirect mapExitVar = mapNew.getCopy(); + mapExitVar.complement(mapTrueSource); + + for (Entry<Integer, FastSparseSet<Integer>> ent : mapExitVar.entryList()) { + for (Integer version : ent.getValue()) { + + Integer varindex = ent.getKey(); + VarVersionPaar exitvar = new VarVersionPaar(varindex, version); + FastSparseSet<Integer> newSet = mapNew.get(varindex); + + // remove the actual exit version + newSet.remove(version); + + // get or create phantom version + VarVersionPaar phantomvar = mapPhantom.get(exitvar); + if (phantomvar == null) { + Integer newversion = getNextFreeVersion(exitvar.var, null); + phantomvar = new VarVersionPaar(exitvar.var, newversion.intValue()); + + VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar); + VarVersionNode phantomnode = ssuversions.createNode(phantomvar); + phantomnode.flags |= VarVersionNode.FLAG_PHANTOM_FINEXIT; + + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_PHANTOM, exitnode, phantomnode); + exitnode.addSuccessor(edge); + phantomnode.addPredecessor(edge); + + mapPhantom.put(exitvar, phantomvar); + } + + // add phantom version + newSet.add(phantomvar.version); + } + } + + if (!mapPhantom.isEmpty()) { + phantomexitnodes.put(predid, mapPhantom); + } + } + } + } + + return mapNew; + } + + private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { + + if (map2 != null && !map2.isEmpty()) { + mapTo.union(map2); + } + + return mapTo; + } + + private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { + + if (map1 == null) { + return map2 == null; + } + else if (map2 == null) { + return false; + } + + if (map1.size() != map2.size()) { + return false; + } + + for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) { + if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { + return false; + } + } + + return true; + } + + + private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(vers); + varmap.put(var, set); + } + + private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { + + SFormsFastMapDirect map; + + switch (stat.type) { + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + + List<VarExprent> lstVars; + if (stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)stat).getVars(); + } + else { + lstVars = ((CatchStatement)stat).getVars(); + } + + for (int i = 1; i < stat.getStats().size(); i++) { + int varindex = lstVars.get(i - 1).getIndex(); + int version = getNextFreeVersion(varindex, stat); // == 1 + + map = new SFormsFastMapDirect(); + setCurrentVar(map, varindex, version); + + extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); + //ssuversions.createOrGetNode(new VarVersionPaar(varindex, version)); + ssuversions.createNode(new VarVersionPaar(varindex, version)); + } + } + + for (Statement st : stat.getStats()) { + setCatchMaps(st, dgraph, flatthelper); + } + } + + private SFormsFastMapDirect createFirstMap(StructMethod mt, RootStatement root) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = md.params.length + (thisvar ? 1 : 0); + + int varindex = 0; + SFormsFastMapDirect map = new SFormsFastMapDirect(); + for (int i = 0; i < paramcount; i++) { + int version = getNextFreeVersion(varindex, root); // == 1 + + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(version); + map.put(varindex, set); + ssuversions.createNode(new VarVersionPaar(varindex, version)); + + if (thisvar) { + if (i == 0) { + varindex++; + } + else { + varindex += md.params[i - 1].stack_size; + } + } + else { + varindex += md.params[i].stack_size; + } + } + + return map; + } + + private Integer getFirstProtectedRange(Statement stat) { + + for (; ; ) { + Statement parent = stat.getParent(); + + if (parent == null) { + break; + } + + if (parent.type == Statement.TYPE_CATCHALL || + parent.type == Statement.TYPE_TRYCATCH) { + if (parent.getFirst() == stat) { + return parent.id; + } + } + else if (parent.type == Statement.TYPE_SYNCRONIZED) { + if (((SynchronizedStatement)parent).getBody() == stat) { + return parent.id; + } + } + + stat = parent; + } + + return null; + } + + public HashMap<VarVersionPaar, HashSet<Integer>> getPhi() { + return phi; + } + + public VarVersionsGraph getSsuversions() { + return ssuversions; + } + + public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPaar varpaar) { + + + VarVersionNode node = ssuversions.nodes.getWithKey(varpaar); + if (node != null) { + return node.live; + } + + return null; + } + + public HashMap<VarVersionPaar, Integer> getMapVersionFirstRange() { + return mapVersionFirstRange; + } + + public HashMap<Integer, Integer> getMapFieldVars() { + return mapFieldVars; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java index 5cfa634..ddac0c5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -23,73 +24,73 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; public class BasicBlockStatement extends Statement { - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private BasicBlock block; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public BasicBlockStatement(BasicBlock block) { - - type = Statement.TYPE_BASICBLOCK; - - this.block = block; - - id = block.id; - CounterContainer coun = DecompilerContext.getCountercontainer(); - if(id>=coun.getCounter(CounterContainer.STATEMENT_COUNTER)) { - coun.setCounter(CounterContainer.STATEMENT_COUNTER, id+1); - } - - Instruction instr = block.getLastInstruction(); - if(instr != null) { - if(instr.group==CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) { - lastBasicType = LASTBASICTYPE_IF; - } else if(instr.group==CodeConstants.GROUP_SWITCH) { - lastBasicType = LASTBASICTYPE_SWITCH; - } - } - - // monitorenter and monitorexits - buildMonitorFlags(); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public String toJava(int indent) { - return ExprProcessor.listToJava(varDefinitions, indent)+ - ExprProcessor.listToJava(exprents, indent); - } - - public Statement getSimpleCopy() { - - BasicBlock newblock = new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)); - - SimpleInstructionSequence seq = new SimpleInstructionSequence(); - for(int i=0;i<block.getSeq().length();i++) { - seq.addInstruction(block.getSeq().getInstr(i).clone(), -1); - } - - newblock.setSeq(seq); - - return new BasicBlockStatement(newblock); - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public BasicBlock getBlock() { - return block; - } - + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private BasicBlock block; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public BasicBlockStatement(BasicBlock block) { + + type = Statement.TYPE_BASICBLOCK; + + this.block = block; + + id = block.id; + CounterContainer coun = DecompilerContext.getCountercontainer(); + if (id >= coun.getCounter(CounterContainer.STATEMENT_COUNTER)) { + coun.setCounter(CounterContainer.STATEMENT_COUNTER, id + 1); + } + + Instruction instr = block.getLastInstruction(); + if (instr != null) { + if (instr.group == CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) { + lastBasicType = LASTBASICTYPE_IF; + } + else if (instr.group == CodeConstants.GROUP_SWITCH) { + lastBasicType = LASTBASICTYPE_SWITCH; + } + } + + // monitorenter and monitorexits + buildMonitorFlags(); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + return ExprProcessor.listToJava(varDefinitions, indent) + + ExprProcessor.listToJava(exprents, indent); + } + + public Statement getSimpleCopy() { + + BasicBlock newblock = new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)); + + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + for (int i = 0; i < block.getSeq().length(); i++) { + seq.addInstruction(block.getSeq().getInstr(i).clone(), -1); + } + + newblock.setSeq(seq); + + return new BasicBlockStatement(newblock); + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public BasicBlock getBlock() { + return block; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index 25adb29..9f1a3f5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -1,24 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -30,204 +26,210 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + public class CatchAllStatement extends Statement { - - private Statement handler; - - private boolean isFinally; - - private VarExprent monitor; - - private List<VarExprent> vars = new ArrayList<VarExprent>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private CatchAllStatement(){ - type = Statement.TYPE_CATCHALL; - }; - - private CatchAllStatement(Statement head, Statement handler) { - - this(); - - first = head; - stats.addWithKey(head, head.id); - - this.handler = handler; - stats.addWithKey(handler, handler.id); - - List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstSuccs.isEmpty()) { - StatEdge edge = lstSuccs.get(0); - if(edge.getType() == StatEdge.TYPE_REGULAR) { - post = edge.getDestination(); - } - } - - vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), - new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); - - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead(Statement head) { - - if(head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { - return null; - } - - HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); - - if(setHandlers.size() != 1) { - return null; - } - - for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { - Statement exc = edge.getDestination(); - - if(edge.getExceptions() == null && setHandlers.contains(exc) && exc.getLastBasicType() == LASTBASICTYPE_GENERAL) { - List<StatEdge> lstSuccs = exc.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(lstSuccs.isEmpty() || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { - - if(head.isMonitorEnter() || exc.isMonitorEnter()) { - return null; - } - - if(DecHelper.checkStatementExceptions(Arrays.asList(new Statement[] {head, exc}))) { - return new CatchAllStatement(head, exc); - } - } - } - } - - return null; - } - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - String indstr1 = null; - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - - boolean labeled = isLabeled(); - if(labeled) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(first.type == TYPE_TRYCATCH && first.varDefinitions.isEmpty() && isFinally && - !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) { - String content = ExprProcessor.jmpWrapper(first, indent, true); - content = content.substring(0, content.length()-new_line_separator.length()); - - buf.append(content); - } else { - buf.append(indstr+"try {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"}"); - } - - buf.append((isFinally?" finally": - " catch ("+vars.get(0).toJava(indent)+")")+" {" + new_line_separator); - - if(monitor != null) { - indstr1 = InterpreterUtil.getIndentString(indent+1); - buf.append(indstr1+"if("+monitor.toJava(indent)+") {" + new_line_separator); - } - - buf.append(ExprProcessor.jmpWrapper(handler, indent+1+(monitor != null?1:0), true)); - - if(monitor != null) { - buf.append(indstr1+"}" + new_line_separator); - } - - buf.append(indstr+"}" + new_line_separator); - - return buf.toString(); - } - - public void replaceStatement(Statement oldstat, Statement newstat) { - - if(handler == oldstat) { - handler = newstat; - } - - super.replaceStatement(oldstat, newstat); - } - - public Statement getSimpleCopy() { - - CatchAllStatement cas = new CatchAllStatement(); - - cas.isFinally = this.isFinally; - - if(this.monitor != null) { - cas.monitor = new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), - VarType.VARTYPE_INT, - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)); - } - - if(!this.vars.isEmpty()) { - // FIXME: WTF??? vars?! - vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), - new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); - } - - return cas; - } - - public void initSimpleCopy() { - first = stats.get(0); - handler = stats.get(1); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Statement getHandler() { - return handler; - } - - - public void setHandler(Statement handler) { - this.handler = handler; - } - - - public boolean isFinally() { - return isFinally; - } - - - public void setFinally(boolean isFinally) { - this.isFinally = isFinally; - } - - - public VarExprent getMonitor() { - return monitor; - } - - - public void setMonitor(VarExprent monitor) { - this.monitor = monitor; - } - - public List<VarExprent> getVars() { - return vars; - } - + + private Statement handler; + + private boolean isFinally; + + private VarExprent monitor; + + private List<VarExprent> vars = new ArrayList<VarExprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private CatchAllStatement() { + type = Statement.TYPE_CATCHALL; + } + + ; + + private CatchAllStatement(Statement head, Statement handler) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + this.handler = handler; + stats.addWithKey(handler, handler.id); + + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + if (edge.getType() == StatEdge.TYPE_REGULAR) { + post = edge.getDestination(); + } + } + + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + return null; + } + + HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); + + if (setHandlers.size() != 1) { + return null; + } + + for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement exc = edge.getDestination(); + + if (edge.getExceptions() == null && setHandlers.contains(exc) && exc.getLastBasicType() == LASTBASICTYPE_GENERAL) { + List<StatEdge> lstSuccs = exc.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (lstSuccs.isEmpty() || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { + + if (head.isMonitorEnter() || exc.isMonitorEnter()) { + return null; + } + + if (DecHelper.checkStatementExceptions(Arrays.asList(new Statement[]{head, exc}))) { + return new CatchAllStatement(head, exc); + } + } + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + String indstr1 = null; + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + boolean labeled = isLabeled(); + if (labeled) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (first.type == TYPE_TRYCATCH && first.varDefinitions.isEmpty() && isFinally && + !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) { + String content = ExprProcessor.jmpWrapper(first, indent, true); + content = content.substring(0, content.length() - new_line_separator.length()); + + buf.append(content); + } + else { + buf.append(indstr + "try {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "}"); + } + + buf.append((isFinally ? " finally" : + " catch (" + vars.get(0).toJava(indent) + ")") + " {" + new_line_separator); + + if (monitor != null) { + indstr1 = InterpreterUtil.getIndentString(indent + 1); + buf.append(indstr1 + "if(" + monitor.toJava(indent) + ") {" + new_line_separator); + } + + buf.append(ExprProcessor.jmpWrapper(handler, indent + 1 + (monitor != null ? 1 : 0), true)); + + if (monitor != null) { + buf.append(indstr1 + "}" + new_line_separator); + } + + buf.append(indstr + "}" + new_line_separator); + + return buf.toString(); + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + if (handler == oldstat) { + handler = newstat; + } + + super.replaceStatement(oldstat, newstat); + } + + public Statement getSimpleCopy() { + + CatchAllStatement cas = new CatchAllStatement(); + + cas.isFinally = this.isFinally; + + if (this.monitor != null) { + cas.monitor = new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + VarType.VARTYPE_INT, + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)); + } + + if (!this.vars.isEmpty()) { + // FIXME: WTF??? vars?! + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + + return cas; + } + + public void initSimpleCopy() { + first = stats.get(0); + handler = stats.get(1); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getHandler() { + return handler; + } + + + public void setHandler(Statement handler) { + this.handler = handler; + } + + + public boolean isFinally() { + return isFinally; + } + + + public void setFinally(boolean isFinally) { + this.isFinally = isFinally; + } + + + public VarExprent getMonitor() { + return monitor; + } + + + public void setMonitor(VarExprent monitor) { + this.monitor = monitor; + } + + public List<VarExprent> getVars() { + return vars; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 6387d45..17f5636 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -1,23 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -29,178 +26,184 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + public class CatchStatement extends Statement { - - private List<List<String>> exctstrings = new ArrayList<List<String>>(); - - private List<VarExprent> vars = new ArrayList<VarExprent>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private CatchStatement() { - type = TYPE_TRYCATCH; - } - - private CatchStatement(Statement head, Statement next, HashSet<Statement> setHandlers) { - - this(); - - first = head; - stats.addWithKey(first, first.id); - - for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { - Statement stat = edge.getDestination(); - - if(setHandlers.contains(stat)) { - stats.addWithKey(stat, stat.id); - exctstrings.add(new ArrayList<String>(edge.getExceptions())); - - vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), - new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), // FIXME: for now simply the first type. Should get the first common superclass when possible. - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); - } - } - - if(next != null) { - post = next; - } - - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead(Statement head) { - - if(head.getLastBasicType() != LASTBASICTYPE_GENERAL) { - return null; - } - - HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); - - if(!setHandlers.isEmpty()) { - - int hnextcount = 0; // either no statements with connection to next, or more than 1 - - Statement next = null; - List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstHeadSuccs.isEmpty() && lstHeadSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { - next = lstHeadSuccs.get(0).getDestination(); - hnextcount = 2; - } - - for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { - Statement stat = edge.getDestination(); - - boolean handlerok = true; - - if(edge.getExceptions() != null && setHandlers.contains(stat)) { - if(stat.getLastBasicType() != LASTBASICTYPE_GENERAL) { - handlerok = false; - } else { - List<StatEdge> lstStatSuccs = stat.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstStatSuccs.isEmpty() && lstStatSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { - - Statement statn = lstStatSuccs.get(0).getDestination(); - - if(next == null) { - next = statn; - } else if(next != statn) { - handlerok = false; - } - - if(handlerok) { - hnextcount++; - } - } - } - } else { - handlerok = false; - } - - if(!handlerok) { - setHandlers.remove(stat); - } - } - - if(hnextcount != 1 && !setHandlers.isEmpty()) { - List<Statement> lst = new ArrayList<Statement>(); - lst.add(head); - lst.addAll(setHandlers); - - for(Statement st : lst) { - if(st.isMonitorEnter()) { - return null; - } - } - - if(DecHelper.checkStatementExceptions(lst)) { - return new CatchStatement(head, next, setHandlers); - } - } - } - return null; - } - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuffer buf = new StringBuffer(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - buf.append(indstr+"try {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"}"); - - for(int i=1;i<stats.size();i++) { - List<String> exception_types = exctstrings.get(i - 1); - - buf.append(" catch ("); - if(exception_types.size() > 1) { // multi-catch, Java 7 style - for(int exc_index = 1; exc_index < exception_types.size(); ++exc_index) { - VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index)); - String exc_type_name = ExprProcessor.getCastTypeName(exc_type); - - buf.append(exc_type_name + " | "); - } - } - buf.append(vars.get(i-1).toJava(indent)); - buf.append(") {"+new_line_separator+ExprProcessor.jmpWrapper(stats.get(i), indent+1, true)+indstr+"}"); - } - buf.append(new_line_separator); - - return buf.toString(); - } - - public Statement getSimpleCopy() { - - CatchStatement cs = new CatchStatement(); - - for(List<String> exc : this.exctstrings) { - cs.exctstrings.add(new ArrayList<String>(exc)); - cs.vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), - new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); - } - - return cs; - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public List<VarExprent> getVars() { - return vars; - } - + + private List<List<String>> exctstrings = new ArrayList<List<String>>(); + + private List<VarExprent> vars = new ArrayList<VarExprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private CatchStatement() { + type = TYPE_TRYCATCH; + } + + private CatchStatement(Statement head, Statement next, HashSet<Statement> setHandlers) { + + this(); + + first = head; + stats.addWithKey(first, first.id); + + for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement stat = edge.getDestination(); + + if (setHandlers.contains(stat)) { + stats.addWithKey(stat, stat.id); + exctstrings.add(new ArrayList<String>(edge.getExceptions())); + + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), + // FIXME: for now simply the first type. Should get the first common superclass when possible. + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + } + + if (next != null) { + post = next; + } + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.getLastBasicType() != LASTBASICTYPE_GENERAL) { + return null; + } + + HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); + + if (!setHandlers.isEmpty()) { + + int hnextcount = 0; // either no statements with connection to next, or more than 1 + + Statement next = null; + List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstHeadSuccs.isEmpty() && lstHeadSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { + next = lstHeadSuccs.get(0).getDestination(); + hnextcount = 2; + } + + for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement stat = edge.getDestination(); + + boolean handlerok = true; + + if (edge.getExceptions() != null && setHandlers.contains(stat)) { + if (stat.getLastBasicType() != LASTBASICTYPE_GENERAL) { + handlerok = false; + } + else { + List<StatEdge> lstStatSuccs = stat.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstStatSuccs.isEmpty() && lstStatSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { + + Statement statn = lstStatSuccs.get(0).getDestination(); + + if (next == null) { + next = statn; + } + else if (next != statn) { + handlerok = false; + } + + if (handlerok) { + hnextcount++; + } + } + } + } + else { + handlerok = false; + } + + if (!handlerok) { + setHandlers.remove(stat); + } + } + + if (hnextcount != 1 && !setHandlers.isEmpty()) { + List<Statement> lst = new ArrayList<Statement>(); + lst.add(head); + lst.addAll(setHandlers); + + for (Statement st : lst) { + if (st.isMonitorEnter()) { + return null; + } + } + + if (DecHelper.checkStatementExceptions(lst)) { + return new CatchStatement(head, next, setHandlers); + } + } + } + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + buf.append(indstr + "try {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "}"); + + for (int i = 1; i < stats.size(); i++) { + List<String> exception_types = exctstrings.get(i - 1); + + buf.append(" catch ("); + if (exception_types.size() > 1) { // multi-catch, Java 7 style + for (int exc_index = 1; exc_index < exception_types.size(); ++exc_index) { + VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index)); + String exc_type_name = ExprProcessor.getCastTypeName(exc_type); + + buf.append(exc_type_name + " | "); + } + } + buf.append(vars.get(i - 1).toJava(indent)); + buf.append(") {" + new_line_separator + ExprProcessor.jmpWrapper(stats.get(i), indent + 1, true) + indstr + "}"); + } + buf.append(new_line_separator); + + return buf.toString(); + } + + public Statement getSimpleCopy() { + + CatchStatement cs = new CatchStatement(); + + for (List<String> exc : this.exctstrings) { + cs.exctstrings.add(new ArrayList<String>(exc)); + cs.vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + + return cs; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<VarExprent> getVars() { + return vars; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index 604e36f..2bd8e3a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -1,221 +1,221 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class DoStatement extends Statement { - - public static final int LOOP_DO = 0; - public static final int LOOP_DOWHILE = 1; - public static final int LOOP_WHILE = 2; - public static final int LOOP_FOR = 3; - - private int looptype; - - private List<Exprent> initExprent = new ArrayList<Exprent>(); - private List<Exprent> conditionExprent = new ArrayList<Exprent>(); - private List<Exprent> incExprent = new ArrayList<Exprent>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private DoStatement() { - type = Statement.TYPE_DO; - looptype = LOOP_DO; - - initExprent.add(null); - conditionExprent.add(null); - incExprent.add(null); - } - - private DoStatement(Statement head) { - - this(); - - first = head; - stats.addWithKey(first, first.id); - - // post is always null! - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead(Statement head) { - - if(head.getLastBasicType() == LASTBASICTYPE_GENERAL && !head.isMonitorEnter()) { - - // at most one outgoing edge - StatEdge edge = null; - List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstSuccs.isEmpty()) { - edge = lstSuccs.get(0); - } - - // regular loop - if(edge!=null && edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() == head) { - return new DoStatement(head); - } - - // continues - if(head.type != TYPE_DO && (edge == null || edge.getType() != StatEdge.TYPE_REGULAR) && - head.getContinueSet().contains(head.getBasichead())) { - return new DoStatement(head); - } - } - - return null; - } - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuffer buf = new StringBuffer(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - switch(looptype) { - case LOOP_DO: - buf.append(indstr+"while(true) {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"}" + new_line_separator); - break; - case LOOP_DOWHILE: - buf.append(indstr+"do {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"} while("+conditionExprent.get(0).toJava(indent)+");" + new_line_separator); - break; - case LOOP_WHILE: - buf.append(indstr+"while("+conditionExprent.get(0).toJava(indent)+") {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"}" + new_line_separator); - break; - case LOOP_FOR: - buf.append(indstr+"for("+(initExprent.get(0)==null?"":initExprent.get(0).toJava(indent))+ - "; "+conditionExprent.get(0).toJava(indent)+"; "+incExprent.get(0).toJava(indent)+") {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); - buf.append(indstr+"}" + new_line_separator); - } - - return buf.toString(); - } - - public List<Object> getSequentialObjects() { - - List<Object> lst = new ArrayList<Object>(); - - switch(looptype) { - case LOOP_FOR: - if(getInitExprent() != null) { - lst.add(getInitExprent()); - } - case LOOP_WHILE: - lst.add(getConditionExprent()); - } - - lst.add(first); - - switch(looptype) { - case LOOP_DOWHILE: - lst.add(getConditionExprent()); - break; - case LOOP_FOR: - lst.add(getIncExprent()); - } - - return lst; - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(initExprent.get(0) == oldexpr) { - initExprent.set(0, newexpr); - } - if(conditionExprent.get(0) == oldexpr) { - conditionExprent.set(0, newexpr); - } - if(incExprent.get(0) == oldexpr) { - incExprent.set(0, newexpr); - } - } - - public Statement getSimpleCopy() { - return new DoStatement(); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public List<Exprent> getInitExprentList() { - return initExprent; - } - - public List<Exprent> getConditionExprentList() { - return conditionExprent; - } - - public List<Exprent> getIncExprentList() { - return incExprent; - } - - public Exprent getConditionExprent() { - return conditionExprent.get(0); - } - - public void setConditionExprent(Exprent conditionExprent) { - this.conditionExprent.set(0, conditionExprent); - } - - public Exprent getIncExprent() { - return incExprent.get(0); - } - - public void setIncExprent(Exprent incExprent) { - this.incExprent.set(0, incExprent); - } - - public Exprent getInitExprent() { - return initExprent.get(0); - } - - public void setInitExprent(Exprent initExprent) { - this.initExprent.set(0, initExprent); - } - - public int getLooptype() { - return looptype; - } - - public void setLooptype(int looptype) { - this.looptype = looptype; - } - + + public static final int LOOP_DO = 0; + public static final int LOOP_DOWHILE = 1; + public static final int LOOP_WHILE = 2; + public static final int LOOP_FOR = 3; + + private int looptype; + + private List<Exprent> initExprent = new ArrayList<Exprent>(); + private List<Exprent> conditionExprent = new ArrayList<Exprent>(); + private List<Exprent> incExprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private DoStatement() { + type = Statement.TYPE_DO; + looptype = LOOP_DO; + + initExprent.add(null); + conditionExprent.add(null); + incExprent.add(null); + } + + private DoStatement(Statement head) { + + this(); + + first = head; + stats.addWithKey(first, first.id); + + // post is always null! + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.getLastBasicType() == LASTBASICTYPE_GENERAL && !head.isMonitorEnter()) { + + // at most one outgoing edge + StatEdge edge = null; + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstSuccs.isEmpty()) { + edge = lstSuccs.get(0); + } + + // regular loop + if (edge != null && edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() == head) { + return new DoStatement(head); + } + + // continues + if (head.type != TYPE_DO && (edge == null || edge.getType() != StatEdge.TYPE_REGULAR) && + head.getContinueSet().contains(head.getBasichead())) { + return new DoStatement(head); + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + switch (looptype) { + case LOOP_DO: + buf.append(indstr + "while(true) {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "}" + new_line_separator); + break; + case LOOP_DOWHILE: + buf.append(indstr + "do {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "} while(" + conditionExprent.get(0).toJava(indent) + ");" + new_line_separator); + break; + case LOOP_WHILE: + buf.append(indstr + "while(" + conditionExprent.get(0).toJava(indent) + ") {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "}" + new_line_separator); + break; + case LOOP_FOR: + buf.append(indstr + "for(" + (initExprent.get(0) == null ? "" : initExprent.get(0).toJava(indent)) + + "; " + conditionExprent.get(0).toJava(indent) + "; " + incExprent.get(0).toJava(indent) + ") {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true)); + buf.append(indstr + "}" + new_line_separator); + } + + return buf.toString(); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(); + + switch (looptype) { + case LOOP_FOR: + if (getInitExprent() != null) { + lst.add(getInitExprent()); + } + case LOOP_WHILE: + lst.add(getConditionExprent()); + } + + lst.add(first); + + switch (looptype) { + case LOOP_DOWHILE: + lst.add(getConditionExprent()); + break; + case LOOP_FOR: + lst.add(getIncExprent()); + } + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (initExprent.get(0) == oldexpr) { + initExprent.set(0, newexpr); + } + if (conditionExprent.get(0) == oldexpr) { + conditionExprent.set(0, newexpr); + } + if (incExprent.get(0) == oldexpr) { + incExprent.set(0, newexpr); + } + } + + public Statement getSimpleCopy() { + return new DoStatement(); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<Exprent> getInitExprentList() { + return initExprent; + } + + public List<Exprent> getConditionExprentList() { + return conditionExprent; + } + + public List<Exprent> getIncExprentList() { + return incExprent; + } + + public Exprent getConditionExprent() { + return conditionExprent.get(0); + } + + public void setConditionExprent(Exprent conditionExprent) { + this.conditionExprent.set(0, conditionExprent); + } + + public Exprent getIncExprent() { + return incExprent.get(0); + } + + public void setIncExprent(Exprent incExprent) { + this.incExprent.set(0, incExprent); + } + + public Exprent getInitExprent() { + return initExprent.get(0); + } + + public void setInitExprent(Exprent initExprent) { + this.initExprent.set(0, initExprent); + } + + public int getLooptype() { + return looptype; + } + + public void setLooptype(int looptype) { + this.looptype = looptype; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java index 9cfaeb4..9988881 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java @@ -1,75 +1,74 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.Collection; -import java.util.HashSet; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.Collection; +import java.util.HashSet; public class GeneralStatement extends Statement { - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private GeneralStatement() { - type = Statement.TYPE_GENERAL; - } - - public GeneralStatement(Statement head, Collection<Statement> statements, Statement post) { - - this(); - - first = head; - stats.addWithKey(head, head.id); - - HashSet<Statement> set = new HashSet<Statement>(statements); - set.remove(head); - - for(Statement st : set) { - stats.addWithKey(st, st.id); - } - - this.post = post; - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuffer buf = new StringBuffer(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - buf.append(indstr+"abstract statement {" + new_line_separator); - for(int i=0;i<stats.size();i++) { - buf.append(stats.get(i).toJava(indent+1)); - } - buf.append(indstr+"}"); - - return buf.toString(); - } - + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private GeneralStatement() { + type = Statement.TYPE_GENERAL; + } + + public GeneralStatement(Statement head, Collection<Statement> statements, Statement post) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + HashSet<Statement> set = new HashSet<Statement>(statements); + set.remove(head); + + for (Statement st : set) { + stats.addWithKey(st, st.id); + } + + this.post = post; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + buf.append(indstr + "abstract statement {" + new_line_separator); + for (int i = 0; i < stats.size(); i++) { + buf.append(stats.get(i).toJava(indent + 1)); + } + buf.append(indstr + "}"); + + return buf.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index 3e3e20b..0b05981 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -1,22 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -25,380 +23,394 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class IfStatement extends Statement { - public static int IFTYPE_IF = 0; - public static int IFTYPE_IFELSE = 1; - - public int iftype; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private Statement ifstat; - private Statement elsestat; - - private StatEdge ifedge; - private StatEdge elseedge; - - private boolean negated = false; - - private boolean iffflag; - - private List<Exprent> headexprent = new ArrayList<Exprent>(); // contains IfExprent - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private IfStatement() { - type = TYPE_IF; - - headexprent.add(null); - } - - private IfStatement(Statement head, int regedges, Statement postst) { - - this(); - - first = head; - stats.addWithKey(head, head.id); - - List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); - - switch(regedges) { - case 0: - ifstat = null; - elsestat = null; - - break; - case 1: - ifstat = null; - elsestat = null; - - StatEdge edgeif = lstHeadSuccs.get(1); - if(edgeif.getType() != StatEdge.TYPE_REGULAR) { - post = lstHeadSuccs.get(0).getDestination(); - } else { - post = edgeif.getDestination(); - negated = true; - } - break; - case 2: - elsestat = lstHeadSuccs.get(0).getDestination(); - ifstat = lstHeadSuccs.get(1).getDestination(); - - List<StatEdge> lstSucc = ifstat.getSuccessorEdges(StatEdge.TYPE_REGULAR); - List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(StatEdge.TYPE_REGULAR); - - if(ifstat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc.size()>1) { - post = ifstat; - } else if(elsestat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc1.size()>1) { - post = elsestat; - } else { - if(lstSucc.size() == 0){ - post = elsestat; - } else if(lstSucc1.size() == 0){ - post = ifstat; - } - } - - if(ifstat == post) { - if(elsestat != post) { - ifstat = elsestat; - negated = true; - } else { - ifstat = null; - } - elsestat = null; - } else if(elsestat == post) { - elsestat = null; - } else { - post = postst; - } - - if(elsestat == null) { - regedges = 1; // if without else - } - } - - ifedge = lstHeadSuccs.get(negated?0:1); - elseedge = (regedges == 2)?lstHeadSuccs.get(negated?1:0):null; - - iftype = (regedges == 2)?IFTYPE_IFELSE:IFTYPE_IF; - - if(iftype == IFTYPE_IF) { - if(regedges == 0) { - StatEdge edge = lstHeadSuccs.get(0); - head.removeSuccessor(edge); - edge.setSource(this); - this.addSuccessor(edge); - } else if(regedges == 1) { - StatEdge edge = lstHeadSuccs.get(negated?1:0); - head.removeSuccessor(edge); - } - } - - if(ifstat != null) { - stats.addWithKey(ifstat, ifstat.id); - } - - if(elsestat != null) { - stats.addWithKey(elsestat, elsestat.id); - } - - if(post == head) { - post = this; - } - - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead(Statement head) { - - if(head.type == TYPE_BASICBLOCK && head.getLastBasicType() == LASTBASICTYPE_IF) { - int regsize = head.getSuccessorEdges(StatEdge.TYPE_REGULAR).size(); - - Statement p = null; - - boolean ok = (regsize < 2); - if(!ok) { - List<Statement> lst = new ArrayList<Statement>(); - if(DecHelper.isChoiceStatement(head, lst)) { - p = lst.remove(0); - - for(Statement st : lst) { - if(st.isMonitorEnter()) { - return null; - } - } - - ok = DecHelper.checkStatementExceptions(lst); - } - } - - if(ok) { - return new IfStatement(head, regsize, p); - } - } - - return null; - } - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuffer buf = new StringBuffer(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - buf.append(first.toJava(indent)); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); - - if(ifstat==null) { - buf.append(InterpreterUtil.getIndentString(indent+1)); - - if(ifedge.explicit) { - if(ifedge.getType() == StatEdge.TYPE_BREAK) { - // break - buf.append("break"); - } else { - // continue - buf.append("continue"); - } - - if(ifedge.labeled) { - buf.append(" label"+ifedge.closure.id); - } - } - buf.append(";" + new_line_separator); - } else { - buf.append(ExprProcessor.jmpWrapper(ifstat, indent+1, true)); - } - - boolean elseif = false; - - if(elsestat != null) { - if(elsestat.type == Statement.TYPE_IF - && elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() && - !elsestat.isLabeled() && - (elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty() - || !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if - String content = ExprProcessor.jmpWrapper(elsestat, indent, false); - content = content.substring(indstr.length()); - - buf.append(indstr+"} else "); - buf.append(content); - - elseif = true; - } else { - String content = ExprProcessor.jmpWrapper(elsestat, indent+1, false); - - if(content.length() > 0) { - buf.append(indstr+"} else {" + new_line_separator); - buf.append(content); - } - } - } - - if(!elseif) { - buf.append(indstr+"}" + new_line_separator); - } - - return buf.toString(); - } - - public void initExprents() { - - IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size()-1); - - if(negated) { - ifexpr = (IfExprent)ifexpr.copy(); - ifexpr.negateIf(); - } - - headexprent.set(0, ifexpr); - } - - public List<Object> getSequentialObjects() { - - List<Object> lst = new ArrayList<Object>(stats); - lst.add(1, headexprent.get(0)); - - return lst; - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(headexprent.get(0) == oldexpr) { - headexprent.set(0, newexpr); - } - } - - public void replaceStatement(Statement oldstat, Statement newstat) { - - super.replaceStatement(oldstat, newstat); - - if(ifstat == oldstat) { - ifstat = newstat; - } - - if(elsestat == oldstat) { - elsestat = newstat; - } - - List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); - - if(iftype == IFTYPE_IF) { - ifedge = lstSuccs.get(0); - elseedge = null; - } else { - StatEdge edge0 = lstSuccs.get(0); - StatEdge edge1 = lstSuccs.get(1); - if(edge0.getDestination() == ifstat) { - ifedge = edge0; - elseedge = edge1; - } else { - ifedge = edge1; - elseedge = edge0; - } - } - } - - public Statement getSimpleCopy() { - - IfStatement is = new IfStatement(); - is.iftype = this.iftype; - is.negated = this.negated; - is.iffflag = this.iffflag; - - return is; - } - - public void initSimpleCopy() { - - first = stats.get(0); - - List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); - ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated)?0:1); - if(stats.size() > 1) { - ifstat = stats.get(1); - } - - if(iftype == IFTYPE_IFELSE) { - elseedge = lstSuccs.get(negated?1:0); - elsestat = stats.get(2); - } - - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Statement getElsestat() { - return elsestat; - } - - public void setElsestat(Statement elsestat) { - this.elsestat = elsestat; - } - - public Statement getIfstat() { - return ifstat; - } - - public void setIfstat(Statement ifstat) { - this.ifstat = ifstat; - } - - public boolean isNegated() { - return negated; - } - - public void setNegated(boolean negated) { - this.negated = negated; - } - - public List<Exprent> getHeadexprentList() { - return headexprent; - } - - public IfExprent getHeadexprent() { - return (IfExprent)headexprent.get(0); - } - - public boolean isIffflag() { - return iffflag; - } - - public void setIffflag(boolean iffflag) { - this.iffflag = iffflag; - } - - public void setElseEdge(StatEdge elseedge) { - this.elseedge = elseedge; - } - - public void setIfEdge(StatEdge ifedge) { - this.ifedge = ifedge; - } - - public StatEdge getIfEdge() { - return ifedge; - } - - public StatEdge getElseEdge() { - return elseedge; - } + public static int IFTYPE_IF = 0; + public static int IFTYPE_IFELSE = 1; + + public int iftype; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Statement ifstat; + private Statement elsestat; + + private StatEdge ifedge; + private StatEdge elseedge; + + private boolean negated = false; + + private boolean iffflag; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); // contains IfExprent + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private IfStatement() { + type = TYPE_IF; + + headexprent.add(null); + } + + private IfStatement(Statement head, int regedges, Statement postst) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + + switch (regedges) { + case 0: + ifstat = null; + elsestat = null; + + break; + case 1: + ifstat = null; + elsestat = null; + + StatEdge edgeif = lstHeadSuccs.get(1); + if (edgeif.getType() != StatEdge.TYPE_REGULAR) { + post = lstHeadSuccs.get(0).getDestination(); + } + else { + post = edgeif.getDestination(); + negated = true; + } + break; + case 2: + elsestat = lstHeadSuccs.get(0).getDestination(); + ifstat = lstHeadSuccs.get(1).getDestination(); + + List<StatEdge> lstSucc = ifstat.getSuccessorEdges(StatEdge.TYPE_REGULAR); + List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(StatEdge.TYPE_REGULAR); + + if (ifstat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() > 1 || lstSucc.size() > 1) { + post = ifstat; + } + else if (elsestat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() > 1 || lstSucc1.size() > 1) { + post = elsestat; + } + else { + if (lstSucc.size() == 0) { + post = elsestat; + } + else if (lstSucc1.size() == 0) { + post = ifstat; + } + } + + if (ifstat == post) { + if (elsestat != post) { + ifstat = elsestat; + negated = true; + } + else { + ifstat = null; + } + elsestat = null; + } + else if (elsestat == post) { + elsestat = null; + } + else { + post = postst; + } + + if (elsestat == null) { + regedges = 1; // if without else + } + } + + ifedge = lstHeadSuccs.get(negated ? 0 : 1); + elseedge = (regedges == 2) ? lstHeadSuccs.get(negated ? 1 : 0) : null; + + iftype = (regedges == 2) ? IFTYPE_IFELSE : IFTYPE_IF; + + if (iftype == IFTYPE_IF) { + if (regedges == 0) { + StatEdge edge = lstHeadSuccs.get(0); + head.removeSuccessor(edge); + edge.setSource(this); + this.addSuccessor(edge); + } + else if (regedges == 1) { + StatEdge edge = lstHeadSuccs.get(negated ? 1 : 0); + head.removeSuccessor(edge); + } + } + + if (ifstat != null) { + stats.addWithKey(ifstat, ifstat.id); + } + + if (elsestat != null) { + stats.addWithKey(elsestat, elsestat.id); + } + + if (post == head) { + post = this; + } + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.type == TYPE_BASICBLOCK && head.getLastBasicType() == LASTBASICTYPE_IF) { + int regsize = head.getSuccessorEdges(StatEdge.TYPE_REGULAR).size(); + + Statement p = null; + + boolean ok = (regsize < 2); + if (!ok) { + List<Statement> lst = new ArrayList<Statement>(); + if (DecHelper.isChoiceStatement(head, lst)) { + p = lst.remove(0); + + for (Statement st : lst) { + if (st.isMonitorEnter()) { + return null; + } + } + + ok = DecHelper.checkStatementExceptions(lst); + } + } + + if (ok) { + return new IfStatement(head, regsize, p); + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + buf.append(indstr + headexprent.get(0).toJava(indent) + " {" + new_line_separator); + + if (ifstat == null) { + buf.append(InterpreterUtil.getIndentString(indent + 1)); + + if (ifedge.explicit) { + if (ifedge.getType() == StatEdge.TYPE_BREAK) { + // break + buf.append("break"); + } + else { + // continue + buf.append("continue"); + } + + if (ifedge.labeled) { + buf.append(" label" + ifedge.closure.id); + } + } + buf.append(";" + new_line_separator); + } + else { + buf.append(ExprProcessor.jmpWrapper(ifstat, indent + 1, true)); + } + + boolean elseif = false; + + if (elsestat != null) { + if (elsestat.type == Statement.TYPE_IF + && elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() && + !elsestat.isLabeled() && + (elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty() + || !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if + String content = ExprProcessor.jmpWrapper(elsestat, indent, false); + content = content.substring(indstr.length()); + + buf.append(indstr + "} else "); + buf.append(content); + + elseif = true; + } + else { + String content = ExprProcessor.jmpWrapper(elsestat, indent + 1, false); + + if (content.length() > 0) { + buf.append(indstr + "} else {" + new_line_separator); + buf.append(content); + } + } + } + + if (!elseif) { + buf.append(indstr + "}" + new_line_separator); + } + + return buf.toString(); + } + + public void initExprents() { + + IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size() - 1); + + if (negated) { + ifexpr = (IfExprent)ifexpr.copy(); + ifexpr.negateIf(); + } + + headexprent.set(0, ifexpr); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + super.replaceStatement(oldstat, newstat); + + if (ifstat == oldstat) { + ifstat = newstat; + } + + if (elsestat == oldstat) { + elsestat = newstat; + } + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + + if (iftype == IFTYPE_IF) { + ifedge = lstSuccs.get(0); + elseedge = null; + } + else { + StatEdge edge0 = lstSuccs.get(0); + StatEdge edge1 = lstSuccs.get(1); + if (edge0.getDestination() == ifstat) { + ifedge = edge0; + elseedge = edge1; + } + else { + ifedge = edge1; + elseedge = edge0; + } + } + } + + public Statement getSimpleCopy() { + + IfStatement is = new IfStatement(); + is.iftype = this.iftype; + is.negated = this.negated; + is.iffflag = this.iffflag; + + return is; + } + + public void initSimpleCopy() { + + first = stats.get(0); + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated) ? 0 : 1); + if (stats.size() > 1) { + ifstat = stats.get(1); + } + + if (iftype == IFTYPE_IFELSE) { + elseedge = lstSuccs.get(negated ? 1 : 0); + elsestat = stats.get(2); + } + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getElsestat() { + return elsestat; + } + + public void setElsestat(Statement elsestat) { + this.elsestat = elsestat; + } + + public Statement getIfstat() { + return ifstat; + } + + public void setIfstat(Statement ifstat) { + this.ifstat = ifstat; + } + + public boolean isNegated() { + return negated; + } + + public void setNegated(boolean negated) { + this.negated = negated; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public IfExprent getHeadexprent() { + return (IfExprent)headexprent.get(0); + } + + public boolean isIffflag() { + return iffflag; + } + + public void setIffflag(boolean iffflag) { + this.iffflag = iffflag; + } + + public void setElseEdge(StatEdge elseedge) { + this.elseedge = elseedge; + } + + public void setIfEdge(StatEdge ifedge) { + this.ifedge = ifedge; + } + + public StatEdge getIfEdge() { + return ifedge; + } + + public StatEdge getElseEdge() { + return elseedge; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java index c34888e..2a69447 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -19,31 +20,29 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; public class RootStatement extends Statement { - private Statement dummyExit; - - public RootStatement(Statement head, Statement dummyExit) { - - type = Statement.TYPE_ROOT; - - first = head; - this.dummyExit = dummyExit; - - stats.addWithKey(first, first.id); - first.setParent(this); - - } - - public String toJava(int indent) { - return ExprProcessor.listToJava(varDefinitions, indent)+ - first.toJava(indent); - } - - public Statement getDummyExit() { - return dummyExit; - } - - public void setDummyExit(Statement dummyExit) { - this.dummyExit = dummyExit; - } - + private Statement dummyExit; + + public RootStatement(Statement head, Statement dummyExit) { + + type = Statement.TYPE_ROOT; + + first = head; + this.dummyExit = dummyExit; + + stats.addWithKey(first, first.id); + first.setParent(this); + } + + public String toJava(int indent) { + return ExprProcessor.listToJava(varDefinitions, indent) + + first.toJava(indent); + } + + public Statement getDummyExit() { + return dummyExit; + } + + public void setDummyExit(Statement dummyExit) { + this.dummyExit = dummyExit; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index 79b0b19..8fd8a43 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -1,144 +1,144 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.Arrays; -import java.util.List; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.Arrays; +import java.util.List; + public class SequenceStatement extends Statement { - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private SequenceStatement() { - type = Statement.TYPE_SEQUENCE; - } - - public SequenceStatement(List<Statement> lst) { - - this(); - - lastBasicType = lst.get(lst.size()-1).getLastBasicType(); - - for(Statement st: lst) { - stats.addWithKey(st, st.id); - } - - first = stats.get(0); - } - - private SequenceStatement(Statement head, Statement tail) { - - this(Arrays.asList(new Statement[] {head, tail})); - - List<StatEdge> lstSuccs = tail.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstSuccs.isEmpty()) { - StatEdge edge = lstSuccs.get(0); - - if(edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() != head) { - post = edge.getDestination(); - } - } - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead2Block(Statement head) { - - if(head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { - return null; - } - - // at most one outgoing edge - StatEdge edge = null; - List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstSuccs.isEmpty()) { - edge = lstSuccs.get(0); - } - - if(edge != null && edge.getType() == StatEdge.TYPE_REGULAR) { - Statement stat = edge.getDestination(); - - if(stat != head && stat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() == 1 - && !stat.isMonitorEnter()) { - - if(stat.getLastBasicType() == Statement.LASTBASICTYPE_GENERAL) { - if(DecHelper.checkStatementExceptions(Arrays.asList(new Statement[] {head, stat}))) { - return new SequenceStatement(head, stat); - } - } - } - } - - return null; - } - - public String toJava(int indent) { - - StringBuilder buf = new StringBuilder(); - - String indstr = null; - boolean islabeled = isLabeled(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - - if(islabeled) { - indstr = InterpreterUtil.getIndentString(indent); - indent++; - buf.append(indstr+"label"+this.id+": {" + new_line_separator); - } - - boolean notempty = false; - - for(int i=0;i<stats.size();i++) { - - Statement st = stats.get(i); - - if(i>0 && notempty) { - buf.append(new_line_separator); - } - - String str = ExprProcessor.jmpWrapper(st, indent, false); - buf.append(str); - - notempty = (str.trim().length() > 0); - } - - if(islabeled) { - buf.append(indstr+"}" + new_line_separator); - } - - return buf.toString(); - } - - public Statement getSimpleCopy() { - return new SequenceStatement(); - } - + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private SequenceStatement() { + type = Statement.TYPE_SEQUENCE; + } + + public SequenceStatement(List<Statement> lst) { + + this(); + + lastBasicType = lst.get(lst.size() - 1).getLastBasicType(); + + for (Statement st : lst) { + stats.addWithKey(st, st.id); + } + + first = stats.get(0); + } + + private SequenceStatement(Statement head, Statement tail) { + + this(Arrays.asList(new Statement[]{head, tail})); + + List<StatEdge> lstSuccs = tail.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + + if (edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() != head) { + post = edge.getDestination(); + } + } + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead2Block(Statement head) { + + if (head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + return null; + } + + // at most one outgoing edge + StatEdge edge = null; + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstSuccs.isEmpty()) { + edge = lstSuccs.get(0); + } + + if (edge != null && edge.getType() == StatEdge.TYPE_REGULAR) { + Statement stat = edge.getDestination(); + + if (stat != head && stat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() == 1 + && !stat.isMonitorEnter()) { + + if (stat.getLastBasicType() == Statement.LASTBASICTYPE_GENERAL) { + if (DecHelper.checkStatementExceptions(Arrays.asList(new Statement[]{head, stat}))) { + return new SequenceStatement(head, stat); + } + } + } + } + + return null; + } + + public String toJava(int indent) { + + StringBuilder buf = new StringBuilder(); + + String indstr = null; + boolean islabeled = isLabeled(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if (islabeled) { + indstr = InterpreterUtil.getIndentString(indent); + indent++; + buf.append(indstr + "label" + this.id + ": {" + new_line_separator); + } + + boolean notempty = false; + + for (int i = 0; i < stats.size(); i++) { + + Statement st = stats.get(i); + + if (i > 0 && notempty) { + buf.append(new_line_separator); + } + + String str = ExprProcessor.jmpWrapper(st, indent, false); + buf.append(str); + + notempty = (str.trim().length() > 0); + } + + if (islabeled) { + buf.append(indstr + "}" + new_line_separator); + } + + return buf.toString(); + } + + public Statement getSimpleCopy() { + return new SequenceStatement(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index 2f64035..aceb2b8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -1,27 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -32,840 +25,848 @@ import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.*; + public class Statement { - - public static final int STATEDGE_ALL = 1 << 31; - public static final int STATEDGE_DIRECT_ALL = 1 << 30; - - public static final int DIRECTION_BACKWARD = 0; - public static final int DIRECTION_FORWARD = 1; - - public static final int TYPE_GENERAL = 0; - public static final int TYPE_IF = 2; - public static final int TYPE_DO = 5; - public static final int TYPE_SWITCH = 6; - public static final int TYPE_TRYCATCH = 7; - public static final int TYPE_BASICBLOCK = 8; - public static final int TYPE_FINALLY = 9; - public static final int TYPE_SYNCRONIZED = 10; - public static final int TYPE_PLACEHOLDER = 11; - public static final int TYPE_CATCHALL = 12; - public static final int TYPE_ROOT = 13; - public static final int TYPE_DUMMYEXIT = 14; - public static final int TYPE_SEQUENCE = 15; - - - public static final int LASTBASICTYPE_IF = 0; - public static final int LASTBASICTYPE_SWITCH = 1; - public static final int LASTBASICTYPE_GENERAL = 2; - - - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int type; - - public Integer id; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<Integer, List<StatEdge>>(); - private Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<Integer, List<StatEdge>>(); - - private Map<Integer, List<Statement>> mapSuccStates = new HashMap<Integer, List<Statement>>(); - private Map<Integer, List<Statement>> mapPredStates = new HashMap<Integer, List<Statement>>(); - - // statement as graph - protected VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); - - protected Statement parent; - - protected Statement first; - - protected List<Exprent> exprents; - - protected HashSet<StatEdge> labelEdges = new HashSet<StatEdge>(); - - protected List<Exprent> varDefinitions = new ArrayList<Exprent>(); - - // copied statement, s. deobfuscating of irreducible CFGs - private boolean copied = false; - - // relevant for the first stage of processing only - // set to null after initializing of the statement structure - - protected Statement post; - - protected int lastBasicType = LASTBASICTYPE_GENERAL; - - protected boolean isMonitorEnter; - - protected boolean containsMonitorExit; - - protected HashSet<Statement> continueSet = new HashSet<Statement>(); - - // ***************************************************************************** - // initializers - // ***************************************************************************** - - { - // set statement id - id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void clearTempInformation() { - - post = null; - continueSet = null; - - copied = false; - // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove - //lastBasicType = LASTBASICTYPE_GENERAL; - isMonitorEnter = false;; - containsMonitorExit = false; - - for(Map<Integer, List<StatEdge>> map : new Map[]{mapSuccEdges, mapPredEdges}) { - map.remove(StatEdge.TYPE_EXCEPTION); - - List<StatEdge> lst = map.get(STATEDGE_DIRECT_ALL); - if(lst != null) { - map.put(STATEDGE_ALL, new ArrayList<StatEdge>(lst)); - } else { - map.remove(STATEDGE_ALL); - } - } - - for(Map<Integer, List<Statement>> map : new Map[]{mapSuccStates, mapPredStates}) { - map.remove(StatEdge.TYPE_EXCEPTION); - - List<Statement> lst = map.get(STATEDGE_DIRECT_ALL); - if(lst != null) { - map.put(STATEDGE_ALL, new ArrayList<Statement>(lst)); - } else { - map.remove(STATEDGE_ALL); - } - } - - } - - public void collapseNodesToStatement(Statement stat) { - - Statement head = stat.getFirst(); - Statement post = stat.getPost(); - - VBStyleCollection<Statement, Integer> setNodes = stat.getStats(); - - // post edges - if(post != null) { - for(StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) { - if(stat.containsStatementStrict(edge.getSource())) { - edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); - stat.addLabeledEdge(edge); - } - } - } - - // regular head edges - for(StatEdge prededge : head.getAllPredecessorEdges()) { - - if(prededge.getType() != StatEdge.TYPE_EXCEPTION && - stat.containsStatementStrict(prededge.getSource())) { - prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE); - stat.addLabeledEdge(prededge); - } - - head.removePredecessor(prededge); - prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat); - stat.addPredecessor(prededge); - } - - if(setNodes.containsKey(first.id)) { - first = stat; - } - - // exception edges - Set<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); - for(Statement node : setNodes) { - setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); - } - - if(!setHandlers.isEmpty()) { - - for(StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { - Statement handler = edge.getDestination(); - - if(setHandlers.contains(handler)) { - if(!setNodes.containsKey(handler.id)) { - stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions())); - } - } - } - - for(Statement node : setNodes) { - for(StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { - if(setHandlers.contains(edge.getDestination())) { - node.removeSuccessor(edge); - } - } - } - } - - if(post!=null && !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant? - stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post)); - } - - - // adjust statement collection - for(Statement st: setNodes) { - stats.removeWithKey(st.id); - } - - stats.addWithKey(stat, stat.id); - - stat.setAllParent(); - stat.setParent(this); - - stat.buildContinueSet(); - // monitorenter and monitorexit - stat.buildMonitorFlags(); - - if(stat.type == Statement.TYPE_SWITCH) { - // special case switch, sorting leaf nodes - ((SwitchStatement)stat).sortEdgesAndNodes(); - } - - } - - public void setAllParent() { - for(Statement st: stats) { - st.setParent(this); - } - } - - public void addLabeledEdge(StatEdge edge) { - - if(edge.closure != null) { - edge.closure.getLabelEdges().remove(edge); - } - edge.closure = this; - this.getLabelEdges().add(edge); - } - - private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { - - Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; - Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; - - List<StatEdge> lst = mapEdges.get(edgetype); - if(lst == null) { - mapEdges.put(edgetype, lst = new ArrayList<StatEdge>()); - } - lst.add(edge); - - List<Statement> lstStates = mapStates.get(edgetype); - if(lstStates == null) { - mapStates.put(edgetype, lstStates = new ArrayList<Statement>()); - } - lstStates.add(direction==DIRECTION_BACKWARD?edge.getSource():edge.getDestination()); - } - - private void addEdgeInternal(int direction, StatEdge edge) { - - int type = edge.getType(); - - int[] arrtypes; - if(type == StatEdge.TYPE_EXCEPTION) { - arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; - } else { - arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; - } - - for(int edgetype : arrtypes) { - addEdgeDirectInternal(direction, edge, edgetype); - } - - } - - private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { - - Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; - Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; - - List<StatEdge> lst = mapEdges.get(edgetype); - if(lst != null) { - int index = lst.indexOf(edge); - if(index >= 0) { - lst.remove(index); - mapStates.get(edgetype).remove(index); - } - } - - } - - private void removeEdgeInternal(int direction, StatEdge edge) { - - int type = edge.getType(); - - int[] arrtypes; - if(type == StatEdge.TYPE_EXCEPTION) { - arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; - } else { - arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; - } - - for(int edgetype : arrtypes) { - removeEdgeDirectInternal(direction, edge, edgetype); - } - - } - - public void addPredecessor(StatEdge edge) { - addEdgeInternal(DIRECTION_BACKWARD, edge); - } - - public void removePredecessor(StatEdge edge) { - - if(edge == null) { // FIXME: redundant? - return; - } - - removeEdgeInternal(DIRECTION_BACKWARD, edge); - } - - public void addSuccessor(StatEdge edge) { - addEdgeInternal(DIRECTION_FORWARD, edge); - - if(edge.closure != null) { - edge.closure.getLabelEdges().add(edge); - } - - edge.getDestination().addPredecessor(edge); - } - - public void removeSuccessor(StatEdge edge) { - - if(edge == null) { - return; - } - - removeEdgeInternal(DIRECTION_FORWARD, edge); - - if(edge.closure != null) { - edge.closure.getLabelEdges().remove(edge); - } - - if(edge.getDestination() != null) { // TODO: redundant? - edge.getDestination().removePredecessor(edge); - } - } - - // TODO: make obsolete and remove - public void removeAllSuccessors(Statement stat) { - - if(stat == null) { - return; - } - - for(StatEdge edge : getAllSuccessorEdges()) { - if(edge.getDestination() == stat) { - removeSuccessor(edge); - } - } - } - - public HashSet<Statement> buildContinueSet() { - continueSet.clear(); - - for(Statement st: stats) { - continueSet.addAll(st.buildContinueSet()); - if(st != first) { - continueSet.remove(st.getBasichead()); - } - } - - for(StatEdge edge: getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) { - continueSet.add(edge.getDestination().getBasichead()); - } - - if(type == Statement.TYPE_DO) { - continueSet.remove(first.getBasichead()); - } - - return continueSet; - } - - public void buildMonitorFlags() { - - for(Statement st: stats) { - st.buildMonitorFlags(); - } - - switch(type) { - case TYPE_BASICBLOCK: - BasicBlockStatement bblock = (BasicBlockStatement)this; - InstructionSequence seq = bblock.getBlock().getSeq(); - - if(seq!=null && seq.length()>0) { - for(int i=0;i<seq.length();i++) { - if(seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { - containsMonitorExit = true; - break; - } - } - isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter); - } - break; - case TYPE_SEQUENCE: - case TYPE_IF: - containsMonitorExit = false; - for(Statement st: stats) { - containsMonitorExit |= st.isContainsMonitorExit(); - } - - break; - case TYPE_SYNCRONIZED: - case TYPE_ROOT: - case TYPE_GENERAL: - break; - default: - containsMonitorExit = false; - for(Statement st: stats) { - containsMonitorExit |= st.isContainsMonitorExit(); - } - } - } - - - public List<Statement> getReversePostOrderList() { - return getReversePostOrderList(first); - } - - public List<Statement> getReversePostOrderList(Statement stat) { - List<Statement> res = new ArrayList<Statement>(); - - addToReversePostOrderListIterative(stat, res); - - return res; - } - - public List<Statement> getPostReversePostOrderList() { - return getPostReversePostOrderList(null); - } - - public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) { - - List<Statement> res = new ArrayList<Statement>(); - - if(lstexits == null) { - StrongConnectivityHelper schelper = new StrongConnectivityHelper(this); - lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents()); - } - - HashSet<Statement> setVisited = new HashSet<Statement>(); - - for(Statement exit : lstexits) { - addToPostReversePostOrderList(exit, res, setVisited); - } - - if(res.size() != stats.size()) { - DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.ERROR); - - throw new RuntimeException("parsing failure!"); - } - - return res; - } - - public boolean containsStatement(Statement stat) { - return this == stat || containsStatementStrict(stat); - } - - public boolean containsStatementStrict(Statement stat) { - - if(stats.contains(stat)) { - return true; - } - - for(int i=0;i<stats.size();i++) { - if(stats.get(i).containsStatementStrict(stat)) { - return true; - } - } - - return false; - } - - // to be overwritten - public String toJava() { - return toJava(0); - } - - public String toJava(int indent) { - throw new RuntimeException("not implemented"); - } - - // TODO: make obsolete and remove - public List<Object> getSequentialObjects() { - return new ArrayList<Object>(stats); - } - - public void initExprents() { - ; // do nothing - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - ; // do nothing - } - - public Statement getSimpleCopy() { - throw new RuntimeException("not implemented"); - } - - public void initSimpleCopy() { - if(!stats.isEmpty()) { - first = stats.get(0); - } - } - - public void replaceStatement(Statement oldstat, Statement newstat) { - - for(StatEdge edge : oldstat.getAllPredecessorEdges()) { - oldstat.removePredecessor(edge); - edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat); - newstat.addPredecessor(edge); - } - - for(StatEdge edge : oldstat.getAllSuccessorEdges()) { - oldstat.removeSuccessor(edge); - edge.setSource(newstat); - newstat.addSuccessor(edge); - } - - int statindex = stats.getIndexByKey(oldstat.id); - stats.removeWithKey(oldstat.id); - stats.addWithKeyAndIndex(statindex, newstat, newstat.id); - - newstat.setParent(this); - newstat.post = oldstat.post; - - if(first == oldstat) { - first = newstat; - } - - List<StatEdge> lst = new ArrayList<StatEdge>(oldstat.getLabelEdges()); - - for(int i=lst.size()-1;i>=0;i--) { - StatEdge edge = lst.get(i); - if(edge.getSource() != newstat) { - newstat.addLabeledEdge(edge); - } else { - if(this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) { - edge.closure = null; - } else { - this.addLabeledEdge(edge); - } - } - } - - oldstat.getLabelEdges().clear(); - } - - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void addToReversePostOrderListIterative(Statement root, List<Statement> lst) { - - LinkedList<Statement> stackNode = new LinkedList<Statement>(); - LinkedList<Integer> stackIndex = new LinkedList<Integer>(); - HashSet<Statement> setVisited = new HashSet<Statement>(); - - stackNode.add(root); - stackIndex.add(0); - - while(!stackNode.isEmpty()) { - - Statement node = stackNode.getLast(); - int index = stackIndex.removeLast(); - - setVisited.add(node); - - List<StatEdge> lstEdges = node.getAllSuccessorEdges(); - - for(;index<lstEdges.size();index++) { - StatEdge edge = lstEdges.get(index); - Statement succ = edge.getDestination(); - - if(!setVisited.contains(succ) && - (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter? - - stackIndex.add(index+1); - - stackNode.add(succ); - stackIndex.add(0); - - break; - } - } - - if(index == lstEdges.size()) { - lst.add(0, node); - - stackNode.removeLast(); - } - } - - } - - - private void addToPostReversePostOrderList(Statement stat, List<Statement> lst, HashSet<Statement> setVisited) { - - if(setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible. - return; - } - setVisited.add(stat); - - for(StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) { - Statement pred = prededge.getSource(); - if(!setVisited.contains(pred)) { - addToPostReversePostOrderList(pred, lst, setVisited); - } - } - - lst.add(0, stat); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public void changeEdgeNode(int direction, StatEdge edge, Statement value) { - - Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; - Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; - - int type = edge.getType(); - - int[] arrtypes; - if(type == StatEdge.TYPE_EXCEPTION) { - arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; - } else { - arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; - } - - for(int edgetype : arrtypes) { - List<StatEdge> lst = mapEdges.get(edgetype); - if(lst != null) { - int index = lst.indexOf(edge); - if(index >= 0) { - mapStates.get(edgetype).set(index, value); - } - } - } - - if(direction == DIRECTION_BACKWARD) { - edge.setSource(value); - } else { - edge.setDestination(value); - } - - } - - public void changeEdgeType(int direction, StatEdge edge, int newtype) { - - int oldtype = edge.getType(); - if(oldtype == newtype) { - return; - } - - if(oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) { - throw new RuntimeException("Invalid edge type!"); - } - - removeEdgeDirectInternal(direction, edge, oldtype); - addEdgeDirectInternal(direction, edge, newtype); - - if(direction == DIRECTION_FORWARD) { - edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype); - } - - edge.setType(newtype); - } - - - private List<StatEdge> getEdges(int type, int direction) { - - Map<Integer, List<StatEdge>> map = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; - - List<StatEdge> res; - if((type & (type -1)) == 0) { - res = map.get(type); - res = res==null?new ArrayList<StatEdge>():new ArrayList<StatEdge>(res); - } else { - res = new ArrayList<StatEdge>(); - for(int edgetype : StatEdge.TYPES) { - if((type & edgetype) != 0) { - List<StatEdge> lst = map.get(edgetype); - if(lst != null) { - res.addAll(lst); - } - } - } - } - - return res; - } - - public List<Statement> getNeighbours(int type, int direction) { - - Map<Integer, List<Statement>> map = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; - - List<Statement> res; - if((type & (type -1)) == 0) { - res = map.get(type); - res = res==null?new ArrayList<Statement>():new ArrayList<Statement>(res); - } else { - res = new ArrayList<Statement>(); - for(int edgetype : StatEdge.TYPES) { - if((type & edgetype) != 0) { - List<Statement> lst = map.get(edgetype); - if(lst != null) { - res.addAll(lst); - } - } - } - } - - return res; - } - - public Set<Statement> getNeighboursSet(int type, int direction) { - return new HashSet<Statement>(getNeighbours(type, direction)); - } - - public List<StatEdge> getSuccessorEdges(int type) { - return getEdges(type, DIRECTION_FORWARD); - } - - public List<StatEdge> getPredecessorEdges(int type) { - return getEdges(type, DIRECTION_BACKWARD); - } - - public List<StatEdge> getAllSuccessorEdges() { - return getEdges(STATEDGE_ALL, DIRECTION_FORWARD); - } - - public List<StatEdge> getAllPredecessorEdges() { - return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD); - } - - public Statement getFirst() { - return first; - } - - public void setFirst(Statement first) { - this.first = first; - } - - public Statement getPost() { - return post; - } - - public void setPost(Statement post) { - this.post = post; - } - - public VBStyleCollection<Statement, Integer> getStats() { - return stats; - } - - public int getLastBasicType() { - return lastBasicType; - } - - public HashSet<Statement> getContinueSet() { - return continueSet; - } - - public boolean isContainsMonitorExit() { - return containsMonitorExit; - } - - public boolean isMonitorEnter() { - return isMonitorEnter; - } - - public BasicBlockStatement getBasichead() { - if(type == Statement.TYPE_BASICBLOCK) { - return (BasicBlockStatement)this; - } else { - return first.getBasichead(); - } - } - - public boolean isLabeled() { - - for(StatEdge edge: labelEdges) { - if(edge.labeled && edge.explicit) { // FIXME: consistent setting - return true; - } - } - return false; - } - - public boolean hasBasicSuccEdge() { - boolean res = type == Statement.TYPE_BASICBLOCK || (type == Statement.TYPE_IF && - ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) || - (type == Statement.TYPE_DO && ((DoStatement)this).getLooptype() != DoStatement.LOOP_DO); - - // FIXME: default switch - - return res; - } - - - public Statement getParent() { - return parent; - } - - public void setParent(Statement parent) { - this.parent = parent; - } - - public HashSet<StatEdge> getLabelEdges() { // FIXME: why HashSet? - return labelEdges; - } - - public List<Exprent> getVarDefinitions() { - return varDefinitions; - } - - public List<Exprent> getExprents() { - return exprents; - } - - public void setExprents(List<Exprent> exprents) { - this.exprents = exprents; - } - - public boolean isCopied() { - return copied; - } - - public void setCopied(boolean copied) { - this.copied = copied; - } - - // helper methods - public String toString() { - return id.toString(); - } + + public static final int STATEDGE_ALL = 1 << 31; + public static final int STATEDGE_DIRECT_ALL = 1 << 30; + + public static final int DIRECTION_BACKWARD = 0; + public static final int DIRECTION_FORWARD = 1; + + public static final int TYPE_GENERAL = 0; + public static final int TYPE_IF = 2; + public static final int TYPE_DO = 5; + public static final int TYPE_SWITCH = 6; + public static final int TYPE_TRYCATCH = 7; + public static final int TYPE_BASICBLOCK = 8; + public static final int TYPE_FINALLY = 9; + public static final int TYPE_SYNCRONIZED = 10; + public static final int TYPE_PLACEHOLDER = 11; + public static final int TYPE_CATCHALL = 12; + public static final int TYPE_ROOT = 13; + public static final int TYPE_DUMMYEXIT = 14; + public static final int TYPE_SEQUENCE = 15; + + + public static final int LASTBASICTYPE_IF = 0; + public static final int LASTBASICTYPE_SWITCH = 1; + public static final int LASTBASICTYPE_GENERAL = 2; + + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int type; + + public Integer id; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<Integer, List<StatEdge>>(); + private Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<Integer, List<StatEdge>>(); + + private Map<Integer, List<Statement>> mapSuccStates = new HashMap<Integer, List<Statement>>(); + private Map<Integer, List<Statement>> mapPredStates = new HashMap<Integer, List<Statement>>(); + + // statement as graph + protected VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); + + protected Statement parent; + + protected Statement first; + + protected List<Exprent> exprents; + + protected HashSet<StatEdge> labelEdges = new HashSet<StatEdge>(); + + protected List<Exprent> varDefinitions = new ArrayList<Exprent>(); + + // copied statement, s. deobfuscating of irreducible CFGs + private boolean copied = false; + + // relevant for the first stage of processing only + // set to null after initializing of the statement structure + + protected Statement post; + + protected int lastBasicType = LASTBASICTYPE_GENERAL; + + protected boolean isMonitorEnter; + + protected boolean containsMonitorExit; + + protected HashSet<Statement> continueSet = new HashSet<Statement>(); + + // ***************************************************************************** + // initializers + // ***************************************************************************** + + { + // set statement id + id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void clearTempInformation() { + + post = null; + continueSet = null; + + copied = false; + // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove + //lastBasicType = LASTBASICTYPE_GENERAL; + isMonitorEnter = false; + ; + containsMonitorExit = false; + + for (Map<Integer, List<StatEdge>> map : new Map[]{mapSuccEdges, mapPredEdges}) { + map.remove(StatEdge.TYPE_EXCEPTION); + + List<StatEdge> lst = map.get(STATEDGE_DIRECT_ALL); + if (lst != null) { + map.put(STATEDGE_ALL, new ArrayList<StatEdge>(lst)); + } + else { + map.remove(STATEDGE_ALL); + } + } + + for (Map<Integer, List<Statement>> map : new Map[]{mapSuccStates, mapPredStates}) { + map.remove(StatEdge.TYPE_EXCEPTION); + + List<Statement> lst = map.get(STATEDGE_DIRECT_ALL); + if (lst != null) { + map.put(STATEDGE_ALL, new ArrayList<Statement>(lst)); + } + else { + map.remove(STATEDGE_ALL); + } + } + } + + public void collapseNodesToStatement(Statement stat) { + + Statement head = stat.getFirst(); + Statement post = stat.getPost(); + + VBStyleCollection<Statement, Integer> setNodes = stat.getStats(); + + // post edges + if (post != null) { + for (StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) { + if (stat.containsStatementStrict(edge.getSource())) { + edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); + stat.addLabeledEdge(edge); + } + } + } + + // regular head edges + for (StatEdge prededge : head.getAllPredecessorEdges()) { + + if (prededge.getType() != StatEdge.TYPE_EXCEPTION && + stat.containsStatementStrict(prededge.getSource())) { + prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE); + stat.addLabeledEdge(prededge); + } + + head.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat); + stat.addPredecessor(prededge); + } + + if (setNodes.containsKey(first.id)) { + first = stat; + } + + // exception edges + Set<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); + for (Statement node : setNodes) { + setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); + } + + if (!setHandlers.isEmpty()) { + + for (StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { + Statement handler = edge.getDestination(); + + if (setHandlers.contains(handler)) { + if (!setNodes.containsKey(handler.id)) { + stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions())); + } + } + } + + for (Statement node : setNodes) { + for (StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { + if (setHandlers.contains(edge.getDestination())) { + node.removeSuccessor(edge); + } + } + } + } + + if (post != null && + !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant? + stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post)); + } + + + // adjust statement collection + for (Statement st : setNodes) { + stats.removeWithKey(st.id); + } + + stats.addWithKey(stat, stat.id); + + stat.setAllParent(); + stat.setParent(this); + + stat.buildContinueSet(); + // monitorenter and monitorexit + stat.buildMonitorFlags(); + + if (stat.type == Statement.TYPE_SWITCH) { + // special case switch, sorting leaf nodes + ((SwitchStatement)stat).sortEdgesAndNodes(); + } + } + + public void setAllParent() { + for (Statement st : stats) { + st.setParent(this); + } + } + + public void addLabeledEdge(StatEdge edge) { + + if (edge.closure != null) { + edge.closure.getLabelEdges().remove(edge); + } + edge.closure = this; + this.getLabelEdges().add(edge); + } + + private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { + + Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates; + + List<StatEdge> lst = mapEdges.get(edgetype); + if (lst == null) { + mapEdges.put(edgetype, lst = new ArrayList<StatEdge>()); + } + lst.add(edge); + + List<Statement> lstStates = mapStates.get(edgetype); + if (lstStates == null) { + mapStates.put(edgetype, lstStates = new ArrayList<Statement>()); + } + lstStates.add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination()); + } + + private void addEdgeInternal(int direction, StatEdge edge) { + + int type = edge.getType(); + + int[] arrtypes; + if (type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } + else { + arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for (int edgetype : arrtypes) { + addEdgeDirectInternal(direction, edge, edgetype); + } + } + + private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { + + Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates; + + List<StatEdge> lst = mapEdges.get(edgetype); + if (lst != null) { + int index = lst.indexOf(edge); + if (index >= 0) { + lst.remove(index); + mapStates.get(edgetype).remove(index); + } + } + } + + private void removeEdgeInternal(int direction, StatEdge edge) { + + int type = edge.getType(); + + int[] arrtypes; + if (type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } + else { + arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for (int edgetype : arrtypes) { + removeEdgeDirectInternal(direction, edge, edgetype); + } + } + + public void addPredecessor(StatEdge edge) { + addEdgeInternal(DIRECTION_BACKWARD, edge); + } + + public void removePredecessor(StatEdge edge) { + + if (edge == null) { // FIXME: redundant? + return; + } + + removeEdgeInternal(DIRECTION_BACKWARD, edge); + } + + public void addSuccessor(StatEdge edge) { + addEdgeInternal(DIRECTION_FORWARD, edge); + + if (edge.closure != null) { + edge.closure.getLabelEdges().add(edge); + } + + edge.getDestination().addPredecessor(edge); + } + + public void removeSuccessor(StatEdge edge) { + + if (edge == null) { + return; + } + + removeEdgeInternal(DIRECTION_FORWARD, edge); + + if (edge.closure != null) { + edge.closure.getLabelEdges().remove(edge); + } + + if (edge.getDestination() != null) { // TODO: redundant? + edge.getDestination().removePredecessor(edge); + } + } + + // TODO: make obsolete and remove + public void removeAllSuccessors(Statement stat) { + + if (stat == null) { + return; + } + + for (StatEdge edge : getAllSuccessorEdges()) { + if (edge.getDestination() == stat) { + removeSuccessor(edge); + } + } + } + + public HashSet<Statement> buildContinueSet() { + continueSet.clear(); + + for (Statement st : stats) { + continueSet.addAll(st.buildContinueSet()); + if (st != first) { + continueSet.remove(st.getBasichead()); + } + } + + for (StatEdge edge : getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) { + continueSet.add(edge.getDestination().getBasichead()); + } + + if (type == Statement.TYPE_DO) { + continueSet.remove(first.getBasichead()); + } + + return continueSet; + } + + public void buildMonitorFlags() { + + for (Statement st : stats) { + st.buildMonitorFlags(); + } + + switch (type) { + case TYPE_BASICBLOCK: + BasicBlockStatement bblock = (BasicBlockStatement)this; + InstructionSequence seq = bblock.getBlock().getSeq(); + + if (seq != null && seq.length() > 0) { + for (int i = 0; i < seq.length(); i++) { + if (seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { + containsMonitorExit = true; + break; + } + } + isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter); + } + break; + case TYPE_SEQUENCE: + case TYPE_IF: + containsMonitorExit = false; + for (Statement st : stats) { + containsMonitorExit |= st.isContainsMonitorExit(); + } + + break; + case TYPE_SYNCRONIZED: + case TYPE_ROOT: + case TYPE_GENERAL: + break; + default: + containsMonitorExit = false; + for (Statement st : stats) { + containsMonitorExit |= st.isContainsMonitorExit(); + } + } + } + + + public List<Statement> getReversePostOrderList() { + return getReversePostOrderList(first); + } + + public List<Statement> getReversePostOrderList(Statement stat) { + List<Statement> res = new ArrayList<Statement>(); + + addToReversePostOrderListIterative(stat, res); + + return res; + } + + public List<Statement> getPostReversePostOrderList() { + return getPostReversePostOrderList(null); + } + + public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) { + + List<Statement> res = new ArrayList<Statement>(); + + if (lstexits == null) { + StrongConnectivityHelper schelper = new StrongConnectivityHelper(this); + lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents()); + } + + HashSet<Statement> setVisited = new HashSet<Statement>(); + + for (Statement exit : lstexits) { + addToPostReversePostOrderList(exit, res, setVisited); + } + + if (res.size() != stats.size()) { + DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.ERROR); + + throw new RuntimeException("parsing failure!"); + } + + return res; + } + + public boolean containsStatement(Statement stat) { + return this == stat || containsStatementStrict(stat); + } + + public boolean containsStatementStrict(Statement stat) { + + if (stats.contains(stat)) { + return true; + } + + for (int i = 0; i < stats.size(); i++) { + if (stats.get(i).containsStatementStrict(stat)) { + return true; + } + } + + return false; + } + + // to be overwritten + public String toJava() { + return toJava(0); + } + + public String toJava(int indent) { + throw new RuntimeException("not implemented"); + } + + // TODO: make obsolete and remove + public List<Object> getSequentialObjects() { + return new ArrayList<Object>(stats); + } + + public void initExprents() { + ; // do nothing + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + ; // do nothing + } + + public Statement getSimpleCopy() { + throw new RuntimeException("not implemented"); + } + + public void initSimpleCopy() { + if (!stats.isEmpty()) { + first = stats.get(0); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + for (StatEdge edge : oldstat.getAllPredecessorEdges()) { + oldstat.removePredecessor(edge); + edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat); + newstat.addPredecessor(edge); + } + + for (StatEdge edge : oldstat.getAllSuccessorEdges()) { + oldstat.removeSuccessor(edge); + edge.setSource(newstat); + newstat.addSuccessor(edge); + } + + int statindex = stats.getIndexByKey(oldstat.id); + stats.removeWithKey(oldstat.id); + stats.addWithKeyAndIndex(statindex, newstat, newstat.id); + + newstat.setParent(this); + newstat.post = oldstat.post; + + if (first == oldstat) { + first = newstat; + } + + List<StatEdge> lst = new ArrayList<StatEdge>(oldstat.getLabelEdges()); + + for (int i = lst.size() - 1; i >= 0; i--) { + StatEdge edge = lst.get(i); + if (edge.getSource() != newstat) { + newstat.addLabeledEdge(edge); + } + else { + if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) { + edge.closure = null; + } + else { + this.addLabeledEdge(edge); + } + } + } + + oldstat.getLabelEdges().clear(); + } + + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void addToReversePostOrderListIterative(Statement root, List<Statement> lst) { + + LinkedList<Statement> stackNode = new LinkedList<Statement>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + HashSet<Statement> setVisited = new HashSet<Statement>(); + + stackNode.add(root); + stackIndex.add(0); + + while (!stackNode.isEmpty()) { + + Statement node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<StatEdge> lstEdges = node.getAllSuccessorEdges(); + + for (; index < lstEdges.size(); index++) { + StatEdge edge = lstEdges.get(index); + Statement succ = edge.getDestination(); + + if (!setVisited.contains(succ) && + (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter? + + stackIndex.add(index + 1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if (index == lstEdges.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + } + + + private void addToPostReversePostOrderList(Statement stat, List<Statement> lst, HashSet<Statement> setVisited) { + + if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible. + return; + } + setVisited.add(stat); + + for (StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) { + Statement pred = prededge.getSource(); + if (!setVisited.contains(pred)) { + addToPostReversePostOrderList(pred, lst, setVisited); + } + } + + lst.add(0, stat); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public void changeEdgeNode(int direction, StatEdge edge, Statement value) { + + Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates; + + int type = edge.getType(); + + int[] arrtypes; + if (type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } + else { + arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for (int edgetype : arrtypes) { + List<StatEdge> lst = mapEdges.get(edgetype); + if (lst != null) { + int index = lst.indexOf(edge); + if (index >= 0) { + mapStates.get(edgetype).set(index, value); + } + } + } + + if (direction == DIRECTION_BACKWARD) { + edge.setSource(value); + } + else { + edge.setDestination(value); + } + } + + public void changeEdgeType(int direction, StatEdge edge, int newtype) { + + int oldtype = edge.getType(); + if (oldtype == newtype) { + return; + } + + if (oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) { + throw new RuntimeException("Invalid edge type!"); + } + + removeEdgeDirectInternal(direction, edge, oldtype); + addEdgeDirectInternal(direction, edge, newtype); + + if (direction == DIRECTION_FORWARD) { + edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype); + } + + edge.setType(newtype); + } + + + private List<StatEdge> getEdges(int type, int direction) { + + Map<Integer, List<StatEdge>> map = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges; + + List<StatEdge> res; + if ((type & (type - 1)) == 0) { + res = map.get(type); + res = res == null ? new ArrayList<StatEdge>() : new ArrayList<StatEdge>(res); + } + else { + res = new ArrayList<StatEdge>(); + for (int edgetype : StatEdge.TYPES) { + if ((type & edgetype) != 0) { + List<StatEdge> lst = map.get(edgetype); + if (lst != null) { + res.addAll(lst); + } + } + } + } + + return res; + } + + public List<Statement> getNeighbours(int type, int direction) { + + Map<Integer, List<Statement>> map = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates; + + List<Statement> res; + if ((type & (type - 1)) == 0) { + res = map.get(type); + res = res == null ? new ArrayList<Statement>() : new ArrayList<Statement>(res); + } + else { + res = new ArrayList<Statement>(); + for (int edgetype : StatEdge.TYPES) { + if ((type & edgetype) != 0) { + List<Statement> lst = map.get(edgetype); + if (lst != null) { + res.addAll(lst); + } + } + } + } + + return res; + } + + public Set<Statement> getNeighboursSet(int type, int direction) { + return new HashSet<Statement>(getNeighbours(type, direction)); + } + + public List<StatEdge> getSuccessorEdges(int type) { + return getEdges(type, DIRECTION_FORWARD); + } + + public List<StatEdge> getPredecessorEdges(int type) { + return getEdges(type, DIRECTION_BACKWARD); + } + + public List<StatEdge> getAllSuccessorEdges() { + return getEdges(STATEDGE_ALL, DIRECTION_FORWARD); + } + + public List<StatEdge> getAllPredecessorEdges() { + return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD); + } + + public Statement getFirst() { + return first; + } + + public void setFirst(Statement first) { + this.first = first; + } + + public Statement getPost() { + return post; + } + + public void setPost(Statement post) { + this.post = post; + } + + public VBStyleCollection<Statement, Integer> getStats() { + return stats; + } + + public int getLastBasicType() { + return lastBasicType; + } + + public HashSet<Statement> getContinueSet() { + return continueSet; + } + + public boolean isContainsMonitorExit() { + return containsMonitorExit; + } + + public boolean isMonitorEnter() { + return isMonitorEnter; + } + + public BasicBlockStatement getBasichead() { + if (type == Statement.TYPE_BASICBLOCK) { + return (BasicBlockStatement)this; + } + else { + return first.getBasichead(); + } + } + + public boolean isLabeled() { + + for (StatEdge edge : labelEdges) { + if (edge.labeled && edge.explicit) { // FIXME: consistent setting + return true; + } + } + return false; + } + + public boolean hasBasicSuccEdge() { + boolean res = type == Statement.TYPE_BASICBLOCK || (type == Statement.TYPE_IF && + ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) || + (type == Statement.TYPE_DO && ((DoStatement)this).getLooptype() != DoStatement.LOOP_DO); + + // FIXME: default switch + + return res; + } + + + public Statement getParent() { + return parent; + } + + public void setParent(Statement parent) { + this.parent = parent; + } + + public HashSet<StatEdge> getLabelEdges() { // FIXME: why HashSet? + return labelEdges; + } + + public List<Exprent> getVarDefinitions() { + return varDefinitions; + } + + public List<Exprent> getExprents() { + return exprents; + } + + public void setExprents(List<Exprent> exprents) { + this.exprents = exprents; + } + + public boolean isCopied() { + return copied; + } + + public void setCopied(boolean copied) { + this.copied = copied; + } + + // helper methods + public String toString() { + return id.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index ec03886..379d00d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -1,26 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.code.SwitchInstruction; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -34,337 +28,340 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.*; + public class SwitchStatement extends Statement { - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private List<Statement> caseStatements = new ArrayList<Statement>(); - - private List<List<StatEdge>> caseEdges = new ArrayList<List<StatEdge>>(); - - private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); - - private StatEdge default_edge; - - private List<Exprent> headexprent = new ArrayList<Exprent>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - private SwitchStatement() { - type = TYPE_SWITCH; - - headexprent.add(null); - } - - private SwitchStatement(Statement head, Statement poststat) { - - this(); - - first = head; - stats.addWithKey(head, head.id); - - // find post node - Set<Statement> lstNodes = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); - - // cluster nodes - if(poststat != null) { - post = poststat; - lstNodes.remove(post); - } - - default_edge = head.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); - - for(Statement st: lstNodes) { - stats.addWithKey(st, st.id); - } - - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public static Statement isHead(Statement head) { - - if(head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) { - - List<Statement> lst = new ArrayList<Statement>(); - if(DecHelper.isChoiceStatement(head, lst)) { - Statement post = lst.remove(0); - - for(Statement st : lst) { - if(st.isMonitorEnter()) { - return null; - } - } - - if(DecHelper.checkStatementExceptions(lst)) { - return new SwitchStatement(head, post); - } - } - } - - return null; - } - - public String toJava(int indent) { - - String indstr = InterpreterUtil.getIndentString(indent); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuilder buf = new StringBuilder(); - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - buf.append(first.toJava(indent)); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); - - VarType switch_type = headexprent.get(0).getExprType(); - - for(int i=0;i<caseStatements.size();i++) { - - Statement stat = caseStatements.get(i); - List<StatEdge> edges = caseEdges.get(i); - List<ConstExprent> values = caseValues.get(i); - - for(int j=0;j<edges.size();j++) { - if(edges.get(j) == default_edge) { - buf.append(indstr+"default:" + new_line_separator); - } else { - ConstExprent value = (ConstExprent)values.get(j).copy(); - value.setConsttype(switch_type); - - buf.append(indstr+"case "+ value.toJava(indent)+":" + new_line_separator); - } - } - - buf.append(ExprProcessor.jmpWrapper(stat, indent+1, false)); - } - - buf.append(indstr+"}" + new_line_separator); - - return buf.toString(); - } - - public void initExprents() { - SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size()-1); - swexpr.setCaseValues(caseValues); - - headexprent.set(0, swexpr); - } - - public List<Object> getSequentialObjects() { - - List<Object> lst = new ArrayList<Object>(stats); - lst.add(1, headexprent.get(0)); - - return lst; - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(headexprent.get(0) == oldexpr) { - headexprent.set(0, newexpr); - } - } - - public void replaceStatement(Statement oldstat, Statement newstat) { - - for(int i=0;i<caseStatements.size();i++) { - if(caseStatements.get(i) == oldstat) { - caseStatements.set(i, newstat); - } - } - - super.replaceStatement(oldstat, newstat); - } - - public Statement getSimpleCopy() { - return new SwitchStatement(); - } - - public void initSimpleCopy() { - first = stats.get(0); - default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); - - sortEdgesAndNodes(); - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - public void sortEdgesAndNodes() { - - HashMap<StatEdge, Integer> mapEdgeIndex = new HashMap<StatEdge, Integer>(); - - List<StatEdge> lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); - for(int i=0;i<lstFirstSuccs.size();i++) { - mapEdgeIndex.put(lstFirstSuccs.get(i), i==0?lstFirstSuccs.size():i); - } - - // case values - BasicBlockStatement bbstat = (BasicBlockStatement)first; - int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues(); - - List<Statement> nodes = new ArrayList<Statement>(); - List<List<Integer>> edges = new ArrayList<List<Integer>>(); - - // collect regular edges - for(int i=1;i<stats.size();i++) { - - Statement stat = stats.get(i); - - List<Integer> lst = new ArrayList<Integer>(); - for(StatEdge edge: stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { - if(edge.getSource() == first) { - lst.add(mapEdgeIndex.get(edge)); - } - } - Collections.sort(lst); - - nodes.add(stat); - edges.add(lst); - } - - // collect exit edges - List<StatEdge> lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE); - while(!lstExitEdges.isEmpty()) { - StatEdge edge = lstExitEdges.get(0); - - List<Integer> lst = new ArrayList<Integer>(); - for(int i=lstExitEdges.size()-1;i>=0;i--) { - StatEdge edgeTemp = lstExitEdges.get(i); - if(edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) { - lst.add(mapEdgeIndex.get(edgeTemp)); - lstExitEdges.remove(i); - } - } - Collections.sort(lst); - - nodes.add(null); - edges.add(lst); - } - - // sort edges (bubblesort) - for(int i=0;i<edges.size()-1;i++) { - for(int j=edges.size()-1;j>i;j--) { - if(edges.get(j-1).get(0) > edges.get(j).get(0)) { - edges.set(j, edges.set(j-1, edges.get(j))); - nodes.set(j, nodes.set(j-1, nodes.get(j))); - } - } - } - - // sort statement cliques - for(int index = 0; index < nodes.size(); index++) { - Statement stat = nodes.get(index); - - if(stat != null) { - HashSet<Statement> setPreds = new HashSet<Statement>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); - setPreds.remove(first); - - if(!setPreds.isEmpty()) { - Statement pred = setPreds.iterator().next(); // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code. - for(int j=0;j<nodes.size();j++) { - if(j != (index - 1) && nodes.get(j) == pred) { - nodes.add(j+1, stat); - edges.add(j+1, edges.get(index)); - - if(j > index) { - nodes.remove(index); - edges.remove(index); - index--; - } else { - nodes.remove(index + 1); - edges.remove(index + 1); - } - break; - } - } - } - } - } - - // translate indices back into edges - List<List<StatEdge>> lstEdges = new ArrayList<List<StatEdge>>(); - List<List<ConstExprent>> lstValues = new ArrayList<List<ConstExprent>>(); - - for(List<Integer> lst: edges) { - List<StatEdge> lste = new ArrayList<StatEdge>(); - List<ConstExprent> lstv = new ArrayList<ConstExprent>(); - - List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); - for(Integer in: lst) { - int index = in==lstSuccs.size()?0:in; - - lste.add(lstSuccs.get(index)); - lstv.add(index==0?null:new ConstExprent(values[index-1], false)); - } - lstEdges.add(lste); - lstValues.add(lstv); - } - - // replace null statements with dummy basic blocks - for(int i=0;i<nodes.size();i++) { - if(nodes.get(i) == null) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - - StatEdge sample_edge = lstEdges.get(i).get(0); - - bstat.addSuccessor(new StatEdge(sample_edge.getType(), bstat, sample_edge.getDestination(), sample_edge.closure)); - - for(StatEdge edge : lstEdges.get(i)) { - - edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); - edge.closure.getLabelEdges().remove(edge); - - edge.getDestination().removePredecessor(edge); - edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, bstat); - bstat.addPredecessor(edge); - } - - nodes.set(i, bstat); - stats.addWithKey(bstat, bstat.id); - bstat.setParent(this); - } - } - - caseStatements = nodes; - caseEdges = lstEdges; - caseValues = lstValues; - } - - public List<Exprent> getHeadexprentList() { - return headexprent; - } - - public Exprent getHeadexprent() { - return headexprent.get(0); - } - - public List<List<StatEdge>> getCaseEdges() { - return caseEdges; - } - - public List<Statement> getCaseStatements() { - return caseStatements; - } - - public StatEdge getDefault_edge() { - return default_edge; - } - - public List<List<ConstExprent>> getCaseValues() { - return caseValues; - } + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private List<Statement> caseStatements = new ArrayList<Statement>(); + + private List<List<StatEdge>> caseEdges = new ArrayList<List<StatEdge>>(); + + private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); + + private StatEdge default_edge; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private SwitchStatement() { + type = TYPE_SWITCH; + + headexprent.add(null); + } + + private SwitchStatement(Statement head, Statement poststat) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + // find post node + Set<Statement> lstNodes = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); + + // cluster nodes + if (poststat != null) { + post = poststat; + lstNodes.remove(post); + } + + default_edge = head.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + for (Statement st : lstNodes) { + stats.addWithKey(st, st.id); + } + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) { + + List<Statement> lst = new ArrayList<Statement>(); + if (DecHelper.isChoiceStatement(head, lst)) { + Statement post = lst.remove(0); + + for (Statement st : lst) { + if (st.isMonitorEnter()) { + return null; + } + } + + if (DecHelper.checkStatementExceptions(lst)) { + return new SwitchStatement(head, post); + } + } + } + + return null; + } + + public String toJava(int indent) { + + String indstr = InterpreterUtil.getIndentString(indent); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuilder buf = new StringBuilder(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + buf.append(indstr + headexprent.get(0).toJava(indent) + " {" + new_line_separator); + + VarType switch_type = headexprent.get(0).getExprType(); + + for (int i = 0; i < caseStatements.size(); i++) { + + Statement stat = caseStatements.get(i); + List<StatEdge> edges = caseEdges.get(i); + List<ConstExprent> values = caseValues.get(i); + + for (int j = 0; j < edges.size(); j++) { + if (edges.get(j) == default_edge) { + buf.append(indstr + "default:" + new_line_separator); + } + else { + ConstExprent value = (ConstExprent)values.get(j).copy(); + value.setConsttype(switch_type); + + buf.append(indstr + "case " + value.toJava(indent) + ":" + new_line_separator); + } + } + + buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false)); + } + + buf.append(indstr + "}" + new_line_separator); + + return buf.toString(); + } + + public void initExprents() { + SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1); + swexpr.setCaseValues(caseValues); + + headexprent.set(0, swexpr); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + for (int i = 0; i < caseStatements.size(); i++) { + if (caseStatements.get(i) == oldstat) { + caseStatements.set(i, newstat); + } + } + + super.replaceStatement(oldstat, newstat); + } + + public Statement getSimpleCopy() { + return new SwitchStatement(); + } + + public void initSimpleCopy() { + first = stats.get(0); + default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + sortEdgesAndNodes(); + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + public void sortEdgesAndNodes() { + + HashMap<StatEdge, Integer> mapEdgeIndex = new HashMap<StatEdge, Integer>(); + + List<StatEdge> lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for (int i = 0; i < lstFirstSuccs.size(); i++) { + mapEdgeIndex.put(lstFirstSuccs.get(i), i == 0 ? lstFirstSuccs.size() : i); + } + + // case values + BasicBlockStatement bbstat = (BasicBlockStatement)first; + int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues(); + + List<Statement> nodes = new ArrayList<Statement>(); + List<List<Integer>> edges = new ArrayList<List<Integer>>(); + + // collect regular edges + for (int i = 1; i < stats.size(); i++) { + + Statement stat = stats.get(i); + + List<Integer> lst = new ArrayList<Integer>(); + for (StatEdge edge : stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + if (edge.getSource() == first) { + lst.add(mapEdgeIndex.get(edge)); + } + } + Collections.sort(lst); + + nodes.add(stat); + edges.add(lst); + } + + // collect exit edges + List<StatEdge> lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE); + while (!lstExitEdges.isEmpty()) { + StatEdge edge = lstExitEdges.get(0); + + List<Integer> lst = new ArrayList<Integer>(); + for (int i = lstExitEdges.size() - 1; i >= 0; i--) { + StatEdge edgeTemp = lstExitEdges.get(i); + if (edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) { + lst.add(mapEdgeIndex.get(edgeTemp)); + lstExitEdges.remove(i); + } + } + Collections.sort(lst); + + nodes.add(null); + edges.add(lst); + } + + // sort edges (bubblesort) + for (int i = 0; i < edges.size() - 1; i++) { + for (int j = edges.size() - 1; j > i; j--) { + if (edges.get(j - 1).get(0) > edges.get(j).get(0)) { + edges.set(j, edges.set(j - 1, edges.get(j))); + nodes.set(j, nodes.set(j - 1, nodes.get(j))); + } + } + } + + // sort statement cliques + for (int index = 0; index < nodes.size(); index++) { + Statement stat = nodes.get(index); + + if (stat != null) { + HashSet<Statement> setPreds = new HashSet<Statement>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); + setPreds.remove(first); + + if (!setPreds.isEmpty()) { + Statement pred = + setPreds.iterator().next(); // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code. + for (int j = 0; j < nodes.size(); j++) { + if (j != (index - 1) && nodes.get(j) == pred) { + nodes.add(j + 1, stat); + edges.add(j + 1, edges.get(index)); + + if (j > index) { + nodes.remove(index); + edges.remove(index); + index--; + } + else { + nodes.remove(index + 1); + edges.remove(index + 1); + } + break; + } + } + } + } + } + + // translate indices back into edges + List<List<StatEdge>> lstEdges = new ArrayList<List<StatEdge>>(); + List<List<ConstExprent>> lstValues = new ArrayList<List<ConstExprent>>(); + + for (List<Integer> lst : edges) { + List<StatEdge> lste = new ArrayList<StatEdge>(); + List<ConstExprent> lstv = new ArrayList<ConstExprent>(); + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for (Integer in : lst) { + int index = in == lstSuccs.size() ? 0 : in; + + lste.add(lstSuccs.get(index)); + lstv.add(index == 0 ? null : new ConstExprent(values[index - 1], false)); + } + lstEdges.add(lste); + lstValues.add(lstv); + } + + // replace null statements with dummy basic blocks + for (int i = 0; i < nodes.size(); i++) { + if (nodes.get(i) == null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + + StatEdge sample_edge = lstEdges.get(i).get(0); + + bstat.addSuccessor(new StatEdge(sample_edge.getType(), bstat, sample_edge.getDestination(), sample_edge.closure)); + + for (StatEdge edge : lstEdges.get(i)) { + + edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + + edge.getDestination().removePredecessor(edge); + edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, bstat); + bstat.addPredecessor(edge); + } + + nodes.set(i, bstat); + stats.addWithKey(bstat, bstat.id); + bstat.setParent(this); + } + } + + caseStatements = nodes; + caseEdges = lstEdges; + caseValues = lstValues; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public Exprent getHeadexprent() { + return headexprent.get(0); + } + + public List<List<StatEdge>> getCaseEdges() { + return caseEdges; + } + + public List<Statement> getCaseStatements() { + return caseStatements; + } + + public StatEdge getDefault_edge() { + return default_edge; + } + public List<List<ConstExprent>> getCaseValues() { + return caseValues; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java index 5336f12..3fcb042 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java @@ -1,22 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.stats; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper; @@ -24,131 +22,132 @@ import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import java.util.ArrayList; +import java.util.List; + public class SynchronizedStatement extends Statement { - private Statement body; - - private List<Exprent> headexprent = new ArrayList<Exprent>(); - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public SynchronizedStatement() { - type = TYPE_SYNCRONIZED; - - headexprent.add(null); - } - - public SynchronizedStatement(Statement head, Statement body, Statement exc) { - - this(); - - first = head; - stats.addWithKey(head, head.id); - - this.body = body; - stats.addWithKey(body, body.id); - - stats.addWithKey(exc, exc.id); - - List<StatEdge> lstSuccs = body.getSuccessorEdges(STATEDGE_DIRECT_ALL); - if(!lstSuccs.isEmpty()) { - StatEdge edge = lstSuccs.get(0); - if(edge.getType() == StatEdge.TYPE_REGULAR) { - post = edge.getDestination(); - } - } - - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public String toJava(int indent) { - String indstr = InterpreterUtil.getIndentString(indent); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuffer buf = new StringBuffer(); - buf.append(ExprProcessor.listToJava(varDefinitions, indent)); - buf.append(first.toJava(indent)); - - if(isLabeled()) { - buf.append(indstr+"label"+this.id+":" + new_line_separator); - } - - buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); - buf.append(ExprProcessor.jmpWrapper(body, indent+1, true)); - buf.append(indstr+"}" + new_line_separator); - - return buf.toString(); - } - - public void initExprents() { - headexprent.set(0, first.getExprents().remove(first.getExprents().size()-1)); - } - - public List<Object> getSequentialObjects() { - - List<Object> lst = new ArrayList<Object>(stats); - lst.add(1, headexprent.get(0)); - - return lst; - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if(headexprent.get(0) == oldexpr) { - headexprent.set(0, newexpr); - } - } - - public void replaceStatement(Statement oldstat, Statement newstat) { - - if(body == oldstat) { - body = newstat; - } - - super.replaceStatement(oldstat, newstat); - } - - public void removeExc() { - Statement exc = stats.get(2); - SequenceHelper.destroyStatementContent(exc, true); - - stats.removeWithKey(exc.id); - } - - public Statement getSimpleCopy() { - return new SynchronizedStatement(); - } - - public void initSimpleCopy() { - first = stats.get(0); - body = stats.get(1); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Statement getBody() { - return body; - } - - public void setBody(Statement body) { - this.body = body; - } - - public List<Exprent> getHeadexprentList() { - return headexprent; - } - - public Exprent getHeadexprent() { - return headexprent.get(0); - } - + private Statement body; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public SynchronizedStatement() { + type = TYPE_SYNCRONIZED; + + headexprent.add(null); + } + + public SynchronizedStatement(Statement head, Statement body, Statement exc) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + this.body = body; + stats.addWithKey(body, body.id); + + stats.addWithKey(exc, exc.id); + + List<StatEdge> lstSuccs = body.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if (!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + if (edge.getType() == StatEdge.TYPE_REGULAR) { + post = edge.getDestination(); + } + } + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if (isLabeled()) { + buf.append(indstr + "label" + this.id + ":" + new_line_separator); + } + + buf.append(indstr + headexprent.get(0).toJava(indent) + " {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(body, indent + 1, true)); + buf.append(indstr + "}" + new_line_separator); + + return buf.toString(); + } + + public void initExprents() { + headexprent.set(0, first.getExprents().remove(first.getExprents().size() - 1)); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + if (body == oldstat) { + body = newstat; + } + + super.replaceStatement(oldstat, newstat); + } + + public void removeExc() { + Statement exc = stats.get(2); + SequenceHelper.destroyStatementContent(exc, true); + + stats.removeWithKey(exc.id); + } + + public Statement getSimpleCopy() { + return new SynchronizedStatement(); + } + + public void initSimpleCopy() { + first = stats.get(0); + body = stats.get(1); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getBody() { + return body; + } + + public void setBody(Statement body) { + this.body = body; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public Exprent getHeadexprent() { + return headexprent.get(0); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java index 5519797..6e7c967 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java @@ -1,57 +1,57 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.ArrayList; +import java.util.List; + public class CheckTypesResult { - private List<ExprentTypePair> lstMaxTypeExprents = new ArrayList<ExprentTypePair>(); - - private List<ExprentTypePair> lstMinTypeExprents = new ArrayList<ExprentTypePair>(); - - public void addMaxTypeExprent(Exprent exprent, VarType type) { - lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null)); - } - - public void addMinTypeExprent(Exprent exprent, VarType type) { - lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null)); - } - - public List<ExprentTypePair> getLstMaxTypeExprents() { - return lstMaxTypeExprents; - } - - public List<ExprentTypePair> getLstMinTypeExprents() { - return lstMinTypeExprents; - } - - public class ExprentTypePair { - public Exprent exprent; - public VarType type; - public VarType desttype; - - public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) { - this.exprent = exprent; - this.type = type; - this.desttype = desttype; - } - } - + private List<ExprentTypePair> lstMaxTypeExprents = new ArrayList<ExprentTypePair>(); + + private List<ExprentTypePair> lstMinTypeExprents = new ArrayList<ExprentTypePair>(); + + public void addMaxTypeExprent(Exprent exprent, VarType type) { + lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null)); + } + + public void addMinTypeExprent(Exprent exprent, VarType type) { + lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null)); + } + + public List<ExprentTypePair> getLstMaxTypeExprents() { + return lstMaxTypeExprents; + } + + public List<ExprentTypePair> getLstMinTypeExprents() { + return lstMinTypeExprents; + } + + public class ExprentTypePair { + public Exprent exprent; + public VarType type; + public VarType desttype; + + public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) { + this.exprent = exprent; + this.type = type; + this.desttype = desttype; + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 5c04ccd..7e5a56f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -1,29 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; @@ -38,321 +29,332 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import java.util.*; +import java.util.Map.Entry; + public class VarDefinitionHelper { - private HashMap<Integer, Statement> mapVarDefStatements; - - // statement.id, defined vars - private HashMap<Integer, HashSet<Integer>> mapStatementVars; - - private HashSet<Integer> implDefVars; - - private VarProcessor varproc; - - public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { - - mapVarDefStatements = new HashMap<Integer, Statement>(); - mapStatementVars = new HashMap<Integer, HashSet<Integer>>(); - implDefVars = new HashSet<Integer>(); - - this.varproc = varproc; - - VarNamesCollector vc = DecompilerContext.getVarncollector(); - - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - int paramcount = 0; - if(thisvar) { - paramcount = 1; - } - paramcount += md.params.length; - - - // method parameters are implicitly defined - int varindex = 0; - for(int i=0;i<paramcount;i++) { - implDefVars.add(varindex); - varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); - - if(thisvar) { - if(i==0) { - varindex++; - } else { - varindex+=md.params[i-1].stack_size; - } - } else { - varindex+=md.params[i].stack_size; - } - } - - if(thisvar) { - StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS); - - varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName); - varproc.setVarName(new VarVersionPaar(0, 0), "this"); - vc.addName("this"); - } - - // catch variables are implicitly defined - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(root); - - while(!stack.isEmpty()) { - Statement st = stack.removeFirst(); - - List<VarExprent> lstVars = null; - if(st.type == Statement.TYPE_CATCHALL) { - lstVars = ((CatchAllStatement)st).getVars(); - } else if(st.type == Statement.TYPE_TRYCATCH) { - lstVars = ((CatchStatement)st).getVars(); - } - - if(lstVars != null) { - for(VarExprent var: lstVars) { - implDefVars.add(var.getIndex()); - varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex())); - var.setDefinition(true); - } - } - - stack.addAll(st.getStats()); - } - - initStatement(root); - } - - - public void setVarDefinitions() { - - VarNamesCollector vc = DecompilerContext.getVarncollector(); - - Iterator<Entry<Integer, Statement>> it = mapVarDefStatements.entrySet().iterator(); - while(it.hasNext()) { - Entry<Integer, Statement> en = it.next(); - - Statement stat = en.getValue(); - Integer index = en.getKey(); - - if(implDefVars.contains(index)) { - // already implicitly defined - continue; - } - - varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index)); - - // special case for - if(stat.type == Statement.TYPE_DO) { - DoStatement dstat = (DoStatement)stat; - if(dstat.getLooptype() == DoStatement.LOOP_FOR) { - - if(dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) { - continue; - } else { - List<Exprent> lstSpecial = Arrays.asList(new Exprent[]{dstat.getConditionExprent(), dstat.getIncExprent()}); - for(VarExprent var: getAllVars(lstSpecial)) { - if(var.getIndex() == index.intValue()) { - stat = stat.getParent(); - break; - } - } - } - } - } - - - Statement first = findFirstBlock(stat, index); - - List<Exprent> lst; - if(first == null) { - lst = stat.getVarDefinitions(); - } else if(first.getExprents() == null) { - lst = first.getVarDefinitions(); - } else { - lst = first.getExprents(); - } - - - boolean defset = false; - - // search for the first assignement to var [index] - int addindex = 0; - for(Exprent expr: lst) { - if(setDefinition(expr, index)) { - defset = true; - break; - } else { - boolean foundvar = false; - for(Exprent exp: expr.getAllExprents(true)) { - if(exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) { - foundvar = true; - break; - } - } - if(foundvar) { - break; - } - } - addindex++; - } - - if(!defset) { - VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc); - var.setDefinition(true); - - lst.add(addindex, var); - } - - } - - } - - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private Statement findFirstBlock(Statement stat, Integer varindex) { - - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(stat); - - while(!stack.isEmpty()) { - Statement st = stack.remove(0); - - if(stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) { - - if(st.isLabeled() && !stack.isEmpty()) { - return st; - } - - if(st.getExprents() != null) { - return st; - } else { - stack.clear(); - - switch(st.type) { - case Statement.TYPE_SEQUENCE: - stack.addAll(0, st.getStats()); - break; - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - stack.add(st.getFirst()); - break; - default: - return st; - } - } - } - } - - return null; - } - - private Set<Integer> initStatement(Statement stat) { - - HashMap<Integer, Integer> mapCount = new HashMap<Integer, Integer>(); - - List<VarExprent> condlst; - - if(stat.getExprents() == null) { - - // recurse on children statements - List<Integer> childVars = new ArrayList<Integer>(); - List<Exprent> currVars = new ArrayList<Exprent>(); - - for(Object obj: stat.getSequentialObjects()) { - if(obj instanceof Statement) { - Statement st = (Statement)obj; - childVars.addAll(initStatement(st)); - - if(st.type == DoStatement.TYPE_DO) { - DoStatement dost = (DoStatement)st; - if(dost.getLooptype() != DoStatement.LOOP_FOR && - dost.getLooptype() != DoStatement.LOOP_DO) { - currVars.add(dost.getConditionExprent()); - } - } else if(st.type == DoStatement.TYPE_CATCHALL) { - CatchAllStatement fin = (CatchAllStatement)st; - if(fin.isFinally() && fin.getMonitor() != null) { - currVars.add(fin.getMonitor()); - } - } - } else if(obj instanceof Exprent) { - currVars.add((Exprent)obj); - } - } - - // children statements - for(Integer index: childVars) { - Integer count = mapCount.get(index); - if(count == null) { - count = new Integer(0); - } - mapCount.put(index, new Integer(count.intValue()+1)); - } - - condlst = getAllVars(currVars); - } else { - condlst = getAllVars(stat.getExprents()); - } - - // this statement - for(VarExprent var: condlst) { - mapCount.put(new Integer(var.getIndex()), new Integer(2)); - } - - - HashSet<Integer> set = new HashSet<Integer>(mapCount.keySet()); - - // put all variables defined in this statement into the set - Iterator<Entry<Integer, Integer>> itMult = mapCount.entrySet().iterator(); - while(itMult.hasNext()) { - Entry<Integer, Integer> en = itMult.next(); - if(en.getValue().intValue()>1) { - mapVarDefStatements.put(en.getKey(), stat); - } - } - - mapStatementVars.put(stat.id, set); - - return set; - } - - private List<VarExprent> getAllVars(List<Exprent> lst) { - - List<VarExprent> res = new ArrayList<VarExprent>(); - List<Exprent> listTemp = new ArrayList<Exprent>(); - - for(Exprent expr: lst) { - listTemp.addAll(expr.getAllExprents(true)); - listTemp.add(expr); - } - - for(Exprent exprent: listTemp) { - if(exprent.type == Exprent.EXPRENT_VAR) { - res.add((VarExprent)exprent); - } - } - - return res; - } - - private boolean setDefinition(Exprent expr, Integer index) { - if(expr.type == Exprent.EXPRENT_ASSIGNMENT) { - Exprent left = ((AssignmentExprent)expr).getLeft(); - if(left.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)left; - if(var.getIndex() == index.intValue()) { - var.setDefinition(true); - return true; - } - } - } - return false; - } + private HashMap<Integer, Statement> mapVarDefStatements; + + // statement.id, defined vars + private HashMap<Integer, HashSet<Integer>> mapStatementVars; + + private HashSet<Integer> implDefVars; + + private VarProcessor varproc; + + public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { + + mapVarDefStatements = new HashMap<Integer, Statement>(); + mapStatementVars = new HashMap<Integer, HashSet<Integer>>(); + implDefVars = new HashSet<Integer>(); + + this.varproc = varproc; + + VarNamesCollector vc = DecompilerContext.getVarncollector(); + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = 0; + if (thisvar) { + paramcount = 1; + } + paramcount += md.params.length; + + + // method parameters are implicitly defined + int varindex = 0; + for (int i = 0; i < paramcount; i++) { + implDefVars.add(varindex); + varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); + + if (thisvar) { + if (i == 0) { + varindex++; + } + else { + varindex += md.params[i - 1].stack_size; + } + } + else { + varindex += md.params[i].stack_size; + } + } + + if (thisvar) { + StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS); + + varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName); + varproc.setVarName(new VarVersionPaar(0, 0), "this"); + vc.addName("this"); + } + + // catch variables are implicitly defined + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while (!stack.isEmpty()) { + Statement st = stack.removeFirst(); + + List<VarExprent> lstVars = null; + if (st.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)st).getVars(); + } + else if (st.type == Statement.TYPE_TRYCATCH) { + lstVars = ((CatchStatement)st).getVars(); + } + + if (lstVars != null) { + for (VarExprent var : lstVars) { + implDefVars.add(var.getIndex()); + varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex())); + var.setDefinition(true); + } + } + + stack.addAll(st.getStats()); + } + + initStatement(root); + } + + + public void setVarDefinitions() { + + VarNamesCollector vc = DecompilerContext.getVarncollector(); + + Iterator<Entry<Integer, Statement>> it = mapVarDefStatements.entrySet().iterator(); + while (it.hasNext()) { + Entry<Integer, Statement> en = it.next(); + + Statement stat = en.getValue(); + Integer index = en.getKey(); + + if (implDefVars.contains(index)) { + // already implicitly defined + continue; + } + + varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index)); + + // special case for + if (stat.type == Statement.TYPE_DO) { + DoStatement dstat = (DoStatement)stat; + if (dstat.getLooptype() == DoStatement.LOOP_FOR) { + + if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) { + continue; + } + else { + List<Exprent> lstSpecial = Arrays.asList(new Exprent[]{dstat.getConditionExprent(), dstat.getIncExprent()}); + for (VarExprent var : getAllVars(lstSpecial)) { + if (var.getIndex() == index.intValue()) { + stat = stat.getParent(); + break; + } + } + } + } + } + + + Statement first = findFirstBlock(stat, index); + + List<Exprent> lst; + if (first == null) { + lst = stat.getVarDefinitions(); + } + else if (first.getExprents() == null) { + lst = first.getVarDefinitions(); + } + else { + lst = first.getExprents(); + } + + + boolean defset = false; + + // search for the first assignement to var [index] + int addindex = 0; + for (Exprent expr : lst) { + if (setDefinition(expr, index)) { + defset = true; + break; + } + else { + boolean foundvar = false; + for (Exprent exp : expr.getAllExprents(true)) { + if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) { + foundvar = true; + break; + } + } + if (foundvar) { + break; + } + } + addindex++; + } + + if (!defset) { + VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc); + var.setDefinition(true); + + lst.add(addindex, var); + } + } + } + + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private Statement findFirstBlock(Statement stat, Integer varindex) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(stat); + + while (!stack.isEmpty()) { + Statement st = stack.remove(0); + + if (stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) { + + if (st.isLabeled() && !stack.isEmpty()) { + return st; + } + + if (st.getExprents() != null) { + return st; + } + else { + stack.clear(); + + switch (st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; + } + } + } + } + + return null; + } + + private Set<Integer> initStatement(Statement stat) { + + HashMap<Integer, Integer> mapCount = new HashMap<Integer, Integer>(); + + List<VarExprent> condlst; + + if (stat.getExprents() == null) { + + // recurse on children statements + List<Integer> childVars = new ArrayList<Integer>(); + List<Exprent> currVars = new ArrayList<Exprent>(); + + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + Statement st = (Statement)obj; + childVars.addAll(initStatement(st)); + + if (st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + if (dost.getLooptype() != DoStatement.LOOP_FOR && + dost.getLooptype() != DoStatement.LOOP_DO) { + currVars.add(dost.getConditionExprent()); + } + } + else if (st.type == DoStatement.TYPE_CATCHALL) { + CatchAllStatement fin = (CatchAllStatement)st; + if (fin.isFinally() && fin.getMonitor() != null) { + currVars.add(fin.getMonitor()); + } + } + } + else if (obj instanceof Exprent) { + currVars.add((Exprent)obj); + } + } + + // children statements + for (Integer index : childVars) { + Integer count = mapCount.get(index); + if (count == null) { + count = new Integer(0); + } + mapCount.put(index, new Integer(count.intValue() + 1)); + } + + condlst = getAllVars(currVars); + } + else { + condlst = getAllVars(stat.getExprents()); + } + + // this statement + for (VarExprent var : condlst) { + mapCount.put(new Integer(var.getIndex()), new Integer(2)); + } + + + HashSet<Integer> set = new HashSet<Integer>(mapCount.keySet()); + + // put all variables defined in this statement into the set + Iterator<Entry<Integer, Integer>> itMult = mapCount.entrySet().iterator(); + while (itMult.hasNext()) { + Entry<Integer, Integer> en = itMult.next(); + if (en.getValue().intValue() > 1) { + mapVarDefStatements.put(en.getKey(), stat); + } + } + + mapStatementVars.put(stat.id, set); + + return set; + } + + private List<VarExprent> getAllVars(List<Exprent> lst) { + + List<VarExprent> res = new ArrayList<VarExprent>(); + List<Exprent> listTemp = new ArrayList<Exprent>(); + + for (Exprent expr : lst) { + listTemp.addAll(expr.getAllExprents(true)); + listTemp.add(expr); + } + + for (Exprent exprent : listTemp) { + if (exprent.type == Exprent.EXPRENT_VAR) { + res.add((VarExprent)exprent); + } + } + + return res; + } + private boolean setDefinition(Exprent expr, Integer index) { + if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { + Exprent left = ((AssignmentExprent)expr).getLeft(); + if (left.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)left; + if (var.getIndex() == index.intValue()) { + var.setDefinition(true); + return true; + } + } + } + return false; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 6af5720..5783ea7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -1,27 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; @@ -29,107 +22,109 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.*; +import java.util.Map.Entry; + public class VarProcessor { - private HashMap<VarVersionPaar, String> mapVarNames = new HashMap<VarVersionPaar, String>(); - - private VarVersionsProcessor varvers; - - private HashMap<VarVersionPaar, String> thisvars = new HashMap<VarVersionPaar, String>(); - - private HashSet<VarVersionPaar> externvars = new HashSet<VarVersionPaar>(); - - public void setVarVersions(RootStatement root) { - - varvers = new VarVersionsProcessor(); - varvers.setVarVersions(root); - } - - public void setVarDefinitions(Statement root) { - mapVarNames = new HashMap<VarVersionPaar, String>(); - - VarDefinitionHelper defproc = new VarDefinitionHelper(root, - (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD), this); - defproc.setVarDefinitions(); - } - - public void setDebugVarNames(HashMap<Integer, String> mapDebugVarNames) { - - if(varvers == null) { - return; - } - - HashMap<Integer, Integer> mapOriginalVarIndices = varvers.getMapOriginalVarIndices(); - - List<VarVersionPaar> listVars = new ArrayList<VarVersionPaar>(mapVarNames.keySet()); - Collections.sort(listVars, new Comparator<VarVersionPaar>() { - public int compare(VarVersionPaar o1, VarVersionPaar o2) { - return o1.var>o2.var?1:(o1.var==o2.var?0:-1); - } - }); - - HashMap<String, Integer> mapNames = new HashMap<String, Integer>(); - - for(VarVersionPaar varpaar : listVars) { - String name = mapVarNames.get(varpaar); - - Integer orindex = mapOriginalVarIndices.get(varpaar.var); - if(orindex != null && mapDebugVarNames.containsKey(orindex)) { - name = mapDebugVarNames.get(orindex); - } - - Integer counter = mapNames.get(name); - mapNames.put(name, counter==null?counter = new Integer(0):++counter); - - if(counter > 0) { - name+=String.valueOf(counter); - } - - mapVarNames.put(varpaar, name); - } - - } - - public void refreshVarNames(VarNamesCollector vc) { - - HashMap<VarVersionPaar, String> tempVarNames = new HashMap<VarVersionPaar, String>(mapVarNames); - for(Entry<VarVersionPaar, String> ent: tempVarNames.entrySet()) { - mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue())); - } - } - - - public VarType getVarType(VarVersionPaar varpaar) { - return varvers==null?null:varvers.getVarType(varpaar); - } - - public void setVarType(VarVersionPaar varpaar, VarType type) { - varvers.setVarType(varpaar, type); - } - - public String getVarName(VarVersionPaar varpaar) { - return mapVarNames==null?null:mapVarNames.get(varpaar); - } - - public void setVarName(VarVersionPaar varpaar, String name) { - mapVarNames.put(varpaar, name); - } - - public int getVarFinal(VarVersionPaar varpaar) { - return varvers==null?VarTypeProcessor.VAR_FINAL:varvers.getVarFinal(varpaar); - } - - public void setVarFinal(VarVersionPaar varpaar, int finaltype) { - varvers.setVarFinal(varpaar, finaltype); - } - - public HashMap<VarVersionPaar, String> getThisvars() { - return thisvars; - } - - public HashSet<VarVersionPaar> getExternvars() { - return externvars; - } + private HashMap<VarVersionPaar, String> mapVarNames = new HashMap<VarVersionPaar, String>(); + + private VarVersionsProcessor varvers; + + private HashMap<VarVersionPaar, String> thisvars = new HashMap<VarVersionPaar, String>(); + + private HashSet<VarVersionPaar> externvars = new HashSet<VarVersionPaar>(); + + public void setVarVersions(RootStatement root) { + + varvers = new VarVersionsProcessor(); + varvers.setVarVersions(root); + } + + public void setVarDefinitions(Statement root) { + mapVarNames = new HashMap<VarVersionPaar, String>(); + + VarDefinitionHelper defproc = new VarDefinitionHelper(root, + (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD), + this); + defproc.setVarDefinitions(); + } + + public void setDebugVarNames(HashMap<Integer, String> mapDebugVarNames) { + + if (varvers == null) { + return; + } + + HashMap<Integer, Integer> mapOriginalVarIndices = varvers.getMapOriginalVarIndices(); + + List<VarVersionPaar> listVars = new ArrayList<VarVersionPaar>(mapVarNames.keySet()); + Collections.sort(listVars, new Comparator<VarVersionPaar>() { + public int compare(VarVersionPaar o1, VarVersionPaar o2) { + return o1.var > o2.var ? 1 : (o1.var == o2.var ? 0 : -1); + } + }); + + HashMap<String, Integer> mapNames = new HashMap<String, Integer>(); + + for (VarVersionPaar varpaar : listVars) { + String name = mapVarNames.get(varpaar); + + Integer orindex = mapOriginalVarIndices.get(varpaar.var); + if (orindex != null && mapDebugVarNames.containsKey(orindex)) { + name = mapDebugVarNames.get(orindex); + } + + Integer counter = mapNames.get(name); + mapNames.put(name, counter == null ? counter = new Integer(0) : ++counter); + + if (counter > 0) { + name += String.valueOf(counter); + } + + mapVarNames.put(varpaar, name); + } + } + + public void refreshVarNames(VarNamesCollector vc) { + + HashMap<VarVersionPaar, String> tempVarNames = new HashMap<VarVersionPaar, String>(mapVarNames); + for (Entry<VarVersionPaar, String> ent : tempVarNames.entrySet()) { + mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue())); + } + } + + + public VarType getVarType(VarVersionPaar varpaar) { + return varvers == null ? null : varvers.getVarType(varpaar); + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + varvers.setVarType(varpaar, type); + } + + public String getVarName(VarVersionPaar varpaar) { + return mapVarNames == null ? null : mapVarNames.get(varpaar); + } + + public void setVarName(VarVersionPaar varpaar, String name) { + mapVarNames.put(varpaar, name); + } + + public int getVarFinal(VarVersionPaar varpaar) { + return varvers == null ? VarTypeProcessor.VAR_FINAL : varvers.getVarFinal(varpaar); + } + + public void setVarFinal(VarVersionPaar varpaar, int finaltype) { + varvers.setVarFinal(varpaar, finaltype); + } + + public HashMap<VarVersionPaar, String> getThisvars() { + return thisvars; + } + public HashSet<VarVersionPaar> getExternvars() { + return externvars; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java index 3e2e2b3..8792f3e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -1,30 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; @@ -35,239 +28,251 @@ import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + public class VarTypeProcessor { - - public static final int VAR_NONFINAL = 1; - public static final int VAR_FINALEXPLICIT = 2; - public static final int VAR_FINAL = 3; - - private HashMap<VarVersionPaar, VarType> mapExprentMinTypes = new HashMap<VarVersionPaar, VarType>(); - - private HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = new HashMap<VarVersionPaar, VarType>(); - - private HashMap<VarVersionPaar, Integer> mapFinalVars = new HashMap<VarVersionPaar, Integer>(); - - private void setInitVars(RootStatement root) { - - StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); - - // method descriptor - boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; - - MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR); - - if(thisvar) { - VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0, - ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName); - mapExprentMinTypes.put(new VarVersionPaar(0,1), cltype); - mapExprentMaxTypes.put(new VarVersionPaar(0,1), cltype); - } - - int varindex = 0; - for(int i=0;i<md.params.length;i++) { - mapExprentMinTypes.put(new VarVersionPaar(varindex+(thisvar?1:0), 1), md.params[i]); - mapExprentMaxTypes.put(new VarVersionPaar(varindex+(thisvar?1:0), 1), md.params[i]); - varindex+=md.params[i].stack_size; - } - - // catch variables - LinkedList<Statement> stack = new LinkedList<Statement>(); - stack.add(root); - - while(!stack.isEmpty()) { - Statement stat = stack.removeFirst(); - - List<VarExprent> lstVars = null; - if(stat.type == Statement.TYPE_CATCHALL) { - lstVars = ((CatchAllStatement)stat).getVars(); - } else if(stat.type == Statement.TYPE_TRYCATCH) { - lstVars = ((CatchStatement)stat).getVars(); - } - - if(lstVars != null) { - for(VarExprent var: lstVars) { - mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); - mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); - } - } - - stack.addAll(stat.getStats()); - } - } - - public void calculateVarTypes(RootStatement root, DirectGraph dgraph) { - - setInitVars(root); - - resetExprentTypes(dgraph); - - while(!processVarTypes(dgraph)); - } - - private void resetExprentTypes(DirectGraph dgraph) { - - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN); - } else if(expr.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)expr; - if(cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) { - cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype()); - } - } - } - return 0; - } - }); - } - - private boolean processVarTypes(DirectGraph dgraph) { - - return dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - return checkTypeExprent(exprent)?0:1; - } - }); - } - - - private boolean checkTypeExprent(Exprent exprent) { - - for(Exprent expr: exprent.getAllExprents()) { - if(!checkTypeExprent(expr)) { - return false; - } - } - - if(exprent.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)exprent; - if(cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer - VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1); - if(!mapExprentMinTypes.containsKey(cpaar)) { - mapExprentMinTypes.put(cpaar, cexpr.getConsttype()); - } - } - } - - CheckTypesResult result = exprent.checkExprTypeBounds(); - - for(CheckTypesResult.ExprentTypePair entry: result.getLstMaxTypeExprents()) { - if(entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) { - changeExprentType(entry.exprent, entry.type, 1); - } - } - - boolean res = true; - for(CheckTypesResult.ExprentTypePair entry: result.getLstMinTypeExprents()) { - res &= changeExprentType(entry.exprent, entry.type, 0); - } - - return res; - } - - - private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) { - - boolean res = true; - - switch(exprent.type) { - case Exprent.EXPRENT_CONST: - ConstExprent cexpr = (ConstExprent)exprent; - VarType consttype = cexpr.getConsttype(); - - if(newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) { - return true; - } else if(newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) { - VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype(); - if(mininteger.isStrictSuperset(newtype)) { - newtype = mininteger; - } - } - case Exprent.EXPRENT_VAR: - VarVersionPaar varpaar = null; - if(exprent.type == Exprent.EXPRENT_CONST) { - varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1); - } else if(exprent.type == Exprent.EXPRENT_VAR) { - varpaar = new VarVersionPaar((VarExprent)exprent); - } - - if(minmax == 0) { // min - VarType currentMinType = mapExprentMinTypes.get(varpaar); - VarType newMinType; - if(currentMinType==null || newtype.type_family > currentMinType.type_family) { - newMinType = newtype; - } else if(newtype.type_family < currentMinType.type_family) { - return true; - } else { - newMinType = VarType.getCommonSupertype(currentMinType, newtype); - } - - mapExprentMinTypes.put(varpaar, newMinType); - if(exprent.type == Exprent.EXPRENT_CONST) { - ((ConstExprent)exprent).setConsttype(newMinType); - } - - if(currentMinType != null && (newMinType.type_family > currentMinType.type_family || - newMinType.isStrictSuperset(currentMinType))) { - return false; - } - } else { // max - VarType currentMaxType = mapExprentMaxTypes.get(varpaar); - VarType newMaxType; - if(currentMaxType==null || newtype.type_family < currentMaxType.type_family) { - newMaxType = newtype; - } else if(newtype.type_family > currentMaxType.type_family) { - return true; - } else { - newMaxType = VarType.getCommonMinType(currentMaxType, newtype); - } - - mapExprentMaxTypes.put(varpaar, newMaxType); - } - break; - case Exprent.EXPRENT_ASSIGNMENT: - return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax); - case Exprent.EXPRENT_FUNCTION: - FunctionExprent func = (FunctionExprent)exprent; - switch(func.getFunctype()){ - case FunctionExprent.FUNCTION_IIF: // FIXME: - res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); - res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax); - break; - case FunctionExprent.FUNCTION_AND: - case FunctionExprent.FUNCTION_OR: - case FunctionExprent.FUNCTION_XOR: - res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax); - res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); - } - } - - return res; - } - - public HashMap<VarVersionPaar, VarType> getMapExprentMaxTypes() { - return mapExprentMaxTypes; - } - - public HashMap<VarVersionPaar, VarType> getMapExprentMinTypes() { - return mapExprentMinTypes; - } - - public HashMap<VarVersionPaar, Integer> getMapFinalVars() { - return mapFinalVars; - } - - public void setVarType(VarVersionPaar varpaar, VarType type) { - mapExprentMinTypes.put(varpaar, type); - } - - public VarType getVarType(VarVersionPaar varpaar) { - return mapExprentMinTypes.get(varpaar); - } + public static final int VAR_NONFINAL = 1; + public static final int VAR_FINALEXPLICIT = 2; + public static final int VAR_FINAL = 3; + + private HashMap<VarVersionPaar, VarType> mapExprentMinTypes = new HashMap<VarVersionPaar, VarType>(); + + private HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = new HashMap<VarVersionPaar, VarType>(); + + private HashMap<VarVersionPaar, Integer> mapFinalVars = new HashMap<VarVersionPaar, Integer>(); + + private void setInitVars(RootStatement root) { + + StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + + // method descriptor + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR); + + if (thisvar) { + VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0, + ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName); + mapExprentMinTypes.put(new VarVersionPaar(0, 1), cltype); + mapExprentMaxTypes.put(new VarVersionPaar(0, 1), cltype); + } + + int varindex = 0; + for (int i = 0; i < md.params.length; i++) { + mapExprentMinTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]); + mapExprentMaxTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]); + varindex += md.params[i].stack_size; + } + + // catch variables + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while (!stack.isEmpty()) { + Statement stat = stack.removeFirst(); + + List<VarExprent> lstVars = null; + if (stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)stat).getVars(); + } + else if (stat.type == Statement.TYPE_TRYCATCH) { + lstVars = ((CatchStatement)stat).getVars(); + } + + if (lstVars != null) { + for (VarExprent var : lstVars) { + mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); + mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); + } + } + + stack.addAll(stat.getStats()); + } + } + + public void calculateVarTypes(RootStatement root, DirectGraph dgraph) { + + setInitVars(root); + + resetExprentTypes(dgraph); + + while (!processVarTypes(dgraph)) ; + } + + private void resetExprentTypes(DirectGraph dgraph) { + + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN); + } + else if (expr.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)expr; + if (cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) { + cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype()); + } + } + } + return 0; + } + }); + } + + private boolean processVarTypes(DirectGraph dgraph) { + + return dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + return checkTypeExprent(exprent) ? 0 : 1; + } + }); + } + + + private boolean checkTypeExprent(Exprent exprent) { + + for (Exprent expr : exprent.getAllExprents()) { + if (!checkTypeExprent(expr)) { + return false; + } + } + + if (exprent.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)exprent; + if (cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer + VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1); + if (!mapExprentMinTypes.containsKey(cpaar)) { + mapExprentMinTypes.put(cpaar, cexpr.getConsttype()); + } + } + } + + CheckTypesResult result = exprent.checkExprTypeBounds(); + + for (CheckTypesResult.ExprentTypePair entry : result.getLstMaxTypeExprents()) { + if (entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) { + changeExprentType(entry.exprent, entry.type, 1); + } + } + + boolean res = true; + for (CheckTypesResult.ExprentTypePair entry : result.getLstMinTypeExprents()) { + res &= changeExprentType(entry.exprent, entry.type, 0); + } + + return res; + } + + + private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) { + + boolean res = true; + + switch (exprent.type) { + case Exprent.EXPRENT_CONST: + ConstExprent cexpr = (ConstExprent)exprent; + VarType consttype = cexpr.getConsttype(); + + if (newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) { + return true; + } + else if (newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) { + VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype(); + if (mininteger.isStrictSuperset(newtype)) { + newtype = mininteger; + } + } + case Exprent.EXPRENT_VAR: + VarVersionPaar varpaar = null; + if (exprent.type == Exprent.EXPRENT_CONST) { + varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1); + } + else if (exprent.type == Exprent.EXPRENT_VAR) { + varpaar = new VarVersionPaar((VarExprent)exprent); + } + + if (minmax == 0) { // min + VarType currentMinType = mapExprentMinTypes.get(varpaar); + VarType newMinType; + if (currentMinType == null || newtype.type_family > currentMinType.type_family) { + newMinType = newtype; + } + else if (newtype.type_family < currentMinType.type_family) { + return true; + } + else { + newMinType = VarType.getCommonSupertype(currentMinType, newtype); + } + + mapExprentMinTypes.put(varpaar, newMinType); + if (exprent.type == Exprent.EXPRENT_CONST) { + ((ConstExprent)exprent).setConsttype(newMinType); + } + + if (currentMinType != null && (newMinType.type_family > currentMinType.type_family || + newMinType.isStrictSuperset(currentMinType))) { + return false; + } + } + else { // max + VarType currentMaxType = mapExprentMaxTypes.get(varpaar); + VarType newMaxType; + if (currentMaxType == null || newtype.type_family < currentMaxType.type_family) { + newMaxType = newtype; + } + else if (newtype.type_family > currentMaxType.type_family) { + return true; + } + else { + newMaxType = VarType.getCommonMinType(currentMaxType, newtype); + } + + mapExprentMaxTypes.put(varpaar, newMaxType); + } + break; + case Exprent.EXPRENT_ASSIGNMENT: + return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax); + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)exprent; + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_IIF: // FIXME: + res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); + res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax); + break; + case FunctionExprent.FUNCTION_AND: + case FunctionExprent.FUNCTION_OR: + case FunctionExprent.FUNCTION_XOR: + res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax); + res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); + } + } + + return res; + } + + public HashMap<VarVersionPaar, VarType> getMapExprentMaxTypes() { + return mapExprentMaxTypes; + } + + public HashMap<VarVersionPaar, VarType> getMapExprentMinTypes() { + return mapExprentMinTypes; + } + + public HashMap<VarVersionPaar, Integer> getMapFinalVars() { + return mapFinalVars; + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + mapExprentMinTypes.put(varpaar, type); + } + + public VarType getVarType(VarVersionPaar varpaar) { + return mapExprentMinTypes.get(varpaar); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java index cac86ef..9d97982 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java @@ -1,56 +1,56 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; public class VarVersionEdge { // FIXME: can be removed? - public static final int EDGE_GENERAL = 0; - public static final int EDGE_PHANTOM = 1; - - public int type; - - public VarVersionNode source; - - public VarVersionNode dest; - - private int hashCode; - - public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) { - this.type = type; - this.source = source; - this.dest = dest; - this.hashCode = source.hashCode() ^ dest.hashCode() + type; - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof VarVersionEdge)) return false; - - VarVersionEdge edge = (VarVersionEdge)o; - return type == edge.type && source == edge.source && dest == edge.dest; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public String toString() { - return source.toString() + " ->" + type + "-> " + dest.toString(); - } - + public static final int EDGE_GENERAL = 0; + public static final int EDGE_PHANTOM = 1; + + public int type; + + public VarVersionNode source; + + public VarVersionNode dest; + + private int hashCode; + + public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) { + this.type = type; + this.source = source; + this.dest = dest; + this.hashCode = source.hashCode() ^ dest.hashCode() + type; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarVersionEdge)) return false; + + VarVersionEdge edge = (VarVersionEdge)o; + return type == edge.type && source == edge.source && dest == edge.dest; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return source.toString() + " ->" + type + "-> " + dest.toString(); + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java index 7e2b69b..587c653 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java @@ -1,80 +1,80 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; +import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; +import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; -import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; - public class VarVersionNode implements IGraphNode { - - public static final int FLAG_PHANTOM_FINEXIT = 2; - - public int var; - - public int version; - - public Set<VarVersionEdge> succs = new HashSet<VarVersionEdge>(); - - public Set<VarVersionEdge> preds = new HashSet<VarVersionEdge>(); - - public int flags; - - public SFormsFastMapDirect live = new SFormsFastMapDirect(); - - - public VarVersionNode(int var, int version) { - this.var = var; - this.version = version; - } - - public VarVersionPaar getVarPaar() { - return new VarVersionPaar(var, version); - } - - public List<IGraphNode> getPredecessors() { - List<IGraphNode> lst = new ArrayList<IGraphNode>(preds.size()); - for(VarVersionEdge edge : preds) { - lst.add(edge.source); - } - return lst; - } - - public void removeSuccessor(VarVersionEdge edge) { - succs.remove(edge); - } - - public void removePredecessor(VarVersionEdge edge) { - preds.remove(edge); - } - - public void addSuccessor(VarVersionEdge edge) { - succs.add(edge); - } - - public void addPredecessor(VarVersionEdge edge) { - preds.add(edge); - } - - @Override - public String toString() { - return "("+var+"_"+version+")"; - } - + + public static final int FLAG_PHANTOM_FINEXIT = 2; + + public int var; + + public int version; + + public Set<VarVersionEdge> succs = new HashSet<VarVersionEdge>(); + + public Set<VarVersionEdge> preds = new HashSet<VarVersionEdge>(); + + public int flags; + + public SFormsFastMapDirect live = new SFormsFastMapDirect(); + + + public VarVersionNode(int var, int version) { + this.var = var; + this.version = version; + } + + public VarVersionPaar getVarPaar() { + return new VarVersionPaar(var, version); + } + + public List<IGraphNode> getPredecessors() { + List<IGraphNode> lst = new ArrayList<IGraphNode>(preds.size()); + for (VarVersionEdge edge : preds) { + lst.add(edge.source); + } + return lst; + } + + public void removeSuccessor(VarVersionEdge edge) { + succs.remove(edge); + } + + public void removePredecessor(VarVersionEdge edge) { + preds.remove(edge); + } + + public void addSuccessor(VarVersionEdge edge) { + succs.add(edge); + } + + public void addPredecessor(VarVersionEdge edge) { + preds.add(edge); + } + + @Override + public String toString() { + return "(" + var + "_" + version + ")"; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java index b9a3cec..5f3e520 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java @@ -1,65 +1,63 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; public class VarVersionPaar { - public int var; - public int version; - - private int hashCode = -1; - - public VarVersionPaar(int var, int version) { - this.var = var; - this.version = version; - } - - public VarVersionPaar(Integer var, Integer version) { - this.var = var.intValue(); - this.version = version.intValue(); - } - - public VarVersionPaar(VarExprent var) { - this.var = var.getIndex(); - this.version = var.getVersion(); - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof VarVersionPaar)) return false; - - VarVersionPaar paar = (VarVersionPaar)o; - return var == paar.var && version == paar.version; - } - - @Override - public int hashCode() { - if(hashCode == -1) { - hashCode = this.var * 3 + this.version; - } - return hashCode; - } - - @Override - public String toString() { - return "("+var+","+version+")"; - } - - - + public int var; + public int version; + + private int hashCode = -1; + + public VarVersionPaar(int var, int version) { + this.var = var; + this.version = version; + } + + public VarVersionPaar(Integer var, Integer version) { + this.var = var.intValue(); + this.version = version.intValue(); + } + + public VarVersionPaar(VarExprent var) { + this.var = var.getIndex(); + this.version = var.getVersion(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarVersionPaar)) return false; + + VarVersionPaar paar = (VarVersionPaar)o; + return var == paar.var && version == paar.version; + } + + @Override + public int hashCode() { + if (hashCode == -1) { + hashCode = this.var * 3 + this.version; + } + return hashCode; + } + + @Override + public String toString() { + return "(" + var + "," + version + ")"; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java index 2ad672b..ce05071 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java @@ -1,172 +1,167 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.util.*; + public class VarVersionsGraph { - public int counter = 0; - - public VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = new VBStyleCollection<VarVersionNode, VarVersionPaar>(); - - private GenericDominatorEngine engine; - - public VarVersionNode createNode(VarVersionPaar ver) { - VarVersionNode node; - nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver); - return node; - } - - public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPaar> colpaars) { - nodes.addAllWithKey(colnodes, colpaars); - } - - public boolean isDominatorSet(VarVersionNode node, HashSet<VarVersionNode> domnodes) { - - if(domnodes.size() == 1) { - return engine.isDominator(node, domnodes.iterator().next()); - } else { - - HashSet<VarVersionNode> marked = new HashSet<VarVersionNode>(); - - if(domnodes.contains(node)) { - return true; - } - - LinkedList<VarVersionNode> lstNodes = new LinkedList<VarVersionNode>(); - lstNodes.add(node); - - while(!lstNodes.isEmpty()) { - - VarVersionNode nd = lstNodes.remove(0); - if(marked.contains(nd)) { - continue; - } else { - marked.add(nd); - } - - if(nd.preds.isEmpty()) { - return false; - } - - for(VarVersionEdge edge: nd.preds) { - VarVersionNode pred = edge.source; - if(!marked.contains(pred) && !domnodes.contains(pred)) { - lstNodes.add(pred); - } - } - } - } - - return true; - } - - public void initDominators() { - - final HashSet<VarVersionNode> roots = new HashSet<VarVersionNode>(); - - for(VarVersionNode node: nodes) { - if(node.preds.isEmpty()) { - roots.add(node); - } - } - - engine = new GenericDominatorEngine(new IGraph() { - public List<? extends IGraphNode> getReversePostOrderList() { - return getReversedPostOrder(roots); - } - - public Set<? extends IGraphNode> getRoots() { - return new HashSet<IGraphNode>(roots); - } - }); - - engine.initialize(); - } - - private LinkedList<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) { - - LinkedList<VarVersionNode> lst = new LinkedList<VarVersionNode>(); - HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); - - for(VarVersionNode root: roots) { - - LinkedList<VarVersionNode> lstTemp = new LinkedList<VarVersionNode>(); - addToReversePostOrderListIterative(root, lstTemp, setVisited); - - lst.addAll(lstTemp); - } - - return lst; - } - - private void addToReversePostOrderListIterative(VarVersionNode root, List<VarVersionNode> lst, HashSet<VarVersionNode> setVisited) { - - HashMap<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<VarVersionNode, List<VarVersionEdge>>(); - - LinkedList<VarVersionNode> stackNode = new LinkedList<VarVersionNode>(); - LinkedList<Integer> stackIndex = new LinkedList<Integer>(); - - stackNode.add(root); - stackIndex.add(0); - - while(!stackNode.isEmpty()) { - - VarVersionNode node = stackNode.getLast(); - int index = stackIndex.removeLast(); - - setVisited.add(node); - - List<VarVersionEdge> lstSuccs = mapNodeSuccs.get(node); - if(lstSuccs == null) { - mapNodeSuccs.put(node, lstSuccs = new ArrayList<VarVersionEdge>(node.succs)); - } - - for(;index<lstSuccs.size();index++) { - VarVersionNode succ = lstSuccs.get(index).dest; - - if(!setVisited.contains(succ)) { - stackIndex.add(index+1); - - stackNode.add(succ); - stackIndex.add(0); - - break; - } - } - - if(index == lstSuccs.size()) { - lst.add(0, node); - - stackNode.removeLast(); - } - } - - } - + public int counter = 0; + + public VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = new VBStyleCollection<VarVersionNode, VarVersionPaar>(); + + private GenericDominatorEngine engine; + + public VarVersionNode createNode(VarVersionPaar ver) { + VarVersionNode node; + nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver); + return node; + } + + public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPaar> colpaars) { + nodes.addAllWithKey(colnodes, colpaars); + } + + public boolean isDominatorSet(VarVersionNode node, HashSet<VarVersionNode> domnodes) { + + if (domnodes.size() == 1) { + return engine.isDominator(node, domnodes.iterator().next()); + } + else { + + HashSet<VarVersionNode> marked = new HashSet<VarVersionNode>(); + + if (domnodes.contains(node)) { + return true; + } + + LinkedList<VarVersionNode> lstNodes = new LinkedList<VarVersionNode>(); + lstNodes.add(node); + + while (!lstNodes.isEmpty()) { + + VarVersionNode nd = lstNodes.remove(0); + if (marked.contains(nd)) { + continue; + } + else { + marked.add(nd); + } + + if (nd.preds.isEmpty()) { + return false; + } + + for (VarVersionEdge edge : nd.preds) { + VarVersionNode pred = edge.source; + if (!marked.contains(pred) && !domnodes.contains(pred)) { + lstNodes.add(pred); + } + } + } + } + + return true; + } + + public void initDominators() { + + final HashSet<VarVersionNode> roots = new HashSet<VarVersionNode>(); + + for (VarVersionNode node : nodes) { + if (node.preds.isEmpty()) { + roots.add(node); + } + } + + engine = new GenericDominatorEngine(new IGraph() { + public List<? extends IGraphNode> getReversePostOrderList() { + return getReversedPostOrder(roots); + } + + public Set<? extends IGraphNode> getRoots() { + return new HashSet<IGraphNode>(roots); + } + }); + + engine.initialize(); + } + + private LinkedList<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) { + + LinkedList<VarVersionNode> lst = new LinkedList<VarVersionNode>(); + HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); + + for (VarVersionNode root : roots) { + + LinkedList<VarVersionNode> lstTemp = new LinkedList<VarVersionNode>(); + addToReversePostOrderListIterative(root, lstTemp, setVisited); + + lst.addAll(lstTemp); + } + + return lst; + } + + private void addToReversePostOrderListIterative(VarVersionNode root, List<VarVersionNode> lst, HashSet<VarVersionNode> setVisited) { + + HashMap<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<VarVersionNode, List<VarVersionEdge>>(); + + LinkedList<VarVersionNode> stackNode = new LinkedList<VarVersionNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + stackNode.add(root); + stackIndex.add(0); + + while (!stackNode.isEmpty()) { + + VarVersionNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<VarVersionEdge> lstSuccs = mapNodeSuccs.get(node); + if (lstSuccs == null) { + mapNodeSuccs.put(node, lstSuccs = new ArrayList<VarVersionEdge>(node.succs)); + } + + for (; index < lstSuccs.size(); index++) { + VarVersionNode succ = lstSuccs.get(index).dest; + + if (!setVisited.contains(succ)) { + stackIndex.add(index + 1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if (index == lstSuccs.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index 5e2adca..4093992 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -1,26 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.vars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -35,306 +29,306 @@ import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import java.util.*; +import java.util.Map.Entry; + public class VarVersionsProcessor { - - private HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); - - private VarTypeProcessor typeproc; - - public void setVarVersions(RootStatement root) { - - StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); - - SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); - ssa.splitVariables(root, mt); - - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); - -// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - mergePhiVersions(ssa, dgraph); - -// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - typeproc = new VarTypeProcessor(); - typeproc.calculateVarTypes(root, dgraph); - - simpleMerge(typeproc, dgraph, mt); - - // FIXME: advanced merging - - eliminateNonJavaTypes(typeproc); - - setNewVarIndices(typeproc, dgraph); - } - - private void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) { - - // collect phi versions - List<HashSet<VarVersionPaar>> lst = new ArrayList<HashSet<VarVersionPaar>>(); - for(Entry<VarVersionPaar, FastSparseSet<Integer>> ent: ssa.getPhi().entrySet()) { - HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); - set.add(ent.getKey()); - for(Integer vers: ent.getValue()) { - set.add(new VarVersionPaar(ent.getKey().var, vers.intValue())); - } - - for(int i=lst.size()-1;i>=0;i--) { - HashSet<VarVersionPaar> tset = lst.get(i); - HashSet<VarVersionPaar> intersection = new HashSet<VarVersionPaar>(set); - intersection.retainAll(tset); - - if(!intersection.isEmpty()) { - set.addAll(tset); - lst.remove(i); - } - } - - lst.add(set); - } - - final HashMap<VarVersionPaar, Integer> phivers = new HashMap<VarVersionPaar, Integer>(); - for(HashSet<VarVersionPaar> set: lst) { - int min = Integer.MAX_VALUE; - for(VarVersionPaar paar: set) { - if(paar.version<min) { - min = paar.version; - } - } - - for(VarVersionPaar paar: set) { - phivers.put(new VarVersionPaar(paar.var, paar.version), min); - } - } - - - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)expr; - Integer vers = phivers.get(new VarVersionPaar(var)); - if(vers != null) { - var.setVersion(vers); - } - } - } - return 0; - } - }); - - } - - private void eliminateNonJavaTypes(VarTypeProcessor typeproc) { - - HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); - - HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); - for(VarVersionPaar paar: set) { - VarType type = mapExprentMinTypes.get(paar); - VarType maxtype = mapExprentMaxTypes.get(paar); - - if(type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) { - if(maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) { - type = VarType.VARTYPE_CHAR; - } else { - type = type.type == CodeConstants.TYPE_BYTECHAR?VarType.VARTYPE_BYTE:VarType.VARTYPE_SHORT; - } - mapExprentMinTypes.put(paar, type); - //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int - // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT); - } else if(type.type == CodeConstants.TYPE_NULL) { - mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT); - } - } - - } - - private void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) { - - HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); - - HashMap<Integer, HashSet<Integer>> mapVarVersions = new HashMap<Integer, HashSet<Integer>>(); - - for(VarVersionPaar varpaar: mapExprentMinTypes.keySet()) { - if(varpaar.version >= 0) { // don't merge constants - HashSet<Integer> set = mapVarVersions.get(varpaar.var); - if(set == null) { - set = new HashSet<Integer>(); - mapVarVersions.put(varpaar.var, set); - } - set.add(varpaar.version); - } - } - - boolean is_method_static = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0; - - final HashMap<VarVersionPaar, Integer> mapMergedVersions = new HashMap<VarVersionPaar, Integer>(); - - for(Entry<Integer, HashSet<Integer>> ent: mapVarVersions.entrySet()) { - - if(ent.getValue().size() > 1) { - List<Integer> lstVersions = new ArrayList<Integer>(ent.getValue()); - Collections.sort(lstVersions); - - for(int i=0;i<lstVersions.size();i++) { - VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i)); - VarType firsttype = mapExprentMinTypes.get(firstpaar); - - if(firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) { - continue; // don't merge 'this' variable - } - - for(int j=i+1;j<lstVersions.size();j++) { - VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j)); - VarType sectype = mapExprentMinTypes.get(secpaar); - - if(firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT) - || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) { - - VarType firstMaxType = mapExprentMaxTypes.get(firstpaar); - VarType secMaxType = mapExprentMaxTypes.get(secpaar); - mapExprentMaxTypes.put(firstpaar, firstMaxType==null?secMaxType: - (secMaxType==null?firstMaxType:VarType.getCommonMinType(firstMaxType, secMaxType))); - - - mapMergedVersions.put(secpaar, firstpaar.version); - mapExprentMaxTypes.remove(secpaar); - mapExprentMinTypes.remove(secpaar); - - if(firsttype.equals(VarType.VARTYPE_NULL)) { - mapExprentMinTypes.put(firstpaar, sectype); - firsttype = sectype; - } - - typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL); - - lstVersions.remove(j); - j--; - } - - } - } - } - } - - if(!mapMergedVersions.isEmpty()) { - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - VarExprent varex = (VarExprent)expr; - Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex)); - if(newversion != null) { - varex.setVersion(newversion); - } - } - } - - return 0; - } - }); - } - - } - - private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) { - - final HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); - HashMap<VarVersionPaar, Integer> mapFinalVars = typeproc.getMapFinalVars(); - - CounterContainer ccon = DecompilerContext.getCountercontainer(); - - final HashMap<VarVersionPaar, Integer> mapVarPaar = new HashMap<VarVersionPaar, Integer>(); - HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); - - // map var-version paars on new var indexes - HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); - for(VarVersionPaar vpaar: set) { - - if(vpaar.version >= 0) { - int newindex = vpaar.version == 1?vpaar.var: - ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER); - - VarVersionPaar newvar = new VarVersionPaar(newindex, 0); - - mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar)); - mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar)); - - if(mapFinalVars.containsKey(vpaar)) { - mapFinalVars.put(newvar, mapFinalVars.remove(vpaar)); - } - - mapVarPaar.put(vpaar, newindex); - mapOriginalVarIndices.put(newindex, vpaar.var); - } - } - - // set new vars - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List<Exprent> lst = exprent.getAllExprents(true); - lst.add(exprent); - - for(Exprent expr: lst) { - if(expr.type == Exprent.EXPRENT_VAR) { - VarExprent varex = (VarExprent)expr; - Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex)); - if(newvarindex != null) { - varex.setIndex(newvarindex); - varex.setVersion(0); - } - } else if(expr.type == Exprent.EXPRENT_CONST) { - VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1)); - if(maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) { - ((ConstExprent)expr).setConsttype(maxType); - } - } - } - - return 0; - } - }); - - this.mapOriginalVarIndices = mapOriginalVarIndices; - } - - public VarType getVarType(VarVersionPaar varpaar) { - return typeproc==null?null:typeproc.getVarType(varpaar); - } - - public void setVarType(VarVersionPaar varpaar, VarType type) { - typeproc.setVarType(varpaar, type); - } - - public int getVarFinal(VarVersionPaar varpaar) { - - int ret = VarTypeProcessor.VAR_FINAL; - if(typeproc!=null) { - Integer fin = typeproc.getMapFinalVars().get(varpaar); - ret = fin==null?VarTypeProcessor.VAR_FINAL:fin.intValue(); - } - - return ret; - } - - public void setVarFinal(VarVersionPaar varpaar, int finaltype) { - typeproc.getMapFinalVars().put(varpaar, finaltype); - } - - public HashMap<Integer, Integer> getMapOriginalVarIndices() { - return mapOriginalVarIndices; - } - - + + private HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); + + private VarTypeProcessor typeproc; + + public void setVarVersions(RootStatement root) { + + StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + + SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); + ssa.splitVariables(root, mt); + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + mergePhiVersions(ssa, dgraph); + + // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + typeproc = new VarTypeProcessor(); + typeproc.calculateVarTypes(root, dgraph); + + simpleMerge(typeproc, dgraph, mt); + + // FIXME: advanced merging + + eliminateNonJavaTypes(typeproc); + + setNewVarIndices(typeproc, dgraph); + } + + private void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) { + + // collect phi versions + List<HashSet<VarVersionPaar>> lst = new ArrayList<HashSet<VarVersionPaar>>(); + for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) { + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); + set.add(ent.getKey()); + for (Integer vers : ent.getValue()) { + set.add(new VarVersionPaar(ent.getKey().var, vers.intValue())); + } + + for (int i = lst.size() - 1; i >= 0; i--) { + HashSet<VarVersionPaar> tset = lst.get(i); + HashSet<VarVersionPaar> intersection = new HashSet<VarVersionPaar>(set); + intersection.retainAll(tset); + + if (!intersection.isEmpty()) { + set.addAll(tset); + lst.remove(i); + } + } + + lst.add(set); + } + + final HashMap<VarVersionPaar, Integer> phivers = new HashMap<VarVersionPaar, Integer>(); + for (HashSet<VarVersionPaar> set : lst) { + int min = Integer.MAX_VALUE; + for (VarVersionPaar paar : set) { + if (paar.version < min) { + min = paar.version; + } + } + + for (VarVersionPaar paar : set) { + phivers.put(new VarVersionPaar(paar.var, paar.version), min); + } + } + + + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + Integer vers = phivers.get(new VarVersionPaar(var)); + if (vers != null) { + var.setVersion(vers); + } + } + } + return 0; + } + }); + } + + private void eliminateNonJavaTypes(VarTypeProcessor typeproc) { + + HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); + for (VarVersionPaar paar : set) { + VarType type = mapExprentMinTypes.get(paar); + VarType maxtype = mapExprentMaxTypes.get(paar); + + if (type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) { + if (maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) { + type = VarType.VARTYPE_CHAR; + } + else { + type = type.type == CodeConstants.TYPE_BYTECHAR ? VarType.VARTYPE_BYTE : VarType.VARTYPE_SHORT; + } + mapExprentMinTypes.put(paar, type); + //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int + // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT); + } + else if (type.type == CodeConstants.TYPE_NULL) { + mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT); + } + } + } + + private void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) { + + HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + + HashMap<Integer, HashSet<Integer>> mapVarVersions = new HashMap<Integer, HashSet<Integer>>(); + + for (VarVersionPaar varpaar : mapExprentMinTypes.keySet()) { + if (varpaar.version >= 0) { // don't merge constants + HashSet<Integer> set = mapVarVersions.get(varpaar.var); + if (set == null) { + set = new HashSet<Integer>(); + mapVarVersions.put(varpaar.var, set); + } + set.add(varpaar.version); + } + } + + boolean is_method_static = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0; + + final HashMap<VarVersionPaar, Integer> mapMergedVersions = new HashMap<VarVersionPaar, Integer>(); + + for (Entry<Integer, HashSet<Integer>> ent : mapVarVersions.entrySet()) { + + if (ent.getValue().size() > 1) { + List<Integer> lstVersions = new ArrayList<Integer>(ent.getValue()); + Collections.sort(lstVersions); + + for (int i = 0; i < lstVersions.size(); i++) { + VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i)); + VarType firsttype = mapExprentMinTypes.get(firstpaar); + + if (firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) { + continue; // don't merge 'this' variable + } + + for (int j = i + 1; j < lstVersions.size(); j++) { + VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j)); + VarType sectype = mapExprentMinTypes.get(secpaar); + + if (firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT) + || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) { + + VarType firstMaxType = mapExprentMaxTypes.get(firstpaar); + VarType secMaxType = mapExprentMaxTypes.get(secpaar); + mapExprentMaxTypes.put(firstpaar, firstMaxType == null ? secMaxType : + (secMaxType == null ? firstMaxType : VarType.getCommonMinType(firstMaxType, secMaxType))); + + + mapMergedVersions.put(secpaar, firstpaar.version); + mapExprentMaxTypes.remove(secpaar); + mapExprentMinTypes.remove(secpaar); + + if (firsttype.equals(VarType.VARTYPE_NULL)) { + mapExprentMinTypes.put(firstpaar, sectype); + firsttype = sectype; + } + + typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL); + + lstVersions.remove(j); + j--; + } + } + } + } + } + + if (!mapMergedVersions.isEmpty()) { + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent varex = (VarExprent)expr; + Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex)); + if (newversion != null) { + varex.setVersion(newversion); + } + } + } + + return 0; + } + }); + } + } + + private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) { + + final HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + HashMap<VarVersionPaar, Integer> mapFinalVars = typeproc.getMapFinalVars(); + + CounterContainer ccon = DecompilerContext.getCountercontainer(); + + final HashMap<VarVersionPaar, Integer> mapVarPaar = new HashMap<VarVersionPaar, Integer>(); + HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); + + // map var-version paars on new var indexes + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); + for (VarVersionPaar vpaar : set) { + + if (vpaar.version >= 0) { + int newindex = vpaar.version == 1 ? vpaar.var : + ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER); + + VarVersionPaar newvar = new VarVersionPaar(newindex, 0); + + mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar)); + mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar)); + + if (mapFinalVars.containsKey(vpaar)) { + mapFinalVars.put(newvar, mapFinalVars.remove(vpaar)); + } + + mapVarPaar.put(vpaar, newindex); + mapOriginalVarIndices.put(newindex, vpaar.var); + } + } + + // set new vars + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent varex = (VarExprent)expr; + Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex)); + if (newvarindex != null) { + varex.setIndex(newvarindex); + varex.setVersion(0); + } + } + else if (expr.type == Exprent.EXPRENT_CONST) { + VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1)); + if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) { + ((ConstExprent)expr).setConsttype(maxType); + } + } + } + + return 0; + } + }); + + this.mapOriginalVarIndices = mapOriginalVarIndices; + } + + public VarType getVarType(VarVersionPaar varpaar) { + return typeproc == null ? null : typeproc.getVarType(varpaar); + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + typeproc.setVarType(varpaar, type); + } + + public int getVarFinal(VarVersionPaar varpaar) { + + int ret = VarTypeProcessor.VAR_FINAL; + if (typeproc != null) { + Integer fin = typeproc.getMapFinalVars().get(varpaar); + ret = fin == null ? VarTypeProcessor.VAR_FINAL : fin.intValue(); + } + + return ret; + } + + public void setVarFinal(VarVersionPaar varpaar, int finaltype) { + typeproc.getMapFinalVars().put(varpaar, finaltype); + } + + public HashMap<Integer, Integer> getMapOriginalVarIndices() { + return mapOriginalVarIndices; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java index a3015a8..273579a 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java @@ -1,41 +1,55 @@ +/* + * 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.renamer; +import org.jetbrains.java.decompiler.struct.StructClass; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.struct.StructClass; - public class ClassWrapperNode { - private StructClass classStruct; - - private ClassWrapperNode superclass; - - private List<ClassWrapperNode> subclasses = new ArrayList<ClassWrapperNode>(); - - public ClassWrapperNode(StructClass cl) { - this.classStruct = cl; - } - - public void addSubclass(ClassWrapperNode node) { - node.setSuperclass(this); - subclasses.add(node); - } - - public StructClass getClassStruct() { - return classStruct; - } - - public List<ClassWrapperNode> getSubclasses() { - return subclasses; - } - - public ClassWrapperNode getSuperclass() { - return superclass; - } - - public void setSuperclass(ClassWrapperNode superclass) { - this.superclass = superclass; - } + private StructClass classStruct; + + private ClassWrapperNode superclass; + + private List<ClassWrapperNode> subclasses = new ArrayList<ClassWrapperNode>(); + + public ClassWrapperNode(StructClass cl) { + this.classStruct = cl; + } + + public void addSubclass(ClassWrapperNode node) { + node.setSuperclass(this); + subclasses.add(node); + } + + public StructClass getClassStruct() { + return classStruct; + } + + public List<ClassWrapperNode> getSubclasses() { + return subclasses; + } + + public ClassWrapperNode getSuperclass() { + return superclass; + } + public void setSuperclass(ClassWrapperNode superclass) { + this.superclass = superclass; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java index d85d51f..f2cb1ec 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java @@ -1,127 +1,143 @@ +/* + * 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.renamer; -import java.util.HashSet; - import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; +import java.util.HashSet; + public class ConverterHelper implements IIdentifierRenamer { - private static HashSet<String> setReserved = new HashSet<String>(); - - static { - setReserved.add("abstract"); - setReserved.add("do"); - setReserved.add("if"); - setReserved.add("package"); - setReserved.add("synchronized"); - setReserved.add("boolean"); - setReserved.add("double"); - setReserved.add("implements"); - setReserved.add("private"); - setReserved.add("this"); - setReserved.add("break"); - setReserved.add("else"); - setReserved.add("import"); - setReserved.add("protected"); - setReserved.add("throw"); - setReserved.add("byte"); - setReserved.add("extends"); - setReserved.add("instanceof"); - setReserved.add("public"); - setReserved.add("throws"); - setReserved.add("case"); - setReserved.add("false"); - setReserved.add("int"); - setReserved.add("return"); - setReserved.add("transient"); - setReserved.add("catch"); - setReserved.add("final"); - setReserved.add("interface"); - setReserved.add("short"); - setReserved.add("true"); - setReserved.add("char"); - setReserved.add("finally"); - setReserved.add("long"); - setReserved.add("static"); - setReserved.add("try"); - setReserved.add("class"); - setReserved.add("float"); - setReserved.add("native"); - setReserved.add("strictfp"); - setReserved.add("void"); - setReserved.add("const"); - setReserved.add("for"); - setReserved.add("new"); - setReserved.add("super"); - setReserved.add("volatile"); - setReserved.add("continue"); - setReserved.add("goto"); - setReserved.add("null"); - setReserved.add("switch"); - setReserved.add("while"); - setReserved.add("default"); - setReserved.add("assert"); - setReserved.add("enum"); - } - - private int class_counter = 0; - - private int field_counter = 0; - - private int method_counter = 0; - - private HashSet<String> setNonStandardClassNames = new HashSet<String>(); - - public boolean toBeRenamed(int element_type, String classname, String element, String descriptor) { - String value = (element_type == IIdentifierRenamer.ELEMENT_CLASS)?classname:element; - return value == null || value.length() == 0 || value.length()<=2 || setReserved.contains(value) || Character.isDigit(value.charAt(0)); - } - - // TODO: consider possible conflicts with not renamed classes, fields and methods! - // We should get all relevant information here. - public String getNextClassname(String fullname, String shortname) { - - if(shortname == null) { - return "class_"+(class_counter++); - } - - int index = 0; - while(Character.isDigit(shortname.charAt(index))) { - index++; - } - - if(index == 0 || index == shortname.length()) { - return "class_"+(class_counter++); - } else { - String name = shortname.substring(index); - - if(setNonStandardClassNames.contains(name)) { - return "Inner"+name+"_"+(class_counter++); - } else { - setNonStandardClassNames.add(name); - return "Inner"+name; - } - } - } - - public String getNextFieldname(String classname, String field, String descriptor) { - return "field_"+(field_counter++); - } - - public String getNextMethodname(String classname, String method, String descriptor) { - return "method_"+(method_counter++); - } - - // ***************************************************************************** - // static methods - // ***************************************************************************** - - public static String getSimpleClassName(String fullname) { - return fullname.substring(fullname.lastIndexOf('/')+1); - } - - public static String replaceSimpleClassName(String fullname, String newname) { - return fullname.substring(0, fullname.lastIndexOf('/')+1)+newname; - } - + private static HashSet<String> setReserved = new HashSet<String>(); + + static { + setReserved.add("abstract"); + setReserved.add("do"); + setReserved.add("if"); + setReserved.add("package"); + setReserved.add("synchronized"); + setReserved.add("boolean"); + setReserved.add("double"); + setReserved.add("implements"); + setReserved.add("private"); + setReserved.add("this"); + setReserved.add("break"); + setReserved.add("else"); + setReserved.add("import"); + setReserved.add("protected"); + setReserved.add("throw"); + setReserved.add("byte"); + setReserved.add("extends"); + setReserved.add("instanceof"); + setReserved.add("public"); + setReserved.add("throws"); + setReserved.add("case"); + setReserved.add("false"); + setReserved.add("int"); + setReserved.add("return"); + setReserved.add("transient"); + setReserved.add("catch"); + setReserved.add("final"); + setReserved.add("interface"); + setReserved.add("short"); + setReserved.add("true"); + setReserved.add("char"); + setReserved.add("finally"); + setReserved.add("long"); + setReserved.add("static"); + setReserved.add("try"); + setReserved.add("class"); + setReserved.add("float"); + setReserved.add("native"); + setReserved.add("strictfp"); + setReserved.add("void"); + setReserved.add("const"); + setReserved.add("for"); + setReserved.add("new"); + setReserved.add("super"); + setReserved.add("volatile"); + setReserved.add("continue"); + setReserved.add("goto"); + setReserved.add("null"); + setReserved.add("switch"); + setReserved.add("while"); + setReserved.add("default"); + setReserved.add("assert"); + setReserved.add("enum"); + } + + private int class_counter = 0; + + private int field_counter = 0; + + private int method_counter = 0; + + private HashSet<String> setNonStandardClassNames = new HashSet<String>(); + + public boolean toBeRenamed(int element_type, String classname, String element, String descriptor) { + String value = (element_type == IIdentifierRenamer.ELEMENT_CLASS) ? classname : element; + return value == null || value.length() == 0 || value.length() <= 2 || setReserved.contains(value) || Character.isDigit(value.charAt(0)); + } + + // TODO: consider possible conflicts with not renamed classes, fields and methods! + // We should get all relevant information here. + public String getNextClassname(String fullname, String shortname) { + + if (shortname == null) { + return "class_" + (class_counter++); + } + + int index = 0; + while (Character.isDigit(shortname.charAt(index))) { + index++; + } + + if (index == 0 || index == shortname.length()) { + return "class_" + (class_counter++); + } + else { + String name = shortname.substring(index); + + if (setNonStandardClassNames.contains(name)) { + return "Inner" + name + "_" + (class_counter++); + } + else { + setNonStandardClassNames.add(name); + return "Inner" + name; + } + } + } + + public String getNextFieldname(String classname, String field, String descriptor) { + return "field_" + (field_counter++); + } + + public String getNextMethodname(String classname, String method, String descriptor) { + return "method_" + (method_counter++); + } + + // ***************************************************************************** + // static methods + // ***************************************************************************** + + public static String getSimpleClassName(String fullname) { + return fullname.substring(fullname.lastIndexOf('/') + 1); + } + + public static String replaceSimpleClassName(String fullname, String newname) { + return fullname.substring(0, fullname.lastIndexOf('/') + 1) + newname; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java index 634dc59..a665de8 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -1,12 +1,20 @@ +/* + * 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.renamer; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; @@ -20,428 +28,437 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.IOException; +import java.util.*; + public class IdentifierConverter { - private StructContext context; - - private IIdentifierRenamer helper; - - private PoolInterceptor interceptor; - - private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); - - private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); - - private HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); - - public void rename(StructContext context) { - - try { - this.context = context; - - String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS); - if(user_class != null) { - try { - helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance(); - } catch(Exception ex) { - ; // ignore errors - } - } - - if(helper == null) { - helper = new ConverterHelper(); - } - - interceptor = new PoolInterceptor(helper); - - buildInheritanceTree(); - - renameAllClasses(); - - renameInterfaces(); - - renameClasses(); - - DecompilerContext.setPoolInterceptor(interceptor); - context.reloadContext(); - - } catch(IOException ex){ - throw new RuntimeException("Renaming failed!"); - } - - } - - private void renameClasses() { - - List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses); - - HashMap<String, HashMap<String, String>> classNameMaps = new HashMap<String, HashMap<String, String>>(); - - for(ClassWrapperNode node : lstClasses) { - - StructClass cl = node.getClassStruct(); - HashMap<String, String> names = new HashMap<String, String>(); - - // merge informations on super class - if(cl.superClass != null) { - HashMap<String, String> mapClass = classNameMaps.get(cl.superClass.getString()); - if(mapClass != null) { - names.putAll(mapClass); - } - } - - // merge informations on interfaces - for(String intrName : cl.getInterfaceNames()) { - HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); - if(mapInt != null) { - names.putAll(mapInt); - } else { - StructClass clintr = context.getClass(intrName); - if(clintr!=null) { - names.putAll(processExternalInterface(clintr)); - } - } - } - - renameClassIdentifiers(cl, names); - - if(!node.getSubclasses().isEmpty()) { - classNameMaps.put(cl.qualifiedName, names); - } - } - - } - - private HashMap<String, String> processExternalInterface(StructClass cl) { - - HashMap<String, String> names = new HashMap<String, String>(); - - for(String intrName : cl.getInterfaceNames()) { - - HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); - if(mapInt != null) { - names.putAll(mapInt); - } else { - StructClass clintr = context.getClass(intrName); - if(clintr!=null) { - names.putAll(processExternalInterface(clintr)); - } - } - } - - renameClassIdentifiers(cl, names); - - return names; - } - - private void renameInterfaces() { - - List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces); - - HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); - - // rename methods and fields - for(ClassWrapperNode node : lstInterfaces) { - - StructClass cl = node.getClassStruct(); - HashMap<String, String> names = new HashMap<String, String>(); - - // merge informations on super interfaces - for(String intrName : cl.getInterfaceNames()) { - HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); - if(mapInt != null) { - names.putAll(mapInt); - } - } - - renameClassIdentifiers(cl, names); - - interfaceNameMaps.put(cl.qualifiedName, names); - } - - this.interfaceNameMaps = interfaceNameMaps; - } - - private void renameAllClasses() { - - // order not important - List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces)); - lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses)); - - // rename all interfaces and classes - for(ClassWrapperNode node : lstAllClasses) { - renameClass(node.getClassStruct()); - } - } - - private void renameClass(StructClass cl) { - - if(!cl.isOwn()) { - return; - } - - String classOldFullName = cl.qualifiedName; - String classNewFullName = classOldFullName; - - // TODO: rename packages - String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName); - if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) { - do { - classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, - helper.getNextClassname(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName))); - } while(context.getClasses().containsKey(classNewFullName)); - - interceptor.addName(classOldFullName, classNewFullName); - } - - } - - private void renameClassIdentifiers(StructClass cl, HashMap<String, String> names) { - - // all classes are already renamed - String classOldFullName = cl.qualifiedName; - String classNewFullName = interceptor.getName(classOldFullName); - - if(classNewFullName == null) { - classNewFullName = classOldFullName; - } - - // methods - HashSet<String> setMethodNames = new HashSet<String>(); - for(StructMethod md : cl.getMethods()) { - setMethodNames.add(md.getName()); - } - - VBStyleCollection<StructMethod, String> methods = cl.getMethods(); - for(int i=0;i<methods.size();i++) { - - StructMethod mt = methods.get(i); - String key = methods.getKey(i); - - int access_flags = mt.getAccessFlags(); - boolean isPrivate = ((access_flags & CodeConstants.ACC_PRIVATE) != 0); - - String name = mt.getName(); - if(!cl.isOwn() || (access_flags & CodeConstants.ACC_NATIVE) != 0) { - // external and native methods must not be renamed - if(!isPrivate) { - names.put(key, name); - } - } else if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) { - if(isPrivate || !names.containsKey(key)) { - do { - name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor()); - } while(setMethodNames.contains(name)); - - if(!isPrivate) { - names.put(key, name); - } - } else { - name = names.get(key); - } - - interceptor.addName(classOldFullName+" "+mt.getName()+" "+mt.getDescriptor(), - classNewFullName+" "+name+" "+buildNewDescriptor(false, mt.getDescriptor())); - } - } - - // external fields are not being renamed - if(!cl.isOwn()) { - return; - } - - // fields - // FIXME: should overloaded fields become the same name? - HashSet<String> setFieldNames = new HashSet<String>(); - for(StructField fd : cl.getFields()) { - setFieldNames.add(fd.getName()); - } - - for(StructField fd : cl.getFields()) { - if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) { - String newname; - - do { - newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor()); - } while(setFieldNames.contains(newname)); - - interceptor.addName(classOldFullName+" "+fd.getName()+" "+fd.getDescriptor(), - classNewFullName+" "+newname+" "+buildNewDescriptor(true, fd.getDescriptor())); - } - } - - } - - private String buildNewDescriptor(boolean isField, String descriptor) { - - boolean updated = false; - - if(isField) { - FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); - - VarType ftype = fd.type; - if(ftype.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(ftype.value); - if(newclname != null) { - ftype.value = newclname; - updated = true; - } - } - - if(updated) { - return fd.getDescriptor(); - } - - } else { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - // params - for(VarType partype : md.params) { - if(partype.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(partype.value); - if(newclname != null) { - partype.value = newclname; - updated = true; - } - } - } - - // return value - if(md.ret.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(md.ret.value); - if(newclname!=null) { - md.ret.value = newclname; - updated = true; - } - } - - if(updated) { - return md.getDescriptor(); - } - } - - return descriptor; - } - - private List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) { - - List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>(); - - LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>(); - LinkedList<Integer> stackIndex = new LinkedList<Integer>(); - - HashSet<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>(); - - for(ClassWrapperNode root : roots) { - stackNode.add(root); - stackIndex.add(0); - } - - while(!stackNode.isEmpty()) { - - ClassWrapperNode node = stackNode.getLast(); - int index = stackIndex.removeLast(); - - setVisited.add(node); - - List<ClassWrapperNode> lstSubs = node.getSubclasses(); - - for(; index < lstSubs.size(); index++) { - ClassWrapperNode sub = lstSubs.get(index); - if(!setVisited.contains(sub)) { - stackIndex.add(index+1); - - stackNode.add(sub); - stackIndex.add(0); - - break; - } - } - - if(index == lstSubs.size()) { - res.add(0, node); - - stackNode.removeLast(); - } - } - - return res; - } - - - private void buildInheritanceTree() { - - HashMap<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); - HashMap<String, StructClass> classes = context.getClasses(); - - List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); - List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); - - for(StructClass cl : classes.values()) { - - if(!cl.isOwn()) { - continue; - } - - LinkedList<StructClass> stack = new LinkedList<StructClass>(); - LinkedList<ClassWrapperNode> stackSubnodes = new LinkedList<ClassWrapperNode>(); - - stack.add(cl); - stackSubnodes.add(null); - - while(!stack.isEmpty()) { - - StructClass clstr = stack.removeFirst(); - ClassWrapperNode child = stackSubnodes.removeFirst(); - - ClassWrapperNode node = nodes.get(clstr.qualifiedName); - boolean isNewNode = (node == null); - - if(isNewNode) { - nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr)); - } - - if(child!=null) { - node.addSubclass(child); - } - - if(!isNewNode) { - break; - } else { - - boolean isInterface = ((clstr.access_flags & CodeConstants.ACC_INTERFACE) != 0); - boolean found_parent = false; - - if(isInterface) { - for(String intrName : clstr.getInterfaceNames()) { - StructClass clparent = classes.get(intrName); - if(clparent != null) { - stack.add(clparent); - stackSubnodes.add(node); - found_parent = true; - } - } - } else { - if(clstr.superClass != null) { // null iff java/lang/Object - StructClass clparent = classes.get(clstr.superClass.getString()); - - if(clparent != null) { - stack.add(clparent); - stackSubnodes.add(node); - found_parent = true; - } - } - } - - if(!found_parent) { // no super class or interface - (isInterface?rootInterfaces:rootClasses).add(node); - } - } - } - } - - this.rootClasses = rootClasses; - this.rootInterfaces = rootInterfaces; - } - + private StructContext context; + + private IIdentifierRenamer helper; + + private PoolInterceptor interceptor; + + private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); + + private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); + + private HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); + + public void rename(StructContext context) { + + try { + this.context = context; + + String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS); + if (user_class != null) { + try { + helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance(); + } + catch (Exception ex) { + ; // ignore errors + } + } + + if (helper == null) { + helper = new ConverterHelper(); + } + + interceptor = new PoolInterceptor(helper); + + buildInheritanceTree(); + + renameAllClasses(); + + renameInterfaces(); + + renameClasses(); + + DecompilerContext.setPoolInterceptor(interceptor); + context.reloadContext(); + } + catch (IOException ex) { + throw new RuntimeException("Renaming failed!"); + } + } + + private void renameClasses() { + + List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses); + + HashMap<String, HashMap<String, String>> classNameMaps = new HashMap<String, HashMap<String, String>>(); + + for (ClassWrapperNode node : lstClasses) { + + StructClass cl = node.getClassStruct(); + HashMap<String, String> names = new HashMap<String, String>(); + + // merge informations on super class + if (cl.superClass != null) { + HashMap<String, String> mapClass = classNameMaps.get(cl.superClass.getString()); + if (mapClass != null) { + names.putAll(mapClass); + } + } + + // merge informations on interfaces + for (String intrName : cl.getInterfaceNames()) { + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if (mapInt != null) { + names.putAll(mapInt); + } + else { + StructClass clintr = context.getClass(intrName); + if (clintr != null) { + names.putAll(processExternalInterface(clintr)); + } + } + } + + renameClassIdentifiers(cl, names); + + if (!node.getSubclasses().isEmpty()) { + classNameMaps.put(cl.qualifiedName, names); + } + } + } + + private HashMap<String, String> processExternalInterface(StructClass cl) { + + HashMap<String, String> names = new HashMap<String, String>(); + + for (String intrName : cl.getInterfaceNames()) { + + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if (mapInt != null) { + names.putAll(mapInt); + } + else { + StructClass clintr = context.getClass(intrName); + if (clintr != null) { + names.putAll(processExternalInterface(clintr)); + } + } + } + + renameClassIdentifiers(cl, names); + + return names; + } + + private void renameInterfaces() { + + List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces); + + HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); + + // rename methods and fields + for (ClassWrapperNode node : lstInterfaces) { + + StructClass cl = node.getClassStruct(); + HashMap<String, String> names = new HashMap<String, String>(); + + // merge informations on super interfaces + for (String intrName : cl.getInterfaceNames()) { + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if (mapInt != null) { + names.putAll(mapInt); + } + } + + renameClassIdentifiers(cl, names); + + interfaceNameMaps.put(cl.qualifiedName, names); + } + + this.interfaceNameMaps = interfaceNameMaps; + } + + private void renameAllClasses() { + + // order not important + List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces)); + lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses)); + + // rename all interfaces and classes + for (ClassWrapperNode node : lstAllClasses) { + renameClass(node.getClassStruct()); + } + } + + private void renameClass(StructClass cl) { + + if (!cl.isOwn()) { + return; + } + + String classOldFullName = cl.qualifiedName; + String classNewFullName = classOldFullName; + + // TODO: rename packages + String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName); + if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) { + do { + classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, + helper.getNextClassname(classOldFullName, ConverterHelper + .getSimpleClassName(classOldFullName))); + } + while (context.getClasses().containsKey(classNewFullName)); + + interceptor.addName(classOldFullName, classNewFullName); + } + } + + private void renameClassIdentifiers(StructClass cl, HashMap<String, String> names) { + + // all classes are already renamed + String classOldFullName = cl.qualifiedName; + String classNewFullName = interceptor.getName(classOldFullName); + + if (classNewFullName == null) { + classNewFullName = classOldFullName; + } + + // methods + HashSet<String> setMethodNames = new HashSet<String>(); + for (StructMethod md : cl.getMethods()) { + setMethodNames.add(md.getName()); + } + + VBStyleCollection<StructMethod, String> methods = cl.getMethods(); + for (int i = 0; i < methods.size(); i++) { + + StructMethod mt = methods.get(i); + String key = methods.getKey(i); + + int access_flags = mt.getAccessFlags(); + boolean isPrivate = ((access_flags & CodeConstants.ACC_PRIVATE) != 0); + + String name = mt.getName(); + if (!cl.isOwn() || (access_flags & CodeConstants.ACC_NATIVE) != 0) { + // external and native methods must not be renamed + if (!isPrivate) { + names.put(key, name); + } + } + else if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) { + if (isPrivate || !names.containsKey(key)) { + do { + name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor()); + } + while (setMethodNames.contains(name)); + + if (!isPrivate) { + names.put(key, name); + } + } + else { + name = names.get(key); + } + + interceptor.addName(classOldFullName + " " + mt.getName() + " " + mt.getDescriptor(), + classNewFullName + " " + name + " " + buildNewDescriptor(false, mt.getDescriptor())); + } + } + + // external fields are not being renamed + if (!cl.isOwn()) { + return; + } + + // fields + // FIXME: should overloaded fields become the same name? + HashSet<String> setFieldNames = new HashSet<String>(); + for (StructField fd : cl.getFields()) { + setFieldNames.add(fd.getName()); + } + + for (StructField fd : cl.getFields()) { + if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) { + String newname; + + do { + newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor()); + } + while (setFieldNames.contains(newname)); + + interceptor.addName(classOldFullName + " " + fd.getName() + " " + fd.getDescriptor(), + classNewFullName + " " + newname + " " + buildNewDescriptor(true, fd.getDescriptor())); + } + } + } + + private String buildNewDescriptor(boolean isField, String descriptor) { + + boolean updated = false; + + if (isField) { + FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); + + VarType ftype = fd.type; + if (ftype.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(ftype.value); + if (newclname != null) { + ftype.value = newclname; + updated = true; + } + } + + if (updated) { + return fd.getDescriptor(); + } + } + else { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + // params + for (VarType partype : md.params) { + if (partype.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(partype.value); + if (newclname != null) { + partype.value = newclname; + updated = true; + } + } + } + + // return value + if (md.ret.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(md.ret.value); + if (newclname != null) { + md.ret.value = newclname; + updated = true; + } + } + + if (updated) { + return md.getDescriptor(); + } + } + + return descriptor; + } + + private List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) { + + List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>(); + + LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + HashSet<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>(); + + for (ClassWrapperNode root : roots) { + stackNode.add(root); + stackIndex.add(0); + } + + while (!stackNode.isEmpty()) { + + ClassWrapperNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<ClassWrapperNode> lstSubs = node.getSubclasses(); + + for (; index < lstSubs.size(); index++) { + ClassWrapperNode sub = lstSubs.get(index); + if (!setVisited.contains(sub)) { + stackIndex.add(index + 1); + + stackNode.add(sub); + stackIndex.add(0); + + break; + } + } + + if (index == lstSubs.size()) { + res.add(0, node); + + stackNode.removeLast(); + } + } + + return res; + } + + + private void buildInheritanceTree() { + + HashMap<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); + HashMap<String, StructClass> classes = context.getClasses(); + + List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); + List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); + + for (StructClass cl : classes.values()) { + + if (!cl.isOwn()) { + continue; + } + + LinkedList<StructClass> stack = new LinkedList<StructClass>(); + LinkedList<ClassWrapperNode> stackSubnodes = new LinkedList<ClassWrapperNode>(); + + stack.add(cl); + stackSubnodes.add(null); + + while (!stack.isEmpty()) { + + StructClass clstr = stack.removeFirst(); + ClassWrapperNode child = stackSubnodes.removeFirst(); + + ClassWrapperNode node = nodes.get(clstr.qualifiedName); + boolean isNewNode = (node == null); + + if (isNewNode) { + nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr)); + } + + if (child != null) { + node.addSubclass(child); + } + + if (!isNewNode) { + break; + } + else { + + boolean isInterface = ((clstr.access_flags & CodeConstants.ACC_INTERFACE) != 0); + boolean found_parent = false; + + if (isInterface) { + for (String intrName : clstr.getInterfaceNames()) { + StructClass clparent = classes.get(intrName); + if (clparent != null) { + stack.add(clparent); + stackSubnodes.add(node); + found_parent = true; + } + } + } + else { + if (clstr.superClass != null) { // null iff java/lang/Object + StructClass clparent = classes.get(clstr.superClass.getString()); + + if (clparent != null) { + stack.add(clparent); + stackSubnodes.add(node); + found_parent = true; + } + } + } + + if (!found_parent) { // no super class or interface + (isInterface ? rootInterfaces : rootClasses).add(node); + } + } + } + } + + this.rootClasses = rootClasses; + this.rootInterfaces = rootInterfaces; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java index 73260f4..74e2ed8 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java @@ -1,36 +1,50 @@ +/* + * 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.renamer; -import java.util.HashMap; - import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; +import java.util.HashMap; + public class PoolInterceptor { - private IIdentifierRenamer helper; - - private HashMap<String, String> mapOldToNewNames = new HashMap<String, String>(); - - private HashMap<String, String> mapNewToOldNames = new HashMap<String, String>(); - - public PoolInterceptor(IIdentifierRenamer helper) { - this.helper = helper; - } - - public void addName(String oldName, String newName) { - mapOldToNewNames.put(oldName, newName); - mapNewToOldNames.put(newName, oldName); - } - - public String getName(String oldName) { - return mapOldToNewNames.get(oldName); - } - - public String getOldName(String newName) { - return mapNewToOldNames.get(newName); - } - - public IIdentifierRenamer getHelper() { - return helper; - } - + private IIdentifierRenamer helper; + + private HashMap<String, String> mapOldToNewNames = new HashMap<String, String>(); + + private HashMap<String, String> mapNewToOldNames = new HashMap<String, String>(); + + public PoolInterceptor(IIdentifierRenamer helper) { + this.helper = helper; + } + + public void addName(String oldName, String newName) { + mapOldToNewNames.put(oldName, newName); + mapNewToOldNames.put(newName, oldName); + } + + public String getName(String oldName) { + return mapOldToNewNames.get(oldName); + } + + public String getOldName(String newName) { + return mapNewToOldNames.get(newName); + } + + public IIdentifierRenamer getHelper() { + return helper; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index d859b29..c942e76 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -1,210 +1,210 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; +import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link; + import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.jar.Manifest; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; -import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; -import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link; - public class ContextUnit { - - public static final int TYPE_FOLDER = 0; - public static final int TYPE_JAR = 1; - public static final int TYPE_ZIP = 2; - - private static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF"; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private int type; - - // relative path to jar/zip - private String archivepath; - - // folder: relative path - // archive: file name - private String filename; - - private List<StructClass> classes = new ArrayList<StructClass>(); - - // class file or jar/zip entry. Should, but doesn't have to be the same as qualifiedName of the class - private List<String> classentries = new ArrayList<String>(); - - private List<String> direntries = new ArrayList<String>(); - - private List<String[]> otherentries = new ArrayList<String[]>(); - - private Manifest manifest; - - private IDecompilatSaver decompilatSaver; - - private IDecompiledData decompiledData; - - private boolean own = true; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public ContextUnit(int type, String archivepath, String filename, boolean own, - IDecompilatSaver decompilatSaver, IDecompiledData decompiledData) { - this.type = type; - this.own = own; - this.archivepath = archivepath; - this.filename = filename; - this.decompilatSaver = decompilatSaver; - this.decompiledData = decompiledData; - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void addClass(StructClass cl, String entryname) { - classes.add(cl); - classentries.add(entryname); - } - - public void addDirEntry(String entry) { - direntries.add(entry); - } - - public void addOtherEntry(String fullpath, String entry) { - otherentries.add(new String[]{fullpath, entry}); - } - - public void reload(LazyLoader loader) throws IOException { - - List<StructClass> lstClasses = new ArrayList<StructClass>(); - for(StructClass cl : classes) { - String oldname = cl.qualifiedName; - StructClass newcl = new StructClass(loader.getClassStream(oldname), cl.isOwn(), loader); - - lstClasses.add(newcl); - - Link lnk = loader.getClassLink(oldname); - loader.removeClassLink(oldname); - loader.addClassLink(newcl.qualifiedName, lnk); - } - - classes = lstClasses; - } - - public void save() { - - switch(type) { - case TYPE_FOLDER: - - // create folder - decompilatSaver.saveFolder(filename); - - // non-class files - for(String[] arr: otherentries) { - decompilatSaver.copyFile(arr[0], filename, arr[0]); - } - - // classes - for(int i=0;i<classes.size();i++) { - - StructClass cl = classes.get(i); - String entryname = classentries.get(i); - - entryname = decompiledData.getClassEntryName(cl, entryname); - if(entryname != null) { - String content = decompiledData.getClassContent(cl); - if(content != null) { - decompilatSaver.saveClassFile(filename, cl.qualifiedName, entryname, content); - } - } - } - - break; - case TYPE_JAR: - case TYPE_ZIP: - - // create archive file - decompilatSaver.saveFolder(archivepath); - decompilatSaver.createArchive(archivepath, filename, manifest); - - // directory entries - for(String direntry: direntries) { - decompilatSaver.saveEntry(archivepath, filename, direntry, null); - } - - // non-class entries - for(String[] arr: otherentries) { - // manifest was defined by constructor invocation - if(type != TYPE_JAR || !MANIFEST_ENTRY.equalsIgnoreCase(arr[1])) { - decompilatSaver.copyEntry(arr[0], archivepath, filename, arr[1]); - } - } - - // classes - for(int i=0;i<classes.size();i++) { - - StructClass cl = classes.get(i); - String entryname = classentries.get(i); - - entryname = decompiledData.getClassEntryName(cl, entryname); - if(entryname != null) { - String content = decompiledData.getClassContent(cl); - decompilatSaver.saveClassEntry(archivepath, filename, cl.qualifiedName, entryname, content); - } - } - - decompilatSaver.closeArchive(archivepath, filename); - } - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public void setManifest(Manifest manifest) { - this.manifest = manifest; - } - - public boolean isOwn() { - return own; - } - - public List<StructClass> getClasses() { - return classes; - } - - public int getType() { - return type; - } - - public void setDecompilatSaver(IDecompilatSaver decompilatSaver) { - this.decompilatSaver = decompilatSaver; - } - - public void setDecompiledData(IDecompiledData decompiledData) { - this.decompiledData = decompiledData; - } + public static final int TYPE_FOLDER = 0; + public static final int TYPE_JAR = 1; + public static final int TYPE_ZIP = 2; + + private static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF"; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private int type; + + // relative path to jar/zip + private String archivepath; + + // folder: relative path + // archive: file name + private String filename; + + private List<StructClass> classes = new ArrayList<StructClass>(); + + // class file or jar/zip entry. Should, but doesn't have to be the same as qualifiedName of the class + private List<String> classentries = new ArrayList<String>(); + + private List<String> direntries = new ArrayList<String>(); + + private List<String[]> otherentries = new ArrayList<String[]>(); + + private Manifest manifest; + + private IDecompilatSaver decompilatSaver; + + private IDecompiledData decompiledData; + + private boolean own = true; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public ContextUnit(int type, String archivepath, String filename, boolean own, + IDecompilatSaver decompilatSaver, IDecompiledData decompiledData) { + this.type = type; + this.own = own; + this.archivepath = archivepath; + this.filename = filename; + this.decompilatSaver = decompilatSaver; + this.decompiledData = decompiledData; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void addClass(StructClass cl, String entryname) { + classes.add(cl); + classentries.add(entryname); + } + + public void addDirEntry(String entry) { + direntries.add(entry); + } + + public void addOtherEntry(String fullpath, String entry) { + otherentries.add(new String[]{fullpath, entry}); + } + + public void reload(LazyLoader loader) throws IOException { + + List<StructClass> lstClasses = new ArrayList<StructClass>(); + for (StructClass cl : classes) { + String oldname = cl.qualifiedName; + StructClass newcl = new StructClass(loader.getClassStream(oldname), cl.isOwn(), loader); + + lstClasses.add(newcl); + + Link lnk = loader.getClassLink(oldname); + loader.removeClassLink(oldname); + loader.addClassLink(newcl.qualifiedName, lnk); + } + + classes = lstClasses; + } + + public void save() { + + switch (type) { + case TYPE_FOLDER: + + // create folder + decompilatSaver.saveFolder(filename); + + // non-class files + for (String[] arr : otherentries) { + decompilatSaver.copyFile(arr[0], filename, arr[0]); + } + + // classes + for (int i = 0; i < classes.size(); i++) { + + StructClass cl = classes.get(i); + String entryname = classentries.get(i); + + entryname = decompiledData.getClassEntryName(cl, entryname); + if (entryname != null) { + String content = decompiledData.getClassContent(cl); + if (content != null) { + decompilatSaver.saveClassFile(filename, cl.qualifiedName, entryname, content); + } + } + } + + break; + case TYPE_JAR: + case TYPE_ZIP: + + // create archive file + decompilatSaver.saveFolder(archivepath); + decompilatSaver.createArchive(archivepath, filename, manifest); + + // directory entries + for (String direntry : direntries) { + decompilatSaver.saveEntry(archivepath, filename, direntry, null); + } + + // non-class entries + for (String[] arr : otherentries) { + // manifest was defined by constructor invocation + if (type != TYPE_JAR || !MANIFEST_ENTRY.equalsIgnoreCase(arr[1])) { + decompilatSaver.copyEntry(arr[0], archivepath, filename, arr[1]); + } + } + + // classes + for (int i = 0; i < classes.size(); i++) { + + StructClass cl = classes.get(i); + String entryname = classentries.get(i); + + entryname = decompiledData.getClassEntryName(cl, entryname); + if (entryname != null) { + String content = decompiledData.getClassContent(cl); + decompilatSaver.saveClassEntry(archivepath, filename, cl.qualifiedName, entryname, content); + } + } + + decompilatSaver.closeArchive(archivepath, filename); + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public void setManifest(Manifest manifest) { + this.manifest = manifest; + } + + public boolean isOwn() { + return own; + } + + public List<StructClass> getClasses() { + return classes; + } + + public int getType() { + return type; + } + + public void setDecompilatSaver(IDecompilatSaver decompilatSaver) { + this.decompilatSaver = decompilatSaver; + } + + public void setDecompiledData(IDecompiledData decompiledData) { + this.decompiledData = decompiledData; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java index 9c8bc14..1976216 100644 --- a/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java +++ b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java @@ -1,23 +1,23 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; public interface IDecompiledData { - public String getClassEntryName(StructClass cl, String entryname); + public String getClassEntryName(StructClass cl, String entryname); - public String getClassContent(StructClass cl); - + public String getClassContent(StructClass cl); } diff --git a/src/org/jetbrains/java/decompiler/struct/ISaveClass.java b/src/org/jetbrains/java/decompiler/struct/ISaveClass.java index 6fbfb9c..69a90f9 100644 --- a/src/org/jetbrains/java/decompiler/struct/ISaveClass.java +++ b/src/org/jetbrains/java/decompiler/struct/ISaveClass.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; import java.io.DataOutputStream; @@ -21,10 +22,9 @@ import java.io.IOException; public interface ISaveClass { - public String getClassEntryName(StructClass cl, String entryname); - - public void saveClassToFile(StructClass cl, File file) throws FileNotFoundException, IOException; + public String getClassEntryName(StructClass cl, String entryname); + + public void saveClassToFile(StructClass cl, File file) throws FileNotFoundException, IOException; - public void saveClassToStream(StructClass cl, DataOutputStream out); - + public void saveClassToStream(StructClass cl, DataOutputStream out); } diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 456cc70..19946a0 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -1,27 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -31,6 +24,8 @@ import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.*; + /* ClassFile { u4 magic; @@ -54,299 +49,298 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; public class StructClass { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int minor_version; - - public int major_version; - - public int access_flags; - - public int this_class; - - public int super_class; - - public PrimitiveConstant thisClass; - - public PrimitiveConstant superClass; - - public String qualifiedName; - - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private ConstantPool pool; - - private int[] interfaces; - - private String[] interfaceNames; - - private VBStyleCollection<StructField, String> fields = new VBStyleCollection<StructField, String>(); - - private VBStyleCollection<StructMethod, String> methods = new VBStyleCollection<StructMethod, String>(); - - private VBStyleCollection<StructGeneralAttribute, String> attributes = new VBStyleCollection<StructGeneralAttribute, String>(); - - private boolean own = true; - - private LazyLoader loader; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public StructClass(String filename, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { - this(new FileInputStream(filename), own, loader); - } - - public StructClass(InputStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { - this(new DataInputFullStream(inStream), own, loader); - } - - public StructClass(DataInputFullStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { - this.own = own; - this.loader = loader; - - initStruct(inStream); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public boolean hasField(String name, String descriptor) { - return getField(name, descriptor) != null; - } - - public StructField getField(String name, String descriptor) { - return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); - } - - public StructMethod getMethod(String key) { - return methods.getWithKey(key); - } - - public StructMethod getMethod(String name, String descriptor) { - return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); - } - - public void writeToFile(File file) throws IOException { - DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); - writeToOutputStream(out); - out.close(); - } - - public void writeToOutputStream(DataOutputStream out) throws IOException { - - out.writeInt(0xCAFEBABE); - out.writeShort(minor_version); - out.writeShort(major_version); - - getPool().writeToOutputStream(out); - - out.writeShort(access_flags); - out.writeShort(this_class); - out.writeShort(super_class); - - out.writeShort(interfaces.length); - for(int i=0;i<interfaces.length;i++) { - out.writeShort(interfaces[i]); - } - - out.writeShort(fields.size()); - for(int i=0;i<fields.size();i++) { - fields.get(i).writeToStream(out); - } - - out.writeShort(methods.size()); - for(int i=0;i<methods.size();i++) { - methods.get(i).writeToStream(out); - } - - out.writeShort(attributes.size()); - for(StructGeneralAttribute attr: attributes) { - attr.writeToStream(out); - } - - } - - public String getInterface(int i) { - return interfaceNames[i]; - } - - public void releaseResources() { - if(loader != null) { - pool = null; - } - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void initStruct(DataInputFullStream in) throws IOException { - - in.skip(4); - - this.minor_version = in.readUnsignedShort(); - this.major_version = in.readUnsignedShort(); - - pool = new ConstantPool(in); - - this.access_flags = in.readUnsignedShort(); - - this_class = in.readUnsignedShort(); - thisClass = pool.getPrimitiveConstant(this_class); - qualifiedName = thisClass.getString(); - - super_class = in.readUnsignedShort(); - superClass = pool.getPrimitiveConstant(super_class); - - // interfaces - int length = in.readUnsignedShort(); - int[] arrInterfaces = new int[length]; - String[] arrInterfaceNames = new String[length]; - - for (int i = 0; i < length; i++) { - arrInterfaces[i] = in.readUnsignedShort(); - arrInterfaceNames[i] = pool.getPrimitiveConstant(arrInterfaces[i]).getString(); - } - this.interfaces = arrInterfaces; - this.interfaceNames = arrInterfaceNames; - - // fields - VBStyleCollection<StructField, String> lstFields = new VBStyleCollection<StructField, String>(); - length = in.readUnsignedShort(); - for (int i = 0; i < length; i++) { - StructField field = new StructField(); - field.access_flags = in.readUnsignedShort(); - field.name_index = in.readUnsignedShort(); - field.descriptor_index = in.readUnsignedShort(); - - field.initStrings(pool, this_class); - - field.setAttributes(readAttributes(in)); - - lstFields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); - } - this.fields = lstFields; - - // methods - length = in.readUnsignedShort(); - for (int i = 0; i < length; i++) { - StructMethod meth = new StructMethod(in, own, this); - - //if(qualifiedName.endsWith("JUnitStatusLine") && !meth.getName().equals("onProcessStarted") && !meth.getName().startsWith("access")) { - //if(!meth.getName().equals("run")) { - // continue; - //} - - methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(meth.getName(), meth.getDescriptor())); - } - - // attributes - this.attributes = readAttributes(in); - - - // release memory - if(loader != null) { - pool = null; - } - } - - private VBStyleCollection<StructGeneralAttribute, String> readAttributes(DataInputFullStream in) throws IOException { - - VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); - - int length = in.readUnsignedShort(); - for (int i = 0; i < length; i++) { - int attr_nameindex = in.readUnsignedShort(); - String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); - - StructGeneralAttribute attr = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); - - if(attr != null) { - byte[] arr = new byte[in.readInt()]; - in.readFull(arr); - attr.setInfo(arr); - - attr.initContent(pool); - lstAttribute.addWithKey(attr, attr.getName()); - } else { - in.skip(in.readInt()); - } - } - - return lstAttribute; - } - - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public ConstantPool getPool() { - - if(pool == null && loader != null) { - pool = loader.loadPool(qualifiedName); - } - - return pool; - } - - public int[] getInterfaces() { - return interfaces; - } - - public String[] getInterfaceNames() { - return interfaceNames; - } - - public VBStyleCollection<StructMethod, String> getMethods() { - return methods; - } - - public VBStyleCollection<StructField, String> getFields() { - return fields; - } - - public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { - return attributes; - } - - public boolean isOwn() { - return own; - } - - public LazyLoader getLoader() { - return loader; - } - - public boolean isVersionGE_1_5() { - return (major_version > 48 || (major_version == 48 && minor_version > 0)); // FIXME: check second condition - } - - public boolean isVersionGE_1_7() { - return (major_version >= 51); - } - - public int getBytecodeVersion() { - switch(major_version) { - case 52: - return CodeConstants.BYTECODE_JAVA_8; - case 51: - return CodeConstants.BYTECODE_JAVA_7; - case 50: - return CodeConstants.BYTECODE_JAVA_6; - case 49: - return CodeConstants.BYTECODE_JAVA_5; - } - - return CodeConstants.BYTECODE_JAVA_LE_4; - } + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int minor_version; + + public int major_version; + + public int access_flags; + + public int this_class; + + public int super_class; + + public PrimitiveConstant thisClass; + + public PrimitiveConstant superClass; + + public String qualifiedName; + + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private ConstantPool pool; + + private int[] interfaces; + + private String[] interfaceNames; + + private VBStyleCollection<StructField, String> fields = new VBStyleCollection<StructField, String>(); + + private VBStyleCollection<StructMethod, String> methods = new VBStyleCollection<StructMethod, String>(); + + private VBStyleCollection<StructGeneralAttribute, String> attributes = new VBStyleCollection<StructGeneralAttribute, String>(); + + private boolean own = true; + + private LazyLoader loader; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructClass(String filename, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this(new FileInputStream(filename), own, loader); + } + + public StructClass(InputStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this(new DataInputFullStream(inStream), own, loader); + } + + public StructClass(DataInputFullStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this.own = own; + this.loader = loader; + + initStruct(inStream); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public boolean hasField(String name, String descriptor) { + return getField(name, descriptor) != null; + } + + public StructField getField(String name, String descriptor) { + return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); + } + + public StructMethod getMethod(String key) { + return methods.getWithKey(key); + } + + public StructMethod getMethod(String name, String descriptor) { + return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); + } + + public void writeToFile(File file) throws IOException { + DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); + writeToOutputStream(out); + out.close(); + } + + public void writeToOutputStream(DataOutputStream out) throws IOException { + + out.writeInt(0xCAFEBABE); + out.writeShort(minor_version); + out.writeShort(major_version); + + getPool().writeToOutputStream(out); + + out.writeShort(access_flags); + out.writeShort(this_class); + out.writeShort(super_class); + + out.writeShort(interfaces.length); + for (int i = 0; i < interfaces.length; i++) { + out.writeShort(interfaces[i]); + } + + out.writeShort(fields.size()); + for (int i = 0; i < fields.size(); i++) { + fields.get(i).writeToStream(out); + } + + out.writeShort(methods.size()); + for (int i = 0; i < methods.size(); i++) { + methods.get(i).writeToStream(out); + } + + out.writeShort(attributes.size()); + for (StructGeneralAttribute attr : attributes) { + attr.writeToStream(out); + } + } + + public String getInterface(int i) { + return interfaceNames[i]; + } + + public void releaseResources() { + if (loader != null) { + pool = null; + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void initStruct(DataInputFullStream in) throws IOException { + + in.skip(4); + + this.minor_version = in.readUnsignedShort(); + this.major_version = in.readUnsignedShort(); + + pool = new ConstantPool(in); + + this.access_flags = in.readUnsignedShort(); + + this_class = in.readUnsignedShort(); + thisClass = pool.getPrimitiveConstant(this_class); + qualifiedName = thisClass.getString(); + + super_class = in.readUnsignedShort(); + superClass = pool.getPrimitiveConstant(super_class); + + // interfaces + int length = in.readUnsignedShort(); + int[] arrInterfaces = new int[length]; + String[] arrInterfaceNames = new String[length]; + + for (int i = 0; i < length; i++) { + arrInterfaces[i] = in.readUnsignedShort(); + arrInterfaceNames[i] = pool.getPrimitiveConstant(arrInterfaces[i]).getString(); + } + this.interfaces = arrInterfaces; + this.interfaceNames = arrInterfaceNames; + + // fields + VBStyleCollection<StructField, String> lstFields = new VBStyleCollection<StructField, String>(); + length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + StructField field = new StructField(); + field.access_flags = in.readUnsignedShort(); + field.name_index = in.readUnsignedShort(); + field.descriptor_index = in.readUnsignedShort(); + + field.initStrings(pool, this_class); + + field.setAttributes(readAttributes(in)); + + lstFields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); + } + this.fields = lstFields; + + // methods + length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + StructMethod meth = new StructMethod(in, own, this); + + //if(qualifiedName.endsWith("JUnitStatusLine") && !meth.getName().equals("onProcessStarted") && !meth.getName().startsWith("access")) { + //if(!meth.getName().equals("run")) { + // continue; + //} + + methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(meth.getName(), meth.getDescriptor())); + } + + // attributes + this.attributes = readAttributes(in); + + + // release memory + if (loader != null) { + pool = null; + } + } + + private VBStyleCollection<StructGeneralAttribute, String> readAttributes(DataInputFullStream in) throws IOException { + + VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); + + int length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + StructGeneralAttribute attr = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); + + if (attr != null) { + byte[] arr = new byte[in.readInt()]; + in.readFull(arr); + attr.setInfo(arr); + + attr.initContent(pool); + lstAttribute.addWithKey(attr, attr.getName()); + } + else { + in.skip(in.readInt()); + } + } + + return lstAttribute; + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public ConstantPool getPool() { + + if (pool == null && loader != null) { + pool = loader.loadPool(qualifiedName); + } + + return pool; + } + + public int[] getInterfaces() { + return interfaces; + } + + public String[] getInterfaceNames() { + return interfaceNames; + } + + public VBStyleCollection<StructMethod, String> getMethods() { + return methods; + } + + public VBStyleCollection<StructField, String> getFields() { + return fields; + } + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public boolean isOwn() { + return own; + } + + public LazyLoader getLoader() { + return loader; + } + + public boolean isVersionGE_1_5() { + return (major_version > 48 || (major_version == 48 && minor_version > 0)); // FIXME: check second condition + } + + public boolean isVersionGE_1_7() { + return (major_version >= 51); + } + + public int getBytecodeVersion() { + switch (major_version) { + case 52: + return CodeConstants.BYTECODE_JAVA_8; + case 51: + return CodeConstants.BYTECODE_JAVA_7; + case 50: + return CodeConstants.BYTECODE_JAVA_6; + case 49: + return CodeConstants.BYTECODE_JAVA_5; + } + + return CodeConstants.BYTECODE_JAVA_LE_4; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index c253a10..3728a2a 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -1,19 +1,25 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; + import java.io.File; import java.io.IOException; import java.util.Enumeration; @@ -22,190 +28,189 @@ import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; - public class StructContext { - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private LazyLoader loader; - - private HashMap<String, StructClass> classes = new HashMap<String, StructClass>(); - - private HashMap<String, ContextUnit> units = new HashMap<String, ContextUnit>(); - - private ContextUnit defaultUnit; - - private IDecompilatSaver saver; - - private IDecompiledData decdata; - - public StructContext(IDecompilatSaver saver, IDecompiledData decdata, LazyLoader loader) { - - this.saver = saver; - this.decdata = decdata; - this.loader = loader; - - defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, "", true, saver, decdata); - units.put("", defaultUnit); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public StructClass getClass(String name) { - return classes.get(name); - } - - public void reloadContext() throws IOException { - - for(ContextUnit unit: units.values()) { - - for(StructClass cl : unit.getClasses()) { - classes.remove(cl.qualifiedName); - } - - unit.reload(loader); - - // adjust lobal class collection - for(StructClass cl : unit.getClasses()) { - classes.put(cl.qualifiedName, cl); - } - } - } - - public void saveContext() { - - for(ContextUnit unit: units.values()) { - if(unit.isOwn()) { - unit.save(); - } - } - } - - public void addSpace(File file, boolean isOwn) throws IOException { - addSpace("", file, isOwn); - } - - private void addSpace(String path, File file, boolean isOwn) throws IOException { - - if(file.isDirectory()) { - - File[] files = file.listFiles(); - path += "/" + (path.length()==0?"":file.getName()); - - for(int i=files.length-1;i>=0;i--) { - addSpace(path, files[i], isOwn); - } - - } else { - - String filename = file.getName(); - - boolean isArchive = false; - - try { - if(filename.endsWith(".jar")) { - addArchive(path, file, ContextUnit.TYPE_JAR, isOwn); - isArchive = true; - } else if(filename.endsWith(".zip")) { - addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn); - isArchive = true; - } - } catch(IOException ex) { - DecompilerContext.getLogger() - .writeMessage("Invalid archive file: "+(path.length()>0?path+"/":"")+filename, IFernflowerLogger.ERROR); - } - - if(!isArchive) { - ContextUnit unit = units.get(path); - if(unit == null) { - unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decdata); - units.put(path, unit); - } - - boolean isClass = false; - - if(filename.endsWith(".class")) { - try { - StructClass cl = new StructClass(loader.getClassStream(file.getAbsolutePath(), null), isOwn, loader); - - classes.put(cl.qualifiedName, cl); - unit.addClass(cl, filename); - loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.CLASS, file.getAbsolutePath(), null)); - - isClass = true; - } catch(IOException ex) { - DecompilerContext.getLogger() - .writeMessage("Invalid class file: "+(path.length()>0?path+"/":"")+filename, IFernflowerLogger.ERROR); - } - } - - if(!isClass) { - unit.addOtherEntry(file.getAbsolutePath(), filename); - } - } - } - } - - - private void addArchive(String path, File file, int type, boolean isOwn) throws IOException { - - ZipFile archive; - - if(type == ContextUnit.TYPE_JAR) { // jar - archive = new JarFile(file); - } else { // zip - archive = new ZipFile(file); - } - - Enumeration<? extends ZipEntry> en = archive.entries(); - while(en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); - - ContextUnit unit = units.get(path+"/"+file.getName()); - if(unit == null) { - unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decdata); - if(type == ContextUnit.TYPE_JAR) { - unit.setManifest(((JarFile)archive).getManifest()); - } - units.put(path+"/"+file.getName(), unit); - } - - String name = entr.getName(); - if(!entr.isDirectory()) { - if(name.endsWith(".class")) { - StructClass cl = new StructClass(archive.getInputStream(entr), isOwn, loader); - classes.put(cl.qualifiedName, cl); - - unit.addClass(cl, name); - - if(loader != null) { - loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.ENTRY, file.getAbsolutePath(), name)); - } - - } else { - unit.addOtherEntry(file.getAbsolutePath(), name); - } - } else if(entr.isDirectory()) { - unit.addDirEntry(name); - } - } - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public HashMap<String, StructClass> getClasses() { - return classes; - } + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private LazyLoader loader; + + private HashMap<String, StructClass> classes = new HashMap<String, StructClass>(); + + private HashMap<String, ContextUnit> units = new HashMap<String, ContextUnit>(); + + private ContextUnit defaultUnit; + + private IDecompilatSaver saver; + + private IDecompiledData decdata; + + public StructContext(IDecompilatSaver saver, IDecompiledData decdata, LazyLoader loader) { + + this.saver = saver; + this.decdata = decdata; + this.loader = loader; + + defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, "", true, saver, decdata); + units.put("", defaultUnit); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public StructClass getClass(String name) { + return classes.get(name); + } + + public void reloadContext() throws IOException { + + for (ContextUnit unit : units.values()) { + + for (StructClass cl : unit.getClasses()) { + classes.remove(cl.qualifiedName); + } + + unit.reload(loader); + + // adjust lobal class collection + for (StructClass cl : unit.getClasses()) { + classes.put(cl.qualifiedName, cl); + } + } + } + + public void saveContext() { + + for (ContextUnit unit : units.values()) { + if (unit.isOwn()) { + unit.save(); + } + } + } + + public void addSpace(File file, boolean isOwn) throws IOException { + addSpace("", file, isOwn); + } + + private void addSpace(String path, File file, boolean isOwn) throws IOException { + + if (file.isDirectory()) { + File[] files = file.listFiles(); + path += "/" + (path.length() == 0 ? "" : file.getName()); + + for (int i = files.length - 1; i >= 0; i--) { + addSpace(path, files[i], isOwn); + } + } + else { + + String filename = file.getName(); + + boolean isArchive = false; + + try { + if (filename.endsWith(".jar")) { + addArchive(path, file, ContextUnit.TYPE_JAR, isOwn); + isArchive = true; + } + else if (filename.endsWith(".zip")) { + addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn); + isArchive = true; + } + } + catch (IOException ex) { + DecompilerContext.getLogger() + .writeMessage("Invalid archive file: " + (path.length() > 0 ? path + "/" : "") + filename, IFernflowerLogger.ERROR); + } + + if (!isArchive) { + ContextUnit unit = units.get(path); + if (unit == null) { + unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decdata); + units.put(path, unit); + } + + boolean isClass = false; + + if (filename.endsWith(".class")) { + try { + StructClass cl = new StructClass(loader.getClassStream(file.getAbsolutePath(), null), isOwn, loader); + + classes.put(cl.qualifiedName, cl); + unit.addClass(cl, filename); + loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.CLASS, file.getAbsolutePath(), null)); + + isClass = true; + } + catch (IOException ex) { + DecompilerContext.getLogger() + .writeMessage("Invalid class file: " + (path.length() > 0 ? path + "/" : "") + filename, IFernflowerLogger.ERROR); + } + } + + if (!isClass) { + unit.addOtherEntry(file.getAbsolutePath(), filename); + } + } + } + } + + + private void addArchive(String path, File file, int type, boolean isOwn) throws IOException { + + ZipFile archive; + + if (type == ContextUnit.TYPE_JAR) { // jar + archive = new JarFile(file); + } + else { // zip + archive = new ZipFile(file); + } + + Enumeration<? extends ZipEntry> en = archive.entries(); + while (en.hasMoreElements()) { + ZipEntry entr = en.nextElement(); + + ContextUnit unit = units.get(path + "/" + file.getName()); + if (unit == null) { + unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decdata); + if (type == ContextUnit.TYPE_JAR) { + unit.setManifest(((JarFile)archive).getManifest()); + } + units.put(path + "/" + file.getName(), unit); + } + + String name = entr.getName(); + if (!entr.isDirectory()) { + if (name.endsWith(".class")) { + StructClass cl = new StructClass(archive.getInputStream(entr), isOwn, loader); + classes.put(cl.qualifiedName, cl); + + unit.addClass(cl, name); + + if (loader != null) { + loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.ENTRY, file.getAbsolutePath(), name)); + } + } + else { + unit.addOtherEntry(file.getAbsolutePath(), name); + } + } + else if (entr.isDirectory()) { + unit.addDirEntry(name); + } + } + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public HashMap<String, StructClass> getClasses() { + return classes; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 2537e7b..4c0f25d 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -1,28 +1,29 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; -import java.io.DataOutputStream; -import java.io.IOException; - import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.VBStyleCollection; +import java.io.DataOutputStream; +import java.io.IOException; + /* - field_info { + field_info { u2 access_flags; u2 name_index; u2 descriptor_index; @@ -33,77 +34,77 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; public class StructField { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int access_flags; - public int name_index; - public int descriptor_index; - - private String name; - private String descriptor; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private VBStyleCollection<StructGeneralAttribute, String> attributes; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public StructField() {} - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void writeToStream(DataOutputStream out) throws IOException { - - out.writeShort(access_flags); - out.writeShort(name_index); - out.writeShort(descriptor_index); - - out.writeShort(attributes.size()); - for(StructGeneralAttribute attr: attributes) { - attr.writeToStream(out); - } - } - - public void initStrings(ConstantPool pool, int class_index) { - String[] values = pool.getClassElement(ConstantPool.FIELD, class_index, name_index, descriptor_index); - name = values[0]; - descriptor = values[1]; - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { - return attributes; - } - - public void setAttributes(VBStyleCollection<StructGeneralAttribute, String> attributes) { - this.attributes = attributes; - } - - public String getDescriptor() { - return descriptor; - } - - public void setDescriptor(String descriptor) { - this.descriptor = descriptor; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int access_flags; + public int name_index; + public int descriptor_index; + + private String name; + private String descriptor; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private VBStyleCollection<StructGeneralAttribute, String> attributes; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructField() { + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(access_flags); + out.writeShort(name_index); + out.writeShort(descriptor_index); + + out.writeShort(attributes.size()); + for (StructGeneralAttribute attr : attributes) { + attr.writeToStream(out); + } + } + + public void initStrings(ConstantPool pool, int class_index) { + String[] values = pool.getClassElement(ConstantPool.FIELD, class_index, name_index, descriptor_index); + name = values[0]; + descriptor = values[1]; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public void setAttributes(VBStyleCollection<StructGeneralAttribute, String> attributes) { + this.attributes = attributes; + } + + public String getDescriptor() { + return descriptor; + } + + public void setDescriptor(String descriptor) { + this.descriptor = descriptor; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 20fdae9..baaf633 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -1,19 +1,27 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct; +import org.jetbrains.java.decompiler.code.*; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -21,19 +29,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.ConstantsUtil; -import org.jetbrains.java.decompiler.code.ExceptionHandler; -import org.jetbrains.java.decompiler.code.ExceptionTable; -import org.jetbrains.java.decompiler.code.FullInstructionSequence; -import org.jetbrains.java.decompiler.code.Instruction; -import org.jetbrains.java.decompiler.code.InstructionSequence; -import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; -import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; -import org.jetbrains.java.decompiler.util.DataInputFullStream; -import org.jetbrains.java.decompiler.util.VBStyleCollection; - /* method_info { u2 access_flags; @@ -45,516 +40,525 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; */ public class StructMethod implements CodeConstants { - - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int name_index; - - public int descriptor_index; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private static final int[] opr_iconst = new int[] {-1,0,1,2,3,4,5}; - - private static final int[] opr_loadstore = new int[] {0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3}; - - private static final int[] opcs_load = new int[] {opc_iload,opc_lload,opc_fload,opc_dload,opc_aload}; - - private static final int[] opcs_store = new int[] {opc_istore,opc_lstore,opc_fstore,opc_dstore,opc_astore}; - - - private int accessFlags; - - private VBStyleCollection<StructGeneralAttribute, String> attributes; - - private int localVariables; - - private int maxStack; - - private String name; - - private String descriptor; - - private InstructionSequence seq; - - private boolean containsCode = false; - - private boolean own; - - private StructClass classStruct; - - - // lazy properties - private boolean lazy; - - private boolean expanded; - - private byte[] code_content; - - private int code_length = 0; - - private int code_fulllength = 0; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public StructMethod(DataInputFullStream in, boolean own, StructClass clstruct) throws IOException { - this(in, true, own, clstruct); - } - - public StructMethod(DataInputFullStream in, boolean lazy, boolean own, StructClass clstruct) throws IOException { - - this.own = own; - this.lazy = lazy; - this.expanded = !lazy; - this.classStruct = clstruct; - - accessFlags = in.readUnsignedShort(); - name_index = in.readUnsignedShort(); - descriptor_index = in.readUnsignedShort(); - - ConstantPool pool = clstruct.getPool(); - - initStrings(pool, clstruct.this_class); - - VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); - int len = in.readUnsignedShort(); - for(int i=0;i<len;i++) { - - int attr_nameindex = in.readUnsignedShort(); - String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); - - if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { - if(!this.own) { - // skip code in foreign classes - in.skip(8); - in.skip(in.readInt()); - in.skip(8*in.readUnsignedShort()); - } else { - containsCode = true; - - in.skip(4); - - maxStack = in.readUnsignedShort(); - localVariables = in.readUnsignedShort(); - - if(lazy) { - code_length = in.readInt(); - - in.skip(code_length); - - int exc_length = in.readUnsignedShort(); - code_fulllength = code_length + exc_length*8+2; - - in.skip(exc_length*8); - - } else { - seq = parseBytecode(in, in.readInt(), pool); - } - } - - // code attributes - int length = in.readUnsignedShort(); - for (int j = 0; j < length; j++) { - int codeattr_nameindex = in.readUnsignedShort(); - String codeattrname = pool.getPrimitiveConstant(codeattr_nameindex).getString(); - - readAttribute(in, pool, lstAttribute, codeattr_nameindex, codeattrname); - } - } else { - readAttribute(in, pool, lstAttribute, attr_nameindex, attrname); - } - } - - attributes = lstAttribute; - } - - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void writeToStream(DataOutputStream out) throws IOException { - - out.writeShort(accessFlags); - out.writeShort(name_index); - out.writeShort(descriptor_index); - - out.writeShort(attributes.size()); - - for(StructGeneralAttribute attr: attributes) { - if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attr.getName())){ - out.writeShort(attr.getAttribute_name_index()); - - if(lazy && !expanded) { - out.writeInt(10+code_content.length); - out.writeShort(maxStack); - out.writeShort(localVariables); - out.writeInt(code_length); - out.write(code_content); - } else { - ByteArrayOutputStream codeout = new ByteArrayOutputStream(); - seq.writeCodeToStream(new DataOutputStream(codeout)); - - ByteArrayOutputStream excout = new ByteArrayOutputStream(); - seq.writeExceptionsToStream(new DataOutputStream(excout)); - - out.writeInt(10+codeout.size()+excout.size()); - - out.writeShort(maxStack); - out.writeShort(localVariables); - out.writeInt(codeout.size()); - codeout.writeTo(out); - excout.writeTo(out); - } - // no attributes - out.writeShort(0); - } else { - attr.writeToStream(out); - } - } - - } - - private void readAttribute(DataInputFullStream in, ConstantPool pool, VBStyleCollection<StructGeneralAttribute, String> lstAttribute, - int attr_nameindex, String attrname) throws IOException { - - StructGeneralAttribute attribute = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); - - if(attribute != null) { - attrname = attribute.getName(); - - byte[] arr = new byte[in.readInt()]; - in.readFull(arr); - attribute.setInfo(arr); - - attribute.initContent(pool); - - if(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname) && - lstAttribute.containsKey(attrname)) { - // merge all variable tables - StructLocalVariableTableAttribute oldattr = (StructLocalVariableTableAttribute)lstAttribute.getWithKey(attrname); - oldattr.addLocalVariableTable((StructLocalVariableTableAttribute)attribute); - } else { - lstAttribute.addWithKey(attribute, attribute.getName()); - } - } else { - in.skip(in.readInt()); - } - } - - private void initStrings(ConstantPool pool, int class_index) { - String[] values = pool.getClassElement(ConstantPool.METHOD, class_index, name_index, descriptor_index); - name = values[0]; - descriptor = values[1]; - } - - public void expandData() throws IOException { - if(containsCode && lazy && !expanded) { - - byte[] codearr = classStruct.getLoader().loadBytecode(this, code_fulllength); - - seq = parseBytecode(new DataInputFullStream(new ByteArrayInputStream(codearr)), code_length, classStruct.getPool()); - expanded = true; - } - } - - public void releaseResources() throws IOException { - if(containsCode && lazy && expanded) { - seq = null; - expanded = false; - } - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { - - VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); - - int bytecode_version = classStruct.getBytecodeVersion(); - - for(int i=0;i<length;) { - - int offset = i; - - int opcode = in.readUnsignedByte(); - int group = GROUP_GENERAL; - - boolean wide = (opcode == opc_wide); - - if(wide) { - i++; - opcode = in.readUnsignedByte(); - } - - List<Integer> operands = new ArrayList<Integer>(); - - if(opcode>=opc_iconst_m1 && opcode<=opc_iconst_5) { - operands.add(new Integer(opr_iconst[opcode-opc_iconst_m1])); - opcode = opc_bipush; - }else if(opcode>=opc_iload_0 && opcode<=opc_aload_3) { - operands.add(new Integer(opr_loadstore[opcode-opc_iload_0])); - opcode = opcs_load[(opcode-opc_iload_0)/4]; - }else if(opcode>=opc_istore_0 && opcode<=opc_astore_3) { - operands.add(new Integer(opr_loadstore[opcode-opc_istore_0])); - opcode = opcs_store[(opcode-opc_istore_0)/4]; - } else { - switch (opcode) { - case opc_bipush: - operands.add(new Integer(in.readByte())); - i++; - break; - case opc_ldc: - case opc_newarray: - operands.add(new Integer(in.readUnsignedByte())); - i++; - break; - case opc_sipush: - case opc_ifeq: - case opc_ifne: - case opc_iflt: - case opc_ifge: - case opc_ifgt: - case opc_ifle: - 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: - case opc_goto: - case opc_jsr: - case opc_ifnull: - case opc_ifnonnull: - if(opcode!=opc_sipush) { - group = GROUP_JUMP; - } - operands.add(new Integer(in.readShort())); - i+=2; - break; - case opc_ldc_w: - case opc_ldc2_w: - case opc_getstatic: - case opc_putstatic: - case opc_getfield: - case opc_putfield: - case opc_invokevirtual: - case opc_invokespecial: - case opc_invokestatic: - case opc_new: - case opc_anewarray: - case opc_checkcast: - case opc_instanceof: - operands.add(new Integer(in.readUnsignedShort())); - i+=2; - if(opcode>=opc_getstatic && opcode<=opc_putfield) { - group = GROUP_FIELDACCESS; - } else if(opcode>=opc_invokevirtual && opcode<=opc_invokestatic) { - group = GROUP_INVOCATION; - } - break; - case opc_invokedynamic: - if(classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before - operands.add(new Integer(in.readUnsignedShort())); - in.skip(2); - group = GROUP_INVOCATION; - i+=4; - } - break; - case opc_iload: - case opc_lload: - case opc_fload: - case opc_dload: - case opc_aload: - case opc_istore: - case opc_lstore: - case opc_fstore: - case opc_dstore: - case opc_astore: - case opc_ret: - if(wide) { - operands.add(new Integer(in.readUnsignedShort())); - i+=2; - } else { - operands.add(new Integer(in.readUnsignedByte())); - i++; - } - if(opcode == opc_ret) { - group = GROUP_RETURN; - } - break; - case opc_iinc: - if (wide) { - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readShort())); - i+=4; - } else { - operands.add(new Integer(in.readUnsignedByte())); - operands.add(new Integer(in.readByte())); - i+=2; - } - break; - case opc_goto_w: - case opc_jsr_w: - opcode = opcode == opc_jsr_w?opc_jsr:opc_goto; - operands.add(new Integer(in.readInt())); - group = GROUP_JUMP; - i+=4; - break; - case opc_invokeinterface: - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readUnsignedByte())); - in.skip(1); - group = GROUP_INVOCATION; - i+=4; - break; - case opc_multianewarray: - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readUnsignedByte())); - i+=3; - break; - case opc_tableswitch: - in.skip((4-(i+1)%4)%4); - i+=((4-(i+1)%4)%4); // padding - operands.add(new Integer(in.readInt())); - i+=4; - int low = in.readInt(); - operands.add(new Integer(low)); - i+=4; - int high = in.readInt(); - operands.add(new Integer(high)); - i+=4; - - for(int j=0;j<high-low+1;j++) { - operands.add(new Integer(in.readInt())); - i+=4; - } - group = GROUP_SWITCH; - - break; - case opc_lookupswitch: - in.skip((4-(i+1)%4)%4); - i+=((4-(i+1)%4)%4); // padding - operands.add(new Integer(in.readInt())); - i+=4; - int npairs = in.readInt(); - operands.add(new Integer(npairs)); - i+=4; - - for(int j=0;j<npairs;j++) { - operands.add(new Integer(in.readInt())); - i+=4; - operands.add(new Integer(in.readInt())); - i+=4; - } - group = GROUP_SWITCH; - break; - case opc_ireturn: - case opc_lreturn: - case opc_freturn: - case opc_dreturn: - case opc_areturn: - case opc_return: - case opc_athrow: - group = GROUP_RETURN; - } - } - - int[] ops = new int[operands.size()]; - for(int j=0;j<operands.size();j++) { - ops[j] = ((Integer)operands.get(j)).intValue(); - } - - Instruction instr = ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, ops); - - collinstr.addWithKey(instr, new Integer(offset)); - - i++; - } - - // initialize exception table - List<ExceptionHandler> lstHandlers = new ArrayList<ExceptionHandler>(); - - int exception_count = in.readUnsignedShort(); - for(int i=0;i<exception_count;i++) { - ExceptionHandler handler = new ExceptionHandler(); - handler.from = in.readUnsignedShort(); - handler.to = in.readUnsignedShort(); - handler.handler = in.readUnsignedShort(); - - int excclass = in.readUnsignedShort(); - handler.class_index = excclass; - if(excclass!=0) { - handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString(); - } - - lstHandlers.add(handler); - } - - InstructionSequence seq = new FullInstructionSequence(collinstr, new ExceptionTable(lstHandlers)); - - // initialize instructions - int i = seq.length()-1; - seq.setPointer(i); - - while(i>=0) { - Instruction instr = seq.getInstr(i--); - if(instr.group!=GROUP_GENERAL) { - instr.initInstruction(seq); - } - seq.addToPointer(-1); - } - - return seq; - - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public InstructionSequence getInstructionSequence() { - return seq; - } - - public String getDescriptor() { - return descriptor; - } - - public String getName() { - return name; - } - - public int getAccessFlags() { - return accessFlags; - } - - public int getLocalVariables() { - return localVariables; - } - - public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { - return attributes; - } - - public StructClass getClassStruct() { - return classStruct; - } - - public boolean containsCode() { - return containsCode; - } + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int name_index; + + public int descriptor_index; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private static final int[] opr_iconst = new int[]{-1, 0, 1, 2, 3, 4, 5}; + + private static final int[] opr_loadstore = new int[]{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; + + private static final int[] opcs_load = new int[]{opc_iload, opc_lload, opc_fload, opc_dload, opc_aload}; + + private static final int[] opcs_store = new int[]{opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore}; + + + private int accessFlags; + + private VBStyleCollection<StructGeneralAttribute, String> attributes; + + private int localVariables; + + private int maxStack; + + private String name; + + private String descriptor; + + private InstructionSequence seq; + + private boolean containsCode = false; + + private boolean own; + + private StructClass classStruct; + + + // lazy properties + private boolean lazy; + + private boolean expanded; + + private byte[] code_content; + + private int code_length = 0; + + private int code_fulllength = 0; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructMethod(DataInputFullStream in, boolean own, StructClass clstruct) throws IOException { + this(in, true, own, clstruct); + } + + public StructMethod(DataInputFullStream in, boolean lazy, boolean own, StructClass clstruct) throws IOException { + + this.own = own; + this.lazy = lazy; + this.expanded = !lazy; + this.classStruct = clstruct; + + accessFlags = in.readUnsignedShort(); + name_index = in.readUnsignedShort(); + descriptor_index = in.readUnsignedShort(); + + ConstantPool pool = clstruct.getPool(); + + initStrings(pool, clstruct.this_class); + + VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); + int len = in.readUnsignedShort(); + for (int i = 0; i < len; i++) { + + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + if (StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { + if (!this.own) { + // skip code in foreign classes + in.skip(8); + in.skip(in.readInt()); + in.skip(8 * in.readUnsignedShort()); + } + else { + containsCode = true; + + in.skip(4); + + maxStack = in.readUnsignedShort(); + localVariables = in.readUnsignedShort(); + + if (lazy) { + code_length = in.readInt(); + + in.skip(code_length); + + int exc_length = in.readUnsignedShort(); + code_fulllength = code_length + exc_length * 8 + 2; + + in.skip(exc_length * 8); + } + else { + seq = parseBytecode(in, in.readInt(), pool); + } + } + + // code attributes + int length = in.readUnsignedShort(); + for (int j = 0; j < length; j++) { + int codeattr_nameindex = in.readUnsignedShort(); + String codeattrname = pool.getPrimitiveConstant(codeattr_nameindex).getString(); + + readAttribute(in, pool, lstAttribute, codeattr_nameindex, codeattrname); + } + } + else { + readAttribute(in, pool, lstAttribute, attr_nameindex, attrname); + } + } + + attributes = lstAttribute; + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(accessFlags); + out.writeShort(name_index); + out.writeShort(descriptor_index); + + out.writeShort(attributes.size()); + + for (StructGeneralAttribute attr : attributes) { + if (StructGeneralAttribute.ATTRIBUTE_CODE.equals(attr.getName())) { + out.writeShort(attr.getAttribute_name_index()); + + if (lazy && !expanded) { + out.writeInt(10 + code_content.length); + out.writeShort(maxStack); + out.writeShort(localVariables); + out.writeInt(code_length); + out.write(code_content); + } + else { + ByteArrayOutputStream codeout = new ByteArrayOutputStream(); + seq.writeCodeToStream(new DataOutputStream(codeout)); + + ByteArrayOutputStream excout = new ByteArrayOutputStream(); + seq.writeExceptionsToStream(new DataOutputStream(excout)); + + out.writeInt(10 + codeout.size() + excout.size()); + + out.writeShort(maxStack); + out.writeShort(localVariables); + out.writeInt(codeout.size()); + codeout.writeTo(out); + excout.writeTo(out); + } + // no attributes + out.writeShort(0); + } + else { + attr.writeToStream(out); + } + } + } + + private void readAttribute(DataInputFullStream in, ConstantPool pool, VBStyleCollection<StructGeneralAttribute, String> lstAttribute, + int attr_nameindex, String attrname) throws IOException { + + StructGeneralAttribute attribute = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); + + if (attribute != null) { + attrname = attribute.getName(); + + byte[] arr = new byte[in.readInt()]; + in.readFull(arr); + attribute.setInfo(arr); + + attribute.initContent(pool); + + if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname) && + lstAttribute.containsKey(attrname)) { + // merge all variable tables + StructLocalVariableTableAttribute oldattr = (StructLocalVariableTableAttribute)lstAttribute.getWithKey(attrname); + oldattr.addLocalVariableTable((StructLocalVariableTableAttribute)attribute); + } + else { + lstAttribute.addWithKey(attribute, attribute.getName()); + } + } + else { + in.skip(in.readInt()); + } + } + + private void initStrings(ConstantPool pool, int class_index) { + String[] values = pool.getClassElement(ConstantPool.METHOD, class_index, name_index, descriptor_index); + name = values[0]; + descriptor = values[1]; + } + + public void expandData() throws IOException { + if (containsCode && lazy && !expanded) { + + byte[] codearr = classStruct.getLoader().loadBytecode(this, code_fulllength); + + seq = parseBytecode(new DataInputFullStream(new ByteArrayInputStream(codearr)), code_length, classStruct.getPool()); + expanded = true; + } + } + + public void releaseResources() throws IOException { + if (containsCode && lazy && expanded) { + seq = null; + expanded = false; + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { + + VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); + + int bytecode_version = classStruct.getBytecodeVersion(); + + for (int i = 0; i < length; ) { + + int offset = i; + + int opcode = in.readUnsignedByte(); + int group = GROUP_GENERAL; + + boolean wide = (opcode == opc_wide); + + if (wide) { + i++; + opcode = in.readUnsignedByte(); + } + + List<Integer> operands = new ArrayList<Integer>(); + + if (opcode >= opc_iconst_m1 && opcode <= opc_iconst_5) { + operands.add(new Integer(opr_iconst[opcode - opc_iconst_m1])); + opcode = opc_bipush; + } + else if (opcode >= opc_iload_0 && opcode <= opc_aload_3) { + operands.add(new Integer(opr_loadstore[opcode - opc_iload_0])); + opcode = opcs_load[(opcode - opc_iload_0) / 4]; + } + else if (opcode >= opc_istore_0 && opcode <= opc_astore_3) { + operands.add(new Integer(opr_loadstore[opcode - opc_istore_0])); + opcode = opcs_store[(opcode - opc_istore_0) / 4]; + } + else { + switch (opcode) { + case opc_bipush: + operands.add(new Integer(in.readByte())); + i++; + break; + case opc_ldc: + case opc_newarray: + operands.add(new Integer(in.readUnsignedByte())); + i++; + break; + case opc_sipush: + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + 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: + case opc_goto: + case opc_jsr: + case opc_ifnull: + case opc_ifnonnull: + if (opcode != opc_sipush) { + group = GROUP_JUMP; + } + operands.add(new Integer(in.readShort())); + i += 2; + break; + case opc_ldc_w: + case opc_ldc2_w: + case opc_getstatic: + case opc_putstatic: + case opc_getfield: + case opc_putfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_new: + case opc_anewarray: + case opc_checkcast: + case opc_instanceof: + operands.add(new Integer(in.readUnsignedShort())); + i += 2; + if (opcode >= opc_getstatic && opcode <= opc_putfield) { + group = GROUP_FIELDACCESS; + } + else if (opcode >= opc_invokevirtual && opcode <= opc_invokestatic) { + group = GROUP_INVOCATION; + } + break; + case opc_invokedynamic: + if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before + operands.add(new Integer(in.readUnsignedShort())); + in.skip(2); + group = GROUP_INVOCATION; + i += 4; + } + break; + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + case opc_ret: + if (wide) { + operands.add(new Integer(in.readUnsignedShort())); + i += 2; + } + else { + operands.add(new Integer(in.readUnsignedByte())); + i++; + } + if (opcode == opc_ret) { + group = GROUP_RETURN; + } + break; + case opc_iinc: + if (wide) { + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readShort())); + i += 4; + } + else { + operands.add(new Integer(in.readUnsignedByte())); + operands.add(new Integer(in.readByte())); + i += 2; + } + break; + case opc_goto_w: + case opc_jsr_w: + opcode = opcode == opc_jsr_w ? opc_jsr : opc_goto; + operands.add(new Integer(in.readInt())); + group = GROUP_JUMP; + i += 4; + break; + case opc_invokeinterface: + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readUnsignedByte())); + in.skip(1); + group = GROUP_INVOCATION; + i += 4; + break; + case opc_multianewarray: + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readUnsignedByte())); + i += 3; + break; + case opc_tableswitch: + in.skip((4 - (i + 1) % 4) % 4); + i += ((4 - (i + 1) % 4) % 4); // padding + operands.add(new Integer(in.readInt())); + i += 4; + int low = in.readInt(); + operands.add(new Integer(low)); + i += 4; + int high = in.readInt(); + operands.add(new Integer(high)); + i += 4; + + for (int j = 0; j < high - low + 1; j++) { + operands.add(new Integer(in.readInt())); + i += 4; + } + group = GROUP_SWITCH; + + break; + case opc_lookupswitch: + in.skip((4 - (i + 1) % 4) % 4); + i += ((4 - (i + 1) % 4) % 4); // padding + operands.add(new Integer(in.readInt())); + i += 4; + int npairs = in.readInt(); + operands.add(new Integer(npairs)); + i += 4; + + for (int j = 0; j < npairs; j++) { + operands.add(new Integer(in.readInt())); + i += 4; + operands.add(new Integer(in.readInt())); + i += 4; + } + group = GROUP_SWITCH; + break; + case opc_ireturn: + case opc_lreturn: + case opc_freturn: + case opc_dreturn: + case opc_areturn: + case opc_return: + case opc_athrow: + group = GROUP_RETURN; + } + } + + int[] ops = new int[operands.size()]; + for (int j = 0; j < operands.size(); j++) { + ops[j] = ((Integer)operands.get(j)).intValue(); + } + + Instruction instr = ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, ops); + + collinstr.addWithKey(instr, new Integer(offset)); + + i++; + } + + // initialize exception table + List<ExceptionHandler> lstHandlers = new ArrayList<ExceptionHandler>(); + + int exception_count = in.readUnsignedShort(); + for (int i = 0; i < exception_count; i++) { + ExceptionHandler handler = new ExceptionHandler(); + handler.from = in.readUnsignedShort(); + handler.to = in.readUnsignedShort(); + handler.handler = in.readUnsignedShort(); + + int excclass = in.readUnsignedShort(); + handler.class_index = excclass; + if (excclass != 0) { + handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString(); + } + + lstHandlers.add(handler); + } + + InstructionSequence seq = new FullInstructionSequence(collinstr, new ExceptionTable(lstHandlers)); + + // initialize instructions + int i = seq.length() - 1; + seq.setPointer(i); + + while (i >= 0) { + Instruction instr = seq.getInstr(i--); + if (instr.group != GROUP_GENERAL) { + instr.initInstruction(seq); + } + seq.addToPointer(-1); + } + + return seq; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public InstructionSequence getInstructionSequence() { + return seq; + } + + public String getDescriptor() { + return descriptor; + } + + public String getName() { + return name; + } + + public int getAccessFlags() { + return accessFlags; + } + + public int getLocalVariables() { + return localVariables; + } + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public StructClass getClassStruct() { + return classStruct; + } + + public boolean containsCode() { + return containsCode; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java index d171349..f36df6d 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java @@ -1,40 +1,40 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; - import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; + public class StructAnnDefaultAttribute extends StructGeneralAttribute { - private Exprent defaultValue; - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_ANNOTATION_DEFAULT; - - DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); - defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool); - } - - public Exprent getDefaultValue() { - return defaultValue; - } - + private Exprent defaultValue; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_ANNOTATION_DEFAULT; + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool); + } + + public Exprent getDefaultValue() { + return defaultValue; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java index 8958299..2a2c74c 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java @@ -1,187 +1,182 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + public class StructAnnotationAttribute extends StructGeneralAttribute { - private List<AnnotationExprent> annotations; - - public void initContent(ConstantPool pool) { - - super.initContent(pool); - - annotations = new ArrayList<AnnotationExprent>(); - DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 2, info.length)); - - int len = (((info[0] & 0xFF)<<8) | (info[1] & 0xFF)); - for(int i=0;i<len;i++) { - annotations.add(parseAnnotation(data, pool)); - } - - } - - public static AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) { - - try { - - String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); - VarType cltype = new VarType(classname); - - int len = data.readUnsignedShort(); - - List<String> parnames = new ArrayList<String>(); - List<Exprent> parvalues = new ArrayList<Exprent>(); - - for(int i=0;i<len;i++) { - parnames.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); - parvalues.add(parseAnnotationElement(data, pool)); - } - - return new AnnotationExprent(cltype.value, parnames, parvalues); - - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - } - - public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) { - - try { - int tag = data.readUnsignedByte(); - - switch(tag) { - case 'e': // enum constant - String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); - String constname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); - - FieldDescriptor descr = FieldDescriptor.parseDescriptor(classname); - return new FieldExprent(constname, descr.type.value, true, null, descr); - case 'c': // class - String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); - VarType type = FieldDescriptor.parseDescriptor(descriptor).type; - - String value; - switch(type.type) { - case CodeConstants.TYPE_OBJECT: - value = type.value; - break; - case CodeConstants.TYPE_BYTE: - value = byte.class.getName(); - break; - case CodeConstants.TYPE_CHAR: - value = char.class.getName(); - break; - case CodeConstants.TYPE_DOUBLE: - value = double.class.getName(); - break; - case CodeConstants.TYPE_FLOAT: - value = float.class.getName(); - break; - case CodeConstants.TYPE_INT: - value = int.class.getName(); - break; - case CodeConstants.TYPE_LONG: - value = long.class.getName(); - break; - case CodeConstants.TYPE_SHORT: - value = short.class.getName(); - break; - case CodeConstants.TYPE_BOOLEAN: - value = boolean.class.getName(); - break; - case CodeConstants.TYPE_VOID: - value = void.class.getName(); - break; - default: - throw new RuntimeException("invalid class type: " + type.type); - } - return new ConstExprent(VarType.VARTYPE_CLASS, value); - case '[': // array - int len = data.readUnsignedShort(); - List<Exprent> lst = new ArrayList<Exprent>(); - - for(int i=0;i<len;i++) { - lst.add(parseAnnotationElement(data, pool)); - } - - VarType newtype; - if(lst.isEmpty()) { - newtype = new VarType(CodeConstants.TYPE_OBJECT, 1, "java/lang/Object"); - } else { - VarType eltype = lst.get(0).getExprType(); - newtype = new VarType(eltype.type, 1, eltype.value); - } - - NewExprent newexpr = new NewExprent(newtype, new ArrayList<Exprent>()); - newexpr.setDirectArrayInit(true); - newexpr.setLstArrayElements(lst); - return newexpr; - case '@': // annotation - return parseAnnotation(data, pool); - default: - PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); - switch(tag) { - case 'B': - return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); - case 'C': - return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); - case 'D': - return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); - case 'F': - return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); - case 'I': - return new ConstExprent(VarType.VARTYPE_INT, cn.value); - case 'J': - return new ConstExprent(VarType.VARTYPE_LONG, cn.value); - case 'S': - return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); - case 'Z': - return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); - case 's': - return new ConstExprent(VarType.VARTYPE_STRING, cn.value); - default: - throw new RuntimeException("invalid element type!"); - } - } - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - } - - - public List<AnnotationExprent> getAnnotations() { - return annotations; - } - + private List<AnnotationExprent> annotations; + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + annotations = new ArrayList<AnnotationExprent>(); + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 2, info.length)); + + int len = (((info[0] & 0xFF) << 8) | (info[1] & 0xFF)); + for (int i = 0; i < len; i++) { + annotations.add(parseAnnotation(data, pool)); + } + } + + public static AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) { + + try { + + String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + VarType cltype = new VarType(classname); + + int len = data.readUnsignedShort(); + + List<String> parnames = new ArrayList<String>(); + List<Exprent> parvalues = new ArrayList<Exprent>(); + + for (int i = 0; i < len; i++) { + parnames.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); + parvalues.add(parseAnnotationElement(data, pool)); + } + + return new AnnotationExprent(cltype.value, parnames, parvalues); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) { + + try { + int tag = data.readUnsignedByte(); + + switch (tag) { + case 'e': // enum constant + String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + String constname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + + FieldDescriptor descr = FieldDescriptor.parseDescriptor(classname); + return new FieldExprent(constname, descr.type.value, true, null, descr); + case 'c': // class + String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + VarType type = FieldDescriptor.parseDescriptor(descriptor).type; + + String value; + switch (type.type) { + case CodeConstants.TYPE_OBJECT: + value = type.value; + break; + case CodeConstants.TYPE_BYTE: + value = byte.class.getName(); + break; + case CodeConstants.TYPE_CHAR: + value = char.class.getName(); + break; + case CodeConstants.TYPE_DOUBLE: + value = double.class.getName(); + break; + case CodeConstants.TYPE_FLOAT: + value = float.class.getName(); + break; + case CodeConstants.TYPE_INT: + value = int.class.getName(); + break; + case CodeConstants.TYPE_LONG: + value = long.class.getName(); + break; + case CodeConstants.TYPE_SHORT: + value = short.class.getName(); + break; + case CodeConstants.TYPE_BOOLEAN: + value = boolean.class.getName(); + break; + case CodeConstants.TYPE_VOID: + value = void.class.getName(); + break; + default: + throw new RuntimeException("invalid class type: " + type.type); + } + return new ConstExprent(VarType.VARTYPE_CLASS, value); + case '[': // array + int len = data.readUnsignedShort(); + List<Exprent> lst = new ArrayList<Exprent>(); + + for (int i = 0; i < len; i++) { + lst.add(parseAnnotationElement(data, pool)); + } + + VarType newtype; + if (lst.isEmpty()) { + newtype = new VarType(CodeConstants.TYPE_OBJECT, 1, "java/lang/Object"); + } + else { + VarType eltype = lst.get(0).getExprType(); + newtype = new VarType(eltype.type, 1, eltype.value); + } + + NewExprent newexpr = new NewExprent(newtype, new ArrayList<Exprent>()); + newexpr.setDirectArrayInit(true); + newexpr.setLstArrayElements(lst); + return newexpr; + case '@': // annotation + return parseAnnotation(data, pool); + default: + PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); + switch (tag) { + case 'B': + return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); + case 'C': + return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); + case 'D': + return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); + case 'F': + return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); + case 'I': + return new ConstExprent(VarType.VARTYPE_INT, cn.value); + case 'J': + return new ConstExprent(VarType.VARTYPE_LONG, cn.value); + case 'S': + return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); + case 'Z': + return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); + case 's': + return new ConstExprent(VarType.VARTYPE_STRING, cn.value); + default: + throw new RuntimeException("invalid element type!"); + } + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + + public List<AnnotationExprent> getAnnotations() { + return annotations; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java index dd28aa7..432d3b6 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java @@ -1,57 +1,58 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - public class StructAnnotationParameterAttribute extends StructGeneralAttribute { - private List<List<AnnotationExprent>> paramAnnotations; - - public void initContent(ConstantPool pool) { - - super.initContent(pool); - - paramAnnotations = new ArrayList<List<AnnotationExprent>>(); - DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); - - try { - int len = data.readUnsignedByte(); - for(int i=0;i<len;i++) { - List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); - int annsize = data.readUnsignedShort(); - - for(int j=0;j<annsize;j++) { - lst.add(StructAnnotationAttribute.parseAnnotation(data, pool)); - } - paramAnnotations.add(lst); - } - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - } - - public List<List<AnnotationExprent>> getParamAnnotations() { - return paramAnnotations; - } + private List<List<AnnotationExprent>> paramAnnotations; + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + paramAnnotations = new ArrayList<List<AnnotationExprent>>(); + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + + try { + int len = data.readUnsignedByte(); + for (int i = 0; i < len; i++) { + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + int annsize = data.readUnsignedShort(); + + for (int j = 0; j < annsize; j++) { + lst.add(StructAnnotationAttribute.parseAnnotation(data, pool)); + } + paramAnnotations.add(lst); + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public List<List<AnnotationExprent>> getParamAnnotations() { + return paramAnnotations; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java index 9677ddd..f228ed5 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java @@ -1,188 +1,203 @@ +/* + * 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.struct.attr; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - public class StructAnnotationTypeAttribute extends StructGeneralAttribute { - - public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; - public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; - public static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; - public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; - public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; - public static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; - public static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; - public static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; - public static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; - public static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; - public static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; - public static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; - public static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; - public static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; - public static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; - public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW = 0x45; - public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID = 0x46; - public static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; - public static final int ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR = 0x48; - public static final int ANNOTATION_TARGET_TYPE_INVOKATION_METHOD = 0x49; - public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW = 0x4A; - public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID = 0x4B; - - public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; - public static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; - public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; - public static final int ANNOTATION_TARGET_UNION_EMPTY = 4; - public static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; - public static final int ANNOTATION_TARGET_UNION_THROWS = 6; - public static final int ANNOTATION_TARGET_UNION_LOCALVAR = 7; - public static final int ANNOTATION_TARGET_UNION_CATCH = 8; - public static final int ANNOTATION_TARGET_UNION_OFFSET = 9; - public static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; - - - List<AnnotationLocation> locations = new ArrayList<AnnotationLocation>(); - List<AnnotationExprent> annotations = new ArrayList<AnnotationExprent>(); - - public void initContent(ConstantPool pool) { - - super.initContent(pool); - - DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); - - try { - - int ann_number = data.readUnsignedByte(); - for(int i = 0; i < ann_number; i++) { - locations.add(parseAnnotationLocation(data)); - annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); - } - - } catch(IOException ex) { - throw new RuntimeException(ex); - } - } - - public AnnotationLocation parseAnnotationLocation(DataInputStream data) throws IOException { - - AnnotationLocation ann_location = new AnnotationLocation(); - - // target type - - ann_location.target_type = data.readUnsignedByte(); - - // target union - - switch(ann_location.target_type) { - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS: - ann_location.target_union = ANNOTATION_TARGET_UNION_SUPERTYPE; - break; - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND; - break; - case ANNOTATION_TARGET_TYPE_FIELD: - case ANNOTATION_TARGET_TYPE_RETURN: - case ANNOTATION_TARGET_TYPE_RECEIVER: - ann_location.target_union = ANNOTATION_TARGET_UNION_EMPTY; - break; - case ANNOTATION_TARGET_TYPE_FORMAL: - ann_location.target_union = ANNOTATION_TARGET_UNION_FORMAL_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_THROWS: - ann_location.target_union = ANNOTATION_TARGET_UNION_THROWS; - break; - case ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE: - case ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE: - ann_location.target_union = ANNOTATION_TARGET_UNION_LOCALVAR; - break; - case ANNOTATION_TARGET_TYPE_EXCEPTION: - ann_location.target_union = ANNOTATION_TARGET_UNION_CATCH; - break; - case ANNOTATION_TARGET_TYPE_INSTANCEOF: - case ANNOTATION_TARGET_TYPE_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; - break; - case ANNOTATION_TARGET_TYPE_CAST: - case ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR: - case ANNOTATION_TARGET_TYPE_INVOKATION_METHOD: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_ARGUMENT; - break; - default: - throw new RuntimeException("Unknown target type in a type annotation!"); - } - - // target union data - - switch(ann_location.target_union) { - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER: - case ANNOTATION_TARGET_UNION_FORMAL_PARAMETER: - ann_location.data = new int[] {data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_SUPERTYPE: - case ANNOTATION_TARGET_UNION_THROWS: - case ANNOTATION_TARGET_UNION_CATCH: - case ANNOTATION_TARGET_UNION_OFFSET: - ann_location.data = new int[] {data.readUnsignedShort()}; - break; - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND: - ann_location.data = new int[] {data.readUnsignedByte(), data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_EMPTY: - break; - case ANNOTATION_TARGET_UNION_LOCALVAR: - int table_length = data.readUnsignedShort(); - - ann_location.data = new int[table_length * 3 + 1]; - ann_location.data[0] = table_length; - - for(int i = 0; i < table_length; ++i) { - ann_location.data[3 * i + 1] = data.readUnsignedShort(); - ann_location.data[3 * i + 2] = data.readUnsignedShort(); - ann_location.data[3 * i + 3] = data.readUnsignedShort(); - } - break; - case ANNOTATION_TARGET_UNION_TYPE_ARGUMENT: - ann_location.data = new int[] {data.readUnsignedShort(), data.readUnsignedByte()}; - } - - // target path - - int path_length = data.readUnsignedByte(); - - ann_location.target_path_kind = new int[path_length]; - ann_location.target_argument_index = new int[path_length]; - - for(int i = 0; i < path_length; ++i) { - ann_location.target_path_kind[i] = data.readUnsignedByte(); - ann_location.target_argument_index[i] = data.readUnsignedByte(); - } - - return ann_location; - } - - private static class AnnotationLocation { - - public int target_type; - public int target_union; - - public int[] data; - - public int[] target_path_kind; - public int[] target_argument_index; - } + + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; + public static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; + public static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; + public static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; + public static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; + public static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; + public static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; + public static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; + public static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; + public static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; + public static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; + public static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW = 0x45; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID = 0x46; + public static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR = 0x48; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_METHOD = 0x49; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW = 0x4A; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID = 0x4B; + + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; + public static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; + public static final int ANNOTATION_TARGET_UNION_EMPTY = 4; + public static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; + public static final int ANNOTATION_TARGET_UNION_THROWS = 6; + public static final int ANNOTATION_TARGET_UNION_LOCALVAR = 7; + public static final int ANNOTATION_TARGET_UNION_CATCH = 8; + public static final int ANNOTATION_TARGET_UNION_OFFSET = 9; + public static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; + + + List<AnnotationLocation> locations = new ArrayList<AnnotationLocation>(); + List<AnnotationExprent> annotations = new ArrayList<AnnotationExprent>(); + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + + try { + + int ann_number = data.readUnsignedByte(); + for (int i = 0; i < ann_number; i++) { + locations.add(parseAnnotationLocation(data)); + annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public AnnotationLocation parseAnnotationLocation(DataInputStream data) throws IOException { + + AnnotationLocation ann_location = new AnnotationLocation(); + + // target type + + ann_location.target_type = data.readUnsignedByte(); + + // target union + + switch (ann_location.target_type) { + case ANNOTATION_TARGET_TYPE_GENERIC_CLASS: + case ANNOTATION_TARGET_TYPE_GENERIC_METHOD: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER; + break; + case ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS: + ann_location.target_union = ANNOTATION_TARGET_UNION_SUPERTYPE; + break; + case ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND: + case ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND; + break; + case ANNOTATION_TARGET_TYPE_FIELD: + case ANNOTATION_TARGET_TYPE_RETURN: + case ANNOTATION_TARGET_TYPE_RECEIVER: + ann_location.target_union = ANNOTATION_TARGET_UNION_EMPTY; + break; + case ANNOTATION_TARGET_TYPE_FORMAL: + ann_location.target_union = ANNOTATION_TARGET_UNION_FORMAL_PARAMETER; + break; + case ANNOTATION_TARGET_TYPE_THROWS: + ann_location.target_union = ANNOTATION_TARGET_UNION_THROWS; + break; + case ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE: + case ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE: + ann_location.target_union = ANNOTATION_TARGET_UNION_LOCALVAR; + break; + case ANNOTATION_TARGET_TYPE_EXCEPTION: + ann_location.target_union = ANNOTATION_TARGET_UNION_CATCH; + break; + case ANNOTATION_TARGET_TYPE_INSTANCEOF: + case ANNOTATION_TARGET_TYPE_NEW: + case ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID: + ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; + break; + case ANNOTATION_TARGET_TYPE_CAST: + case ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR: + case ANNOTATION_TARGET_TYPE_INVOKATION_METHOD: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_ARGUMENT; + break; + default: + throw new RuntimeException("Unknown target type in a type annotation!"); + } + + // target union data + + switch (ann_location.target_union) { + case ANNOTATION_TARGET_UNION_TYPE_PARAMETER: + case ANNOTATION_TARGET_UNION_FORMAL_PARAMETER: + ann_location.data = new int[]{data.readUnsignedByte()}; + break; + case ANNOTATION_TARGET_UNION_SUPERTYPE: + case ANNOTATION_TARGET_UNION_THROWS: + case ANNOTATION_TARGET_UNION_CATCH: + case ANNOTATION_TARGET_UNION_OFFSET: + ann_location.data = new int[]{data.readUnsignedShort()}; + break; + case ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND: + ann_location.data = new int[]{data.readUnsignedByte(), data.readUnsignedByte()}; + break; + case ANNOTATION_TARGET_UNION_EMPTY: + break; + case ANNOTATION_TARGET_UNION_LOCALVAR: + int table_length = data.readUnsignedShort(); + + ann_location.data = new int[table_length * 3 + 1]; + ann_location.data[0] = table_length; + + for (int i = 0; i < table_length; ++i) { + ann_location.data[3 * i + 1] = data.readUnsignedShort(); + ann_location.data[3 * i + 2] = data.readUnsignedShort(); + ann_location.data[3 * i + 3] = data.readUnsignedShort(); + } + break; + case ANNOTATION_TARGET_UNION_TYPE_ARGUMENT: + ann_location.data = new int[]{data.readUnsignedShort(), data.readUnsignedByte()}; + } + + // target path + + int path_length = data.readUnsignedByte(); + + ann_location.target_path_kind = new int[path_length]; + ann_location.target_argument_index = new int[path_length]; + + for (int i = 0; i < path_length; ++i) { + ann_location.target_path_kind[i] = data.readUnsignedByte(); + ann_location.target_argument_index[i] = data.readUnsignedByte(); + } + + return ann_location; + } + + private static class AnnotationLocation { + + public int target_type; + public int target_union; + + public int[] data; + + public int[] target_path_kind; + public int[] target_argument_index; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java index ac51027..717bad9 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java @@ -1,62 +1,75 @@ +/* + * 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.struct.attr; +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 java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; -import org.jetbrains.java.decompiler.struct.consts.LinkConstant; -import org.jetbrains.java.decompiler.struct.consts.PooledConstant; - public class StructBootstrapMethodsAttribute extends StructGeneralAttribute { - - private List<LinkConstant> method_refs = new ArrayList<LinkConstant>(); - private List<List<PooledConstant>> method_arguments = new ArrayList<List<PooledConstant>>(); - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_BOOTSTRAP_METHODS; - - try { - - DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 0, info.length)); - - int method_number = data.readUnsignedShort(); - - for(int i = 0; i < method_number; ++i) { - int bootstrap_method_ref = data.readUnsignedShort(); - int num_bootstrap_arguments = data.readUnsignedShort(); - - List<PooledConstant> list_arguments = new ArrayList<PooledConstant>(); - - for(int j = 0; j < num_bootstrap_arguments; ++j) { - int bootstrap_argument_ref = data.readUnsignedShort(); - - list_arguments.add(pool.getConstant(bootstrap_argument_ref)); - } - - method_refs.add(pool.getLinkConstant(bootstrap_method_ref)); - method_arguments.add(list_arguments); - } - - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - } - - public int getMethodsNumber() { - return method_refs.size(); - } - - public LinkConstant getMethodReference(int index) { - return method_refs.get(index); - } - - public List<PooledConstant> getMethodArguments(int index) { - return method_arguments.get(index); - } - + + private List<LinkConstant> method_refs = new ArrayList<LinkConstant>(); + private List<List<PooledConstant>> method_arguments = new ArrayList<List<PooledConstant>>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_BOOTSTRAP_METHODS; + + try { + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 0, info.length)); + + int method_number = data.readUnsignedShort(); + + for (int i = 0; i < method_number; ++i) { + int bootstrap_method_ref = data.readUnsignedShort(); + int num_bootstrap_arguments = data.readUnsignedShort(); + + List<PooledConstant> list_arguments = new ArrayList<PooledConstant>(); + + for (int j = 0; j < num_bootstrap_arguments; ++j) { + int bootstrap_argument_ref = data.readUnsignedShort(); + + list_arguments.add(pool.getConstant(bootstrap_argument_ref)); + } + + method_refs.add(pool.getLinkConstant(bootstrap_method_ref)); + method_arguments.add(list_arguments); + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public int getMethodsNumber() { + return method_refs.size(); + } + + public LinkConstant getMethodReference(int index) { + return method_refs.get(index); + } + + public List<PooledConstant> getMethodArguments(int index) { + return method_arguments.get(index); + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java index f186767..ae3bd1d 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java @@ -1,34 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; public class StructConstantValueAttribute extends StructGeneralAttribute { - private int index; + private int index; + + public void initContent(ConstantPool pool) { - public void initContent(ConstantPool pool) { + name = ATTRIBUTE_CONSTANT_VALUE; + index = ((info[0] & 0xFF) << 8) | (info[1] & 0xFF); + } - name = ATTRIBUTE_CONSTANT_VALUE; - index = ((info[0] & 0xFF)<<8) | (info[1] & 0xFF); - } - - public int getIndex() { - return index; - } - - + public int getIndex() { + return index; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java index 61bb886..ebf9407 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -19,39 +20,37 @@ import org.jetbrains.java.decompiler.struct.consts.LinkConstant; public class StructEnclosingMethodAttribute extends StructGeneralAttribute { - private String classname; - - private String mtname; - - private String methodDescriptor; - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_ENCLOSING_METHOD; - - int clindex = (((info[0] & 0xFF)<<8) | (info[1] & 0xFF)); - int mtindex = (((info[2] & 0xFF)<<8) | (info[3] & 0xFF)); - - classname = pool.getPrimitiveConstant(clindex).getString(); - if(mtindex != 0) { - LinkConstant lk = pool.getLinkConstant(mtindex); - - mtname = lk.elementname; - methodDescriptor = lk.descriptor; - } - } - - public String getClassname() { - return classname; - } - - public String getMethodDescriptor() { - return methodDescriptor; - } - - public String getMethodName() { - return mtname; - } - - + private String classname; + + private String mtname; + + private String methodDescriptor; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_ENCLOSING_METHOD; + + int clindex = (((info[0] & 0xFF) << 8) | (info[1] & 0xFF)); + int mtindex = (((info[2] & 0xFF) << 8) | (info[3] & 0xFF)); + + classname = pool.getPrimitiveConstant(clindex).getString(); + if (mtindex != 0) { + LinkConstant lk = pool.getLinkConstant(mtindex); + + mtname = lk.elementname; + methodDescriptor = lk.descriptor; + } + } + + public String getClassname() { + return classname; + } + + public String getMethodDescriptor() { + return methodDescriptor; + } + + public String getMethodName() { + return mtname; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java index 4d6bd4c..930db78 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java @@ -1,78 +1,76 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - public class StructExceptionsAttribute extends StructGeneralAttribute { - private List<Integer> throwsExceptions = new ArrayList<Integer>(); - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_EXCEPTIONS; - - int length = 2+(((info[0] & 0xFF)<<8) | (info[1] & 0xFF))*2; - for(int i=2;i<length;i+=2) { - int index = ((info[i] & 0xFF)<<8) | (info[i+1] & 0xFF); - throwsExceptions.add(index); - } - - } - - public void writeToStream(DataOutputStream out) throws IOException { - - out.writeShort(attribute_name_index); - - ByteArrayOutputStream codeout = new ByteArrayOutputStream(); - DataOutputStream dataout = new DataOutputStream(codeout); - - int len = throwsExceptions.size(); - dataout.writeShort(len); - - if(len>0) { - info = new byte[len*2]; - for(int i=0,j=0;i<len;i++,j+=2) { - int index = ((Integer)throwsExceptions.get(i)).intValue(); - info[j] = (byte)(index >> 8); - info[j+1] = (byte)(index & 0xFF); - } - dataout.write(info); - } - - out.writeInt(codeout.size()); - out.write(codeout.toByteArray()); - } - - public String getExcClassname(int index, ConstantPool pool) { - return pool.getPrimitiveConstant(((Integer)throwsExceptions.get(index)).intValue()).getString(); - } - - public List<Integer> getThrowsExceptions() { - return throwsExceptions; - } - - public void setThrowsExceptions(List<Integer> throwsExceptions) { - this.throwsExceptions = throwsExceptions; - } - - + private List<Integer> throwsExceptions = new ArrayList<Integer>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_EXCEPTIONS; + + int length = 2 + (((info[0] & 0xFF) << 8) | (info[1] & 0xFF)) * 2; + for (int i = 2; i < length; i += 2) { + int index = ((info[i] & 0xFF) << 8) | (info[i + 1] & 0xFF); + throwsExceptions.add(index); + } + } + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(attribute_name_index); + + ByteArrayOutputStream codeout = new ByteArrayOutputStream(); + DataOutputStream dataout = new DataOutputStream(codeout); + + int len = throwsExceptions.size(); + dataout.writeShort(len); + + if (len > 0) { + info = new byte[len * 2]; + for (int i = 0, j = 0; i < len; i++, j += 2) { + int index = ((Integer)throwsExceptions.get(i)).intValue(); + info[j] = (byte)(index >> 8); + info[j + 1] = (byte)(index & 0xFF); + } + dataout.write(info); + } + + out.writeInt(codeout.size()); + out.write(codeout.toByteArray()); + } + + public String getExcClassname(int index, ConstantPool pool) { + return pool.getPrimitiveConstant(((Integer)throwsExceptions.get(index)).intValue()).getString(); + } + + public List<Integer> getThrowsExceptions() { + return throwsExceptions; + } + + public void setThrowsExceptions(List<Integer> throwsExceptions) { + this.throwsExceptions = throwsExceptions; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 2648b3c..bd0596a 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -1,24 +1,25 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - /* attribute_info { u2 attribute_name_index; @@ -29,117 +30,128 @@ import org.jetbrains.java.decompiler.struct.consts.ConstantPool; public class StructGeneralAttribute { - public static final String ATTRIBUTE_CODE = "Code"; - public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; - public static final String ATTRIBUTE_SIGNATURE = "Signature"; - public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; - public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; - public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; - public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; - public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; - public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; + public static final String ATTRIBUTE_CODE = "Code"; + public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; + public static final String ATTRIBUTE_SIGNATURE = "Signature"; + public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; + public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; + public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; + public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; + public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; + public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; public static final String ATTRIBUTE_SYNTHETIC = "Synthetic"; public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; - // ***************************************************************************** - // private fields - // ***************************************************************************** - - protected int attribute_name_index; - - protected byte[] info; - - protected String name; - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void writeToStream(DataOutputStream out) throws IOException { - out.writeShort(attribute_name_index); - out.writeInt(info.length); - if(info.length>0) { - out.write(info); - } - } - - public void initContent(ConstantPool pool) { - name = pool.getPrimitiveConstant(attribute_name_index).getString(); - } - - public static StructGeneralAttribute getMatchingAttributeInstance(int nameindex, String attrname) { - - StructGeneralAttribute attr; - - if(ATTRIBUTE_INNER_CLASSES.equals(attrname)) { - attr = new StructInnerClassesAttribute(); - } else if(ATTRIBUTE_CONSTANT_VALUE.equals(attrname)) { - attr = new StructConstantValueAttribute(); - } else if(ATTRIBUTE_SIGNATURE.equals(attrname)) { - attr = new StructGenericSignatureAttribute(); - } else if(ATTRIBUTE_ANNOTATION_DEFAULT.equals(attrname)) { - attr = new StructAnnDefaultAttribute(); - } else if(ATTRIBUTE_EXCEPTIONS.equals(attrname)) { - attr = new StructExceptionsAttribute(); - } else if(ATTRIBUTE_ENCLOSING_METHOD.equals(attrname)) { - attr = new StructEnclosingMethodAttribute(); - } else if(ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(attrname) || - ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(attrname)) { - attr = new StructAnnotationAttribute(); - } else if(ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attrname) || - ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attrname)) { - attr = new StructAnnotationParameterAttribute(); - } else if(ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attrname) || - ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attrname)) { - attr = new StructAnnotationTypeAttribute(); - } else if(ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname)) { - attr = new StructLocalVariableTableAttribute(); - } else if(ATTRIBUTE_BOOTSTRAP_METHODS.equals(attrname)) { - attr = new StructBootstrapMethodsAttribute(); - } else if(ATTRIBUTE_SYNTHETIC.equals(attrname) || ATTRIBUTE_DEPRECATED.equals(attrname)) { - attr = new StructGeneralAttribute(); - } else { - // unsupported attribute - return null; - } - - attr.setAttribute_name_index(nameindex); - return attr; - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public byte[] getInfo() { - return info; - } - - public void setInfo(byte[] info) { - this.info = info; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAttribute_name_index() { - return attribute_name_index; - } - - public void setAttribute_name_index(int attribute_name_index) { - this.attribute_name_index = attribute_name_index; - } - + // ***************************************************************************** + // private fields + // ***************************************************************************** + + protected int attribute_name_index; + + protected byte[] info; + + protected String name; + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + out.writeShort(attribute_name_index); + out.writeInt(info.length); + if (info.length > 0) { + out.write(info); + } + } + + public void initContent(ConstantPool pool) { + name = pool.getPrimitiveConstant(attribute_name_index).getString(); + } + + public static StructGeneralAttribute getMatchingAttributeInstance(int nameindex, String attrname) { + + StructGeneralAttribute attr; + + if (ATTRIBUTE_INNER_CLASSES.equals(attrname)) { + attr = new StructInnerClassesAttribute(); + } + else if (ATTRIBUTE_CONSTANT_VALUE.equals(attrname)) { + attr = new StructConstantValueAttribute(); + } + else if (ATTRIBUTE_SIGNATURE.equals(attrname)) { + attr = new StructGenericSignatureAttribute(); + } + else if (ATTRIBUTE_ANNOTATION_DEFAULT.equals(attrname)) { + attr = new StructAnnDefaultAttribute(); + } + else if (ATTRIBUTE_EXCEPTIONS.equals(attrname)) { + attr = new StructExceptionsAttribute(); + } + else if (ATTRIBUTE_ENCLOSING_METHOD.equals(attrname)) { + attr = new StructEnclosingMethodAttribute(); + } + else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationAttribute(); + } + else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationParameterAttribute(); + } + else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationTypeAttribute(); + } + else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname)) { + attr = new StructLocalVariableTableAttribute(); + } + else if (ATTRIBUTE_BOOTSTRAP_METHODS.equals(attrname)) { + attr = new StructBootstrapMethodsAttribute(); + } + else if (ATTRIBUTE_SYNTHETIC.equals(attrname) || ATTRIBUTE_DEPRECATED.equals(attrname)) { + attr = new StructGeneralAttribute(); + } + else { + // unsupported attribute + return null; + } + + attr.setAttribute_name_index(nameindex); + return attr; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public byte[] getInfo() { + return info; + } + + public void setInfo(byte[] info) { + this.info = info; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAttribute_name_index() { + return attribute_name_index; + } + + public void setAttribute_name_index(int attribute_name_index) { + this.attribute_name_index = attribute_name_index; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java index 11206e0..6f2224c 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java @@ -1,34 +1,33 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; public class StructGenericSignatureAttribute extends StructGeneralAttribute { - private String signature; + private String signature; + + public void initContent(ConstantPool pool) { - public void initContent(ConstantPool pool) { + name = ATTRIBUTE_SIGNATURE; + signature = pool.getPrimitiveConstant(((info[0] & 0xFF) << 8) | (info[1] & 0xFF)).getString(); + } - name = ATTRIBUTE_SIGNATURE; - signature = pool.getPrimitiveConstant(((info[0] & 0xFF)<<8) | (info[1] & 0xFF)).getString(); - } - - public String getSignature() { - return signature; - } - - + public String getSignature() { + return signature; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java index 794da15..14ecd36 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java @@ -1,73 +1,72 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - public class StructInnerClassesAttribute extends StructGeneralAttribute { - private List<int[]> classentries = new ArrayList<int[]>(); - - private List<String[]> stringentries = new ArrayList<String[]>(); - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_INNER_CLASSES; - - int length = 2+(((info[0] & 0xFF)<<8) | (info[1] & 0xFF))*8; - int i=2; - - while(i<length) { - - int[] arr = new int[4]; - for(int j=0;j<4;j++) { - arr[j] = ((info[i] & 0xFF)<<8) | (info[i+1] & 0xFF); - i+=2; - } - - classentries.add(arr); - } - - for(int[] entry: classentries) { - - String[] arr = new String[3]; - // inner name - arr[0] = pool.getPrimitiveConstant(entry[0]).getString(); - //enclosing class - if(entry[1] != 0) { - arr[1] = pool.getPrimitiveConstant(entry[1]).getString(); - } - // original simple name - if(entry[2]!=0) { - arr[2] = pool.getPrimitiveConstant(entry[2]).getString(); - } - - stringentries.add(arr); - } - - } - - public List<int[]> getClassentries() { - return classentries; - } - - public List<String[]> getStringentries() { - return stringentries; - } + private List<int[]> classentries = new ArrayList<int[]>(); + + private List<String[]> stringentries = new ArrayList<String[]>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_INNER_CLASSES; + + int length = 2 + (((info[0] & 0xFF) << 8) | (info[1] & 0xFF)) * 8; + int i = 2; + + while (i < length) { + + int[] arr = new int[4]; + for (int j = 0; j < 4; j++) { + arr[j] = ((info[i] & 0xFF) << 8) | (info[i + 1] & 0xFF); + i += 2; + } + + classentries.add(arr); + } + + for (int[] entry : classentries) { + + String[] arr = new String[3]; + // inner name + arr[0] = pool.getPrimitiveConstant(entry[0]).getString(); + //enclosing class + if (entry[1] != 0) { + arr[1] = pool.getPrimitiveConstant(entry[1]).getString(); + } + // original simple name + if (entry[2] != 0) { + arr[2] = pool.getPrimitiveConstant(entry[2]).getString(); + } + + stringentries.add(arr); + } + } + + public List<int[]> getClassentries() { + return classentries; + } + public List<String[]> getStringentries() { + return stringentries; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 119d1ff..0cf2a89 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -1,47 +1,48 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.attr; -import java.util.HashMap; - import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import java.util.HashMap; + public class StructLocalVariableTableAttribute extends StructGeneralAttribute { - private HashMap<Integer, String> mapVarNames = new HashMap<Integer, String>(); - - public void initContent(ConstantPool pool) { - - name = ATTRIBUTE_LOCAL_VARIABLE_TABLE; - - int len = ((info[0] & 0xFF)<<8) | (info[1] & 0xFF); - - int ind = 6; - for(int i=0;i<len;i++, ind+=10) { - int nindex = ((info[ind] & 0xFF)<<8) | (info[ind+1] & 0xFF); - int vindex = ((info[ind+4] & 0xFF)<<8) | (info[ind+5] & 0xFF); - - mapVarNames.put(vindex, pool.getPrimitiveConstant(nindex).getString()); - } - } - - public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { - mapVarNames.putAll(attr.getMapVarNames()); - } - - public HashMap<Integer, String> getMapVarNames() { - return mapVarNames; - } + private HashMap<Integer, String> mapVarNames = new HashMap<Integer, String>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_LOCAL_VARIABLE_TABLE; + + int len = ((info[0] & 0xFF) << 8) | (info[1] & 0xFF); + + int ind = 6; + for (int i = 0; i < len; i++, ind += 10) { + int nindex = ((info[ind] & 0xFF) << 8) | (info[ind + 1] & 0xFF); + int vindex = ((info[ind + 4] & 0xFF) << 8) | (info[ind + 5] & 0xFF); + + mapVarNames.put(vindex, pool.getPrimitiveConstant(nindex).getString()); + } + } + + public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { + mapVarNames.putAll(attr.getMapVarNames()); + } + + public HashMap<Integer, String> getMapVarNames() { + return mapVarNames; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index 8951cd1..5a593af 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -1,26 +1,20 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.consts; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; @@ -28,289 +22,297 @@ import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + public class ConstantPool { - - public static final int FIELD = 1; - - public static final int METHOD = 2; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private List<PooledConstant> pool = new ArrayList<PooledConstant>(); - - private PoolInterceptor interceptor; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public ConstantPool(DataInputStream in) throws IOException { - - int size = in.readUnsignedShort(); - - int[] pass = new int[size]; - - // first dummy constant - pool.add(null); - - // first pass: read the elements - for (int i = 1; i < size; i++) { - - byte tag = (byte)in.readUnsignedByte(); - - switch (tag) { - case CodeConstants.CONSTANT_Utf8: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF())); - break; - case CodeConstants.CONSTANT_Integer: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, new Integer(in.readInt()))); - break; - case CodeConstants.CONSTANT_Float: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, new Float(in.readFloat()))); - break; - case CodeConstants.CONSTANT_Long: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, new Long(in.readLong()))); - pool.add(null); - i++; - break; - case CodeConstants.CONSTANT_Double: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, new Double(in.readDouble()))); - pool.add(null); - i++; - break; - case CodeConstants.CONSTANT_Class: - case CodeConstants.CONSTANT_String: - case CodeConstants.CONSTANT_MethodType: - pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); - pass[i] = 1; - break; - case CodeConstants.CONSTANT_Fieldref: - case CodeConstants.CONSTANT_Methodref: - case CodeConstants.CONSTANT_InterfaceMethodref: - case CodeConstants.CONSTANT_NameAndType: - case CodeConstants.CONSTANT_InvokeDynamic: - pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); - if(tag == CodeConstants.CONSTANT_NameAndType) { - pass[i] = 1; - } else { - pass[i] = 2; - } - break; - case CodeConstants.CONSTANT_MethodHandle: - pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); - pass[i] = 3; - break; - } - } - - - // resolving complex pool elements - for(int pass_index = 1; pass_index <= 3; pass_index++) { - for(int i = 1; i < size; i++) { - if(pass[i] == pass_index) { - pool.get(i).resolveConstant(this); - } - } - } - - // get global constant pool interceptor instance, if any available - interceptor = DecompilerContext.getPoolInterceptor(); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void writeToOutputStream(DataOutputStream out) throws FileNotFoundException, IOException { - - out.writeShort(pool.size()); - for(int i=1;i<pool.size();i++) { - PooledConstant cnst = (PooledConstant)pool.get(i); - if(cnst!=null) { - cnst.writeToStream(out); - } - } - } - - public static void skipPool(DataInputStream in) throws IOException { - - int size = in.readUnsignedShort(); - - for (int i = 1; i < size; i++) { - switch (in.readUnsignedByte()) { - case CodeConstants.CONSTANT_Utf8: - in.readUTF(); - break; - case CodeConstants.CONSTANT_Integer: - case CodeConstants.CONSTANT_Float: - case CodeConstants.CONSTANT_Fieldref: - case CodeConstants.CONSTANT_Methodref: - case CodeConstants.CONSTANT_InterfaceMethodref: - case CodeConstants.CONSTANT_NameAndType: - case CodeConstants.CONSTANT_InvokeDynamic: - in.skip(4); - break; - case CodeConstants.CONSTANT_Long: - case CodeConstants.CONSTANT_Double: - in.skip(8); - i++; - break; - case CodeConstants.CONSTANT_Class: - case CodeConstants.CONSTANT_String: - case CodeConstants.CONSTANT_MethodType: - in.skip(2); - break; - case CodeConstants.CONSTANT_MethodHandle: - in.skip(3); - } - } - } - - public int size() { - return pool.size(); - } - - public String[] getClassElement(int element_type, int class_index, int name_index, int descriptor_index) { - - String classname = ((PrimitiveConstant)getConstant(class_index)).getString(); - String elementname = ((PrimitiveConstant)getConstant(name_index)).getString(); - String descriptor = ((PrimitiveConstant)getConstant(descriptor_index)).getString(); - - if(interceptor != null) { - String new_element = interceptor.getName(classname+" "+elementname+" "+descriptor); - - if(new_element != null) { - elementname = new_element.split(" ")[1]; - } - - String new_descriptor = buildNewDescriptor(element_type == FIELD?CodeConstants.CONSTANT_Fieldref:CodeConstants.CONSTANT_Methodref, - descriptor); - if(new_descriptor != null) { - descriptor = new_descriptor; - } - } - - return new String[] {elementname, descriptor}; - } - - public PooledConstant getConstant(int index) { - return pool.get(index); - } - - public PrimitiveConstant getPrimitiveConstant(int index) { - PrimitiveConstant cn = (PrimitiveConstant)getConstant(index); - - if(cn != null && interceptor != null) { - if(cn.type == CodeConstants.CONSTANT_Class) { - String newname = buildNewClassname(cn.getString()); - if(newname != null) { - cn = new PrimitiveConstant(CodeConstants.CONSTANT_Class, newname); - } - } - } - - return cn; - } - - public LinkConstant getLinkConstant(int index) { - LinkConstant ln = (LinkConstant)getConstant(index); - - if(ln != null && interceptor != null) { - if(ln.type == CodeConstants.CONSTANT_Fieldref || - ln.type == CodeConstants.CONSTANT_Methodref || - ln.type == CodeConstants.CONSTANT_InterfaceMethodref) { - - String new_classname = buildNewClassname(ln.classname); - String new_element = interceptor.getName(ln.classname+" "+ln.elementname+" "+ln.descriptor); - String new_descriptor = buildNewDescriptor(ln.type, ln.descriptor); - - if(new_classname != null || new_element != null || new_descriptor != null) { - - ln = new LinkConstant(ln.type, new_classname==null?ln.classname:new_classname, - new_element==null?ln.elementname:new_element.split(" ")[1], - new_descriptor==null?ln.descriptor:new_descriptor); - } - } - } - - return ln; - } - - private String buildNewClassname(String classname) { - - VarType vt = new VarType(classname, true); - - String newname = interceptor.getName(vt.value); - if(newname != null) { - StringBuilder buffer = new StringBuilder(); - - if(vt.arraydim > 0) { - for(int i=0;i<vt.arraydim;i++) { - buffer.append("["); - } - - buffer.append("L"+newname+";"); - } else { - buffer.append(newname); - } - - return buffer.toString(); - } - - return null; - } - - private String buildNewDescriptor(int type, String descriptor) { - - boolean updated = false; - - if(type == CodeConstants.CONSTANT_Fieldref) { - FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); - - VarType ftype = fd.type; - if(ftype.type == CodeConstants.TYPE_OBJECT) { - String newclname = buildNewClassname(ftype.value); - if(newclname != null) { - ftype.value = newclname; - updated = true; - } - } - - if(updated) { - return fd.getDescriptor(); - } - - } else { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - // params - for(VarType partype : md.params) { - if(partype.type == CodeConstants.TYPE_OBJECT) { - String newclname = buildNewClassname(partype.value); - if(newclname != null) { - partype.value = newclname; - updated = true; - } - } - } - - // return value - if(md.ret.type == CodeConstants.TYPE_OBJECT) { - String newclname = buildNewClassname(md.ret.value); - if(newclname!=null) { - md.ret.value = newclname; - updated = true; - } - } - - if(updated) { - return md.getDescriptor(); - } - } - - return null; - } - + + public static final int FIELD = 1; + + public static final int METHOD = 2; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private List<PooledConstant> pool = new ArrayList<PooledConstant>(); + + private PoolInterceptor interceptor; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public ConstantPool(DataInputStream in) throws IOException { + + int size = in.readUnsignedShort(); + + int[] pass = new int[size]; + + // first dummy constant + pool.add(null); + + // first pass: read the elements + for (int i = 1; i < size; i++) { + + byte tag = (byte)in.readUnsignedByte(); + + switch (tag) { + case CodeConstants.CONSTANT_Utf8: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF())); + break; + case CodeConstants.CONSTANT_Integer: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, new Integer(in.readInt()))); + break; + case CodeConstants.CONSTANT_Float: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, new Float(in.readFloat()))); + break; + case CodeConstants.CONSTANT_Long: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, new Long(in.readLong()))); + pool.add(null); + i++; + break; + case CodeConstants.CONSTANT_Double: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, new Double(in.readDouble()))); + pool.add(null); + i++; + break; + case CodeConstants.CONSTANT_Class: + case CodeConstants.CONSTANT_String: + case CodeConstants.CONSTANT_MethodType: + pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); + pass[i] = 1; + break; + case CodeConstants.CONSTANT_Fieldref: + case CodeConstants.CONSTANT_Methodref: + case CodeConstants.CONSTANT_InterfaceMethodref: + case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: + pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); + if (tag == CodeConstants.CONSTANT_NameAndType) { + pass[i] = 1; + } + else { + pass[i] = 2; + } + break; + case CodeConstants.CONSTANT_MethodHandle: + pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); + pass[i] = 3; + break; + } + } + + + // resolving complex pool elements + for (int pass_index = 1; pass_index <= 3; pass_index++) { + for (int i = 1; i < size; i++) { + if (pass[i] == pass_index) { + pool.get(i).resolveConstant(this); + } + } + } + + // get global constant pool interceptor instance, if any available + interceptor = DecompilerContext.getPoolInterceptor(); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToOutputStream(DataOutputStream out) throws FileNotFoundException, IOException { + + out.writeShort(pool.size()); + for (int i = 1; i < pool.size(); i++) { + PooledConstant cnst = (PooledConstant)pool.get(i); + if (cnst != null) { + cnst.writeToStream(out); + } + } + } + + public static void skipPool(DataInputStream in) throws IOException { + + int size = in.readUnsignedShort(); + + for (int i = 1; i < size; i++) { + switch (in.readUnsignedByte()) { + case CodeConstants.CONSTANT_Utf8: + in.readUTF(); + break; + case CodeConstants.CONSTANT_Integer: + case CodeConstants.CONSTANT_Float: + case CodeConstants.CONSTANT_Fieldref: + case CodeConstants.CONSTANT_Methodref: + case CodeConstants.CONSTANT_InterfaceMethodref: + case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: + in.skip(4); + break; + case CodeConstants.CONSTANT_Long: + case CodeConstants.CONSTANT_Double: + in.skip(8); + i++; + break; + case CodeConstants.CONSTANT_Class: + case CodeConstants.CONSTANT_String: + case CodeConstants.CONSTANT_MethodType: + in.skip(2); + break; + case CodeConstants.CONSTANT_MethodHandle: + in.skip(3); + } + } + } + + public int size() { + return pool.size(); + } + + public String[] getClassElement(int element_type, int class_index, int name_index, int descriptor_index) { + + String classname = ((PrimitiveConstant)getConstant(class_index)).getString(); + String elementname = ((PrimitiveConstant)getConstant(name_index)).getString(); + String descriptor = ((PrimitiveConstant)getConstant(descriptor_index)).getString(); + + if (interceptor != null) { + String new_element = interceptor.getName(classname + " " + elementname + " " + descriptor); + + if (new_element != null) { + elementname = new_element.split(" ")[1]; + } + + String new_descriptor = buildNewDescriptor(element_type == FIELD ? CodeConstants.CONSTANT_Fieldref : CodeConstants.CONSTANT_Methodref, + descriptor); + if (new_descriptor != null) { + descriptor = new_descriptor; + } + } + + return new String[]{elementname, descriptor}; + } + + public PooledConstant getConstant(int index) { + return pool.get(index); + } + + public PrimitiveConstant getPrimitiveConstant(int index) { + PrimitiveConstant cn = (PrimitiveConstant)getConstant(index); + + if (cn != null && interceptor != null) { + if (cn.type == CodeConstants.CONSTANT_Class) { + String newname = buildNewClassname(cn.getString()); + if (newname != null) { + cn = new PrimitiveConstant(CodeConstants.CONSTANT_Class, newname); + } + } + } + + return cn; + } + + public LinkConstant getLinkConstant(int index) { + LinkConstant ln = (LinkConstant)getConstant(index); + + if (ln != null && interceptor != null) { + if (ln.type == CodeConstants.CONSTANT_Fieldref || + ln.type == CodeConstants.CONSTANT_Methodref || + ln.type == CodeConstants.CONSTANT_InterfaceMethodref) { + + String new_classname = buildNewClassname(ln.classname); + String new_element = interceptor.getName(ln.classname + " " + ln.elementname + " " + ln.descriptor); + String new_descriptor = buildNewDescriptor(ln.type, ln.descriptor); + + if (new_classname != null || new_element != null || new_descriptor != null) { + + ln = new LinkConstant(ln.type, new_classname == null ? ln.classname : new_classname, + new_element == null ? ln.elementname : new_element.split(" ")[1], + new_descriptor == null ? ln.descriptor : new_descriptor); + } + } + } + + return ln; + } + + private String buildNewClassname(String classname) { + + VarType vt = new VarType(classname, true); + + String newname = interceptor.getName(vt.value); + if (newname != null) { + StringBuilder buffer = new StringBuilder(); + + if (vt.arraydim > 0) { + for (int i = 0; i < vt.arraydim; i++) { + buffer.append("["); + } + + buffer.append("L" + newname + ";"); + } + else { + buffer.append(newname); + } + + return buffer.toString(); + } + + return null; + } + + private String buildNewDescriptor(int type, String descriptor) { + + boolean updated = false; + + if (type == CodeConstants.CONSTANT_Fieldref) { + FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); + + VarType ftype = fd.type; + if (ftype.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(ftype.value); + if (newclname != null) { + ftype.value = newclname; + updated = true; + } + } + + if (updated) { + return fd.getDescriptor(); + } + } + else { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + // params + for (VarType partype : md.params) { + if (partype.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(partype.value); + if (newclname != null) { + partype.value = newclname; + updated = true; + } + } + } + + // return value + if (md.ret.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(md.ret.value); + if (newclname != null) { + md.ret.value = newclname; + updated = true; + } + } + + if (updated) { + return md.getDescriptor(); + } + } + + return null; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java index 770a5e3..c20b5af 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.consts; import java.io.DataOutputStream; @@ -24,136 +25,142 @@ import java.io.IOException; public class LinkConstant extends PooledConstant { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int index1, index2; - - public String classname; - - public String elementname; - - public String descriptor; - - public int paramCount = 0; - - public boolean isVoid = false;; - - public boolean returnCategory2 = false; - - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public LinkConstant(int type, String classname, String elementname, String descriptor) { - this.type = type; - this.classname = classname; - this.elementname = elementname; - this.descriptor = descriptor; - - initConstant(); - } - - public LinkConstant(int type, int index1, int index2) { - this.type = type; - this.index1 = index1; - this.index2 = index2; - } - - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void resolveConstant(ConstantPool pool) { - - if(type == CONSTANT_NameAndType) { - elementname = pool.getPrimitiveConstant(index1).getString(); - descriptor = pool.getPrimitiveConstant(index2).getString(); - } else if(type == CONSTANT_MethodHandle) { - LinkConstant ref_info = pool.getLinkConstant(index2); - - classname = ref_info.classname; - elementname = ref_info.elementname; - descriptor = ref_info.descriptor; - - } else { - if(type != CONSTANT_InvokeDynamic) { - classname = pool.getPrimitiveConstant(index1).getString(); - } - - LinkConstant nametype = pool.getLinkConstant(index2); - elementname = nametype.elementname; - descriptor = nametype.descriptor; - } - - initConstant(); - } - - public void writeToStream(DataOutputStream out) throws IOException { - out.writeByte(type); - if(type == CONSTANT_MethodHandle) { - out.writeByte(index1); - } else { - out.writeShort(index1); - } - out.writeShort(index2); - } - - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof LinkConstant)) return false; - - LinkConstant cn = (LinkConstant)o; + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int index1, index2; + + public String classname; + + public String elementname; + + public String descriptor; + + public int paramCount = 0; + + public boolean isVoid = false; + ; + + public boolean returnCategory2 = false; + + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public LinkConstant(int type, String classname, String elementname, String descriptor) { + this.type = type; + this.classname = classname; + this.elementname = elementname; + this.descriptor = descriptor; + + initConstant(); + } + + public LinkConstant(int type, int index1, int index2) { + this.type = type; + this.index1 = index1; + this.index2 = index2; + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void resolveConstant(ConstantPool pool) { + + if (type == CONSTANT_NameAndType) { + elementname = pool.getPrimitiveConstant(index1).getString(); + descriptor = pool.getPrimitiveConstant(index2).getString(); + } + else if (type == CONSTANT_MethodHandle) { + LinkConstant ref_info = pool.getLinkConstant(index2); + + classname = ref_info.classname; + elementname = ref_info.elementname; + descriptor = ref_info.descriptor; + } + else { + if (type != CONSTANT_InvokeDynamic) { + classname = pool.getPrimitiveConstant(index1).getString(); + } + + LinkConstant nametype = pool.getLinkConstant(index2); + elementname = nametype.elementname; + descriptor = nametype.descriptor; + } + + initConstant(); + } + + public void writeToStream(DataOutputStream out) throws IOException { + out.writeByte(type); + if (type == CONSTANT_MethodHandle) { + out.writeByte(index1); + } + else { + out.writeShort(index1); + } + out.writeShort(index2); + } + + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof LinkConstant)) return false; + + LinkConstant cn = (LinkConstant)o; return this.type == cn.type && - this.elementname.equals(cn.elementname) && - this.descriptor.equals(cn.descriptor) && - (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname)); + this.elementname.equals(cn.elementname) && + this.descriptor.equals(cn.descriptor) && + (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname)); + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void initConstant() { + + if (type == CONSTANT_Methodref || + type == CONSTANT_InterfaceMethodref || + type == CONSTANT_InvokeDynamic || + type == CONSTANT_MethodHandle) { + resolveDescriptor(descriptor); + } + else if (type == CONSTANT_Fieldref) { + returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor)); + } + } + + private void resolveDescriptor(String descr) { + + String[] arr = descr.split("[()]"); + String par = arr[1]; + + int index = 0, counter = 0; + int len = par.length(); + + while (index < len) { + + char c = par.charAt(index); + if (c == 'L') { + index = par.indexOf(";", index); + } + else if (c == '[') { + index++; + continue; + } + + counter++; + index++; + } + + paramCount = counter; + isVoid = "V".equals(arr[2]); + returnCategory2 = ("D".equals(arr[2]) || "J".equals(arr[2])); } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void initConstant() { - - if(type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref || type == CONSTANT_InvokeDynamic || type == CONSTANT_MethodHandle) { - resolveDescriptor(descriptor); - } else if(type == CONSTANT_Fieldref) { - returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor)); - } - - } - - private void resolveDescriptor(String descr){ - - String[] arr = descr.split("[()]"); - String par = arr[1]; - - int index = 0, counter = 0; - int len = par.length(); - - while(index<len) { - - char c = par.charAt(index); - if(c == 'L') { - index = par.indexOf(";", index); - } else if (c == '[') { - index++; - continue; - } - - counter++; - index++; - } - - paramCount = counter; - isVoid = "V".equals(arr[2]); - returnCategory2 = ("D".equals(arr[2]) || "J".equals(arr[2])); - } - } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java index ae46604..d3d30c0 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java @@ -1,24 +1,25 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.consts; +import org.jetbrains.java.decompiler.code.CodeConstants; + import java.io.DataOutputStream; import java.io.IOException; -import org.jetbrains.java.decompiler.code.CodeConstants; - /* cp_info { u1 tag; @@ -29,88 +30,88 @@ import org.jetbrains.java.decompiler.code.CodeConstants; public class PooledConstant implements CodeConstants, VariableTypeEnum { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int type; - - public boolean own = false; - - public int returnType; - - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private Object[] values; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public PooledConstant() {} - - public PooledConstant(int type, Object[] values) { - this.type = type; - this.values = values; - this.returnType = poolTypeToIntern(type); - } - - public PooledConstant(int type, boolean own, Object[] values) { - this.type = type; - this.own = own; - this.values = values; - this.returnType = poolTypeToIntern(type); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void resolveConstant(ConstantPool pool) { - // to be overwritten - } - - public void writeToStream(DataOutputStream out) throws IOException { - // to be overwritten - } - - public int poolTypeToIntern(int type) { - - switch(type){ - case CONSTANT_Integer: - return INT; - case CONSTANT_Float: - return FLOAT; - case CONSTANT_Long: - return LONG; - case CONSTANT_Double: - return DOUBLE; - case CONSTANT_String: - case CONSTANT_Class: // 1.5 -> ldc class - return REFERENCE; - default: - throw new RuntimeException("Huh?? What are you trying to load?"); - } - } - - public Object getValue(int index){ - return values[index]; - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Object[] getValues() { - return values; - } - - public void setValues(Object[] values) { - this.values = values; - } + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int type; + + public boolean own = false; + + public int returnType; + + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Object[] values; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public PooledConstant() { + } + + public PooledConstant(int type, Object[] values) { + this.type = type; + this.values = values; + this.returnType = poolTypeToIntern(type); + } + + public PooledConstant(int type, boolean own, Object[] values) { + this.type = type; + this.own = own; + this.values = values; + this.returnType = poolTypeToIntern(type); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void resolveConstant(ConstantPool pool) { + // to be overwritten + } + + public void writeToStream(DataOutputStream out) throws IOException { + // to be overwritten + } + + public int poolTypeToIntern(int type) { + + switch (type) { + case CONSTANT_Integer: + return INT; + case CONSTANT_Float: + return FLOAT; + case CONSTANT_Long: + return LONG; + case CONSTANT_Double: + return DOUBLE; + case CONSTANT_String: + case CONSTANT_Class: // 1.5 -> ldc class + return REFERENCE; + default: + throw new RuntimeException("Huh?? What are you trying to load?"); + } + } + + public Object getValue(int index) { + return values[index]; + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Object[] getValues() { + return values; + } + public void setValues(Object[] values) { + this.values = values; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java index f01a695..4cd4dd8 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.consts; import java.io.DataOutputStream; @@ -23,104 +24,103 @@ import java.io.IOException; public class PrimitiveConstant extends PooledConstant { - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int index; - - public Object value; - - public boolean isArray; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public PrimitiveConstant(int type, Object value) { - this.type = type; - this.value = value; - - initConstant(); - } - - public PrimitiveConstant(int type, int index) { - this.type = type; - this.index = index; - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public int getInt() { - return ((Integer)value).intValue(); - } - - public long getLong() { - return ((Long)value).longValue(); - } - - public float getFloat() { - return ((Float)value).floatValue(); - } - - public double getDouble() { - return ((Double)value).doubleValue(); - } - - public String getString() { - return (String)value; - } - - public void resolveConstant(ConstantPool pool) { - - if(type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { - value = pool.getPrimitiveConstant(index).getString(); - initConstant(); - } - } - - public void writeToStream(DataOutputStream out) throws IOException { - - out.writeByte(type); - switch(type) { - case CONSTANT_Integer: - out.writeInt(getInt()); - break; - case CONSTANT_Float: - out.writeFloat(getFloat()); - break; - case CONSTANT_Long: - out.writeLong(getLong()); - break; - case CONSTANT_Double: - out.writeDouble(getDouble()); - break; - case CONSTANT_Utf8: - out.writeUTF(getString()); - break; - default: // CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType - out.writeShort(index); - } - } - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof PrimitiveConstant)) return false; - - PrimitiveConstant cn = (PrimitiveConstant)o; - return this.type == cn.type && - this.isArray == cn.isArray && - this.value.equals(cn.value); - - } - - private void initConstant() { - if(type == CONSTANT_Class) { - String className = getString(); - isArray = (className.length() > 0 && className.charAt(0)=='['); // empty string for a class name seems to be possible in some android files - } - } - + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int index; + + public Object value; + + public boolean isArray; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public PrimitiveConstant(int type, Object value) { + this.type = type; + this.value = value; + + initConstant(); + } + + public PrimitiveConstant(int type, int index) { + this.type = type; + this.index = index; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public int getInt() { + return ((Integer)value).intValue(); + } + + public long getLong() { + return ((Long)value).longValue(); + } + + public float getFloat() { + return ((Float)value).floatValue(); + } + + public double getDouble() { + return ((Double)value).doubleValue(); + } + + public String getString() { + return (String)value; + } + + public void resolveConstant(ConstantPool pool) { + + if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { + value = pool.getPrimitiveConstant(index).getString(); + initConstant(); + } + } + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeByte(type); + switch (type) { + case CONSTANT_Integer: + out.writeInt(getInt()); + break; + case CONSTANT_Float: + out.writeFloat(getFloat()); + break; + case CONSTANT_Long: + out.writeLong(getLong()); + break; + case CONSTANT_Double: + out.writeDouble(getDouble()); + break; + case CONSTANT_Utf8: + out.writeUTF(getString()); + break; + default: // CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType + out.writeShort(index); + } + } + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof PrimitiveConstant)) return false; + + PrimitiveConstant cn = (PrimitiveConstant)o; + return this.type == cn.type && + this.isArray == cn.isArray && + this.value.equals(cn.value); + } + + private void initConstant() { + if (type == CONSTANT_Class) { + String className = getString(); + isArray = + (className.length() > 0 && className.charAt(0) == '['); // empty string for a class name seems to be possible in some android files + } + } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java b/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java index fc58c7e..ef949aa 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java @@ -1,47 +1,47 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.consts; public interface VariableTypeEnum { - public final static int BOOLEAN = 1; - public final static int BYTE = 2; - public final static int CHAR = 3; - public final static int SHORT = 4; - public final static int INT = 5; - public final static int FLOAT = 6; - public final static int LONG = 7; - public final static int DOUBLE = 8; - public final static int RETURN_ADDRESS = 9; - public final static int REFERENCE = 10; - public final static int INSTANCE_UNINITIALIZED = 11; - public final static int VALUE_UNKNOWN = 12; - public final static int VOID = 13; - - public final static Integer BOOLEAN_OBJ = new Integer(BOOLEAN); - public final static Integer BYTE_OBJ = new Integer(BYTE); - public final static Integer CHAR_OBJ = new Integer(CHAR); - public final static Integer SHORT_OBJ = new Integer(SHORT); - public final static Integer INT_OBJ = new Integer(INT); - public final static Integer FLOAT_OBJ = new Integer(FLOAT); - public final static Integer LONG_OBJ = new Integer(LONG); - public final static Integer DOUBLE_OBJ = new Integer(DOUBLE); - public final static Integer RETURN_ADDRESS_OBJ = new Integer(RETURN_ADDRESS); - public final static Integer REFERENCE_OBJ = new Integer(REFERENCE); - public final static Integer INSTANCE_UNINITIALIZED_OBJ = new Integer(INSTANCE_UNINITIALIZED); - public final static Integer VALUE_UNKNOWN_OBJ = new Integer(VALUE_UNKNOWN); - public final static Integer VOID_OBJ = new Integer(VOID); - + public final static int BOOLEAN = 1; + public final static int BYTE = 2; + public final static int CHAR = 3; + public final static int SHORT = 4; + public final static int INT = 5; + public final static int FLOAT = 6; + public final static int LONG = 7; + public final static int DOUBLE = 8; + public final static int RETURN_ADDRESS = 9; + public final static int REFERENCE = 10; + public final static int INSTANCE_UNINITIALIZED = 11; + public final static int VALUE_UNKNOWN = 12; + public final static int VOID = 13; + + public final static Integer BOOLEAN_OBJ = new Integer(BOOLEAN); + public final static Integer BYTE_OBJ = new Integer(BYTE); + public final static Integer CHAR_OBJ = new Integer(CHAR); + public final static Integer SHORT_OBJ = new Integer(SHORT); + public final static Integer INT_OBJ = new Integer(INT); + public final static Integer FLOAT_OBJ = new Integer(FLOAT); + public final static Integer LONG_OBJ = new Integer(LONG); + public final static Integer DOUBLE_OBJ = new Integer(DOUBLE); + public final static Integer RETURN_ADDRESS_OBJ = new Integer(RETURN_ADDRESS); + public final static Integer REFERENCE_OBJ = new Integer(REFERENCE); + public final static Integer INSTANCE_UNINITIALIZED_OBJ = new Integer(INSTANCE_UNINITIALIZED); + public final static Integer VALUE_UNKNOWN_OBJ = new Integer(VALUE_UNKNOWN); + public final static Integer VOID_OBJ = new Integer(VOID); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java index db6ecd1..53269da 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java @@ -1,98 +1,100 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.util.ListStack; +import java.util.ArrayList; +import java.util.List; + public class DataPoint { - private List<VarType> localVariables = new ArrayList<VarType>(); - - private ListStack<VarType> stack = new ListStack<VarType>(); - - - public void setVariable(int index, VarType value) { - if(index>=localVariables.size()) { - for(int i=localVariables.size();i<=index;i++) { - localVariables.add(new VarType(CodeConstants.TYPE_NOTINITIALIZED)); - } - } - - localVariables.set(index, value); - } - - public VarType getVariable(int index) { - if(index<localVariables.size()) { - return localVariables.get(index); - } else if(index<0) { - throw new IndexOutOfBoundsException(); - } else { - return new VarType(CodeConstants.TYPE_NOTINITIALIZED); - } - } - - public DataPoint copy() { - DataPoint point = new DataPoint(); - point.setLocalVariables(new ArrayList<VarType>(localVariables)); - point.setStack(stack.clone()); - return point; - } - - public static DataPoint getInitialDataPoint(StructMethod mt) { - - DataPoint point = new DataPoint(); - - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - int k = 0; - if((mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0) { - point.setVariable(k++, new VarType(CodeConstants.TYPE_OBJECT, 0, null)); - } - - for(int i=0;i<md.params.length;i++) { - VarType var = md.params[i]; - - point.setVariable(k++, var); - if(var.stack_size == 2) { - point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY)); - } - } - - return point; - } - - - public List<VarType> getLocalVariables() { - return localVariables; - } - - public void setLocalVariables(List<VarType> localVariables) { - this.localVariables = localVariables; - } - - public ListStack<VarType> getStack() { - return stack; - } - - public void setStack(ListStack<VarType> stack) { - this.stack = stack; - } - + private List<VarType> localVariables = new ArrayList<VarType>(); + + private ListStack<VarType> stack = new ListStack<VarType>(); + + + public void setVariable(int index, VarType value) { + if (index >= localVariables.size()) { + for (int i = localVariables.size(); i <= index; i++) { + localVariables.add(new VarType(CodeConstants.TYPE_NOTINITIALIZED)); + } + } + + localVariables.set(index, value); + } + + public VarType getVariable(int index) { + if (index < localVariables.size()) { + return localVariables.get(index); + } + else if (index < 0) { + throw new IndexOutOfBoundsException(); + } + else { + return new VarType(CodeConstants.TYPE_NOTINITIALIZED); + } + } + + public DataPoint copy() { + DataPoint point = new DataPoint(); + point.setLocalVariables(new ArrayList<VarType>(localVariables)); + point.setStack(stack.clone()); + return point; + } + + public static DataPoint getInitialDataPoint(StructMethod mt) { + + DataPoint point = new DataPoint(); + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int k = 0; + if ((mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0) { + point.setVariable(k++, new VarType(CodeConstants.TYPE_OBJECT, 0, null)); + } + + for (int i = 0; i < md.params.length; i++) { + VarType var = md.params[i]; + + point.setVariable(k++, var); + if (var.stack_size == 2) { + point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + } + } + + return point; + } + + + public List<VarType> getLocalVariables() { + return localVariables; + } + + public void setLocalVariables(List<VarType> localVariables) { + this.localVariables = localVariables; + } + + public ListStack<VarType> getStack() { + return stack; + } + + public void setStack(ListStack<VarType> stack) { + this.stack = stack; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java index a475937..ff731ae 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java @@ -1,58 +1,59 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen; public class FieldDescriptor { - - public static final FieldDescriptor INTEGER_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Integer;"); - public static final FieldDescriptor LONG_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Long;"); - public static final FieldDescriptor FLOAT_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Float;"); - public static final FieldDescriptor DOUBLE_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Double;"); - - public VarType type; - - public String descriptorString; - - private FieldDescriptor() {} - - public static FieldDescriptor parseDescriptor(String descr) { - - FieldDescriptor fd = new FieldDescriptor(); - - fd.type = new VarType(descr); - fd.descriptorString = descr; - - return fd; - } - - public String getDescriptor() { - return type.toString(); - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FieldDescriptor)) return false; + + public static final FieldDescriptor INTEGER_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Integer;"); + public static final FieldDescriptor LONG_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Long;"); + public static final FieldDescriptor FLOAT_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Float;"); + public static final FieldDescriptor DOUBLE_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Double;"); + + public VarType type; + + public String descriptorString; + + private FieldDescriptor() { + } + + public static FieldDescriptor parseDescriptor(String descr) { + + FieldDescriptor fd = new FieldDescriptor(); + + fd.type = new VarType(descr); + fd.descriptorString = descr; + + return fd; + } + + public String getDescriptor() { + return type.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FieldDescriptor)) return false; FieldDescriptor fd = (FieldDescriptor)o; return type.equals(fd.type); } - @Override - public int hashCode() { - return type.hashCode(); - } - + @Override + public int hashCode() { + return type.hashCode(); + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 77a8394..7dbc16c 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen; import java.util.ArrayList; @@ -20,83 +21,82 @@ import java.util.List; public class MethodDescriptor { - public VarType[] params; - - public VarType ret; - - - public static MethodDescriptor parseDescriptor(String mdescr) { - - MethodDescriptor md = new MethodDescriptor(); - - List<String> lst = new ArrayList<String>(); - String[] pars = mdescr.split("[()]"); - - String par = pars[1]; - - int indexFrom = -1, ind,index = 0; - int len = par.length(); - - for(;index<len;index++) { - - switch(par.charAt(index)){ - case '[': - if(indexFrom<0){ - indexFrom = index; - } - break; - case 'L': - ind = par.indexOf(";", index); - lst.add(par.substring(indexFrom<0?index:indexFrom, ind+1)); - index = ind; - indexFrom = -1; - break; - default: - lst.add(par.substring(indexFrom<0?index:indexFrom, index+1)); - indexFrom = -1; - } - } - - lst.add(pars[2]); - - - md.params = new VarType[lst.size()-1]; - - int i = 0; - for(;i<lst.size()-1;i++) { - md.params[i] = new VarType(lst.get(i)); - } - md.ret = new VarType(lst.get(i)); - - return md; - } - - public String getDescriptor() { - String res = "("; - - for(int j = 0;j<params.length;j++) { - res+=params[j].toString(); - } - - res+=")"+ret.toString(); - - return res; - } - - @Override - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof MethodDescriptor)) return false; + public VarType[] params; + + public VarType ret; + + + public static MethodDescriptor parseDescriptor(String mdescr) { + + MethodDescriptor md = new MethodDescriptor(); + + List<String> lst = new ArrayList<String>(); + String[] pars = mdescr.split("[()]"); + + String par = pars[1]; + + int indexFrom = -1, ind, index = 0; + int len = par.length(); + + for (; index < len; index++) { + + switch (par.charAt(index)) { + case '[': + if (indexFrom < 0) { + indexFrom = index; + } + break; + case 'L': + ind = par.indexOf(";", index); + lst.add(par.substring(indexFrom < 0 ? index : indexFrom, ind + 1)); + index = ind; + indexFrom = -1; + break; + default: + lst.add(par.substring(indexFrom < 0 ? index : indexFrom, index + 1)); + indexFrom = -1; + } + } + + lst.add(pars[2]); + + + md.params = new VarType[lst.size() - 1]; + + int i = 0; + for (; i < lst.size() - 1; i++) { + md.params[i] = new VarType(lst.get(i)); + } + md.ret = new VarType(lst.get(i)); + + return md; + } + + public String getDescriptor() { + String res = "("; + + for (int j = 0; j < params.length; j++) { + res += params[j].toString(); + } + + res += ")" + ret.toString(); + + return res; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof MethodDescriptor)) return false; MethodDescriptor md = (MethodDescriptor)o; return ret.equals(md.ret) && Arrays.equals(params, md.params); } - @Override - public int hashCode() { + @Override + public int hashCode() { int result = ret.hashCode(); result = 31 * result + params.length; return result; - } - + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index 5ee0c4b..79e2cbf 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -1,410 +1,422 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.util.InterpreterUtil; public class VarType { // TODO: optimize switch - - public static final int FALSEBOOLEAN = 1; - - public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN); - public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT); - public static final VarType VARTYPE_FLOAT = new VarType(CodeConstants.TYPE_FLOAT); - public static final VarType VARTYPE_LONG = new VarType(CodeConstants.TYPE_LONG); - public static final VarType VARTYPE_DOUBLE = new VarType(CodeConstants.TYPE_DOUBLE); - public static final VarType VARTYPE_BYTE = new VarType(CodeConstants.TYPE_BYTE); - public static final VarType VARTYPE_CHAR = new VarType(CodeConstants.TYPE_CHAR); - public static final VarType VARTYPE_SHORT = new VarType(CodeConstants.TYPE_SHORT); - public static final VarType VARTYPE_BOOLEAN = new VarType(CodeConstants.TYPE_BOOLEAN); - public static final VarType VARTYPE_BYTECHAR = new VarType(CodeConstants.TYPE_BYTECHAR); - public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR); - - public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL,0,null); - public static final VarType VARTYPE_GROUP2EMPTY = new VarType(CodeConstants.TYPE_GROUP2EMPTY); - public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String"); - public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"); - public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Object"); - public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID); - - public int type; - - public int type_family; - - public int arraydim; - - public String value; - - public int stack_size; - - public int convinfo; - - public VarType(int type) { - this.type = type; - this.arraydim = 0; - - value = getChar(type); - setStackSize(type); - setFamily(); - } - - public VarType(int type, int arraydim) { - this(type); - this.arraydim = arraydim; - setFamily(); - } - - public VarType(int type, int arraydim, String value) { - this(type); - this.arraydim = arraydim; - this.value = value; - setFamily(); - } - - public VarType(String strtype) { - this(strtype, false); - } - - public VarType(String strtype, boolean cltype) { - parseTypeString(strtype, cltype); - setStackSize(type); - setFamily(); - } - - public void decArrayDim() { - if(arraydim > 0) { - arraydim--; - setFamily(); - } else { - // throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case - } - } - - public String toString() { - String res = ""; - - for(int i=0;i<arraydim;i++) { - res+="["; - } - - if(type == CodeConstants.TYPE_OBJECT) { - res+="L"+value+";"; - } else { - res+=value; - } - - return res; - } - - public VarType copy() { - VarType v = new VarType(type, arraydim, value); - v.convinfo = convinfo; - return v; - } - - public boolean isFalseBoolean() { - return (convinfo & VarType.FALSEBOOLEAN) != 0; - } - - public boolean isSuperset(VarType val) { - - return this.equals(val) || this.isStrictSuperset(val); - } - - public boolean isStrictSuperset(VarType val) { - - int valtype = val.type; - - if(valtype == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) { - return true; - } - - if(val.arraydim > 0) { - return this.equals(VARTYPE_OBJECT); - } else if(arraydim > 0) { - return (valtype == CodeConstants.TYPE_NULL); - } - - boolean res = false; - - switch(type) { - case CodeConstants.TYPE_INT: - res |= (valtype == CodeConstants.TYPE_SHORT || - valtype == CodeConstants.TYPE_CHAR); - case CodeConstants.TYPE_SHORT: - res |= (valtype == CodeConstants.TYPE_BYTE); - case CodeConstants.TYPE_CHAR: - res |= (valtype == CodeConstants.TYPE_SHORTCHAR); - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_SHORTCHAR: - res |= (valtype == CodeConstants.TYPE_BYTECHAR); - case CodeConstants.TYPE_BYTECHAR: - res |= (valtype == CodeConstants.TYPE_BOOLEAN); - break; - case CodeConstants.TYPE_OBJECT: - if(valtype == CodeConstants.TYPE_NULL) { - return true; - } else if(this.equals(VARTYPE_OBJECT)) { - return valtype == CodeConstants.TYPE_OBJECT && - !val.equals(VARTYPE_OBJECT); - } - } - - return res; - } - - // type1 and type2 must not be null - public static VarType getCommonMinType(VarType type1, VarType type2) { - - if(type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans - return type1.isFalseBoolean() ? type2 : type1; - } - - if(type1.isSuperset(type2)) { - return type2; - } else if(type2.isSuperset(type1)) { - return type1; - } else if(type1.type_family == type2.type_family) { - switch(type1.type_family) { - case CodeConstants.TYPE_FAMILY_INTEGER: - if((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT) - || (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) { - return VarType.VARTYPE_SHORTCHAR; - } else { - return VarType.VARTYPE_BYTECHAR; - } - case CodeConstants.TYPE_FAMILY_OBJECT: - return VarType.VARTYPE_NULL; - } - } - - return null; - } - - // type1 and type2 must not be null - public static VarType getCommonSupertype(VarType type1, VarType type2) { - - if(type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans - return type1.isFalseBoolean() ? type1 : type2; - } - - if(type1.isSuperset(type2)) { - return type1; - } else if(type2.isSuperset(type1)) { - return type2; - } else if(type1.type_family == type2.type_family) { - switch(type1.type_family) { - case CodeConstants.TYPE_FAMILY_INTEGER: - if((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE) - || (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) { - return VarType.VARTYPE_SHORT; - } else { - return VarType.VARTYPE_INT; - } - case CodeConstants.TYPE_FAMILY_OBJECT: - return VarType.VARTYPE_OBJECT; - } - } - - return null; - } - - public static VarType getMinTypeInFamily(int family) { - switch(family) { - case CodeConstants.TYPE_FAMILY_BOOLEAN: - return VarType.VARTYPE_BOOLEAN; - case CodeConstants.TYPE_FAMILY_INTEGER: - return VarType.VARTYPE_BYTECHAR; - case CodeConstants.TYPE_FAMILY_OBJECT: - return VarType.VARTYPE_NULL; - case CodeConstants.TYPE_FAMILY_FLOAT: - return VarType.VARTYPE_FLOAT; - case CodeConstants.TYPE_FAMILY_LONG: - return VarType.VARTYPE_LONG; - case CodeConstants.TYPE_FAMILY_DOUBLE: - return VarType.VARTYPE_DOUBLE; - case CodeConstants.TYPE_FAMILY_UNKNOWN: - return VarType.VARTYPE_UNKNOWN; - default: - throw new RuntimeException("invalid type family!"); - } - } - - public boolean equals(Object o) { - - if(o == this) { - return true; - } - - if(o == null || !(o instanceof VarType)) { - return false; - } - - VarType vt = (VarType) o; - return type == vt.type && arraydim == vt.arraydim && InterpreterUtil.equalObjects(value, vt.value); - } - - private void parseTypeString(String strtype, boolean cltype) { - - for(int i=0;i<strtype.length();i++) { - switch(strtype.charAt(i)){ - case '[': - arraydim++; - break; - case 'L': - if(strtype.charAt(strtype.length()-1) == ';') { - type = CodeConstants.TYPE_OBJECT; - value = strtype.substring(i+1, strtype.length()-1); - return; - } - default: - value = strtype.substring(i, strtype.length()); - if((cltype && i == 0) || value.length()>1) { - type = CodeConstants.TYPE_OBJECT; - } else { - type = getType(value.charAt(0)); - } - return; - } - } - } - - private void setStackSize(int type) { - if(arraydim > 0) { - stack_size = 1; - } else { - stack_size = (type == CodeConstants.TYPE_DOUBLE || - type == CodeConstants.TYPE_LONG)?2: - ((type == CodeConstants.TYPE_VOID || - type == CodeConstants.TYPE_GROUP2EMPTY)?0:1); - } - } - - private int getType(char c) { - switch(c) { - case 'B': - return CodeConstants.TYPE_BYTE; - case 'C': - return CodeConstants.TYPE_CHAR; - case 'D': - return CodeConstants.TYPE_DOUBLE; - case 'F': - return CodeConstants.TYPE_FLOAT; - case 'I': - return CodeConstants.TYPE_INT; - case 'J': - return CodeConstants.TYPE_LONG; - case 'S': - return CodeConstants.TYPE_SHORT; - case 'Z': - return CodeConstants.TYPE_BOOLEAN; - case 'V': - return CodeConstants.TYPE_VOID; - case 'G': - return CodeConstants.TYPE_GROUP2EMPTY; - case 'N': - return CodeConstants.TYPE_NOTINITIALIZED; - case 'A': - return CodeConstants.TYPE_ADDRESS; - case 'X': - return CodeConstants.TYPE_BYTECHAR; - case 'Y': - return CodeConstants.TYPE_SHORTCHAR; - case 'U': - return CodeConstants.TYPE_UNKNOWN; - default: - throw new RuntimeException("Invalid type"); - } - } - - private String getChar(int type) { - switch(type) { - case CodeConstants.TYPE_BYTE: - return "B"; - case CodeConstants.TYPE_CHAR: - return "C"; - case CodeConstants.TYPE_DOUBLE: - return "D"; - case CodeConstants.TYPE_FLOAT: - return "F"; - case CodeConstants.TYPE_INT: - return "I"; - case CodeConstants.TYPE_LONG: - return "J"; - case CodeConstants.TYPE_SHORT: - return "S"; - case CodeConstants.TYPE_BOOLEAN: - return "Z"; - case CodeConstants.TYPE_VOID: - return "V"; - case CodeConstants.TYPE_GROUP2EMPTY: - return "G"; - case CodeConstants.TYPE_NOTINITIALIZED: - return "N"; - case CodeConstants.TYPE_ADDRESS: - return "A"; - case CodeConstants.TYPE_BYTECHAR: - return "X"; - case CodeConstants.TYPE_SHORTCHAR: - return "Y"; - case CodeConstants.TYPE_UNKNOWN: - return "U"; - case CodeConstants.TYPE_NULL: - case CodeConstants.TYPE_OBJECT: - return null; - default: - throw new RuntimeException("Invalid type"); - } - } - - public void setFamily() { - - if(arraydim > 0) { - this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; - return; - } - - switch(type) { - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_BYTECHAR: - case CodeConstants.TYPE_SHORTCHAR: - case CodeConstants.TYPE_CHAR: - case CodeConstants.TYPE_SHORT: - case CodeConstants.TYPE_INT: - this.type_family = CodeConstants.TYPE_FAMILY_INTEGER; - break; - case CodeConstants.TYPE_DOUBLE: - this.type_family = CodeConstants.TYPE_FAMILY_DOUBLE; - break; - case CodeConstants.TYPE_FLOAT: - this.type_family = CodeConstants.TYPE_FAMILY_FLOAT; - break; - case CodeConstants.TYPE_LONG: - this.type_family = CodeConstants.TYPE_FAMILY_LONG; - break; - case CodeConstants.TYPE_BOOLEAN: - this.type_family = CodeConstants.TYPE_FAMILY_BOOLEAN; - break; - case CodeConstants.TYPE_NULL: - case CodeConstants.TYPE_OBJECT: - this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; - break; - default: - this.type_family = CodeConstants.TYPE_FAMILY_UNKNOWN; - } - } - + + public static final int FALSEBOOLEAN = 1; + + public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN); + public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT); + public static final VarType VARTYPE_FLOAT = new VarType(CodeConstants.TYPE_FLOAT); + public static final VarType VARTYPE_LONG = new VarType(CodeConstants.TYPE_LONG); + public static final VarType VARTYPE_DOUBLE = new VarType(CodeConstants.TYPE_DOUBLE); + public static final VarType VARTYPE_BYTE = new VarType(CodeConstants.TYPE_BYTE); + public static final VarType VARTYPE_CHAR = new VarType(CodeConstants.TYPE_CHAR); + public static final VarType VARTYPE_SHORT = new VarType(CodeConstants.TYPE_SHORT); + public static final VarType VARTYPE_BOOLEAN = new VarType(CodeConstants.TYPE_BOOLEAN); + public static final VarType VARTYPE_BYTECHAR = new VarType(CodeConstants.TYPE_BYTECHAR); + public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR); + + public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL, 0, null); + public static final VarType VARTYPE_GROUP2EMPTY = new VarType(CodeConstants.TYPE_GROUP2EMPTY); + public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String"); + public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"); + public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Object"); + public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID); + + public int type; + + public int type_family; + + public int arraydim; + + public String value; + + public int stack_size; + + public int convinfo; + + public VarType(int type) { + this.type = type; + this.arraydim = 0; + + value = getChar(type); + setStackSize(type); + setFamily(); + } + + public VarType(int type, int arraydim) { + this(type); + this.arraydim = arraydim; + setFamily(); + } + + public VarType(int type, int arraydim, String value) { + this(type); + this.arraydim = arraydim; + this.value = value; + setFamily(); + } + + public VarType(String strtype) { + this(strtype, false); + } + + public VarType(String strtype, boolean cltype) { + parseTypeString(strtype, cltype); + setStackSize(type); + setFamily(); + } + + public void decArrayDim() { + if (arraydim > 0) { + arraydim--; + setFamily(); + } + else { + // throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case + } + } + + public String toString() { + String res = ""; + + for (int i = 0; i < arraydim; i++) { + res += "["; + } + + if (type == CodeConstants.TYPE_OBJECT) { + res += "L" + value + ";"; + } + else { + res += value; + } + + return res; + } + + public VarType copy() { + VarType v = new VarType(type, arraydim, value); + v.convinfo = convinfo; + return v; + } + + public boolean isFalseBoolean() { + return (convinfo & VarType.FALSEBOOLEAN) != 0; + } + + public boolean isSuperset(VarType val) { + + return this.equals(val) || this.isStrictSuperset(val); + } + + public boolean isStrictSuperset(VarType val) { + + int valtype = val.type; + + if (valtype == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) { + return true; + } + + if (val.arraydim > 0) { + return this.equals(VARTYPE_OBJECT); + } + else if (arraydim > 0) { + return (valtype == CodeConstants.TYPE_NULL); + } + + boolean res = false; + + switch (type) { + case CodeConstants.TYPE_INT: + res |= (valtype == CodeConstants.TYPE_SHORT || + valtype == CodeConstants.TYPE_CHAR); + case CodeConstants.TYPE_SHORT: + res |= (valtype == CodeConstants.TYPE_BYTE); + case CodeConstants.TYPE_CHAR: + res |= (valtype == CodeConstants.TYPE_SHORTCHAR); + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_SHORTCHAR: + res |= (valtype == CodeConstants.TYPE_BYTECHAR); + case CodeConstants.TYPE_BYTECHAR: + res |= (valtype == CodeConstants.TYPE_BOOLEAN); + break; + case CodeConstants.TYPE_OBJECT: + if (valtype == CodeConstants.TYPE_NULL) { + return true; + } + else if (this.equals(VARTYPE_OBJECT)) { + return valtype == CodeConstants.TYPE_OBJECT && + !val.equals(VARTYPE_OBJECT); + } + } + + return res; + } + + // type1 and type2 must not be null + public static VarType getCommonMinType(VarType type1, VarType type2) { + + if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type2 : type1; + } + + if (type1.isSuperset(type2)) { + return type2; + } + else if (type2.isSuperset(type1)) { + return type1; + } + else if (type1.type_family == type2.type_family) { + switch (type1.type_family) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if ((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT) + || (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) { + return VarType.VARTYPE_SHORTCHAR; + } + else { + return VarType.VARTYPE_BYTECHAR; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_NULL; + } + } + + return null; + } + + // type1 and type2 must not be null + public static VarType getCommonSupertype(VarType type1, VarType type2) { + + if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type1 : type2; + } + + if (type1.isSuperset(type2)) { + return type1; + } + else if (type2.isSuperset(type1)) { + return type2; + } + else if (type1.type_family == type2.type_family) { + switch (type1.type_family) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if ((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE) + || (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) { + return VarType.VARTYPE_SHORT; + } + else { + return VarType.VARTYPE_INT; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_OBJECT; + } + } + + return null; + } + + public static VarType getMinTypeInFamily(int family) { + switch (family) { + case CodeConstants.TYPE_FAMILY_BOOLEAN: + return VarType.VARTYPE_BOOLEAN; + case CodeConstants.TYPE_FAMILY_INTEGER: + return VarType.VARTYPE_BYTECHAR; + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_NULL; + case CodeConstants.TYPE_FAMILY_FLOAT: + return VarType.VARTYPE_FLOAT; + case CodeConstants.TYPE_FAMILY_LONG: + return VarType.VARTYPE_LONG; + case CodeConstants.TYPE_FAMILY_DOUBLE: + return VarType.VARTYPE_DOUBLE; + case CodeConstants.TYPE_FAMILY_UNKNOWN: + return VarType.VARTYPE_UNKNOWN; + default: + throw new RuntimeException("invalid type family!"); + } + } + + public boolean equals(Object o) { + + if (o == this) { + return true; + } + + if (o == null || !(o instanceof VarType)) { + return false; + } + + VarType vt = (VarType)o; + return type == vt.type && arraydim == vt.arraydim && InterpreterUtil.equalObjects(value, vt.value); + } + + private void parseTypeString(String strtype, boolean cltype) { + + for (int i = 0; i < strtype.length(); i++) { + switch (strtype.charAt(i)) { + case '[': + arraydim++; + break; + case 'L': + if (strtype.charAt(strtype.length() - 1) == ';') { + type = CodeConstants.TYPE_OBJECT; + value = strtype.substring(i + 1, strtype.length() - 1); + return; + } + default: + value = strtype.substring(i, strtype.length()); + if ((cltype && i == 0) || value.length() > 1) { + type = CodeConstants.TYPE_OBJECT; + } + else { + type = getType(value.charAt(0)); + } + return; + } + } + } + + private void setStackSize(int type) { + if (arraydim > 0) { + stack_size = 1; + } + else { + stack_size = (type == CodeConstants.TYPE_DOUBLE || + type == CodeConstants.TYPE_LONG) ? 2 : + ((type == CodeConstants.TYPE_VOID || + type == CodeConstants.TYPE_GROUP2EMPTY) ? 0 : 1); + } + } + + private int getType(char c) { + switch (c) { + case 'B': + return CodeConstants.TYPE_BYTE; + case 'C': + return CodeConstants.TYPE_CHAR; + case 'D': + return CodeConstants.TYPE_DOUBLE; + case 'F': + return CodeConstants.TYPE_FLOAT; + case 'I': + return CodeConstants.TYPE_INT; + case 'J': + return CodeConstants.TYPE_LONG; + case 'S': + return CodeConstants.TYPE_SHORT; + case 'Z': + return CodeConstants.TYPE_BOOLEAN; + case 'V': + return CodeConstants.TYPE_VOID; + case 'G': + return CodeConstants.TYPE_GROUP2EMPTY; + case 'N': + return CodeConstants.TYPE_NOTINITIALIZED; + case 'A': + return CodeConstants.TYPE_ADDRESS; + case 'X': + return CodeConstants.TYPE_BYTECHAR; + case 'Y': + return CodeConstants.TYPE_SHORTCHAR; + case 'U': + return CodeConstants.TYPE_UNKNOWN; + default: + throw new RuntimeException("Invalid type"); + } + } + + private String getChar(int type) { + switch (type) { + case CodeConstants.TYPE_BYTE: + return "B"; + case CodeConstants.TYPE_CHAR: + return "C"; + case CodeConstants.TYPE_DOUBLE: + return "D"; + case CodeConstants.TYPE_FLOAT: + return "F"; + case CodeConstants.TYPE_INT: + return "I"; + case CodeConstants.TYPE_LONG: + return "J"; + case CodeConstants.TYPE_SHORT: + return "S"; + case CodeConstants.TYPE_BOOLEAN: + return "Z"; + case CodeConstants.TYPE_VOID: + return "V"; + case CodeConstants.TYPE_GROUP2EMPTY: + return "G"; + case CodeConstants.TYPE_NOTINITIALIZED: + return "N"; + case CodeConstants.TYPE_ADDRESS: + return "A"; + case CodeConstants.TYPE_BYTECHAR: + return "X"; + case CodeConstants.TYPE_SHORTCHAR: + return "Y"; + case CodeConstants.TYPE_UNKNOWN: + return "U"; + case CodeConstants.TYPE_NULL: + case CodeConstants.TYPE_OBJECT: + return null; + default: + throw new RuntimeException("Invalid type"); + } + } + + public void setFamily() { + + if (arraydim > 0) { + this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; + return; + } + + switch (type) { + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_INT: + this.type_family = CodeConstants.TYPE_FAMILY_INTEGER; + break; + case CodeConstants.TYPE_DOUBLE: + this.type_family = CodeConstants.TYPE_FAMILY_DOUBLE; + break; + case CodeConstants.TYPE_FLOAT: + this.type_family = CodeConstants.TYPE_FAMILY_FLOAT; + break; + case CodeConstants.TYPE_LONG: + this.type_family = CodeConstants.TYPE_FAMILY_LONG; + break; + case CodeConstants.TYPE_BOOLEAN: + this.type_family = CodeConstants.TYPE_FAMILY_BOOLEAN; + break; + case CodeConstants.TYPE_NULL: + case CodeConstants.TYPE_OBJECT: + this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; + break; + default: + this.type_family = CodeConstants.TYPE_FAMILY_UNKNOWN; + } + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java index d4551ac..66fab96 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen.generics; import java.util.ArrayList; @@ -19,12 +20,11 @@ import java.util.List; public class GenericClassDescriptor { - public GenericType superclass; - - public List<GenericType> superinterfaces = new ArrayList<GenericType>(); - - public List<String> fparameters = new ArrayList<String>(); + public GenericType superclass; + + public List<GenericType> superinterfaces = new ArrayList<GenericType>(); + + public List<String> fparameters = new ArrayList<String>(); - public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); - + public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java index 4b4114d..598d17b 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java @@ -1,21 +1,21 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen.generics; public class GenericFieldDescriptor { - public GenericType type; - + public GenericType type; } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java index 50433a9..db7d84b 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java @@ -1,229 +1,233 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen.generics; -import java.util.ArrayList; -import java.util.List; - import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.struct.StructClass; +import java.util.ArrayList; +import java.util.List; + public class GenericMain { - - private static final String[] typeNames = new String[] { - "byte", - "char", - "double", - "float", - "int", - "long", - "short", - "boolean", - }; - - public static GenericClassDescriptor parseClassSignature(String signature) { - - GenericClassDescriptor descriptor = new GenericClassDescriptor(); - - signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); - - String supercl = GenericType.getNextType(signature); - descriptor.superclass = new GenericType(supercl); - - signature = signature.substring(supercl.length()); - while(signature.length() > 0) { - String superintr = GenericType.getNextType(signature); - descriptor.superinterfaces.add(new GenericType(superintr)); - signature = signature.substring(superintr.length()); - } - - return descriptor; - } - - public static GenericFieldDescriptor parseFieldSignature(String signature) { - GenericFieldDescriptor descriptor = new GenericFieldDescriptor(); - descriptor.type = new GenericType(signature); - return descriptor; - } - - public static GenericMethodDescriptor parseMethodSignature(String signature) { - - GenericMethodDescriptor descriptor = new GenericMethodDescriptor(); - - signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); - - int to = signature.indexOf(")"); - String pars = signature.substring(1, to); - signature = signature.substring(to+1); - - while(pars.length() > 0) { - String par = GenericType.getNextType(pars); - descriptor.params.add(new GenericType(par)); - pars = pars.substring(par.length()); - } - - String par = GenericType.getNextType(signature); - descriptor.ret = new GenericType(par); - signature = signature.substring(par.length()); - - if(signature.length() > 0) { - String[] excs = signature.split("\\^"); - - for(int i=1;i<excs.length;i++) { - descriptor.exceptions.add(new GenericType(excs[i])); - } - } - - return descriptor; - } - - private static String parseFormalParameters(String signature, List<String> fparameters, List<List<GenericType>> fbounds) { - - if(signature.charAt(0) != '<') { - return signature; - } - - int counter = 1; - int index = 1; - - loop: - while(index < signature.length()) { - switch(signature.charAt(index)) { - case '<': - counter++; - break; - case '>': - counter--; - if(counter == 0) { - break loop; - } - } - - index++; - } - - String value = signature.substring(1, index); - signature = signature.substring(index+1); - - while(value.length() > 0) { - int parto = value.indexOf(":"); - - String param = value.substring(0, parto); - value = value.substring(parto+1); - - List<GenericType> lstBounds = new ArrayList<GenericType>(); - - for(;;) { - if(value.charAt(0) == ':') { - // empty superclass, skip - value = value.substring(1); - } - - String bound = GenericType.getNextType(value); - lstBounds.add(new GenericType(bound)); - value = value.substring(bound.length()); - - - if(value.length() == 0 || value.charAt(0) != ':') { - break; - } else { - value = value.substring(1); - } - } - - fparameters.add(param); - fbounds.add(lstBounds); - } - - return signature; - } - - public static String getGenericCastTypeName(GenericType type) { - String s = getTypeName(type); - int dim = type.arraydim; - while(dim-->0) { - s+="[]"; - } - return s; - } - - public static String getTypeName(GenericType type) { - - int tp = type.type; - if(tp <= CodeConstants.TYPE_BOOLEAN) { - return typeNames[tp]; - } else if(tp == CodeConstants.TYPE_VOID) { - return "void"; - } else if(tp == CodeConstants.TYPE_GENVAR) { - return type.value; - } else if(tp == CodeConstants.TYPE_OBJECT) { - StringBuilder buffer = new StringBuilder(); - buffer.append(DecompilerContext.getImpcollector().getShortName(buildJavaClassName(type))); - - if(!type.getArguments().isEmpty()) { - buffer.append("<"); - for(int i=0;i<type.getArguments().size();i++) { - if(i>0) { - buffer.append(", "); - } - int wildcard = type.getWildcards().get(i); - if(wildcard != GenericType.WILDCARD_NO) { - buffer.append("?"); - - switch(wildcard){ - case GenericType.WILDCARD_EXTENDS: - buffer.append(" extends "); - break; - case GenericType.WILDCARD_SUPER: - buffer.append(" super "); - } - } - - GenericType genpar = type.getArguments().get(i); - if(genpar != null) { - buffer.append(GenericMain.getGenericCastTypeName(genpar)); - } - } - buffer.append(">"); - } - - return buffer.toString(); - } - - throw new RuntimeException("invalid type"); - } - - public static String buildJavaClassName(GenericType type) { - - String name = ""; - for(GenericType tp : type.getEnclosingClasses()) { - name += tp.value+"$"; - } - name+=type.value; - - String res = name.replace('/', '.'); - - if(res.indexOf("$") >=0) { - StructClass cl = DecompilerContext.getStructcontext().getClass(name); - if(cl == null || !cl.isOwn()) { - res = res.replace('$', '.'); - } - } - - return res; - } - + + private static final String[] typeNames = new String[]{ + "byte", + "char", + "double", + "float", + "int", + "long", + "short", + "boolean", + }; + + public static GenericClassDescriptor parseClassSignature(String signature) { + + GenericClassDescriptor descriptor = new GenericClassDescriptor(); + + signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); + + String supercl = GenericType.getNextType(signature); + descriptor.superclass = new GenericType(supercl); + + signature = signature.substring(supercl.length()); + while (signature.length() > 0) { + String superintr = GenericType.getNextType(signature); + descriptor.superinterfaces.add(new GenericType(superintr)); + signature = signature.substring(superintr.length()); + } + + return descriptor; + } + + public static GenericFieldDescriptor parseFieldSignature(String signature) { + GenericFieldDescriptor descriptor = new GenericFieldDescriptor(); + descriptor.type = new GenericType(signature); + return descriptor; + } + + public static GenericMethodDescriptor parseMethodSignature(String signature) { + + GenericMethodDescriptor descriptor = new GenericMethodDescriptor(); + + signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); + + int to = signature.indexOf(")"); + String pars = signature.substring(1, to); + signature = signature.substring(to + 1); + + while (pars.length() > 0) { + String par = GenericType.getNextType(pars); + descriptor.params.add(new GenericType(par)); + pars = pars.substring(par.length()); + } + + String par = GenericType.getNextType(signature); + descriptor.ret = new GenericType(par); + signature = signature.substring(par.length()); + + if (signature.length() > 0) { + String[] excs = signature.split("\\^"); + + for (int i = 1; i < excs.length; i++) { + descriptor.exceptions.add(new GenericType(excs[i])); + } + } + + return descriptor; + } + + private static String parseFormalParameters(String signature, List<String> fparameters, List<List<GenericType>> fbounds) { + + if (signature.charAt(0) != '<') { + return signature; + } + + int counter = 1; + int index = 1; + + loop: + while (index < signature.length()) { + switch (signature.charAt(index)) { + case '<': + counter++; + break; + case '>': + counter--; + if (counter == 0) { + break loop; + } + } + + index++; + } + + String value = signature.substring(1, index); + signature = signature.substring(index + 1); + + while (value.length() > 0) { + int parto = value.indexOf(":"); + + String param = value.substring(0, parto); + value = value.substring(parto + 1); + + List<GenericType> lstBounds = new ArrayList<GenericType>(); + + for (; ; ) { + if (value.charAt(0) == ':') { + // empty superclass, skip + value = value.substring(1); + } + + String bound = GenericType.getNextType(value); + lstBounds.add(new GenericType(bound)); + value = value.substring(bound.length()); + + + if (value.length() == 0 || value.charAt(0) != ':') { + break; + } + else { + value = value.substring(1); + } + } + + fparameters.add(param); + fbounds.add(lstBounds); + } + + return signature; + } + + public static String getGenericCastTypeName(GenericType type) { + String s = getTypeName(type); + int dim = type.arraydim; + while (dim-- > 0) { + s += "[]"; + } + return s; + } + + public static String getTypeName(GenericType type) { + + int tp = type.type; + if (tp <= CodeConstants.TYPE_BOOLEAN) { + return typeNames[tp]; + } + else if (tp == CodeConstants.TYPE_VOID) { + return "void"; + } + else if (tp == CodeConstants.TYPE_GENVAR) { + return type.value; + } + else if (tp == CodeConstants.TYPE_OBJECT) { + StringBuilder buffer = new StringBuilder(); + buffer.append(DecompilerContext.getImpcollector().getShortName(buildJavaClassName(type))); + + if (!type.getArguments().isEmpty()) { + buffer.append("<"); + for (int i = 0; i < type.getArguments().size(); i++) { + if (i > 0) { + buffer.append(", "); + } + int wildcard = type.getWildcards().get(i); + if (wildcard != GenericType.WILDCARD_NO) { + buffer.append("?"); + + switch (wildcard) { + case GenericType.WILDCARD_EXTENDS: + buffer.append(" extends "); + break; + case GenericType.WILDCARD_SUPER: + buffer.append(" super "); + } + } + + GenericType genpar = type.getArguments().get(i); + if (genpar != null) { + buffer.append(GenericMain.getGenericCastTypeName(genpar)); + } + } + buffer.append(">"); + } + + return buffer.toString(); + } + + throw new RuntimeException("invalid type"); + } + + public static String buildJavaClassName(GenericType type) { + + String name = ""; + for (GenericType tp : type.getEnclosingClasses()) { + name += tp.value + "$"; + } + name += type.value; + + String res = name.replace('/', '.'); + + if (res.indexOf("$") >= 0) { + StructClass cl = DecompilerContext.getStructcontext().getClass(name); + if (cl == null || !cl.isOwn()) { + res = res.replace('$', '.'); + } + } + + return res; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java index 7a0a4d1..7b4c9dc 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen.generics; import java.util.ArrayList; @@ -19,14 +20,13 @@ import java.util.List; public class GenericMethodDescriptor { - public List<String> fparameters = new ArrayList<String>(); + public List<String> fparameters = new ArrayList<String>(); + + public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); + + public List<GenericType> params = new ArrayList<GenericType>(); - public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); + public GenericType ret; - public List<GenericType> params = new ArrayList<GenericType>(); - - public GenericType ret; - - public List<GenericType> exceptions = new ArrayList<GenericType>(); - + public List<GenericType> exceptions = new ArrayList<GenericType>(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index 1f720e6..e2faa4a 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -1,270 +1,268 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.gen.generics; +import org.jetbrains.java.decompiler.code.CodeConstants; + import java.util.ArrayList; import java.util.List; -import org.jetbrains.java.decompiler.code.CodeConstants; - public class GenericType { - - public static final int WILDCARD_EXTENDS = 1; - public static final int WILDCARD_SUPER = 2; - public static final int WILDCARD_UNBOUND = 3; - public static final int WILDCARD_NO = 4; - - public int type; - - public int arraydim; - - public String value; - - - private List<GenericType> enclosingClasses = new ArrayList<GenericType>(); - - private List<GenericType> arguments = new ArrayList<GenericType>(); - - private List<Integer> wildcards = new ArrayList<Integer>(); - - - public GenericType(int type, int arraydim, String value) { - this.type = type; - this.arraydim = arraydim; - this.value = value; - } - - - public GenericType(String strtype) { - - parseSignature(strtype); - - } - - private void parseSignature(String sig) { - - int index = 0; - while(index < sig.length()) { - - switch(sig.charAt(index)){ - case '[': - arraydim++; - break; - case 'T': - type = CodeConstants.TYPE_GENVAR; - value = sig.substring(index+1, sig.length()-1); - return; - case 'L': - type = CodeConstants.TYPE_OBJECT; - sig = sig.substring(index+1, sig.length()-1); - - for(;;) { - String cl = getNextClassSignature(sig); - - String name = cl; - String args = null; - - int argfrom = cl.indexOf("<"); - if(argfrom >= 0) { - name = cl.substring(0, argfrom); - args = cl.substring(argfrom+1, cl.length()-1); - } - - if(cl.length() < sig.length()) { - sig = sig.substring(cl.length()+1); // skip '.' - GenericType type = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); - parseArgumentsList(args, type); - enclosingClasses.add(type); - } else { - value = name; - parseArgumentsList(args, this); - break; - } - } - - return; - default: - value = sig.substring(index, index+1); - type = getType(value.charAt(0)); - } - - index++; - } - - } - - private String getNextClassSignature(String value) { - - int counter = 0; - int index = 0; - - loop: - while(index < value.length()) { - switch(value.charAt(index)) { - case '<': - counter++; - break; - case '>': - counter--; - break; - case '.': - if(counter == 0) { - break loop; - } - } - - index++; - } - - return value.substring(0, index); - } - - private void parseArgumentsList(String value, GenericType type) { - - if(value == null) { - return; - } - - while(value.length() > 0) { - - String tstr = getNextType(value); - int len = tstr.length(); - int wildcard = WILDCARD_NO; - - switch(tstr.charAt(0)) { - case '*': - wildcard = WILDCARD_UNBOUND; - break; - case '+': - wildcard = WILDCARD_EXTENDS; - break; - case '-': - wildcard = WILDCARD_SUPER; - break; - } - - type.getWildcards().add(wildcard); - - if(wildcard != WILDCARD_NO) { - tstr = tstr.substring(1); - } - - type.getArguments().add(tstr.length() == 0?null:new GenericType(tstr)); - - value = value.substring(len); - } - - } - - public static String getNextType(String value) { - - int counter = 0; - int index = 0; - - boolean contmode = false; - - loop: - while(index < value.length()) { - switch(value.charAt(index)) { - case '*': - if(!contmode) { - break loop; - } - break; - case 'L': - case 'T': - if(!contmode) { - contmode = true; - } - case '[': - case '+': - case '-': - break; - default: - if(!contmode) { - break loop; - } - break; - case '<': - counter++; - break; - case '>': - counter--; - break; - case ';': - if(counter == 0) { - break loop; - } - } - - index++; - } - - return value.substring(0, index+1); - } - - private int getType(char c) { - switch(c) { - case 'B': - return CodeConstants.TYPE_BYTE; - case 'C': - return CodeConstants.TYPE_CHAR; - case 'D': - return CodeConstants.TYPE_DOUBLE; - case 'F': - return CodeConstants.TYPE_FLOAT; - case 'I': - return CodeConstants.TYPE_INT; - case 'J': - return CodeConstants.TYPE_LONG; - case 'S': - return CodeConstants.TYPE_SHORT; - case 'Z': - return CodeConstants.TYPE_BOOLEAN; - case 'V': - return CodeConstants.TYPE_VOID; - case 'G': - return CodeConstants.TYPE_GROUP2EMPTY; - case 'N': - return CodeConstants.TYPE_NOTINITIALIZED; - case 'A': - return CodeConstants.TYPE_ADDRESS; - case 'X': - return CodeConstants.TYPE_BYTECHAR; - case 'Y': - return CodeConstants.TYPE_SHORTCHAR; - case 'U': - return CodeConstants.TYPE_UNKNOWN; - default: - throw new RuntimeException("Invalid type"); - } - } - - - public List<GenericType> getArguments() { - return arguments; - } - - - public List<GenericType> getEnclosingClasses() { - return enclosingClasses; - } - - - public List<Integer> getWildcards() { - return wildcards; - } - + + public static final int WILDCARD_EXTENDS = 1; + public static final int WILDCARD_SUPER = 2; + public static final int WILDCARD_UNBOUND = 3; + public static final int WILDCARD_NO = 4; + + public int type; + + public int arraydim; + + public String value; + + + private List<GenericType> enclosingClasses = new ArrayList<GenericType>(); + + private List<GenericType> arguments = new ArrayList<GenericType>(); + + private List<Integer> wildcards = new ArrayList<Integer>(); + + + public GenericType(int type, int arraydim, String value) { + this.type = type; + this.arraydim = arraydim; + this.value = value; + } + + + public GenericType(String strtype) { + + parseSignature(strtype); + } + + private void parseSignature(String sig) { + + int index = 0; + while (index < sig.length()) { + + switch (sig.charAt(index)) { + case '[': + arraydim++; + break; + case 'T': + type = CodeConstants.TYPE_GENVAR; + value = sig.substring(index + 1, sig.length() - 1); + return; + case 'L': + type = CodeConstants.TYPE_OBJECT; + sig = sig.substring(index + 1, sig.length() - 1); + + for (; ; ) { + String cl = getNextClassSignature(sig); + + String name = cl; + String args = null; + + int argfrom = cl.indexOf("<"); + if (argfrom >= 0) { + name = cl.substring(0, argfrom); + args = cl.substring(argfrom + 1, cl.length() - 1); + } + + if (cl.length() < sig.length()) { + sig = sig.substring(cl.length() + 1); // skip '.' + GenericType type = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); + parseArgumentsList(args, type); + enclosingClasses.add(type); + } + else { + value = name; + parseArgumentsList(args, this); + break; + } + } + + return; + default: + value = sig.substring(index, index + 1); + type = getType(value.charAt(0)); + } + + index++; + } + } + + private String getNextClassSignature(String value) { + + int counter = 0; + int index = 0; + + loop: + while (index < value.length()) { + switch (value.charAt(index)) { + case '<': + counter++; + break; + case '>': + counter--; + break; + case '.': + if (counter == 0) { + break loop; + } + } + + index++; + } + + return value.substring(0, index); + } + + private void parseArgumentsList(String value, GenericType type) { + + if (value == null) { + return; + } + + while (value.length() > 0) { + + String tstr = getNextType(value); + int len = tstr.length(); + int wildcard = WILDCARD_NO; + + switch (tstr.charAt(0)) { + case '*': + wildcard = WILDCARD_UNBOUND; + break; + case '+': + wildcard = WILDCARD_EXTENDS; + break; + case '-': + wildcard = WILDCARD_SUPER; + break; + } + + type.getWildcards().add(wildcard); + + if (wildcard != WILDCARD_NO) { + tstr = tstr.substring(1); + } + + type.getArguments().add(tstr.length() == 0 ? null : new GenericType(tstr)); + + value = value.substring(len); + } + } + + public static String getNextType(String value) { + + int counter = 0; + int index = 0; + + boolean contmode = false; + + loop: + while (index < value.length()) { + switch (value.charAt(index)) { + case '*': + if (!contmode) { + break loop; + } + break; + case 'L': + case 'T': + if (!contmode) { + contmode = true; + } + case '[': + case '+': + case '-': + break; + default: + if (!contmode) { + break loop; + } + break; + case '<': + counter++; + break; + case '>': + counter--; + break; + case ';': + if (counter == 0) { + break loop; + } + } + + index++; + } + + return value.substring(0, index + 1); + } + + private int getType(char c) { + switch (c) { + case 'B': + return CodeConstants.TYPE_BYTE; + case 'C': + return CodeConstants.TYPE_CHAR; + case 'D': + return CodeConstants.TYPE_DOUBLE; + case 'F': + return CodeConstants.TYPE_FLOAT; + case 'I': + return CodeConstants.TYPE_INT; + case 'J': + return CodeConstants.TYPE_LONG; + case 'S': + return CodeConstants.TYPE_SHORT; + case 'Z': + return CodeConstants.TYPE_BOOLEAN; + case 'V': + return CodeConstants.TYPE_VOID; + case 'G': + return CodeConstants.TYPE_GROUP2EMPTY; + case 'N': + return CodeConstants.TYPE_NOTINITIALIZED; + case 'A': + return CodeConstants.TYPE_ADDRESS; + case 'X': + return CodeConstants.TYPE_BYTECHAR; + case 'Y': + return CodeConstants.TYPE_SHORTCHAR; + case 'U': + return CodeConstants.TYPE_UNKNOWN; + default: + throw new RuntimeException("Invalid type"); + } + } + + + public List<GenericType> getArguments() { + return arguments; + } + + + public List<GenericType> getEnclosingClasses() { + return enclosingClasses; + } + + + public List<Integer> getWildcards() { + return wildcards; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index 94cef9a..46a334e 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -1,186 +1,186 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.struct.lazy; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; - import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + public class LazyLoader { - private HashMap<String, Link> mapClassLinks = new HashMap<String, Link>(); - - private IBytecodeProvider provider; - - public LazyLoader(IBytecodeProvider provider) { - this.provider = provider; - } - - public void addClassLink(String classname, Link link) { - mapClassLinks.put(classname, link); - } - - public void removeClassLink(String classname) { - mapClassLinks.remove(classname); - } - - public Link getClassLink(String classname) { - return mapClassLinks.get(classname); - } - - - public ConstantPool loadPool(String classname) { - - try { - - DataInputFullStream in = getClassStream(classname); - if(in == null) { - return null; - } - - in.skip(8); - - return new ConstantPool(in); - - } catch(IOException ex) { - throw new RuntimeException(ex); - } - } - - public byte[] loadBytecode(StructMethod mt, int code_fulllength) { - - try { - - DataInputFullStream in = getClassStream(mt.getClassStruct().qualifiedName); - if(in == null) { - return null; - } - - byte[] res = null; - - in.skip(8); - - ConstantPool pool = mt.getClassStruct().getPool(); - if(pool == null) { - pool = new ConstantPool(in); - } else { - ConstantPool.skipPool(in); - } - - in.skip(2); - int this_class = in.readUnsignedShort(); - in.skip(2); - - // interfaces - in.skip(in.readUnsignedShort() * 2); - - // fields - int size = in.readUnsignedShort(); - for (int i = 0; i < size; i++) { - in.skip(6); - skipAttributes(in); - } - - // methods - size = in.readUnsignedShort(); - for (int i = 0; i < size; i++) { - in.skip(2); - - int name_index = in.readUnsignedShort(); - int descriptor_index = in.readUnsignedShort(); - - String elem_arr[] = pool.getClassElement(ConstantPool.METHOD, this_class, name_index, descriptor_index); - String name = elem_arr[0]; - - if(mt.getName().equals(name)) { - String descriptor = elem_arr[1]; - if(mt.getDescriptor().equals(descriptor)) { - - int len = in.readUnsignedShort(); - for(int j=0;j<len;j++) { - - int attr_nameindex = in.readUnsignedShort(); - String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); - - if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { - in.skip(12); - - res = new byte[code_fulllength]; - in.readFull(res); - return res; - } else { - in.skip(in.readInt()); - } - } - - return null; - } - } - - skipAttributes(in); - } - - return null; - - } catch(IOException ex) { - throw new RuntimeException(ex); - } - - } - - public DataInputFullStream getClassStream(String externPath, String internPath) throws IOException { - InputStream instream = provider.getBytecodeStream(externPath, internPath); - return instream == null?null:new DataInputFullStream(instream); - } - - public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException { - Link link = mapClassLinks.get(qualifiedClassName); - return link == null?null:getClassStream(link.externPath, link.internPath); - } - - private void skipAttributes(DataInputFullStream in) throws IOException { - - int length = in.readUnsignedShort(); - for (int i = 0; i < length; i++) { - in.skip(2); - in.skip(in.readInt()); - } - - } - - - public static class Link { - - public static final int CLASS = 1; - public static final int ENTRY = 2; - - public int type; - public String externPath; - public String internPath; - - public Link(int type, String externPath, String internPath) { - this.type = type; - this.externPath = externPath; - this.internPath = internPath; - } - } - + private HashMap<String, Link> mapClassLinks = new HashMap<String, Link>(); + + private IBytecodeProvider provider; + + public LazyLoader(IBytecodeProvider provider) { + this.provider = provider; + } + + public void addClassLink(String classname, Link link) { + mapClassLinks.put(classname, link); + } + + public void removeClassLink(String classname) { + mapClassLinks.remove(classname); + } + + public Link getClassLink(String classname) { + return mapClassLinks.get(classname); + } + + + public ConstantPool loadPool(String classname) { + + try { + + DataInputFullStream in = getClassStream(classname); + if (in == null) { + return null; + } + + in.skip(8); + + return new ConstantPool(in); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public byte[] loadBytecode(StructMethod mt, int code_fulllength) { + + try { + + DataInputFullStream in = getClassStream(mt.getClassStruct().qualifiedName); + if (in == null) { + return null; + } + + byte[] res = null; + + in.skip(8); + + ConstantPool pool = mt.getClassStruct().getPool(); + if (pool == null) { + pool = new ConstantPool(in); + } + else { + ConstantPool.skipPool(in); + } + + in.skip(2); + int this_class = in.readUnsignedShort(); + in.skip(2); + + // interfaces + in.skip(in.readUnsignedShort() * 2); + + // fields + int size = in.readUnsignedShort(); + for (int i = 0; i < size; i++) { + in.skip(6); + skipAttributes(in); + } + + // methods + size = in.readUnsignedShort(); + for (int i = 0; i < size; i++) { + in.skip(2); + + int name_index = in.readUnsignedShort(); + int descriptor_index = in.readUnsignedShort(); + + String elem_arr[] = pool.getClassElement(ConstantPool.METHOD, this_class, name_index, descriptor_index); + String name = elem_arr[0]; + + if (mt.getName().equals(name)) { + String descriptor = elem_arr[1]; + if (mt.getDescriptor().equals(descriptor)) { + + int len = in.readUnsignedShort(); + for (int j = 0; j < len; j++) { + + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + if (StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { + in.skip(12); + + res = new byte[code_fulllength]; + in.readFull(res); + return res; + } + else { + in.skip(in.readInt()); + } + } + + return null; + } + } + + skipAttributes(in); + } + + return null; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public DataInputFullStream getClassStream(String externPath, String internPath) throws IOException { + InputStream instream = provider.getBytecodeStream(externPath, internPath); + return instream == null ? null : new DataInputFullStream(instream); + } + + public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException { + Link link = mapClassLinks.get(qualifiedClassName); + return link == null ? null : getClassStream(link.externPath, link.internPath); + } + + private void skipAttributes(DataInputFullStream in) throws IOException { + + int length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + in.skip(2); + in.skip(in.readInt()); + } + } + + + public static class Link { + + public static final int CLASS = 1; + public static final int ENTRY = 2; + + public int type; + public String externPath; + public String internPath; + + public Link(int type, String externPath, String internPath) { + this.type = type; + this.externPath = externPath; + this.internPath = internPath; + } + } } diff --git a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java index 94b68b5..a13b27c 100644 --- a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java +++ b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; import java.io.DataInputStream; @@ -20,31 +21,30 @@ import java.io.InputStream; public class DataInputFullStream extends DataInputStream { - public DataInputFullStream(InputStream in) { - super(in); + public DataInputFullStream(InputStream in) { + super(in); + } + + public final int readFull(byte b[]) throws IOException { + + int length = b.length; + byte[] btemp = new byte[length]; + int pos = 0; + + int bytes_read = -1; + for (; ; ) { + bytes_read = read(btemp, 0, length - pos); + if (bytes_read == -1) { + return -1; + } + + System.arraycopy(btemp, 0, b, pos, bytes_read); + pos += bytes_read; + if (pos == length) { + break; + } } - - public final int readFull(byte b[]) throws IOException { - - int length = b.length; - byte[] btemp = new byte[length]; - int pos = 0; - - int bytes_read = -1; - for(;;) { - bytes_read = read(btemp, 0, length-pos); - if(bytes_read==-1) { - return -1; - } - - System.arraycopy(btemp, 0, b, pos, bytes_read); - pos+=bytes_read; - if(pos == length) { - break; - } - } - - return length; - } - + + return length; + } } diff --git a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java index 524f50d..a26b77e 100644 --- a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java @@ -1,363 +1,360 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import java.util.*; public class FastFixedSetFactory<E> { - private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); - - private int dataLength; - - public FastFixedSetFactory(Collection<E> set) { - - dataLength = set.size() / 32 + 1; - - int index = 0; - int mask = 1; - - for(E element : set) { - - int block = index / 32; - - if(index % 32 == 0) { - mask = 1; - } - - colValuesInternal.putWithKey(new int[] {block, mask}, element); - - index++; - mask <<= 1; - } - } - - public FastFixedSet<E> spawnEmptySet() { - return new FastFixedSet<E>(this); - } - - private int getDataLength() { - return dataLength; - } - - private VBStyleCollection<int[], E> getInternalValuesCollection() { - return colValuesInternal; - } - - public static class FastFixedSet<E> implements Iterable<E> { - - private FastFixedSetFactory<E> factory; - - private VBStyleCollection<int[], E> colValuesInternal; - - private int[] data; - - - private FastFixedSet(FastFixedSetFactory<E> factory) { - this.factory = factory; - this.colValuesInternal = factory.getInternalValuesCollection(); - this.data = new int[factory.getDataLength()]; - } - - public FastFixedSet<E> getCopy() { - - FastFixedSet<E> copy = new FastFixedSet<E>(factory); - - int arrlength = data.length; - int[] cpdata = new int[arrlength]; - System.arraycopy(data, 0, cpdata, 0, arrlength); - copy.setData(cpdata); - - return copy; - } - - public void setAllElements() { - - int[] lastindex = colValuesInternal.get(colValuesInternal.size()-1); - - for(int i=lastindex[0]-1;i>=0;i--) { - data[i] = 0xFFFFFFFF; - } - - data[lastindex[0]] = lastindex[1] | (lastindex[1]-1); - } - - public void add(E element) { - int[] index = colValuesInternal.getWithKey(element); - data[index[0]] |= index[1]; - } - - public void addAll(Collection<E> set) { - for(E element : set) { - add(element); - } - } - - public void remove(E element) { - int[] index = colValuesInternal.getWithKey(element); - data[index[0]] &= ~index[1]; - } - - public void removeAll(Collection<E> set) { - for(E element : set) { - remove(element); - } - } - - public boolean contains(E element) { - int[] index = colValuesInternal.getWithKey(element); - return (data[index[0]] & index[1]) != 0; - } - - public boolean contains(FastFixedSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - if((extdata[i] & ~intdata[i]) != 0) { - return false; - } - } - - return true; - } - - public void union(FastFixedSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - intdata[i] |= extdata[i]; - } - } - - public void intersection(FastFixedSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - intdata[i] &= extdata[i]; - } - } - - public void symdiff(FastFixedSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - intdata[i] ^= extdata[i]; - } - } - - public void complement(FastFixedSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - intdata[i] &= ~extdata[i]; - } - } - - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FastFixedSet)) return false; - - int[] extdata = ((FastFixedSet)o).getData(); - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - if(intdata[i] != extdata[i]) { - return false; - } - } - - return true; - } - - public boolean isEmpty() { - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - if(intdata[i] != 0) { - return false; - } - } - - return true; - } - - public Iterator<E> iterator() { - return new FastFixedSetIterator<E>(this); - } - - public Set<E> toPlainSet() { - return toPlainCollection(new HashSet<E>()); - } - - public List<E> toPlainList() { - return toPlainCollection(new ArrayList<E>()); - } - - - private <T extends Collection<E>> T toPlainCollection(T cl) { - - int[] intdata = data; - for(int bindex=0; bindex < intdata.length; bindex++) { - int block = intdata[bindex]; - if(block != 0) { - int index = bindex << 5; // * 32 - for(int i = 31; i>=0; i--){ - if((block & 1) != 0) { - cl.add(colValuesInternal.getKey(index)); - } - index++; - block >>>= 1; - } - } - } - - return cl; - } - - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for(int i=0;i<intdata.length;i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - - public String toString() { - - StringBuilder buffer = new StringBuilder("{"); - - int[] intdata = data; - boolean first = true; - - for(int i=colValuesInternal.size()-1;i>=0;i--) { - int[] index = colValuesInternal.get(i); - - if((intdata[index[0]] & index[1]) != 0) { - if(first) { - first = false; - } else { - buffer.append(","); - } - buffer.append(colValuesInternal.getKey(i)); - } - } - - buffer.append("}"); - - return buffer.toString(); - } - - private int[] getData() { - return data; - } - - private void setData(int[] data) { - this.data = data; - } - - public FastFixedSetFactory<E> getFactory() { - return factory; - } - } - - public static class FastFixedSetIterator<E> implements Iterator<E> { - - private VBStyleCollection<int[], E> colValuesInternal; - private int[] data; - private int size; - - private int pointer = -1; - private int next_pointer = -1; - - private FastFixedSetIterator(FastFixedSet<E> set) { - colValuesInternal = set.getFactory().getInternalValuesCollection(); - data = set.getData(); - size = colValuesInternal.size(); - } - - private int getNextIndex(int index) { - - index++; - int ret = index; - int bindex = index / 32; - int dindex = index % 32; - - while(bindex < data.length) { - int block = data[bindex]; - - if(block != 0) { - block >>>= dindex; - while(dindex < 32) { - if((block & 1) != 0) { - return ret; - } - block >>>= 1; - dindex++; - ret++; - } - } else { - ret += (32 - dindex); - } - - dindex = 0; - bindex++; - } - - return -1; - } - - public boolean hasNext() { - next_pointer = getNextIndex(pointer); - return (next_pointer >= 0); - } - - public E next() { - if(next_pointer >= 0) { - pointer = next_pointer; - } else { - pointer = getNextIndex(pointer); - if(pointer == -1) { - pointer = size; - } - } - - next_pointer = -1; - return pointer<size?colValuesInternal.getKey(pointer):null; - } - - public void remove() { - int[] index = colValuesInternal.get(pointer); - data[index[0]] &= ~index[1]; - } - - } - + private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); + + private int dataLength; + + public FastFixedSetFactory(Collection<E> set) { + + dataLength = set.size() / 32 + 1; + + int index = 0; + int mask = 1; + + for (E element : set) { + + int block = index / 32; + + if (index % 32 == 0) { + mask = 1; + } + + colValuesInternal.putWithKey(new int[]{block, mask}, element); + + index++; + mask <<= 1; + } + } + + public FastFixedSet<E> spawnEmptySet() { + return new FastFixedSet<E>(this); + } + + private int getDataLength() { + return dataLength; + } + + private VBStyleCollection<int[], E> getInternalValuesCollection() { + return colValuesInternal; + } + + public static class FastFixedSet<E> implements Iterable<E> { + + private FastFixedSetFactory<E> factory; + + private VBStyleCollection<int[], E> colValuesInternal; + + private int[] data; + + + private FastFixedSet(FastFixedSetFactory<E> factory) { + this.factory = factory; + this.colValuesInternal = factory.getInternalValuesCollection(); + this.data = new int[factory.getDataLength()]; + } + + public FastFixedSet<E> getCopy() { + + FastFixedSet<E> copy = new FastFixedSet<E>(factory); + + int arrlength = data.length; + int[] cpdata = new int[arrlength]; + System.arraycopy(data, 0, cpdata, 0, arrlength); + copy.setData(cpdata); + + return copy; + } + + public void setAllElements() { + + int[] lastindex = colValuesInternal.get(colValuesInternal.size() - 1); + + for (int i = lastindex[0] - 1; i >= 0; i--) { + data[i] = 0xFFFFFFFF; + } + + data[lastindex[0]] = lastindex[1] | (lastindex[1] - 1); + } + + public void add(E element) { + int[] index = colValuesInternal.getWithKey(element); + data[index[0]] |= index[1]; + } + + public void addAll(Collection<E> set) { + for (E element : set) { + add(element); + } + } + + public void remove(E element) { + int[] index = colValuesInternal.getWithKey(element); + data[index[0]] &= ~index[1]; + } + + public void removeAll(Collection<E> set) { + for (E element : set) { + remove(element); + } + } + + public boolean contains(E element) { + int[] index = colValuesInternal.getWithKey(element); + return (data[index[0]] & index[1]) != 0; + } + + public boolean contains(FastFixedSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + if ((extdata[i] & ~intdata[i]) != 0) { + return false; + } + } + + return true; + } + + public void union(FastFixedSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + intdata[i] |= extdata[i]; + } + } + + public void intersection(FastFixedSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + intdata[i] &= extdata[i]; + } + } + + public void symdiff(FastFixedSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + intdata[i] ^= extdata[i]; + } + } + + public void complement(FastFixedSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + intdata[i] &= ~extdata[i]; + } + } + + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FastFixedSet)) return false; + + int[] extdata = ((FastFixedSet)o).getData(); + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + if (intdata[i] != extdata[i]) { + return false; + } + } + + return true; + } + + public boolean isEmpty() { + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + if (intdata[i] != 0) { + return false; + } + } + + return true; + } + + public Iterator<E> iterator() { + return new FastFixedSetIterator<E>(this); + } + + public Set<E> toPlainSet() { + return toPlainCollection(new HashSet<E>()); + } + + public List<E> toPlainList() { + return toPlainCollection(new ArrayList<E>()); + } + + + private <T extends Collection<E>> T toPlainCollection(T cl) { + + int[] intdata = data; + for (int bindex = 0; bindex < intdata.length; bindex++) { + int block = intdata[bindex]; + if (block != 0) { + int index = bindex << 5; // * 32 + for (int i = 31; i >= 0; i--) { + if ((block & 1) != 0) { + cl.add(colValuesInternal.getKey(index)); + } + index++; + block >>>= 1; + } + } + } + + return cl; + } + + public String toBinary() { + + StringBuilder buffer = new StringBuilder(); + int[] intdata = data; + + for (int i = 0; i < intdata.length; i++) { + buffer.append(" ").append(Integer.toBinaryString(intdata[i])); + } + + return buffer.toString(); + } + + public String toString() { + + StringBuilder buffer = new StringBuilder("{"); + + int[] intdata = data; + boolean first = true; + + for (int i = colValuesInternal.size() - 1; i >= 0; i--) { + int[] index = colValuesInternal.get(i); + + if ((intdata[index[0]] & index[1]) != 0) { + if (first) { + first = false; + } + else { + buffer.append(","); + } + buffer.append(colValuesInternal.getKey(i)); + } + } + + buffer.append("}"); + + return buffer.toString(); + } + + private int[] getData() { + return data; + } + + private void setData(int[] data) { + this.data = data; + } + + public FastFixedSetFactory<E> getFactory() { + return factory; + } + } + + public static class FastFixedSetIterator<E> implements Iterator<E> { + + private VBStyleCollection<int[], E> colValuesInternal; + private int[] data; + private int size; + + private int pointer = -1; + private int next_pointer = -1; + + private FastFixedSetIterator(FastFixedSet<E> set) { + colValuesInternal = set.getFactory().getInternalValuesCollection(); + data = set.getData(); + size = colValuesInternal.size(); + } + + private int getNextIndex(int index) { + + index++; + int ret = index; + int bindex = index / 32; + int dindex = index % 32; + + while (bindex < data.length) { + int block = data[bindex]; + + if (block != 0) { + block >>>= dindex; + while (dindex < 32) { + if ((block & 1) != 0) { + return ret; + } + block >>>= 1; + dindex++; + ret++; + } + } + else { + ret += (32 - dindex); + } + + dindex = 0; + bindex++; + } + + return -1; + } + + public boolean hasNext() { + next_pointer = getNextIndex(pointer); + return (next_pointer >= 0); + } + + public E next() { + if (next_pointer >= 0) { + pointer = next_pointer; + } + else { + pointer = getNextIndex(pointer); + if (pointer == -1) { + pointer = size; + } + } + + next_pointer = -1; + return pointer < size ? colValuesInternal.getKey(pointer) : null; + } + + public void remove() { + int[] index = colValuesInternal.get(pointer); + data[index[0]] &= ~index[1]; + } + } } diff --git a/src/org/jetbrains/java/decompiler/util/FastSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSetFactory.java index 4e5a631..e9cce03 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSetFactory.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; import java.util.Collection; @@ -21,466 +22,468 @@ import java.util.Set; public class FastSetFactory<E> { - private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); - - private int lastBlock; - - private int lastMask; - - public FastSetFactory(Collection<E> set) { - - int block = -1; - int mask = -1; - int index = 0; - - for(E element : set) { - - block = index / 32; - - if(index % 32 == 0) { - mask = 1; - } else { - mask <<= 1; - } - - colValuesInternal.putWithKey(new int[] {block, mask}, element); - - index++; - } - - lastBlock = block; - lastMask = mask; - } - - private int[] addElement(E element) { - - if(lastMask == -1 || lastMask == 0x80000000) { - lastMask = 1; - lastBlock++; - } else { - lastMask <<= 1; - } - - int[] pointer = new int[] {lastBlock, lastMask}; - colValuesInternal.putWithKey(pointer, element); - - return pointer; - } - - public FastSet<E> spawnEmptySet() { - return new FastSet<E>(this); - } - - public int getLastBlock() { - return lastBlock; - } - - public int getLastMask() { - return lastMask; - } - - private VBStyleCollection<int[], E> getInternalValuesCollection() { - return colValuesInternal; - } - - - public static class FastSet<E> implements Iterable<E> { - - private FastSetFactory<E> factory; - - private VBStyleCollection<int[], E> colValuesInternal; - - private int[] data; - - private FastSet(FastSetFactory<E> factory) { - this.factory = factory; - this.colValuesInternal = factory.getInternalValuesCollection(); - this.data = new int[factory.getLastBlock()+1]; - } - - public FastSet<E> getCopy() { - - FastSet<E> copy = new FastSet<E>(factory); - - int arrlength = data.length; - int[] cpdata = new int[arrlength]; - - System.arraycopy(data, 0, cpdata, 0, arrlength); - copy.setData(cpdata); - - return copy; - } - - private int[] ensureCapacity(int index) { - - int newlength = data.length; - if(newlength == 0) { - newlength = 1; - } - - while(newlength <= index) { - newlength *=2; - } - - int[] newdata = new int[newlength]; - System.arraycopy(data, 0, newdata, 0, data.length); - - return data = newdata; - } - - public void add(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - if(index[0] >= data.length) { - ensureCapacity(index[0]); - } - - data[index[0]] |= index[1]; - } - - public void setAllElements() { - - int lastblock = factory.getLastBlock(); - int lastmask = factory.getLastMask(); - - if(lastblock >= data.length) { - ensureCapacity(lastblock); - } - - for(int i=lastblock-1;i>=0;i--) { - data[i] = 0xFFFFFFFF; - } - - data[lastblock] = lastmask | (lastmask-1); - } - - public void addAll(Set<E> set) { - for(E element : set) { - add(element); - } - } - - public void remove(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - if(index[0] < data.length) { - data[index[0]] &= ~index[1]; - } - } - - public void removeAll(Set<E> set) { - for(E element : set) { - remove(element); - } - } - - public boolean contains(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - return index[0] >= data.length?false:((data[index[0]] & index[1]) != 0); - } - - public boolean contains(FastSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - if((extdata[i] & ~intdata[i]) != 0) { - return false; - } - } - - for(int i=extdata.length-1;i>=minlength;i--) { - if(extdata[i] != 0) { - return false; - } - } - - return true; - } - - public void union(FastSet<E> set) { - - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] |= extdata[i]; - } - - boolean expanded = false; - for(int i=extdata.length-1;i>=minlength;i--) { - if(extdata[i] != 0) { - if(!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - } - - public void intersection(FastSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] &= extdata[i]; - } - - for(int i=intdata.length-1;i>=minlength;i--) { - intdata[i] = 0; - } - - } - - public void symdiff(FastSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] ^= extdata[i]; - } - - boolean expanded = false; - for(int i=extdata.length-1;i>=minlength;i--) { - if(extdata[i] != 0) { - if(!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - } - - public void complement(FastSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] &= ~extdata[i]; - } - } - - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FastSet)) return false; - - int[] longdata = ((FastSet)o).getData(); - int[] shortdata = data; - - if(data.length > longdata.length) { - shortdata = longdata; - longdata = data; - } - - for(int i=shortdata.length-1;i>=0;i--) { - if(shortdata[i] != longdata[i]) { - return false; - } - } - - for(int i=longdata.length-1;i>=shortdata.length;i--) { - if(longdata[i] != 0) { - return false; - } - } - - return true; - } - - public int getCardinality() { - - boolean found = false; - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - int block = intdata[i]; - if(block != 0) { - if(found) { - return 2; - } else { - if ((block & (block-1)) == 0) { - found = true; - } else { - return 2; - } - } - } - } - - return found?1:0; - } - - public int size() { - - int size = 0; - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - size+=Integer.bitCount(intdata[i]); - } - - return size; - } - - public boolean isEmpty() { - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - if(intdata[i] != 0) { - return false; - } - } - - return true; - } - - public Iterator<E> iterator() { - return new FastSetIterator<E>(this); - } - - public Set<E> toPlainSet() { - HashSet<E> set = new HashSet<E>(); - - int[] intdata = data; - - int size = data.length*32; - if(size > colValuesInternal.size()) { - size = colValuesInternal.size(); - } - - for(int i=size-1;i>=0;i--) { - int[] index = colValuesInternal.get(i); - - if((intdata[index[0]] & index[1]) != 0) { - set.add(colValuesInternal.getKey(i)); - } - } - - return set; - } - - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for(int i=0;i<intdata.length;i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - - private int[] getData() { - return data; - } - - private void setData(int[] data) { - this.data = data; - } - - public int[] getLoad() { - int[] intdata = data; - int notempty = 0; - - for(int i=0;i<intdata.length;i++) { - if(intdata[i] != 0) { - notempty++; - } - } - - return new int[]{intdata.length, notempty}; - } - - public FastSetFactory<E> getFactory() { - return factory; - } - } - - public static class FastSetIterator<E> implements Iterator<E> { - - private VBStyleCollection<int[], E> colValuesInternal; - private int[] data; - private int size; - - private int pointer = -1; - private int next_pointer = -1; - - private FastSetIterator(FastSet<E> set) { - colValuesInternal = set.getFactory().getInternalValuesCollection(); - data = set.getData(); - - size = colValuesInternal.size(); - int datasize = data.length*32; - - if(datasize < size) { - size = datasize; - } - } - - public boolean hasNext() { - - next_pointer = pointer; - - while(++next_pointer < size) { - int[] index = colValuesInternal.get(next_pointer); - if((data[index[0]] & index[1]) != 0) { - return true; - } - } - - next_pointer = -1; - return false; - } - - public E next() { - if(next_pointer >= 0) { - pointer = next_pointer; - } else { - while(++pointer < size) { - int[] index = colValuesInternal.get(pointer); - if((data[index[0]] & index[1]) != 0) { - break; - } - } - } - - next_pointer = -1; - return pointer<size?colValuesInternal.getKey(pointer):null; - } - - public void remove() { - int[] index = colValuesInternal.get(pointer); - data[index[0]] &= ~index[1]; - - pointer--; - } - - } - + private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); + + private int lastBlock; + + private int lastMask; + + public FastSetFactory(Collection<E> set) { + + int block = -1; + int mask = -1; + int index = 0; + + for (E element : set) { + + block = index / 32; + + if (index % 32 == 0) { + mask = 1; + } + else { + mask <<= 1; + } + + colValuesInternal.putWithKey(new int[]{block, mask}, element); + + index++; + } + + lastBlock = block; + lastMask = mask; + } + + private int[] addElement(E element) { + + if (lastMask == -1 || lastMask == 0x80000000) { + lastMask = 1; + lastBlock++; + } + else { + lastMask <<= 1; + } + + int[] pointer = new int[]{lastBlock, lastMask}; + colValuesInternal.putWithKey(pointer, element); + + return pointer; + } + + public FastSet<E> spawnEmptySet() { + return new FastSet<E>(this); + } + + public int getLastBlock() { + return lastBlock; + } + + public int getLastMask() { + return lastMask; + } + + private VBStyleCollection<int[], E> getInternalValuesCollection() { + return colValuesInternal; + } + + + public static class FastSet<E> implements Iterable<E> { + + private FastSetFactory<E> factory; + + private VBStyleCollection<int[], E> colValuesInternal; + + private int[] data; + + private FastSet(FastSetFactory<E> factory) { + this.factory = factory; + this.colValuesInternal = factory.getInternalValuesCollection(); + this.data = new int[factory.getLastBlock() + 1]; + } + + public FastSet<E> getCopy() { + + FastSet<E> copy = new FastSet<E>(factory); + + int arrlength = data.length; + int[] cpdata = new int[arrlength]; + + System.arraycopy(data, 0, cpdata, 0, arrlength); + copy.setData(cpdata); + + return copy; + } + + private int[] ensureCapacity(int index) { + + int newlength = data.length; + if (newlength == 0) { + newlength = 1; + } + + while (newlength <= index) { + newlength *= 2; + } + + int[] newdata = new int[newlength]; + System.arraycopy(data, 0, newdata, 0, data.length); + + return data = newdata; + } + + public void add(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + if (index[0] >= data.length) { + ensureCapacity(index[0]); + } + + data[index[0]] |= index[1]; + } + + public void setAllElements() { + + int lastblock = factory.getLastBlock(); + int lastmask = factory.getLastMask(); + + if (lastblock >= data.length) { + ensureCapacity(lastblock); + } + + for (int i = lastblock - 1; i >= 0; i--) { + data[i] = 0xFFFFFFFF; + } + + data[lastblock] = lastmask | (lastmask - 1); + } + + public void addAll(Set<E> set) { + for (E element : set) { + add(element); + } + } + + public void remove(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + if (index[0] < data.length) { + data[index[0]] &= ~index[1]; + } + } + + public void removeAll(Set<E> set) { + for (E element : set) { + remove(element); + } + } + + public boolean contains(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + return index[0] >= data.length ? false : ((data[index[0]] & index[1]) != 0); + } + + public boolean contains(FastSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + if ((extdata[i] & ~intdata[i]) != 0) { + return false; + } + } + + for (int i = extdata.length - 1; i >= minlength; i--) { + if (extdata[i] != 0) { + return false; + } + } + + return true; + } + + public void union(FastSet<E> set) { + + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] |= extdata[i]; + } + + boolean expanded = false; + for (int i = extdata.length - 1; i >= minlength; i--) { + if (extdata[i] != 0) { + if (!expanded) { + intdata = ensureCapacity(extdata.length - 1); + } + intdata[i] = extdata[i]; + } + } + } + + public void intersection(FastSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] &= extdata[i]; + } + + for (int i = intdata.length - 1; i >= minlength; i--) { + intdata[i] = 0; + } + } + + public void symdiff(FastSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] ^= extdata[i]; + } + + boolean expanded = false; + for (int i = extdata.length - 1; i >= minlength; i--) { + if (extdata[i] != 0) { + if (!expanded) { + intdata = ensureCapacity(extdata.length - 1); + } + intdata[i] = extdata[i]; + } + } + } + + public void complement(FastSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] &= ~extdata[i]; + } + } + + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FastSet)) return false; + + int[] longdata = ((FastSet)o).getData(); + int[] shortdata = data; + + if (data.length > longdata.length) { + shortdata = longdata; + longdata = data; + } + + for (int i = shortdata.length - 1; i >= 0; i--) { + if (shortdata[i] != longdata[i]) { + return false; + } + } + + for (int i = longdata.length - 1; i >= shortdata.length; i--) { + if (longdata[i] != 0) { + return false; + } + } + + return true; + } + + public int getCardinality() { + + boolean found = false; + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + int block = intdata[i]; + if (block != 0) { + if (found) { + return 2; + } + else { + if ((block & (block - 1)) == 0) { + found = true; + } + else { + return 2; + } + } + } + } + + return found ? 1 : 0; + } + + public int size() { + + int size = 0; + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + size += Integer.bitCount(intdata[i]); + } + + return size; + } + + public boolean isEmpty() { + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + if (intdata[i] != 0) { + return false; + } + } + + return true; + } + + public Iterator<E> iterator() { + return new FastSetIterator<E>(this); + } + + public Set<E> toPlainSet() { + HashSet<E> set = new HashSet<E>(); + + int[] intdata = data; + + int size = data.length * 32; + if (size > colValuesInternal.size()) { + size = colValuesInternal.size(); + } + + for (int i = size - 1; i >= 0; i--) { + int[] index = colValuesInternal.get(i); + + if ((intdata[index[0]] & index[1]) != 0) { + set.add(colValuesInternal.getKey(i)); + } + } + + return set; + } + + public String toBinary() { + + StringBuilder buffer = new StringBuilder(); + int[] intdata = data; + + for (int i = 0; i < intdata.length; i++) { + buffer.append(" ").append(Integer.toBinaryString(intdata[i])); + } + + return buffer.toString(); + } + + private int[] getData() { + return data; + } + + private void setData(int[] data) { + this.data = data; + } + + public int[] getLoad() { + int[] intdata = data; + int notempty = 0; + + for (int i = 0; i < intdata.length; i++) { + if (intdata[i] != 0) { + notempty++; + } + } + + return new int[]{intdata.length, notempty}; + } + + public FastSetFactory<E> getFactory() { + return factory; + } + } + + public static class FastSetIterator<E> implements Iterator<E> { + + private VBStyleCollection<int[], E> colValuesInternal; + private int[] data; + private int size; + + private int pointer = -1; + private int next_pointer = -1; + + private FastSetIterator(FastSet<E> set) { + colValuesInternal = set.getFactory().getInternalValuesCollection(); + data = set.getData(); + + size = colValuesInternal.size(); + int datasize = data.length * 32; + + if (datasize < size) { + size = datasize; + } + } + + public boolean hasNext() { + + next_pointer = pointer; + + while (++next_pointer < size) { + int[] index = colValuesInternal.get(next_pointer); + if ((data[index[0]] & index[1]) != 0) { + return true; + } + } + + next_pointer = -1; + return false; + } + + public E next() { + if (next_pointer >= 0) { + pointer = next_pointer; + } + else { + while (++pointer < size) { + int[] index = colValuesInternal.get(pointer); + if ((data[index[0]] & index[1]) != 0) { + break; + } + } + } + + next_pointer = -1; + return pointer < size ? colValuesInternal.getKey(pointer) : null; + } + + public void remove() { + int[] index = colValuesInternal.get(pointer); + data[index[0]] &= ~index[1]; + + pointer--; + } + } } diff --git a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java index 352dc2a..301a1ff 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; import java.util.Collection; @@ -21,529 +22,533 @@ import java.util.Set; public class FastSparseSetFactory<E> { - private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); - - private int lastBlock; - - private int lastMask; - - public FastSparseSetFactory(Collection<E> set) { - - int block = -1; - int mask = -1; - int index = 0; - - for(E element : set) { - - block = index / 32; - - if(index % 32 == 0) { - mask = 1; - } else { - mask <<= 1; - } - - colValuesInternal.putWithKey(new int[] {block, mask}, element); - - index++; - } - - lastBlock = block; - lastMask = mask; - } - - private int[] addElement(E element) { - - if(lastMask == -1 || lastMask == 0x80000000) { - lastMask = 1; - lastBlock++; - } else { - lastMask <<= 1; - } - - int[] pointer = new int[] {lastBlock, lastMask}; - colValuesInternal.putWithKey(pointer, element); - - return pointer; - } - - public FastSparseSet<E> spawnEmptySet() { - return new FastSparseSet<E>(this); - } - - public int getLastBlock() { - return lastBlock; - } - - public int getLastMask() { - return lastMask; - } - - private VBStyleCollection<int[], E> getInternalValuesCollection() { - return colValuesInternal; - } - - - public static class FastSparseSet<E> implements Iterable<E> { - - private FastSparseSetFactory<E> factory; - - private VBStyleCollection<int[], E> colValuesInternal; - - private int[] data; - private int[] next; - - private FastSparseSet(FastSparseSetFactory<E> factory) { - this.factory = factory; - this.colValuesInternal = factory.getInternalValuesCollection(); - - int length = factory.getLastBlock()+1; - this.data = new int[length]; - this.next = new int[length]; - } - - private FastSparseSet(FastSparseSetFactory<E> factory, int[] data, int[] next) { - this.factory = factory; - this.colValuesInternal = factory.getInternalValuesCollection(); - - this.data = data; - this.next = next; - } - - public FastSparseSet<E> getCopy() { - - int arrlength = data.length; - int[] cpdata = new int[arrlength]; - int[] cpnext = new int[arrlength]; - - System.arraycopy(data, 0, cpdata, 0, arrlength); - System.arraycopy(next, 0, cpnext, 0, arrlength); - - FastSparseSet<E> copy = new FastSparseSet<E>(factory, cpdata, cpnext); - - return copy; - } - - private int[] ensureCapacity(int index) { - - int newlength = data.length; - if(newlength == 0) { - newlength = 1; - } - - while(newlength <= index) { - newlength *=2; - } - - int[] newdata = new int[newlength]; - System.arraycopy(data, 0, newdata, 0, data.length); - data = newdata; - - int[] newnext = new int[newlength]; - System.arraycopy(next, 0, newnext, 0, next.length); - next = newnext; - - return newdata; - } - - public void add(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - int block = index[0]; - if(block >= data.length) { - ensureCapacity(block); - } - - data[block] |= index[1]; - - changeNext(next, block, next[block], block); - } - - public void setAllElements() { - - int lastblock = factory.getLastBlock(); - int lastmask = factory.getLastMask(); - - if(lastblock >= data.length) { - ensureCapacity(lastblock); - } - - for(int i=lastblock-1;i>=0;i--) { - data[i] = 0xFFFFFFFF; - next[i] = i+1; - } - - data[lastblock] = lastmask | (lastmask-1); - next[lastblock] = 0; - } - - public void addAll(Set<E> set) { - for(E element : set) { - add(element); - } - } - - public void remove(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - int block = index[0]; - if(block < data.length) { - data[block] &= ~index[1]; - - if(data[block] == 0) { - changeNext(next, block, block, next[block]); - } - } - } - - public void removeAll(Set<E> set) { - for(E element : set) { - remove(element); - } - } - - public boolean contains(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if(index == null) { - index = factory.addElement(element); - } - - return index[0] >= data.length?false:((data[index[0]] & index[1]) != 0); - } - - public boolean contains(FastSparseSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - if((extdata[i] & ~intdata[i]) != 0) { - return false; - } - } - - for(int i=extdata.length-1;i>=minlength;i--) { - if(extdata[i] != 0) { - return false; - } - } - - return true; - } - - private void setNext() { - - int link = 0; - for(int i=data.length-1;i>=0;i--) { - next[i] = link; - if(data[i] != 0) { - link = i; - } - } - } - - private void changeNext(int[] arrnext, int key, int oldnext, int newnext) { - for(int i=key-1;i>=0;i--) { - if(arrnext[i] == oldnext) { - arrnext[i] = newnext; - } else { - break; - } - } - } - - public void union(FastSparseSet<E> set) { - - int[] extdata = set.getData(); - int[] extnext = set.getNext(); - int[] intdata = data; - int intlength = intdata.length; - - int pointer = 0; - do { - if(pointer >= intlength) { - intdata = ensureCapacity(extdata.length - 1); - } - - boolean nextrec = (intdata[pointer] == 0); - intdata[pointer] |= extdata[pointer]; - - if(nextrec) { - changeNext(next, pointer, next[pointer], pointer); - } - - pointer = extnext[pointer]; - - } while(pointer!=0); - } - - public void intersection(FastSparseSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] &= extdata[i]; - } - - for(int i=intdata.length-1;i>=minlength;i--) { - intdata[i] = 0; - } - - setNext(); - } - - public void symdiff(FastSparseSet<E> set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for(int i=minlength-1;i>=0;i--) { - intdata[i] ^= extdata[i]; - } - - boolean expanded = false; - for(int i=extdata.length-1;i>=minlength;i--) { - if(extdata[i] != 0) { - if(!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - - setNext(); - } - - public void complement(FastSparseSet<E> set) { - - int[] extdata = set.getData(); - int[] intdata = data; - int extlength = extdata.length; - - int pointer = 0; - do { - if(pointer >= extlength) { - break; - } - - intdata[pointer] &= ~extdata[pointer]; - if(intdata[pointer] == 0) { - changeNext(next, pointer, pointer, next[pointer]); - } - - pointer = next[pointer]; - - } while(pointer!=0); - } - - - public boolean equals(Object o) { - if(o == this) return true; - if(o == null || !(o instanceof FastSparseSet)) return false; - - int[] longdata = ((FastSparseSet)o).getData(); - int[] shortdata = data; - - if(data.length > longdata.length) { - shortdata = longdata; - longdata = data; - } - - for(int i=shortdata.length-1;i>=0;i--) { - if(shortdata[i] != longdata[i]) { - return false; - } - } - - for(int i=longdata.length-1;i>=shortdata.length;i--) { - if(longdata[i] != 0) { - return false; - } - } - - return true; - } - - public int getCardinality() { - - boolean found = false; - int[] intdata = data; - - for(int i=intdata.length-1;i>=0;i--) { - int block = intdata[i]; - if(block != 0) { - if(found) { - return 2; - } else { - if ((block & (block-1)) == 0) { - found = true; - } else { - return 2; - } - } - } - } - - return found?1:0; - } - - public boolean isEmpty() { - return data.length == 0 || (next[0] == 0 && data[0] == 0); - } - - public Iterator<E> iterator() { - return new FastSparseSetIterator<E>(this); - } - - public Set<E> toPlainSet() { - HashSet<E> set = new HashSet<E>(); - - int[] intdata = data; - - int size = data.length*32; - if(size > colValuesInternal.size()) { - size = colValuesInternal.size(); - } - - for(int i=size-1;i>=0;i--) { - int[] index = colValuesInternal.get(i); - - if((intdata[index[0]] & index[1]) != 0) { - set.add(colValuesInternal.getKey(i)); - } - } - - return set; - } - - public String toString() { - return toPlainSet().toString(); - } - - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for(int i=0;i<intdata.length;i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - - private int[] getData() { - return data; - } - - private int[] getNext() { - return next; - } - - public int[] getLoad() { - int[] intdata = data; - int notempty = 0; - - for(int i=0;i<intdata.length;i++) { - if(intdata[i] != 0) { - notempty++; - } - } - - return new int[]{intdata.length, notempty}; - } - - public FastSparseSetFactory<E> getFactory() { - return factory; - } - } - - public static class FastSparseSetIterator<E> implements Iterator<E> { - - private VBStyleCollection<int[], E> colValuesInternal; - private int[] data; - private int[] next; - private int size; - - private int pointer = -1; - private int next_pointer = -1; - - private FastSparseSetIterator(FastSparseSet<E> set) { - colValuesInternal = set.getFactory().getInternalValuesCollection(); - data = set.getData(); - next = set.getNext(); - size = colValuesInternal.size(); - } - - private int getNextIndex(int index) { - - index++; - int bindex = index >>> 5; - int dindex = index & 0x1F; - - while(bindex < data.length) { - int block = data[bindex]; - - if(block != 0) { - block >>>= dindex; - while(dindex < 32) { - if((block & 1) != 0) { - return (bindex << 5) + dindex; - } - block >>>= 1; - dindex++; - } - } - - dindex = 0; - bindex = next[bindex]; - - if(bindex == 0) { - break; - } - } - - return -1; - } - - public boolean hasNext() { - next_pointer = getNextIndex(pointer); - return (next_pointer >= 0); - } - - public E next() { - if(next_pointer >= 0) { - pointer = next_pointer; - } else { - pointer = getNextIndex(pointer); - if(pointer == -1) { - pointer = size; - } - } - - next_pointer = -1; - return pointer<size?colValuesInternal.getKey(pointer):null; - } - - public void remove() { - int[] index = colValuesInternal.get(pointer); - data[index[0]] &= ~index[1]; - } - - } - + private VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<int[], E>(); + + private int lastBlock; + + private int lastMask; + + public FastSparseSetFactory(Collection<E> set) { + + int block = -1; + int mask = -1; + int index = 0; + + for (E element : set) { + + block = index / 32; + + if (index % 32 == 0) { + mask = 1; + } + else { + mask <<= 1; + } + + colValuesInternal.putWithKey(new int[]{block, mask}, element); + + index++; + } + + lastBlock = block; + lastMask = mask; + } + + private int[] addElement(E element) { + + if (lastMask == -1 || lastMask == 0x80000000) { + lastMask = 1; + lastBlock++; + } + else { + lastMask <<= 1; + } + + int[] pointer = new int[]{lastBlock, lastMask}; + colValuesInternal.putWithKey(pointer, element); + + return pointer; + } + + public FastSparseSet<E> spawnEmptySet() { + return new FastSparseSet<E>(this); + } + + public int getLastBlock() { + return lastBlock; + } + + public int getLastMask() { + return lastMask; + } + + private VBStyleCollection<int[], E> getInternalValuesCollection() { + return colValuesInternal; + } + + + public static class FastSparseSet<E> implements Iterable<E> { + + private FastSparseSetFactory<E> factory; + + private VBStyleCollection<int[], E> colValuesInternal; + + private int[] data; + private int[] next; + + private FastSparseSet(FastSparseSetFactory<E> factory) { + this.factory = factory; + this.colValuesInternal = factory.getInternalValuesCollection(); + + int length = factory.getLastBlock() + 1; + this.data = new int[length]; + this.next = new int[length]; + } + + private FastSparseSet(FastSparseSetFactory<E> factory, int[] data, int[] next) { + this.factory = factory; + this.colValuesInternal = factory.getInternalValuesCollection(); + + this.data = data; + this.next = next; + } + + public FastSparseSet<E> getCopy() { + + int arrlength = data.length; + int[] cpdata = new int[arrlength]; + int[] cpnext = new int[arrlength]; + + System.arraycopy(data, 0, cpdata, 0, arrlength); + System.arraycopy(next, 0, cpnext, 0, arrlength); + + FastSparseSet<E> copy = new FastSparseSet<E>(factory, cpdata, cpnext); + + return copy; + } + + private int[] ensureCapacity(int index) { + + int newlength = data.length; + if (newlength == 0) { + newlength = 1; + } + + while (newlength <= index) { + newlength *= 2; + } + + int[] newdata = new int[newlength]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + + int[] newnext = new int[newlength]; + System.arraycopy(next, 0, newnext, 0, next.length); + next = newnext; + + return newdata; + } + + public void add(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + int block = index[0]; + if (block >= data.length) { + ensureCapacity(block); + } + + data[block] |= index[1]; + + changeNext(next, block, next[block], block); + } + + public void setAllElements() { + + int lastblock = factory.getLastBlock(); + int lastmask = factory.getLastMask(); + + if (lastblock >= data.length) { + ensureCapacity(lastblock); + } + + for (int i = lastblock - 1; i >= 0; i--) { + data[i] = 0xFFFFFFFF; + next[i] = i + 1; + } + + data[lastblock] = lastmask | (lastmask - 1); + next[lastblock] = 0; + } + + public void addAll(Set<E> set) { + for (E element : set) { + add(element); + } + } + + public void remove(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + int block = index[0]; + if (block < data.length) { + data[block] &= ~index[1]; + + if (data[block] == 0) { + changeNext(next, block, block, next[block]); + } + } + } + + public void removeAll(Set<E> set) { + for (E element : set) { + remove(element); + } + } + + public boolean contains(E element) { + int[] index = colValuesInternal.getWithKey(element); + + if (index == null) { + index = factory.addElement(element); + } + + return index[0] >= data.length ? false : ((data[index[0]] & index[1]) != 0); + } + + public boolean contains(FastSparseSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + if ((extdata[i] & ~intdata[i]) != 0) { + return false; + } + } + + for (int i = extdata.length - 1; i >= minlength; i--) { + if (extdata[i] != 0) { + return false; + } + } + + return true; + } + + private void setNext() { + + int link = 0; + for (int i = data.length - 1; i >= 0; i--) { + next[i] = link; + if (data[i] != 0) { + link = i; + } + } + } + + private void changeNext(int[] arrnext, int key, int oldnext, int newnext) { + for (int i = key - 1; i >= 0; i--) { + if (arrnext[i] == oldnext) { + arrnext[i] = newnext; + } + else { + break; + } + } + } + + public void union(FastSparseSet<E> set) { + + int[] extdata = set.getData(); + int[] extnext = set.getNext(); + int[] intdata = data; + int intlength = intdata.length; + + int pointer = 0; + do { + if (pointer >= intlength) { + intdata = ensureCapacity(extdata.length - 1); + } + + boolean nextrec = (intdata[pointer] == 0); + intdata[pointer] |= extdata[pointer]; + + if (nextrec) { + changeNext(next, pointer, next[pointer], pointer); + } + + pointer = extnext[pointer]; + } + while (pointer != 0); + } + + public void intersection(FastSparseSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] &= extdata[i]; + } + + for (int i = intdata.length - 1; i >= minlength; i--) { + intdata[i] = 0; + } + + setNext(); + } + + public void symdiff(FastSparseSet<E> set) { + int[] extdata = set.getData(); + int[] intdata = data; + + int minlength = Math.min(extdata.length, intdata.length); + + for (int i = minlength - 1; i >= 0; i--) { + intdata[i] ^= extdata[i]; + } + + boolean expanded = false; + for (int i = extdata.length - 1; i >= minlength; i--) { + if (extdata[i] != 0) { + if (!expanded) { + intdata = ensureCapacity(extdata.length - 1); + } + intdata[i] = extdata[i]; + } + } + + setNext(); + } + + public void complement(FastSparseSet<E> set) { + + int[] extdata = set.getData(); + int[] intdata = data; + int extlength = extdata.length; + + int pointer = 0; + do { + if (pointer >= extlength) { + break; + } + + intdata[pointer] &= ~extdata[pointer]; + if (intdata[pointer] == 0) { + changeNext(next, pointer, pointer, next[pointer]); + } + + pointer = next[pointer]; + } + while (pointer != 0); + } + + + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof FastSparseSet)) return false; + + int[] longdata = ((FastSparseSet)o).getData(); + int[] shortdata = data; + + if (data.length > longdata.length) { + shortdata = longdata; + longdata = data; + } + + for (int i = shortdata.length - 1; i >= 0; i--) { + if (shortdata[i] != longdata[i]) { + return false; + } + } + + for (int i = longdata.length - 1; i >= shortdata.length; i--) { + if (longdata[i] != 0) { + return false; + } + } + + return true; + } + + public int getCardinality() { + + boolean found = false; + int[] intdata = data; + + for (int i = intdata.length - 1; i >= 0; i--) { + int block = intdata[i]; + if (block != 0) { + if (found) { + return 2; + } + else { + if ((block & (block - 1)) == 0) { + found = true; + } + else { + return 2; + } + } + } + } + + return found ? 1 : 0; + } + + public boolean isEmpty() { + return data.length == 0 || (next[0] == 0 && data[0] == 0); + } + + public Iterator<E> iterator() { + return new FastSparseSetIterator<E>(this); + } + + public Set<E> toPlainSet() { + HashSet<E> set = new HashSet<E>(); + + int[] intdata = data; + + int size = data.length * 32; + if (size > colValuesInternal.size()) { + size = colValuesInternal.size(); + } + + for (int i = size - 1; i >= 0; i--) { + int[] index = colValuesInternal.get(i); + + if ((intdata[index[0]] & index[1]) != 0) { + set.add(colValuesInternal.getKey(i)); + } + } + + return set; + } + + public String toString() { + return toPlainSet().toString(); + } + + public String toBinary() { + + StringBuilder buffer = new StringBuilder(); + int[] intdata = data; + + for (int i = 0; i < intdata.length; i++) { + buffer.append(" ").append(Integer.toBinaryString(intdata[i])); + } + + return buffer.toString(); + } + + private int[] getData() { + return data; + } + + private int[] getNext() { + return next; + } + + public int[] getLoad() { + int[] intdata = data; + int notempty = 0; + + for (int i = 0; i < intdata.length; i++) { + if (intdata[i] != 0) { + notempty++; + } + } + + return new int[]{intdata.length, notempty}; + } + + public FastSparseSetFactory<E> getFactory() { + return factory; + } + } + + public static class FastSparseSetIterator<E> implements Iterator<E> { + + private VBStyleCollection<int[], E> colValuesInternal; + private int[] data; + private int[] next; + private int size; + + private int pointer = -1; + private int next_pointer = -1; + + private FastSparseSetIterator(FastSparseSet<E> set) { + colValuesInternal = set.getFactory().getInternalValuesCollection(); + data = set.getData(); + next = set.getNext(); + size = colValuesInternal.size(); + } + + private int getNextIndex(int index) { + + index++; + int bindex = index >>> 5; + int dindex = index & 0x1F; + + while (bindex < data.length) { + int block = data[bindex]; + + if (block != 0) { + block >>>= dindex; + while (dindex < 32) { + if ((block & 1) != 0) { + return (bindex << 5) + dindex; + } + block >>>= 1; + dindex++; + } + } + + dindex = 0; + bindex = next[bindex]; + + if (bindex == 0) { + break; + } + } + + return -1; + } + + public boolean hasNext() { + next_pointer = getNextIndex(pointer); + return (next_pointer >= 0); + } + + public E next() { + if (next_pointer >= 0) { + pointer = next_pointer; + } + else { + pointer = getNextIndex(pointer); + if (pointer == -1) { + pointer = size; + } + } + + next_pointer = -1; + return pointer < size ? colValuesInternal.getKey(pointer) : null; + } + + public void remove() { + int[] index = colValuesInternal.get(pointer); + data[index[0]] &= ~index[1]; + } + } } diff --git a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java index 4914d36..f429636 100644 --- a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java +++ b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java @@ -1,157 +1,155 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; + +import java.io.*; import java.nio.channels.FileChannel; import java.util.Collection; import java.util.HashSet; import java.util.List; -import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; - public class InterpreterUtil { - - public static void copyFile(File in, File out) throws IOException { - FileChannel inChannel = new FileInputStream(in).getChannel(); - FileChannel outChannel = new FileOutputStream(out).getChannel(); - try { - // magic number for Windows, 64Mb - 32Kb) - int maxCount = (64 * 1024 * 1024) - (32 * 1024); - long size = inChannel.size(); - long position = 0; - while (position < size) { - position += inChannel.transferTo(position, maxCount, outChannel); - } - } catch (IOException e) { - throw e; - } - finally { - if (inChannel != null) { - inChannel.close(); - } - if (outChannel != null) { - outChannel.close(); - } - } - } - - public static void copyInputStream(InputStream in, OutputStream out)throws IOException { - - byte[] buffer = new byte[1024]; - int len; - - while((len = in.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } - - } - - public static String getIndentString(int length) { - String indent = (String) DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); + + public static void copyFile(File in, File out) throws IOException { + FileChannel inChannel = new FileInputStream(in).getChannel(); + FileChannel outChannel = new FileOutputStream(out).getChannel(); + try { + // magic number for Windows, 64Mb - 32Kb) + int maxCount = (64 * 1024 * 1024) - (32 * 1024); + long size = inChannel.size(); + long position = 0; + while (position < size) { + position += inChannel.transferTo(position, maxCount, outChannel); + } + } + catch (IOException e) { + throw e; + } + finally { + if (inChannel != null) { + inChannel.close(); + } + if (outChannel != null) { + outChannel.close(); + } + } + } + + public static void copyInputStream(InputStream in, OutputStream out) throws IOException { + + byte[] buffer = new byte[1024]; + int len; + + while ((len = in.read(buffer)) >= 0) { + out.write(buffer, 0, len); + } + } + + public static String getIndentString(int length) { + String indent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); StringBuilder buf = new StringBuilder(); - while(length-->0) { - buf.append(indent); - } - return buf.toString(); - } - - - public static boolean equalSets(Collection<?> c1, Collection<?> c2) { - - if(c1 == null) { - return c2 == null?true:false; - } else { - if(c2 == null) { - return false; - } - } - - if(c1.size() != c2.size()) { - return false; - } - - HashSet<?> set = new HashSet(c1); - set.removeAll(c2); - - return (set.size() == 0); - } - - public static boolean equalObjects(Object first, Object second) { - return first==null?second==null:first.equals(second); - } - - public static boolean equalObjectArrays(Object[] first, Object[] second) { - - if(first == null || second == null) { - return equalObjects(first, second); - } else { - if(first.length != second.length) { - return false; - } - - for(int i=0;i<first.length;i++) { - if(!equalObjects(first[i], second[i])) { - return false; - } - } - } - - return true; - } - - public static boolean equalLists(List<?> first, List<?> second) { - - if(first == null) { - return second == null; - } else if(second == null) { - return first == null; - } - - if(first.size() == second.size()) { - for(int i=0;i<first.size();i++) { - if(!equalObjects(first.get(i), second.get(i))) { - return false; - } - } - return true; - } - - return false; - } - - public static boolean isPrintableUnicode(char c) { + while (length-- > 0) { + buf.append(indent); + } + return buf.toString(); + } + + + public static boolean equalSets(Collection<?> c1, Collection<?> c2) { + + if (c1 == null) { + return c2 == null ? true : false; + } + else { + if (c2 == null) { + return false; + } + } + + if (c1.size() != c2.size()) { + return false; + } + + HashSet<?> set = new HashSet(c1); + set.removeAll(c2); + + return (set.size() == 0); + } + + public static boolean equalObjects(Object first, Object second) { + return first == null ? second == null : first.equals(second); + } + + public static boolean equalObjectArrays(Object[] first, Object[] second) { + + if (first == null || second == null) { + return equalObjects(first, second); + } + else { + if (first.length != second.length) { + return false; + } + + for (int i = 0; i < first.length; i++) { + if (!equalObjects(first[i], second[i])) { + return false; + } + } + } + + return true; + } + + public static boolean equalLists(List<?> first, List<?> second) { + + if (first == null) { + return second == null; + } + else if (second == null) { + return first == null; + } + + if (first.size() == second.size()) { + for (int i = 0; i < first.size(); i++) { + if (!equalObjects(first.get(i), second.get(i))) { + return false; + } + } + return true; + } + + return false; + } + + public static boolean isPrintableUnicode(char c) { int t = Character.getType(c); return t != Character.UNASSIGNED && t != Character.LINE_SEPARATOR && t != Character.PARAGRAPH_SEPARATOR && t != Character.CONTROL && t != Character.FORMAT && t != Character.PRIVATE_USE && t != Character.SURROGATE; } - public static String charToUnicodeLiteral(int value) { - String sTemp = Integer.toHexString(value); - sTemp = ("0000"+sTemp).substring(sTemp.length()); - return "\\u"+sTemp; - } - - public static String makeUniqueKey(String name, String descriptor) { - return name+" "+descriptor; - } - + public static String charToUnicodeLiteral(int value) { + String sTemp = Integer.toHexString(value); + sTemp = ("0000" + sTemp).substring(sTemp.length()); + return "\\u" + sTemp; + } + + public static String makeUniqueKey(String name, String descriptor) { + return name + " " + descriptor; + } } diff --git a/src/org/jetbrains/java/decompiler/util/ListStack.java b/src/org/jetbrains/java/decompiler/util/ListStack.java index fa36a0b..20c6614 100644 --- a/src/org/jetbrains/java/decompiler/util/ListStack.java +++ b/src/org/jetbrains/java/decompiler/util/ListStack.java @@ -1,101 +1,101 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; import java.util.ArrayList; public class ListStack<T> extends ArrayList<T> { - protected int pointer = 0; - - public ListStack(){ - super(); - } - - public ListStack(ArrayList<T> list){ - super(list); - } - - public ListStack<T> clone() { - ListStack<T> newstack = new ListStack<T>(this); - newstack.pointer = this.pointer; - return newstack; - } - - public T push(T item) { - this.add(item); - pointer++; - return item; - } - - public T pop() { - pointer--; - T o = this.get(pointer); - this.remove(pointer); - return o; - } - - public T pop(int count) { - T o = null; - for(int i=count;i>0;i--) { - o = this.pop(); - } - return o; - } - - public void remove() { - pointer--; - this.remove(pointer); - } - - public void removeMultiple(int count) { - while(count>0) { - pointer--; - this.remove(pointer); - count--; - } - } - - public boolean empty() { - return (pointer==0); - } - - public int getPointer() { - return pointer; - } - - public T get(int index) { - return super.get(index); - } - - public T set(int index, T element) { - return super.set(index, element); - } - - public T getByOffset(int offset) { - return this.get(pointer+offset); - } - - public void insertByOffset(int offset, T item) { - this.add(pointer+offset, item); - pointer++; - } - - public void clear() { - super.clear(); - pointer = 0; - } - + protected int pointer = 0; + + public ListStack() { + super(); + } + + public ListStack(ArrayList<T> list) { + super(list); + } + + public ListStack<T> clone() { + ListStack<T> newstack = new ListStack<T>(this); + newstack.pointer = this.pointer; + return newstack; + } + + public T push(T item) { + this.add(item); + pointer++; + return item; + } + + public T pop() { + pointer--; + T o = this.get(pointer); + this.remove(pointer); + return o; + } + + public T pop(int count) { + T o = null; + for (int i = count; i > 0; i--) { + o = this.pop(); + } + return o; + } + + public void remove() { + pointer--; + this.remove(pointer); + } + + public void removeMultiple(int count) { + while (count > 0) { + pointer--; + this.remove(pointer); + count--; + } + } + + public boolean empty() { + return (pointer == 0); + } + + public int getPointer() { + return pointer; + } + + public T get(int index) { + return super.get(index); + } + + public T set(int index, T element) { + return super.set(index, element); + } + + public T getByOffset(int offset) { + return this.get(pointer + offset); + } + + public void insertByOffset(int offset, T item) { + this.add(pointer + offset, item); + pointer++; + } + + public void clear() { + super.clear(); + pointer = 0; + } } diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMap.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMap.java index 546e32e..26fb216 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMap.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMap.java @@ -1,232 +1,251 @@ +/* + * 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.util; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; + import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; - public class SFormsFastMap<E> { - private int size; - - private List<E[]> lstElements = new ArrayList<E[]>(3); - - { - lstElements.add((E[])new Object[5]); - lstElements.add((E[])new Object[5]); - lstElements.add((E[])new Object[5]); - } - - public SFormsFastMap() {} - - public SFormsFastMap(SFormsFastMap<E> map) { - for(int i=2;i>=0;i--) { - E[] arr = map.lstElements.get(i); - E[] arrnew = (E[])new Object[arr.length]; - - System.arraycopy(arr, 0, arrnew, 0, arr.length); - - lstElements.set(i, arrnew); - - for(E element : arrnew) { - if(element != null) { - size++; - } - } - } - - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public void put(int key, E value) { - putInternal(key, value, false); - } - - public void remove(int key) { - putInternal(key, null, true); - } - - public void removeAllFields() { - E[] arr = lstElements.get(2); - for(int i=arr.length-1;i>=0;i--) { - E val = arr[i]; - if(val != null) { - arr[i] = null; - size--; - } - } - } - - public void putInternal(final int key, final E value, boolean remove) { - - int index = 0; - int ikey = key; - if(ikey < 0) { - index = 2; - ikey = -ikey; - } else if(ikey >= VarExprent.STACK_BASE) { - index = 1; - ikey -= VarExprent.STACK_BASE; - } - - E[] arr = lstElements.get(index); - if(ikey >= arr.length) { - if(remove) { - return; - } else { - arr = ensureCapacity(arr, ikey+1, false); - lstElements.set(index, arr); - } - } - - E oldval = arr[ikey]; - arr[ikey] = value; - - if(oldval == null && value != null) { - size++; - } else if(oldval != null && value == null) { - size--; - } - } - - public boolean containsKey(int key) { - return get(key) != null; - } - - public E get(int key) { - - int index = 0; - if(key < 0) { - index = 2; - key = -key; - } else if(key >= VarExprent.STACK_BASE) { - index = 1; - key -= VarExprent.STACK_BASE; - } - - E[] arr = lstElements.get(index); - - if(key < arr.length) { - return arr[key]; - } - return null; - } - - public void union(SFormsFastMap<E> map, IElementsUnion<E> union) { - - for(int i=2;i>=0;i--) { - E[] lstOwn = lstElements.get(i); - E[] lstExtern = map.lstElements.get(i); - - int externsize = lstExtern.length; - - if(lstOwn.length<externsize) { - lstOwn = ensureCapacity(lstOwn, externsize, true); - lstElements.set(i, lstOwn); - } - - int ownsize = lstOwn.length; - int minsize = ownsize>externsize?externsize:ownsize; - - for(int j=minsize-1;j>=0;j--) { - E second = lstExtern[j]; - - if(second != null) { - E first = lstOwn[j]; - - if(first == null) { - lstOwn[j] = union.copy(second); - size++; - } else { - union.union(first, second); - } - } - } - -// ITimer timer = TimerFactory.newTimer(); -// timer.start(); -// -// if(externsize > minsize) { -// for(int j=minsize;j<externsize;j++) { -// E second = lstExtern.get(j); -// if(second != null) { -// lstOwn.add(union.copy(second)); -// size++; -// } else { -// lstOwn.add(null); -// } -// } -// } -// -// timer.stop(); -// Timer.addTime("sformunion", timer.getDuration()); - - } - - } - - public List<Entry<Integer, E>> entryList() { - List<Entry<Integer, E>> list = new ArrayList<Entry<Integer, E>>(); - - for(int i=2;i>=0;i--) { - int ikey = 0; - for(final E ent : lstElements.get(i)) { - if(ent != null) { - final int key = i==0?ikey:(i==1?ikey+VarExprent.STACK_BASE:-ikey); - - list.add(new Entry<Integer, E>() { - - private Integer var = key; - private E val = ent; - - public Integer getKey() { - return var; - } - - public E getValue() { - return val; - } - - public E setValue(E newvalue) { - return null; - } - }); - } - - ikey++; - } - } - - return list; - } - - private E[] ensureCapacity(E[] arr, int size, boolean exact) { - - int minsize = size; - if(!exact) { - minsize = 2*arr.length/3 +1; - if(size > minsize) { - minsize = size; - } - } - - E[] arrnew = (E[])new Object[minsize]; - System.arraycopy(arr, 0, arrnew, 0, arr.length); - - return arrnew; - } - - public static interface IElementsUnion<E> { - public E union(E first, E second); - public E copy(E element); - } - + private int size; + + private List<E[]> lstElements = new ArrayList<E[]>(3); + + { + lstElements.add((E[])new Object[5]); + lstElements.add((E[])new Object[5]); + lstElements.add((E[])new Object[5]); + } + + public SFormsFastMap() { + } + + public SFormsFastMap(SFormsFastMap<E> map) { + for (int i = 2; i >= 0; i--) { + E[] arr = map.lstElements.get(i); + E[] arrnew = (E[])new Object[arr.length]; + + System.arraycopy(arr, 0, arrnew, 0, arr.length); + + lstElements.set(i, arrnew); + + for (E element : arrnew) { + if (element != null) { + size++; + } + } + } + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public void put(int key, E value) { + putInternal(key, value, false); + } + + public void remove(int key) { + putInternal(key, null, true); + } + + public void removeAllFields() { + E[] arr = lstElements.get(2); + for (int i = arr.length - 1; i >= 0; i--) { + E val = arr[i]; + if (val != null) { + arr[i] = null; + size--; + } + } + } + + public void putInternal(final int key, final E value, boolean remove) { + + int index = 0; + int ikey = key; + if (ikey < 0) { + index = 2; + ikey = -ikey; + } + else if (ikey >= VarExprent.STACK_BASE) { + index = 1; + ikey -= VarExprent.STACK_BASE; + } + + E[] arr = lstElements.get(index); + if (ikey >= arr.length) { + if (remove) { + return; + } + else { + arr = ensureCapacity(arr, ikey + 1, false); + lstElements.set(index, arr); + } + } + + E oldval = arr[ikey]; + arr[ikey] = value; + + if (oldval == null && value != null) { + size++; + } + else if (oldval != null && value == null) { + size--; + } + } + + public boolean containsKey(int key) { + return get(key) != null; + } + + public E get(int key) { + + int index = 0; + if (key < 0) { + index = 2; + key = -key; + } + else if (key >= VarExprent.STACK_BASE) { + index = 1; + key -= VarExprent.STACK_BASE; + } + + E[] arr = lstElements.get(index); + + if (key < arr.length) { + return arr[key]; + } + return null; + } + + public void union(SFormsFastMap<E> map, IElementsUnion<E> union) { + + for (int i = 2; i >= 0; i--) { + E[] lstOwn = lstElements.get(i); + E[] lstExtern = map.lstElements.get(i); + + int externsize = lstExtern.length; + + if (lstOwn.length < externsize) { + lstOwn = ensureCapacity(lstOwn, externsize, true); + lstElements.set(i, lstOwn); + } + + int ownsize = lstOwn.length; + int minsize = ownsize > externsize ? externsize : ownsize; + + for (int j = minsize - 1; j >= 0; j--) { + E second = lstExtern[j]; + + if (second != null) { + E first = lstOwn[j]; + + if (first == null) { + lstOwn[j] = union.copy(second); + size++; + } + else { + union.union(first, second); + } + } + } + + // ITimer timer = TimerFactory.newTimer(); + // timer.start(); + // + // if(externsize > minsize) { + // for(int j=minsize;j<externsize;j++) { + // E second = lstExtern.get(j); + // if(second != null) { + // lstOwn.add(union.copy(second)); + // size++; + // } else { + // lstOwn.add(null); + // } + // } + // } + // + // timer.stop(); + // Timer.addTime("sformunion", timer.getDuration()); + + } + } + + public List<Entry<Integer, E>> entryList() { + List<Entry<Integer, E>> list = new ArrayList<Entry<Integer, E>>(); + + for (int i = 2; i >= 0; i--) { + int ikey = 0; + for (final E ent : lstElements.get(i)) { + if (ent != null) { + final int key = i == 0 ? ikey : (i == 1 ? ikey + VarExprent.STACK_BASE : -ikey); + + list.add(new Entry<Integer, E>() { + + private Integer var = key; + private E val = ent; + + public Integer getKey() { + return var; + } + + public E getValue() { + return val; + } + + public E setValue(E newvalue) { + return null; + } + }); + } + + ikey++; + } + } + + return list; + } + + private E[] ensureCapacity(E[] arr, int size, boolean exact) { + + int minsize = size; + if (!exact) { + minsize = 2 * arr.length / 3 + 1; + if (size > minsize) { + minsize = size; + } + } + + E[] arrnew = (E[])new Object[minsize]; + System.arraycopy(arr, 0, arrnew, 0, arr.length); + + return arrnew; + } + + public static interface IElementsUnion<E> { + public E union(E first, E second); + + public E copy(E element); + } } diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java index 615162f..089225d 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java @@ -1,411 +1,413 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.Map.Entry; - -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; -import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import java.util.Set; public class SFormsFastMapDirect { - private int size; - - private FastSparseSet<Integer>[][] elements = new FastSparseSet[3][]; - - private int[][] next = new int[3][]; - - public SFormsFastMapDirect() { - this(true); - } - - private SFormsFastMapDirect(boolean initialize) { - if(initialize) { - for(int i=2; i>=0; i--) { - elements[i] = new FastSparseSet[0]; - next[i] = new int[0]; - } - } - } - - public SFormsFastMapDirect(SFormsFastMapDirect map) { - for(int i=2;i>=0;i--) { - FastSparseSet<Integer>[] arr = map.elements[i]; - int[] arrnext = map.next[i]; - - int length = arr.length; - FastSparseSet<Integer>[] arrnew = new FastSparseSet[length]; - int[] arrnextnew = new int[length]; - - System.arraycopy(arr, 0, arrnew, 0, length); - System.arraycopy(arrnext, 0, arrnextnew, 0, length); - - elements[i] = arrnew; - next[i] = arrnextnew; - - size = map.size; - } - } - - public SFormsFastMapDirect getCopy() { - - SFormsFastMapDirect map = new SFormsFastMapDirect(false); - map.size = size; - - FastSparseSet[][] mapelements = map.elements; - int[][] mapnext = map.next; - - for(int i=2;i>=0;i--) { - FastSparseSet<Integer>[] arr = elements[i]; - int length = arr.length; - - if(length > 0) { - int[] arrnext = next[i]; - - FastSparseSet<Integer>[] arrnew = new FastSparseSet[length]; - int[] arrnextnew = new int[length]; - - System.arraycopy(arrnext, 0, arrnextnew, 0, length); - - mapelements[i] = arrnew; - mapnext[i] = arrnextnew; - - int pointer = 0; - do { - FastSparseSet<Integer> set = arr[pointer]; - if(set != null) { - arrnew[pointer] = set.getCopy(); - } - - pointer = arrnext[pointer]; - } while(pointer != 0); - - } else { - mapelements[i] = new FastSparseSet[0]; - mapnext[i] = new int[0]; - } - } - - return map; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public void put(int key, FastSparseSet<Integer> value) { - putInternal(key, value, false); - } - - public void remove(int key) { - putInternal(key, null, true); - } - - public void removeAllFields() { - FastSparseSet<Integer>[] arr = elements[2]; - int[] arrnext = next[2]; - - for(int i=arr.length-1;i>=0;i--) { - FastSparseSet<Integer> val = arr[i]; - if(val != null) { - arr[i] = null; - size--; - } - arrnext[i] = 0; - } - } - - public void putInternal(final int key, final FastSparseSet<Integer> value, boolean remove) { - - int index = 0; - int ikey = key; - if(ikey < 0) { - index = 2; - ikey = -ikey; - } else if(ikey >= VarExprent.STACK_BASE) { - index = 1; - ikey -= VarExprent.STACK_BASE; - } - - FastSparseSet<Integer>[] arr = elements[index]; - if(ikey >= arr.length) { - if(remove) { - return; - } else { - arr = ensureCapacity(index, ikey+1, false); - } - } - - FastSparseSet<Integer> oldval = arr[ikey]; - arr[ikey] = value; - - int[] arrnext = next[index]; - - if(oldval == null && value != null) { - size++; - changeNext(arrnext, ikey, arrnext[ikey], ikey); - } else if(oldval != null && value == null) { - size--; - changeNext(arrnext, ikey, ikey, arrnext[ikey]); - } - } - - private void changeNext(int[] arrnext, int key, int oldnext, int newnext) { - for(int i=key-1;i>=0;i--) { - if(arrnext[i] == oldnext) { - arrnext[i] = newnext; - } else { - break; - } - } - } - - public boolean containsKey(int key) { - return get(key) != null; - } - - public FastSparseSet<Integer> get(int key) { - - int index = 0; - if(key < 0) { - index = 2; - key = -key; - } else if(key >= VarExprent.STACK_BASE) { - index = 1; - key -= VarExprent.STACK_BASE; - } - - FastSparseSet<Integer>[] arr = elements[index]; - - if(key < arr.length) { - return arr[key]; - } - return null; - } - - public void complement(SFormsFastMapDirect map) { - - for(int i=2;i>=0;i--) { - FastSparseSet<Integer>[] lstOwn = elements[i]; - - if(lstOwn.length == 0) { - continue; - } - - FastSparseSet<Integer>[] lstExtern = map.elements[i]; - int[] arrnext = next[i]; - - int pointer = 0; - do { - FastSparseSet<Integer> first = lstOwn[pointer]; - - if(first != null) { - if(pointer >= lstExtern.length) { - break; - } - FastSparseSet<Integer> second = lstExtern[pointer]; - - if(second != null) { - first.complement(second); - if(first.isEmpty()) { - lstOwn[pointer] = null; - size--; - changeNext(arrnext, pointer, pointer, arrnext[pointer]); - } - } - } - - pointer = arrnext[pointer]; - - } while(pointer != 0); - - } - - } - - public void intersection(SFormsFastMapDirect map) { - - for(int i=2;i>=0;i--) { - FastSparseSet<Integer>[] lstOwn = elements[i]; - - if(lstOwn.length == 0) { - continue; - } - - FastSparseSet<Integer>[] lstExtern = map.elements[i]; - int[] arrnext = next[i]; - - int pointer = 0; - do { - FastSparseSet<Integer> first = lstOwn[pointer]; - - if(first != null) { - FastSparseSet<Integer> second = null; - if(pointer < lstExtern.length) { - second = lstExtern[pointer]; - } - - if(second != null) { - first.intersection(second); - } - - if(second == null || first.isEmpty()) { - lstOwn[pointer] = null; - size--; - changeNext(arrnext, pointer, pointer, arrnext[pointer]); - } - } - - pointer = arrnext[pointer]; - - } while(pointer != 0); - - } - - } - - public void union(SFormsFastMapDirect map) { - - for(int i=2;i>=0;i--) { - FastSparseSet<Integer>[] lstExtern = map.elements[i]; - - if(lstExtern.length == 0) { - continue; - } - - FastSparseSet<Integer>[] lstOwn = elements[i]; - int[] arrnext = next[i]; - int[] arrnextExtern = map.next[i]; - - int pointer = 0; - do { - if(pointer >= lstOwn.length) { - lstOwn = ensureCapacity(i, lstExtern.length, true); - arrnext = next[i]; - } - - FastSparseSet<Integer> second = lstExtern[pointer]; - - if(second != null) { - FastSparseSet<Integer> first = lstOwn[pointer]; - - if(first == null) { - lstOwn[pointer] = second.getCopy(); - size++; - changeNext(arrnext, pointer, arrnext[pointer], pointer); - } else { - first.union(second); - } - } - - pointer = arrnextExtern[pointer]; - - } while(pointer != 0); - - } - - } - - public String toString() { - - StringBuilder buffer = new StringBuilder("{"); - - List<Entry<Integer, FastSparseSet<Integer>>> lst = entryList(); - if(lst != null) { - boolean first = true; - for(Entry<Integer, FastSparseSet<Integer>> entry : lst) { - if(!first) { - buffer.append(", "); - } else { - first = false; - } - - Set<Integer> set = entry.getValue().toPlainSet(); - buffer.append(entry.getKey()+"={"+set.toString()+"}"); - } - } - - buffer.append("}"); - return buffer.toString(); - } - - public List<Entry<Integer, FastSparseSet<Integer>>> entryList() { - List<Entry<Integer, FastSparseSet<Integer>>> list = new ArrayList<Entry<Integer, FastSparseSet<Integer>>>(); - - for(int i=2;i>=0;i--) { - int ikey = 0; - for(final FastSparseSet<Integer> ent : elements[i]) { - if(ent != null) { - final int key = i==0?ikey:(i==1?ikey+VarExprent.STACK_BASE:-ikey); - - list.add(new Entry<Integer, FastSparseSet<Integer>>() { - - private Integer var = key; - private FastSparseSet<Integer> val = ent; - - public Integer getKey() { - return var; - } - - public FastSparseSet<Integer> getValue() { - return val; - } - - public FastSparseSet<Integer> setValue(FastSparseSet<Integer> newvalue) { - return null; - } - }); - } - - ikey++; - } - } - - return list; - } - - private FastSparseSet<Integer>[] ensureCapacity(int index, int size, boolean exact) { - - FastSparseSet<Integer>[] arr = elements[index]; - int[] arrnext = next[index]; - - int minsize = size; - if(!exact) { - minsize = 2*arr.length/3 +1; - if(size > minsize) { - minsize = size; - } - } - - FastSparseSet<Integer>[] arrnew = new FastSparseSet[minsize]; - System.arraycopy(arr, 0, arrnew, 0, arr.length); - - int[] arrnextnew = new int[minsize]; - System.arraycopy(arrnext, 0, arrnextnew, 0, arrnext.length); - - elements[index] = arrnew; - next[index] = arrnextnew; - - return arrnew; - } - + private int size; + + private FastSparseSet<Integer>[][] elements = new FastSparseSet[3][]; + + private int[][] next = new int[3][]; + + public SFormsFastMapDirect() { + this(true); + } + + private SFormsFastMapDirect(boolean initialize) { + if (initialize) { + for (int i = 2; i >= 0; i--) { + elements[i] = new FastSparseSet[0]; + next[i] = new int[0]; + } + } + } + + public SFormsFastMapDirect(SFormsFastMapDirect map) { + for (int i = 2; i >= 0; i--) { + FastSparseSet<Integer>[] arr = map.elements[i]; + int[] arrnext = map.next[i]; + + int length = arr.length; + FastSparseSet<Integer>[] arrnew = new FastSparseSet[length]; + int[] arrnextnew = new int[length]; + + System.arraycopy(arr, 0, arrnew, 0, length); + System.arraycopy(arrnext, 0, arrnextnew, 0, length); + + elements[i] = arrnew; + next[i] = arrnextnew; + + size = map.size; + } + } + + public SFormsFastMapDirect getCopy() { + + SFormsFastMapDirect map = new SFormsFastMapDirect(false); + map.size = size; + + FastSparseSet[][] mapelements = map.elements; + int[][] mapnext = map.next; + + for (int i = 2; i >= 0; i--) { + FastSparseSet<Integer>[] arr = elements[i]; + int length = arr.length; + + if (length > 0) { + int[] arrnext = next[i]; + + FastSparseSet<Integer>[] arrnew = new FastSparseSet[length]; + int[] arrnextnew = new int[length]; + + System.arraycopy(arrnext, 0, arrnextnew, 0, length); + + mapelements[i] = arrnew; + mapnext[i] = arrnextnew; + + int pointer = 0; + do { + FastSparseSet<Integer> set = arr[pointer]; + if (set != null) { + arrnew[pointer] = set.getCopy(); + } + + pointer = arrnext[pointer]; + } + while (pointer != 0); + } + else { + mapelements[i] = new FastSparseSet[0]; + mapnext[i] = new int[0]; + } + } + + return map; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public void put(int key, FastSparseSet<Integer> value) { + putInternal(key, value, false); + } + + public void remove(int key) { + putInternal(key, null, true); + } + + public void removeAllFields() { + FastSparseSet<Integer>[] arr = elements[2]; + int[] arrnext = next[2]; + + for (int i = arr.length - 1; i >= 0; i--) { + FastSparseSet<Integer> val = arr[i]; + if (val != null) { + arr[i] = null; + size--; + } + arrnext[i] = 0; + } + } + + public void putInternal(final int key, final FastSparseSet<Integer> value, boolean remove) { + + int index = 0; + int ikey = key; + if (ikey < 0) { + index = 2; + ikey = -ikey; + } + else if (ikey >= VarExprent.STACK_BASE) { + index = 1; + ikey -= VarExprent.STACK_BASE; + } + + FastSparseSet<Integer>[] arr = elements[index]; + if (ikey >= arr.length) { + if (remove) { + return; + } + else { + arr = ensureCapacity(index, ikey + 1, false); + } + } + + FastSparseSet<Integer> oldval = arr[ikey]; + arr[ikey] = value; + + int[] arrnext = next[index]; + + if (oldval == null && value != null) { + size++; + changeNext(arrnext, ikey, arrnext[ikey], ikey); + } + else if (oldval != null && value == null) { + size--; + changeNext(arrnext, ikey, ikey, arrnext[ikey]); + } + } + + private void changeNext(int[] arrnext, int key, int oldnext, int newnext) { + for (int i = key - 1; i >= 0; i--) { + if (arrnext[i] == oldnext) { + arrnext[i] = newnext; + } + else { + break; + } + } + } + + public boolean containsKey(int key) { + return get(key) != null; + } + + public FastSparseSet<Integer> get(int key) { + + int index = 0; + if (key < 0) { + index = 2; + key = -key; + } + else if (key >= VarExprent.STACK_BASE) { + index = 1; + key -= VarExprent.STACK_BASE; + } + + FastSparseSet<Integer>[] arr = elements[index]; + + if (key < arr.length) { + return arr[key]; + } + return null; + } + + public void complement(SFormsFastMapDirect map) { + + for (int i = 2; i >= 0; i--) { + FastSparseSet<Integer>[] lstOwn = elements[i]; + + if (lstOwn.length == 0) { + continue; + } + + FastSparseSet<Integer>[] lstExtern = map.elements[i]; + int[] arrnext = next[i]; + + int pointer = 0; + do { + FastSparseSet<Integer> first = lstOwn[pointer]; + + if (first != null) { + if (pointer >= lstExtern.length) { + break; + } + FastSparseSet<Integer> second = lstExtern[pointer]; + + if (second != null) { + first.complement(second); + if (first.isEmpty()) { + lstOwn[pointer] = null; + size--; + changeNext(arrnext, pointer, pointer, arrnext[pointer]); + } + } + } + + pointer = arrnext[pointer]; + } + while (pointer != 0); + } + } + + public void intersection(SFormsFastMapDirect map) { + + for (int i = 2; i >= 0; i--) { + FastSparseSet<Integer>[] lstOwn = elements[i]; + + if (lstOwn.length == 0) { + continue; + } + + FastSparseSet<Integer>[] lstExtern = map.elements[i]; + int[] arrnext = next[i]; + + int pointer = 0; + do { + FastSparseSet<Integer> first = lstOwn[pointer]; + + if (first != null) { + FastSparseSet<Integer> second = null; + if (pointer < lstExtern.length) { + second = lstExtern[pointer]; + } + + if (second != null) { + first.intersection(second); + } + + if (second == null || first.isEmpty()) { + lstOwn[pointer] = null; + size--; + changeNext(arrnext, pointer, pointer, arrnext[pointer]); + } + } + + pointer = arrnext[pointer]; + } + while (pointer != 0); + } + } + + public void union(SFormsFastMapDirect map) { + + for (int i = 2; i >= 0; i--) { + FastSparseSet<Integer>[] lstExtern = map.elements[i]; + + if (lstExtern.length == 0) { + continue; + } + + FastSparseSet<Integer>[] lstOwn = elements[i]; + int[] arrnext = next[i]; + int[] arrnextExtern = map.next[i]; + + int pointer = 0; + do { + if (pointer >= lstOwn.length) { + lstOwn = ensureCapacity(i, lstExtern.length, true); + arrnext = next[i]; + } + + FastSparseSet<Integer> second = lstExtern[pointer]; + + if (second != null) { + FastSparseSet<Integer> first = lstOwn[pointer]; + + if (first == null) { + lstOwn[pointer] = second.getCopy(); + size++; + changeNext(arrnext, pointer, arrnext[pointer], pointer); + } + else { + first.union(second); + } + } + + pointer = arrnextExtern[pointer]; + } + while (pointer != 0); + } + } + + public String toString() { + + StringBuilder buffer = new StringBuilder("{"); + + List<Entry<Integer, FastSparseSet<Integer>>> lst = entryList(); + if (lst != null) { + boolean first = true; + for (Entry<Integer, FastSparseSet<Integer>> entry : lst) { + if (!first) { + buffer.append(", "); + } + else { + first = false; + } + + Set<Integer> set = entry.getValue().toPlainSet(); + buffer.append(entry.getKey() + "={" + set.toString() + "}"); + } + } + + buffer.append("}"); + return buffer.toString(); + } + + public List<Entry<Integer, FastSparseSet<Integer>>> entryList() { + List<Entry<Integer, FastSparseSet<Integer>>> list = new ArrayList<Entry<Integer, FastSparseSet<Integer>>>(); + + for (int i = 2; i >= 0; i--) { + int ikey = 0; + for (final FastSparseSet<Integer> ent : elements[i]) { + if (ent != null) { + final int key = i == 0 ? ikey : (i == 1 ? ikey + VarExprent.STACK_BASE : -ikey); + + list.add(new Entry<Integer, FastSparseSet<Integer>>() { + + private Integer var = key; + private FastSparseSet<Integer> val = ent; + + public Integer getKey() { + return var; + } + + public FastSparseSet<Integer> getValue() { + return val; + } + + public FastSparseSet<Integer> setValue(FastSparseSet<Integer> newvalue) { + return null; + } + }); + } + + ikey++; + } + } + + return list; + } + + private FastSparseSet<Integer>[] ensureCapacity(int index, int size, boolean exact) { + + FastSparseSet<Integer>[] arr = elements[index]; + int[] arrnext = next[index]; + + int minsize = size; + if (!exact) { + minsize = 2 * arr.length / 3 + 1; + if (size > minsize) { + minsize = size; + } + } + + FastSparseSet<Integer>[] arrnew = new FastSparseSet[minsize]; + System.arraycopy(arr, 0, arrnew, 0, arr.length); + + int[] arrnextnew = new int[minsize]; + System.arraycopy(arrnext, 0, arrnextnew, 0, arrnext.length); + + elements[index] = arrnew; + next[index] = arrnextnew; + + return arrnew; + } } diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapOld.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapOld.java index be3e7b7..834b3b6 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapOld.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapOld.java @@ -1,270 +1,290 @@ +/* + * 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.util; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; + import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; - public class SFormsFastMapOld<E> { - private List<ArrayList<Entry<Integer, E>>> lstElements = new ArrayList<ArrayList<Entry<Integer, E>>>(3); - - { - lstElements.add(new ArrayList<Entry<Integer, E>>()); - lstElements.add(new ArrayList<Entry<Integer, E>>()); - lstElements.add(new ArrayList<Entry<Integer, E>>()); - } - - public SFormsFastMapOld() {} - - public SFormsFastMapOld(SFormsFastMapOld<E> map) { - for(int i=2;i>=0;i--) { - lstElements.set(i, new ArrayList<Entry<Integer, E>>(map.lstElements.get(i))); - } - } - - public int size() { - int size = 0; - for(int i=2;i>=0;i--) { - size += lstElements.get(i).size(); - } - return size; - } - - public boolean isEmpty() { - - for(int i=2;i>=0;i--) { - if(!lstElements.get(i).isEmpty()) { - return false; - } - } - - return true; - } - - public void put(int key, E value) { - putInternal(key, value, false); - } - - public void remove(int key) { - putInternal(key, null, true); - } - - public void removeAllFields() { - lstElements.get(2).clear(); - } - - public void putInternal(final int key, final E value, boolean remove) { - - int index = 0; - int ikey = key; - if(ikey < 0) { - index = 2; - ikey = -ikey; - } else if(ikey >= VarExprent.STACK_BASE) { - index = 1; - ikey -= VarExprent.STACK_BASE; - } - - ArrayList<Entry<Integer, E>> lst = lstElements.get(index); - if(ikey >= lst.size()) { - if(remove) { - return; - } else { - ensureCapacity(lst, ikey+1, false); - } - } - - lst.set(ikey, value==null?null:new Entry<Integer, E>() { - - private Integer var = key; - private E val = value; - - public Integer getKey() { - return var; - } - - public E getValue() { - return val; - } - - public E setValue(E newvalue) { - val = newvalue; - return null; - }}); - } - - public boolean containsKey(int key) { - return get(key) != null; - } - - public E get(int key) { - - int index = 0; - if(key < 0) { - index = 2; - key = -key; - } else if(key >= VarExprent.STACK_BASE) { - index = 1; - key -= VarExprent.STACK_BASE; - } - - ArrayList<Entry<Integer, E>> lst = lstElements.get(index); - - Entry<Integer, E> ent; - if(key < lst.size() && (ent = lst.get(key)) != null) { - return ent.getValue(); - } - return null; - } - - public void union(SFormsFastMapOld<E> map, IElementsUnion<E> union) { - - for(int i=2;i>=0;i--) { - ArrayList<Entry<Integer, E>> lstOwn = lstElements.get(i); - ArrayList<Entry<Integer, E>> lstExtern = map.lstElements.get(i); - - int ownsize = lstOwn.size(); - int externsize = lstExtern.size(); - - int minsize = ownsize>externsize?externsize:ownsize; - - for(int j=minsize-1;j>=0;j--) { - Entry<Integer, E> second = lstExtern.get(j); - - if(second != null) { - Entry<Integer, E> first = lstOwn.get(j); - - if(first == null) { - putInternal(second.getKey(), union.copy(second.getValue()), false); - } else { - first.setValue(union.union(first.getValue(), second.getValue())); - } - } - } - - if(externsize > minsize) { -// ensureCapacity(lstOwn, externsize, true); -// lstOwn.addAll(lstExtern.subList(minsize, externsize)); - - for(int j=minsize;j<externsize;j++) { - Entry<Integer, E> second = lstExtern.get(j); -// if(first != null) { -// first.setValue(union.copy(first.getValue())); -// } - - if(second != null) { - putInternal(second.getKey(), union.copy(second.getValue()), false); - } -// lstOwn.add(lstExtern.get(j)); - } - } - } - - } - - public List<Entry<Integer, E>> entryList() { - List<Entry<Integer, E>> list = new ArrayList<Entry<Integer, E>>(); - - for(int i=2;i>=0;i--) { - for(Entry<Integer, E> ent : lstElements.get(i)) { - if(ent != null) { - list.add(ent); - } - } - } - - return list; - } - -// public SFormsFastMapIterator iterator() { -// return new SFormsFastMapIterator(); -// } - - private void ensureCapacity(ArrayList<Entry<Integer, E>> lst, int size, boolean exact) { - - if(!exact) { - int minsize = 2*lst.size()/3 +1; - if(minsize > size) { - size = minsize; - } - } - - lst.ensureCapacity(size); - for(int i=size-lst.size();i>0;i--) { - lst.add(null); - } - } - - public static interface IElementsUnion<E> { - public E union(E first, E second); - public E copy(E element); - } - -// public class SFormsFastMapIterator implements Iterator<Entry<Integer, E>> { -// -// private int[] pointer = new int[]{0, -1}; -// private int[] next_pointer = null; -// -// private int[] getNextIndex(int list, int index) { -// -// while(list < 3) { -// ArrayList<E> lst = lstElements.get(list); -// -// while(++index < lst.size()) { -// E element = lst.get(index); -// if(element != null) { -// return new int[] {list, index}; -// } -// } -// -// index = -1; -// list++; -// } -// -// return null; -// } -// -// public boolean hasNext() { -// next_pointer = getNextIndex(pointer[0], pointer[1]); -// return (next_pointer != null); -// } -// -// public Entry<Integer, E> next() { -// if(next_pointer != null) { -// pointer = next_pointer; -// } else { -// int[] nextpointer = getNextIndex(pointer[0], pointer[1]); -// if(nextpointer != null) { -// pointer = nextpointer; -// } else { -// return null; -// } -// } -// -// next_pointer = null; -// -// return new Entry<Integer, E>() { -// public Integer getKey() { -// return null; -// } -// -// public E getValue() { -// return null; -// } -// -// public E setValue(E value) { -// throw new RuntimeException("not implemented!"); -// } -// }; -// //lstElements.get(pointer[0]).get(pointer[1]); -// } -// -// public void remove() { -// throw new RuntimeException("not implemented!"); -// } -// -// } - + private List<ArrayList<Entry<Integer, E>>> lstElements = new ArrayList<ArrayList<Entry<Integer, E>>>(3); + + { + lstElements.add(new ArrayList<Entry<Integer, E>>()); + lstElements.add(new ArrayList<Entry<Integer, E>>()); + lstElements.add(new ArrayList<Entry<Integer, E>>()); + } + + public SFormsFastMapOld() { + } + + public SFormsFastMapOld(SFormsFastMapOld<E> map) { + for (int i = 2; i >= 0; i--) { + lstElements.set(i, new ArrayList<Entry<Integer, E>>(map.lstElements.get(i))); + } + } + + public int size() { + int size = 0; + for (int i = 2; i >= 0; i--) { + size += lstElements.get(i).size(); + } + return size; + } + + public boolean isEmpty() { + + for (int i = 2; i >= 0; i--) { + if (!lstElements.get(i).isEmpty()) { + return false; + } + } + + return true; + } + + public void put(int key, E value) { + putInternal(key, value, false); + } + + public void remove(int key) { + putInternal(key, null, true); + } + + public void removeAllFields() { + lstElements.get(2).clear(); + } + + public void putInternal(final int key, final E value, boolean remove) { + + int index = 0; + int ikey = key; + if (ikey < 0) { + index = 2; + ikey = -ikey; + } + else if (ikey >= VarExprent.STACK_BASE) { + index = 1; + ikey -= VarExprent.STACK_BASE; + } + + ArrayList<Entry<Integer, E>> lst = lstElements.get(index); + if (ikey >= lst.size()) { + if (remove) { + return; + } + else { + ensureCapacity(lst, ikey + 1, false); + } + } + + lst.set(ikey, value == null ? null : new Entry<Integer, E>() { + + private Integer var = key; + private E val = value; + + public Integer getKey() { + return var; + } + + public E getValue() { + return val; + } + + public E setValue(E newvalue) { + val = newvalue; + return null; + } + }); + } + + public boolean containsKey(int key) { + return get(key) != null; + } + + public E get(int key) { + + int index = 0; + if (key < 0) { + index = 2; + key = -key; + } + else if (key >= VarExprent.STACK_BASE) { + index = 1; + key -= VarExprent.STACK_BASE; + } + + ArrayList<Entry<Integer, E>> lst = lstElements.get(index); + + Entry<Integer, E> ent; + if (key < lst.size() && (ent = lst.get(key)) != null) { + return ent.getValue(); + } + return null; + } + + public void union(SFormsFastMapOld<E> map, IElementsUnion<E> union) { + + for (int i = 2; i >= 0; i--) { + ArrayList<Entry<Integer, E>> lstOwn = lstElements.get(i); + ArrayList<Entry<Integer, E>> lstExtern = map.lstElements.get(i); + + int ownsize = lstOwn.size(); + int externsize = lstExtern.size(); + + int minsize = ownsize > externsize ? externsize : ownsize; + + for (int j = minsize - 1; j >= 0; j--) { + Entry<Integer, E> second = lstExtern.get(j); + + if (second != null) { + Entry<Integer, E> first = lstOwn.get(j); + + if (first == null) { + putInternal(second.getKey(), union.copy(second.getValue()), false); + } + else { + first.setValue(union.union(first.getValue(), second.getValue())); + } + } + } + + if (externsize > minsize) { + // ensureCapacity(lstOwn, externsize, true); + // lstOwn.addAll(lstExtern.subList(minsize, externsize)); + + for (int j = minsize; j < externsize; j++) { + Entry<Integer, E> second = lstExtern.get(j); + // if(first != null) { + // first.setValue(union.copy(first.getValue())); + // } + + if (second != null) { + putInternal(second.getKey(), union.copy(second.getValue()), false); + } + // lstOwn.add(lstExtern.get(j)); + } + } + } + } + + public List<Entry<Integer, E>> entryList() { + List<Entry<Integer, E>> list = new ArrayList<Entry<Integer, E>>(); + + for (int i = 2; i >= 0; i--) { + for (Entry<Integer, E> ent : lstElements.get(i)) { + if (ent != null) { + list.add(ent); + } + } + } + + return list; + } + + // public SFormsFastMapIterator iterator() { + // return new SFormsFastMapIterator(); + // } + + private void ensureCapacity(ArrayList<Entry<Integer, E>> lst, int size, boolean exact) { + + if (!exact) { + int minsize = 2 * lst.size() / 3 + 1; + if (minsize > size) { + size = minsize; + } + } + + lst.ensureCapacity(size); + for (int i = size - lst.size(); i > 0; i--) { + lst.add(null); + } + } + + public static interface IElementsUnion<E> { + public E union(E first, E second); + + public E copy(E element); + } + + // public class SFormsFastMapIterator implements Iterator<Entry<Integer, E>> { + // + // private int[] pointer = new int[]{0, -1}; + // private int[] next_pointer = null; + // + // private int[] getNextIndex(int list, int index) { + // + // while(list < 3) { + // ArrayList<E> lst = lstElements.get(list); + // + // while(++index < lst.size()) { + // E element = lst.get(index); + // if(element != null) { + // return new int[] {list, index}; + // } + // } + // + // index = -1; + // list++; + // } + // + // return null; + // } + // + // public boolean hasNext() { + // next_pointer = getNextIndex(pointer[0], pointer[1]); + // return (next_pointer != null); + // } + // + // public Entry<Integer, E> next() { + // if(next_pointer != null) { + // pointer = next_pointer; + // } else { + // int[] nextpointer = getNextIndex(pointer[0], pointer[1]); + // if(nextpointer != null) { + // pointer = nextpointer; + // } else { + // return null; + // } + // } + // + // next_pointer = null; + // + // return new Entry<Integer, E>() { + // public Integer getKey() { + // return null; + // } + // + // public E getValue() { + // return null; + // } + // + // public E setValue(E value) { + // throw new RuntimeException("not implemented!"); + // } + // }; + // //lstElements.get(pointer[0]).get(pointer[1]); + // } + // + // public void remove() { + // throw new RuntimeException("not implemented!"); + // } + // + // } } diff --git a/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java b/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java index 8542019..fcb579d 100644 --- a/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java +++ b/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java @@ -1,17 +1,18 @@ /* - * Fernflower - The Analytical Java Decompiler - * http://www.reversed-java.com + * Copyright 2000-2014 JetBrains s.r.o. * - * (C) 2008 - 2010, Stiver + * 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 * - * This software is NEITHER public domain NOR free software - * as per GNU License. See license.txt for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * This software is distributed WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. + * 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.util; import java.util.ArrayList; @@ -20,185 +21,184 @@ import java.util.Collections; import java.util.HashMap; public class VBStyleCollection<E, K> extends ArrayList<E> { - - private HashMap<K, Integer> map = new HashMap<K, Integer>(); - - private ArrayList<K> lstKeys = new ArrayList<K>(); - - public VBStyleCollection() { - super(); - } - - public VBStyleCollection(int initialCapacity) { - super(initialCapacity); - lstKeys = new ArrayList<K>(initialCapacity); - map = new HashMap<K, Integer>(initialCapacity); - } - - public VBStyleCollection(Collection<E> c) { - super(c); - } - - public boolean add(E element) { - lstKeys.add(null); - super.add(element); - return true; - } - - public boolean remove(Object element) { // TODO: error on void remove(E element) - throw new RuntimeException("not implemented!"); - } - - public boolean addAll(Collection<? extends E> c) { - for(int i=c.size()-1;i>=0;i--) { - lstKeys.add(null); - } - return super.addAll(c); - } - - public void addAllWithKey(VBStyleCollection<E, K> c) { - for(int i=0;i<c.size();i++) { - addWithKey(c.get(i), c.getKey(i)); - } - } - - public void addAllWithKey(Collection<E> elements, Collection<K> keys) { - int index = super.size(); - - for(K key : keys) { - map.put(key, index++); - } - - super.addAll(elements); - lstKeys.addAll(keys); - } - - public void addWithKey(E element, K key) { - map.put(key, super.size()); - super.add(element); - lstKeys.add(key); - } - - // TODO: speed up the method - public E putWithKey(E element, K key) { - Integer index = map.get(key); - if (index == null) { - addWithKey(element, key); - } else { - return super.set(index, element); - } - return null; - } - - public void add(int index, E element) { - addToListIndex(index, 1); - lstKeys.add(index, null); - super.add(index, element); - } - - public void addWithKeyAndIndex(int index, E element, K key) { - addToListIndex(index, 1); - map.put(key, new Integer(index)); - super.add(index, element); - lstKeys.add(index, key); - } - - public void removeWithKey(K key) { - int index = ((Integer) map.get(key)).intValue(); - addToListIndex(index + 1, -1); - super.remove(index); - lstKeys.remove(index); - map.remove(key); - } - - public E remove(int index) { - addToListIndex(index + 1, -1); - Object obj = lstKeys.get(index); - if (obj != null) { - map.remove(obj); - } - lstKeys.remove(index); - return super.remove(index); - } - - public E getWithKey(K key) { - Integer index = map.get(key); - if (index == null) { - return null; - } - return super.get(index.intValue()); - } - - public int getIndexByKey(K key) { - return map.get(key).intValue(); - } - - public E getLast() { - return super.get(super.size()-1); - } - - public boolean containsKey(K key) { - return map.containsKey(key); - } - - public void clear() { - map.clear(); - lstKeys.clear(); - super.clear(); - } - - public VBStyleCollection<E, K> clone() { - VBStyleCollection<E, K> c = new VBStyleCollection<E, K>(); - c.addAll(new ArrayList<E>(this)); - c.setMap(new HashMap<K, Integer>(map)); - c.setLstKeys(new ArrayList<K>(lstKeys)); - return c; - } - - public void swap(int index1, int index2) { - - Collections.swap(this, index1, index2); - Collections.swap(lstKeys, index1, index2); - - K key = lstKeys.get(index1); - if(key!=null) { - map.put(key, new Integer(index1)); - } - - key = lstKeys.get(index2); - if(key!=null) { - map.put(key, new Integer(index2)); - } - - } - - public HashMap<K, Integer> getMap() { - return map; - } - - public void setMap(HashMap<K, Integer> map) { - this.map = map; - } - - public K getKey(int index) { - return lstKeys.get(index); - } - - public ArrayList<K> getLstKeys() { - return lstKeys; - } - - public void setLstKeys(ArrayList<K> lstKeys) { - this.lstKeys = lstKeys; - } - - private void addToListIndex(int index, int diff) { - for (int i = lstKeys.size() - 1; i >= index; i--) { - K obj = lstKeys.get(i); - if (obj != null) { - map.put(obj, new Integer(i + diff)); - } - } - } - + + private HashMap<K, Integer> map = new HashMap<K, Integer>(); + + private ArrayList<K> lstKeys = new ArrayList<K>(); + + public VBStyleCollection() { + super(); + } + + public VBStyleCollection(int initialCapacity) { + super(initialCapacity); + lstKeys = new ArrayList<K>(initialCapacity); + map = new HashMap<K, Integer>(initialCapacity); + } + + public VBStyleCollection(Collection<E> c) { + super(c); + } + + public boolean add(E element) { + lstKeys.add(null); + super.add(element); + return true; + } + + public boolean remove(Object element) { // TODO: error on void remove(E element) + throw new RuntimeException("not implemented!"); + } + + public boolean addAll(Collection<? extends E> c) { + for (int i = c.size() - 1; i >= 0; i--) { + lstKeys.add(null); + } + return super.addAll(c); + } + + public void addAllWithKey(VBStyleCollection<E, K> c) { + for (int i = 0; i < c.size(); i++) { + addWithKey(c.get(i), c.getKey(i)); + } + } + + public void addAllWithKey(Collection<E> elements, Collection<K> keys) { + int index = super.size(); + + for (K key : keys) { + map.put(key, index++); + } + + super.addAll(elements); + lstKeys.addAll(keys); + } + + public void addWithKey(E element, K key) { + map.put(key, super.size()); + super.add(element); + lstKeys.add(key); + } + + // TODO: speed up the method + public E putWithKey(E element, K key) { + Integer index = map.get(key); + if (index == null) { + addWithKey(element, key); + } + else { + return super.set(index, element); + } + return null; + } + + public void add(int index, E element) { + addToListIndex(index, 1); + lstKeys.add(index, null); + super.add(index, element); + } + + public void addWithKeyAndIndex(int index, E element, K key) { + addToListIndex(index, 1); + map.put(key, new Integer(index)); + super.add(index, element); + lstKeys.add(index, key); + } + + public void removeWithKey(K key) { + int index = ((Integer)map.get(key)).intValue(); + addToListIndex(index + 1, -1); + super.remove(index); + lstKeys.remove(index); + map.remove(key); + } + + public E remove(int index) { + addToListIndex(index + 1, -1); + Object obj = lstKeys.get(index); + if (obj != null) { + map.remove(obj); + } + lstKeys.remove(index); + return super.remove(index); + } + + public E getWithKey(K key) { + Integer index = map.get(key); + if (index == null) { + return null; + } + return super.get(index.intValue()); + } + + public int getIndexByKey(K key) { + return map.get(key).intValue(); + } + + public E getLast() { + return super.get(super.size() - 1); + } + + public boolean containsKey(K key) { + return map.containsKey(key); + } + + public void clear() { + map.clear(); + lstKeys.clear(); + super.clear(); + } + + public VBStyleCollection<E, K> clone() { + VBStyleCollection<E, K> c = new VBStyleCollection<E, K>(); + c.addAll(new ArrayList<E>(this)); + c.setMap(new HashMap<K, Integer>(map)); + c.setLstKeys(new ArrayList<K>(lstKeys)); + return c; + } + + public void swap(int index1, int index2) { + + Collections.swap(this, index1, index2); + Collections.swap(lstKeys, index1, index2); + + K key = lstKeys.get(index1); + if (key != null) { + map.put(key, new Integer(index1)); + } + + key = lstKeys.get(index2); + if (key != null) { + map.put(key, new Integer(index2)); + } + } + + public HashMap<K, Integer> getMap() { + return map; + } + + public void setMap(HashMap<K, Integer> map) { + this.map = map; + } + + public K getKey(int index) { + return lstKeys.get(index); + } + + public ArrayList<K> getLstKeys() { + return lstKeys; + } + + public void setLstKeys(ArrayList<K> lstKeys) { + this.lstKeys = lstKeys; + } + + private void addToListIndex(int index, int diff) { + for (int i = lstKeys.size() - 1; i >= index; i--) { + K obj = lstKeys.get(i); + if (obj != null) { + map.put(obj, new Integer(i + diff)); + } + } + } } |