[*] Added a few gross patches to the intellij source tree

[*] Lazily moved intellijs source tree over
This commit is contained in:
Reece Wilson 2021-09-26 00:06:46 +01:00
parent c6921030f4
commit d4d5a1041b
227 changed files with 10320 additions and 15275 deletions

23
pom.xml
View File

@ -13,37 +13,20 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>eclipse-formatter</artifactId>
<version>3.14.0-spigotmc-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>./src</sourceDirectory> <sourceDirectory>./src</sourceDirectory>
<plugins> <plugins>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>determiner</artifactId>
<version>0.1</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.2</version>
<configuration> <configuration>
<source>1.6</source> <source>9</source>
<target>1.6</target> <target>9</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@ -1,31 +1,22 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
@SuppressWarnings({"unused", "SpellCheckingInspection"})
public interface CodeConstants { public interface CodeConstants {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// BYTECODE VERSIONS // BYTECODE VERSIONS
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
int BYTECODE_JAVA_LE_4 = 1; int BYTECODE_JAVA_LE_4 = 48;
int BYTECODE_JAVA_5 = 2; int BYTECODE_JAVA_5 = 49;
int BYTECODE_JAVA_6 = 3; int BYTECODE_JAVA_6 = 50;
int BYTECODE_JAVA_7 = 4; int BYTECODE_JAVA_7 = 51;
int BYTECODE_JAVA_8 = 5; int BYTECODE_JAVA_8 = 52;
int BYTECODE_JAVA_9 = 53;
int BYTECODE_JAVA_10 = 54;
int BYTECODE_JAVA_11 = 55;
int BYTECODE_JAVA_12 = 56;
int BYTECODE_JAVA_13 = 57;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// VARIABLE TYPES // VARIABLE TYPES
@ -63,20 +54,6 @@ public interface CodeConstants {
int TYPE_FAMILY_DOUBLE = 5; int TYPE_FAMILY_DOUBLE = 5;
int TYPE_FAMILY_OBJECT = 6; int TYPE_FAMILY_OBJECT = 6;
// ----------------------------------------------------------------------
// MODULE CONSTANTS
// ----------------------------------------------------------------------
int STACKSIZE_SIMPLE = 1;
int STACKSIZE_DOUBLE = 2;
int VAR_LOCAL = 0;
int VAR_STACK = 1;
int VAR_WRITE = 0;
int VAR_READ = 1;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// ACCESS FLAGS // ACCESS FLAGS
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -87,6 +64,7 @@ public interface CodeConstants {
int ACC_STATIC = 0x0008; int ACC_STATIC = 0x0008;
int ACC_FINAL = 0x0010; int ACC_FINAL = 0x0010;
int ACC_SYNCHRONIZED = 0x0020; int ACC_SYNCHRONIZED = 0x0020;
int ACC_OPEN = 0x0020;
int ACC_NATIVE = 0x0100; int ACC_NATIVE = 0x0100;
int ACC_ABSTRACT = 0x0400; int ACC_ABSTRACT = 0x0400;
int ACC_STRICT = 0x0800; int ACC_STRICT = 0x0800;
@ -97,6 +75,8 @@ public interface CodeConstants {
int ACC_SYNTHETIC = 0x1000; int ACC_SYNTHETIC = 0x1000;
int ACC_ANNOTATION = 0x2000; int ACC_ANNOTATION = 0x2000;
int ACC_ENUM = 0x4000; int ACC_ENUM = 0x4000;
int ACC_MANDATED = 0x8000;
int ACC_MODULE = 0x8000;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// CLASS FLAGS // CLASS FLAGS
@ -105,17 +85,6 @@ public interface CodeConstants {
int ACC_SUPER = 0x0020; int ACC_SUPER = 0x0020;
int ACC_INTERFACE = 0x0200; int ACC_INTERFACE = 0x0200;
// ----------------------------------------------------------------------
// DEPENDENCY CONSTANTS
// ----------------------------------------------------------------------
int DEP_CONSTANT = 0;
int DEP_UNKNOWN = 1;
int DEP_GENERAL = 2;
int DEP_PARAMS = 4;
int DEP_STATIC = 8;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// INSTRUCTION GROUPS // INSTRUCTION GROUPS
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -145,6 +114,8 @@ public interface CodeConstants {
int CONSTANT_MethodHandle = 15; int CONSTANT_MethodHandle = 15;
int CONSTANT_MethodType = 16; int CONSTANT_MethodType = 16;
int CONSTANT_InvokeDynamic = 18; int CONSTANT_InvokeDynamic = 18;
int CONSTANT_Module = 19;
int CONSTANT_Package = 20;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// MethodHandle reference_kind values // MethodHandle reference_kind values
@ -351,7 +322,6 @@ public interface CodeConstants {
int opc_invokestatic = 184; int opc_invokestatic = 184;
int opc_invokeinterface = 185; int opc_invokeinterface = 185;
int opc_invokedynamic = 186; int opc_invokedynamic = 186;
int opc_xxxunusedxxx = 186;
int opc_new = 187; int opc_new = 187;
int opc_newarray = 188; int opc_newarray = 188;
int opc_anewarray = 189; int opc_anewarray = 189;
@ -367,4 +337,7 @@ public interface CodeConstants {
int opc_ifnonnull = 199; int opc_ifnonnull = 199;
int opc_goto_w = 200; int opc_goto_w = 200;
int opc_jsr_w = 201; int opc_jsr_w = 201;
}
String CLINIT_NAME = "<clinit>";
String INIT_NAME = "<init>";
}

View File

@ -1,482 +0,0 @@
/*
* 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;
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) {
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
}
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 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"
};
}

View File

@ -1,27 +1,9 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import java.io.DataOutputStream;
import java.io.IOException;
public class ExceptionHandler { public class ExceptionHandler {
public int from = 0; public int from = 0;
public int to = 0; public int to = 0;
public int handler = 0; public int handler = 0;
@ -30,32 +12,12 @@ public class ExceptionHandler {
public int to_instr = 0; public int to_instr = 0;
public int handler_instr = 0; public int handler_instr = 0;
public int class_index = 0;
public String exceptionClass = null; 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() { public String toString() {
String new_line_separator = DecompilerContext.getNewLineSeparator(); String new_line_separator = DecompilerContext.getNewLineSeparator();
return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator + return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator +
"from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator + "from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator +
"exceptionClass: " + exceptionClass + new_line_separator; "exceptionClass: " + exceptionClass + new_line_separator;
} }
} }

View File

@ -1,58 +1,19 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.code.interpreter.Util; import java.util.Collections;
import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ExceptionTable { public class ExceptionTable {
public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList());
private List<ExceptionHandler> handlers = new ArrayList<ExceptionHandler>(); private final List<ExceptionHandler> handlers;
public ExceptionTable() {
}
public ExceptionTable(List<ExceptionHandler> handlers) { public ExceptionTable(List<ExceptionHandler> handlers) {
this.handlers = 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() { public List<ExceptionHandler> getHandlers() {
return handlers; return handlers;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.util.VBStyleCollection; import org.jetbrains.java.decompiler.util.VBStyleCollection;
@ -25,7 +11,7 @@ public class FullInstructionSequence extends InstructionSequence {
// ***************************************************************************** // *****************************************************************************
public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) { public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) {
this.collinstr = collinstr; super(collinstr);
this.exceptionTable = extable; this.exceptionTable = extable;
// translate raw exception handlers to instr // translate raw exception handlers to instr

View File

@ -1,36 +0,0 @@
/*
* 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;
import java.io.DataOutputStream;
import java.io.IOException;
/*
* opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt,
* opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull
*/
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;
}
}

View File

@ -1,126 +1,87 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import java.io.DataOutputStream; import org.jetbrains.java.decompiler.util.TextUtil;
import java.io.IOException;
public class Instruction implements CodeConstants { public class Instruction implements CodeConstants {
public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) {
// ***************************************************************************** if (opcode >= opc_ifeq && opcode <= opc_if_acmpne ||
// public fields opcode == opc_ifnull || opcode == opc_ifnonnull ||
// ***************************************************************************** opcode == opc_jsr || opcode == opc_jsr_w ||
opcode == opc_goto || opcode == opc_goto_w) {
public int opcode; return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands);
}
public int group = CodeConstants.GROUP_GENERAL; else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) {
return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands);
public boolean wide = false; }
else {
public int bytecode_version = BYTECODE_JAVA_LE_4; return new Instruction(opcode, group, wide, bytecodeVersion, operands);
}
// *****************************************************************************
// private fields
// *****************************************************************************
private int[] operands = null;
// *****************************************************************************
// public methods
// *****************************************************************************
public Instruction() {
} }
public int length() { public static boolean equals(Instruction i1, Instruction i2) {
return 1; return i1 != null && i2 != null &&
(i1 == i2 ||
i1.opcode == i2.opcode &&
i1.wide == i2.wide &&
i1.operandsCount() == i2.operandsCount());
} }
public final int opcode;
public final int group;
public final boolean wide;
public final int bytecodeVersion;
protected final int[] operands;
public Instruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
this.opcode = opcode;
this.group = group;
this.wide = wide;
this.bytecodeVersion = bytecodeVersion;
this.operands = operands;
}
public void initInstruction(InstructionSequence seq) { }
public int operandsCount() { public int operandsCount() {
return (operands == null) ? 0 : operands.length; return operands == null ? 0 : operands.length;
} }
public int getOperand(int index) { public int operand(int index) {
return operands[index]; return operands[index];
} }
public Instruction clone() { public boolean canFallThrough() {
return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands == null ? null : operands.clone()); 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;
} }
@Override
public String toString() { public String toString() {
StringBuilder res = new StringBuilder();
String res = wide ? "@wide " : ""; if (wide) res.append("@wide ");
res += "@" + ConstantsUtil.getName(opcode); res.append("@").append(TextUtil.getInstructionName(opcode));
int len = operandsCount(); int len = operandsCount();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int op = operands[i]; int op = operands[i];
if (op < 0) { if (op < 0) {
res += " -" + Integer.toHexString(-op); res.append(" -").append(Integer.toHexString(-op));
} }
else { else {
res += " " + Integer.toHexString(op); res.append(" ").append(Integer.toHexString(op));
} }
} }
return res; return res.toString();
} }
public boolean canFallthrough() { @Override
return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret && @SuppressWarnings("MethodDoesntCallSuperMethod")
!(opcode >= opc_ireturn && opcode <= opc_return) && opcode != opc_athrow public Instruction clone() {
&& opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch; return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone());
} }
}
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;
}
}

View File

@ -1,50 +1,36 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.code.interpreter.Util;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.util.TextUtil;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection; 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 { public abstract class InstructionSequence {
// ***************************************************************************** // *****************************************************************************
// private fields // private fields
// ***************************************************************************** // *****************************************************************************
protected VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); protected final VBStyleCollection<Instruction, Integer> collinstr;
protected int pointer = 0; protected int pointer = 0;
protected ExceptionTable exceptionTable = new ExceptionTable(); protected ExceptionTable exceptionTable = ExceptionTable.EMPTY;
protected InstructionSequence() {
this(new VBStyleCollection<>());
}
protected InstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {
this.collinstr = collinstr;
}
// ***************************************************************************** // *****************************************************************************
// public methods // public methods
// ***************************************************************************** // *****************************************************************************
// to nbe overwritten // to nbe overwritten
@Override
public InstructionSequence clone() { public InstructionSequence clone() {
return null; return null;
} }
@ -52,7 +38,7 @@ public abstract class InstructionSequence {
public void clear() { public void clear() {
collinstr.clear(); collinstr.clear();
pointer = 0; pointer = 0;
exceptionTable = new ExceptionTable(); exceptionTable = ExceptionTable.EMPTY;
} }
public void addInstruction(Instruction inst, int offset) { public void addInstruction(Instruction inst, int offset) {
@ -73,8 +59,10 @@ public abstract class InstructionSequence {
collinstr.remove(index); collinstr.remove(index);
} }
public Instruction getCurrentInstr() { public void removeLast() {
return collinstr.get(pointer); if (!collinstr.isEmpty()) {
collinstr.remove(collinstr.size() - 1);
}
} }
public Instruction getInstr(int index) { public Instruction getInstr(int index) {
@ -85,16 +73,12 @@ public abstract class InstructionSequence {
return collinstr.getLast(); return collinstr.getLast();
} }
public int getCurrentOffset() { public int getOffset(int index) {
return collinstr.getKey(pointer).intValue(); return collinstr.getKey(index);
}
public int getOffset(int index) {
return collinstr.getKey(index).intValue();
} }
public int getPointerByAbsOffset(int offset) { public int getPointerByAbsOffset(int offset) {
Integer absoffset = new Integer(offset); Integer absoffset = offset;
if (collinstr.containsKey(absoffset)) { if (collinstr.containsKey(absoffset)) {
return collinstr.getIndexByKey(absoffset); return collinstr.getIndexByKey(absoffset);
} }
@ -104,7 +88,7 @@ public int getOffset(int index) {
} }
public int getPointerByRelOffset(int offset) { public int getPointerByRelOffset(int offset) {
Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset); Integer absoffset = collinstr.getKey(pointer) + offset;
if (collinstr.containsKey(absoffset)) { if (collinstr.containsKey(absoffset)) {
return collinstr.getIndexByKey(absoffset); return collinstr.getIndexByKey(absoffset);
} }
@ -113,13 +97,6 @@ public int getOffset(int index) {
} }
} }
public void setPointerByAbsOffset(int offset) {
Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset);
if (collinstr.containsKey(absoffset)) {
pointer = collinstr.getIndexByKey(absoffset);
}
}
public int length() { public int length() {
return collinstr.size(); return collinstr.size();
} }
@ -143,7 +120,7 @@ public int getOffset(int index) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (int i = 0; i < collinstr.size(); i++) { for (int i = 0; i < collinstr.size(); i++) {
buf.append(InterpreterUtil.getIndentString(indent)); buf.append(TextUtil.getIndentString(indent));
buf.append(collinstr.getKey(i).intValue()); buf.append(collinstr.getKey(i).intValue());
buf.append(": "); buf.append(": ");
buf.append(collinstr.get(i).toString()); buf.append(collinstr.get(i).toString());
@ -153,58 +130,6 @@ public int getOffset(int index) {
return buf.toString(); return buf.toString();
} }
public void writeCodeToStream(DataOutputStream out) throws IOException {
for (int i = 0; i < collinstr.size(); i++) {
collinstr.get(i).writeToStream(out, 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++) {
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 // getter and setter methods
// ***************************************************************************** // *****************************************************************************
@ -220,8 +145,4 @@ public int getOffset(int index) {
public ExceptionTable getExceptionTable() { public ExceptionTable getExceptionTable() {
return exceptionTable; return exceptionTable;
} }
}
public void setExceptionTable(ExceptionTable exceptionTable) {
this.exceptionTable = exceptionTable;
}
}

View File

@ -1,42 +1,22 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
/*
* opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt,
* opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull
* opc_goto, opc_jsr, opc_goto_w, opc_jsr_w
*/
public class JumpInstruction extends Instruction { public class JumpInstruction extends Instruction {
public int destination; public int destination;
public JumpInstruction() { public JumpInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
super(opcode, group, wide, bytecodeVersion, operands);
} }
@Override
public void initInstruction(InstructionSequence seq) { public void initInstruction(InstructionSequence seq) {
destination = seq.getPointerByRelOffset(this.getOperand(0)); destination = seq.getPointerByRelOffset(this.operand(0));
} }
@Override
public JumpInstruction clone() { public JumpInstruction clone() {
JumpInstruction newinstr = (JumpInstruction)super.clone(); JumpInstruction copy = (JumpInstruction)super.clone();
copy.destination = destination;
newinstr.destination = destination; return copy;
return newinstr;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.util.VBStyleCollection; import org.jetbrains.java.decompiler.util.VBStyleCollection;
@ -23,17 +9,14 @@ public class SimpleInstructionSequence extends InstructionSequence {
} }
public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) { public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {
this.collinstr = collinstr; super(collinstr);
} }
@Override
public SimpleInstructionSequence clone() { public SimpleInstructionSequence clone() {
SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone());
newseq.setPointer(this.getPointer()); newseq.setPointer(this.getPointer());
return newseq; return newseq;
} }
public void removeInstruction(int index) {
collinstr.remove(index);
}
} }

View File

@ -1,97 +1,61 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.code;
/*
* opc_tableswitch, lookupswitch
*/
public class SwitchInstruction extends Instruction { public class SwitchInstruction extends Instruction {
private int[] destinations; private int[] destinations;
private int[] values; private int[] values;
private int defaultDestination;
private int defaultdest; public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
super(opcode, group, wide, bytecodeVersion, operands);
public SwitchInstruction() {
} }
@Override
public void initInstruction(InstructionSequence seq) { public void initInstruction(InstructionSequence seq) {
defaultDestination = seq.getPointerByRelOffset(operands[0]);
int pref = (opcode == CodeConstants.opc_tableswitch ? 3 : 2); int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2;
int len = this.getOperands().length - pref; int len = operands.length - prefix;
defaultdest = seq.getPointerByRelOffset(this.getOperand(0));
int low = 0; int low = 0;
if (opcode == CodeConstants.opc_lookupswitch) { if (opcode == CodeConstants.opc_lookupswitch) {
len /= 2; len /= 2;
} }
else { else {
low = this.getOperand(1); low = operands[1];
} }
destinations = new int[len]; destinations = new int[len];
values = new int[len]; values = new int[len];
for (int i = 0, k = 0; i < len; i++, k++) { for (int i = 0, k = 0; i < len; i++, k++) {
if (opcode == CodeConstants.opc_lookupswitch) { if (opcode == CodeConstants.opc_lookupswitch) {
values[i] = this.getOperand(pref + k); values[i] = operands[prefix + k];
k++; k++;
} }
else { else {
values[i] = low + k; values[i] = low + k;
} }
destinations[i] = seq.getPointerByRelOffset(this.getOperand(pref + k)); destinations[i] = seq.getPointerByRelOffset(operands[prefix + 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() { public int[] getDestinations() {
return destinations; 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() { public int[] getValues() {
return values; return values;
} }
public void setValues(int[] values) { public int getDefaultDestination() {
this.values = values; return defaultDestination;
} }
}
@Override
public SwitchInstruction clone() {
SwitchInstruction copy = (SwitchInstruction)super.clone();
copy.defaultDestination = defaultDestination;
copy.destinations = destinations.clone();
copy.values = values.clone();
return copy;
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.cfg; package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.Instruction;
@ -30,8 +16,7 @@ public class BasicBlock implements IGraphNode {
// public fields // public fields
// ***************************************************************************** // *****************************************************************************
public int id = 0; public int id;
public int mark = 0; public int mark = 0;
// ***************************************************************************** // *****************************************************************************
@ -40,19 +25,11 @@ public class BasicBlock implements IGraphNode {
private InstructionSequence seq = new SimpleInstructionSequence(); private InstructionSequence seq = new SimpleInstructionSequence();
private List<BasicBlock> preds = new ArrayList<BasicBlock>(); private final List<BasicBlock> preds = new ArrayList<>();
private final List<BasicBlock> succs = new ArrayList<>();
private List<BasicBlock> succs = new ArrayList<BasicBlock>(); private final List<Integer> instrOldOffsets = new ArrayList<>();
private final List<BasicBlock> predExceptions = new ArrayList<>();
private List<Integer> instrOldOffsets = new ArrayList<Integer>(); private final List<BasicBlock> succExceptions = new ArrayList<>();
private List<BasicBlock> predExceptions = new ArrayList<BasicBlock>();
private List<BasicBlock> succExceptions = new ArrayList<BasicBlock>();
public BasicBlock() {
}
public BasicBlock(int id) { public BasicBlock(int id) {
this.id = id; this.id = id;
@ -62,24 +39,17 @@ public class BasicBlock implements IGraphNode {
// public methods // public methods
// ***************************************************************************** // *****************************************************************************
public Object clone() { @Override
@SuppressWarnings("MethodDoesntCallSuperMethod")
public BasicBlock clone() {
BasicBlock block = new BasicBlock(id);
BasicBlock block = new BasicBlock();
block.id = id;
block.setSeq(seq.clone()); block.setSeq(seq.clone());
block.setInstrOldOffsets(new ArrayList<Integer>(instrOldOffsets)); block.instrOldOffsets.addAll(instrOldOffsets);
return block; return block;
} }
public void free() {
preds.clear();
succs.clear();
instrOldOffsets.clear();
succExceptions.clear();
seq = new SimpleInstructionSequence();
}
public Instruction getInstruction(int index) { public Instruction getInstruction(int index) {
return seq.getInstr(index); return seq.getInstr(index);
} }
@ -110,7 +80,7 @@ public class BasicBlock implements IGraphNode {
} }
public void removePredecessor(BasicBlock block) { public void removePredecessor(BasicBlock block) {
while (preds.remove(block)) ; while (preds.remove(block)) /**/;
} }
public void addSuccessor(BasicBlock block) { public void addSuccessor(BasicBlock block) {
@ -119,11 +89,11 @@ public class BasicBlock implements IGraphNode {
} }
public void removeSuccessor(BasicBlock block) { public void removeSuccessor(BasicBlock block) {
while (succs.remove(block)) ; while (succs.remove(block)) /**/;
block.removePredecessor(this); block.removePredecessor(this);
} }
// FIXME: unify block comparisons: id or direkt equality // FIXME: unify block comparisons: id or direct equality
public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) { public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) {
for (int i = 0; i < succs.size(); i++) { for (int i = 0; i < succs.size(); i++) {
if (succs.get(i).id == oldBlock.id) { if (succs.get(i).id == oldBlock.id) {
@ -147,7 +117,7 @@ public class BasicBlock implements IGraphNode {
} }
public void removePredecessorException(BasicBlock block) { public void removePredecessorException(BasicBlock block) {
while (predExceptions.remove(block)) ; while (predExceptions.remove(block)) /**/;
} }
public void addSuccessorException(BasicBlock block) { public void addSuccessorException(BasicBlock block) {
@ -158,7 +128,7 @@ public class BasicBlock implements IGraphNode {
} }
public void removeSuccessorException(BasicBlock block) { public void removeSuccessorException(BasicBlock block) {
while (succExceptions.remove(block)) ; while (succExceptions.remove(block)) /**/;
block.removePredecessorException(this); block.removePredecessorException(this);
} }
@ -173,27 +143,6 @@ public class BasicBlock implements IGraphNode {
return id + ":" + new_line_separator + seq.toString(indent); return id + ":" + new_line_separator + seq.toString(indent);
} }
public String toStringOldIndices() {
String new_line_separator = DecompilerContext.getNewLineSeparator();
StringBuilder buf = new StringBuilder();
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) { public boolean isSuccessor(BasicBlock block) {
for (BasicBlock succ : succs) { for (BasicBlock succ : succs) {
if (succ.id == block.id) { if (succ.id == block.id) {
@ -203,15 +152,6 @@ public class BasicBlock implements IGraphNode {
return false; 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 // getter and setter methods
// ***************************************************************************** // *****************************************************************************
@ -220,12 +160,9 @@ public class BasicBlock implements IGraphNode {
return instrOldOffsets; return instrOldOffsets;
} }
public void setInstrOldOffsets(List<Integer> instrInds) { @Override
this.instrOldOffsets = instrInds;
}
public List<? extends IGraphNode> getPredecessors() { public List<? extends IGraphNode> getPredecessors() {
List<BasicBlock> lst = new ArrayList<BasicBlock>(preds); List<BasicBlock> lst = new ArrayList<>(preds);
lst.addAll(predExceptions); lst.addAll(predExceptions);
return lst; return lst;
} }
@ -234,10 +171,6 @@ public class BasicBlock implements IGraphNode {
return preds; return preds;
} }
public void setPreds(List<BasicBlock> preds) {
this.preds = preds;
}
public InstructionSequence getSeq() { public InstructionSequence getSeq() {
return seq; return seq;
} }
@ -250,25 +183,11 @@ public class BasicBlock implements IGraphNode {
return succs; return succs;
} }
public void setSuccs(List<BasicBlock> succs) {
this.succs = succs;
}
public List<BasicBlock> getSuccExceptions() { public List<BasicBlock> getSuccExceptions() {
return succExceptions; return succExceptions;
} }
public void setSuccExceptions(List<BasicBlock> succExceptions) {
this.succExceptions = succExceptions;
}
public List<BasicBlock> getPredExceptions() { public List<BasicBlock> getPredExceptions() {
return predExceptions; return predExceptions;
} }
}
public void setPredExceptions(List<BasicBlock> predExceptions) {
this.predExceptions = predExceptions;
}
}

View File

@ -1,24 +1,11 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.cfg; package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.DataPoint; import org.jetbrains.java.decompiler.struct.gen.DataPoint;
@ -47,7 +34,7 @@ public class ControlFlowGraph implements CodeConstants {
private Map<BasicBlock, BasicBlock> subroutines; private Map<BasicBlock, BasicBlock> subroutines;
private Set<BasicBlock> finallyExits = new HashSet<BasicBlock>(); private final Set<BasicBlock> finallyExits = new HashSet<>();
// ***************************************************************************** // *****************************************************************************
// constructors // constructors
@ -62,19 +49,6 @@ public class ControlFlowGraph implements CodeConstants {
// public methods // public methods
// ***************************************************************************** // *****************************************************************************
public void free() {
for (BasicBlock block : blocks) {
block.free();
}
blocks.clear();
first = null;
last = null;
exceptions.clear();
finallyExits.clear();
}
public void removeMarkers() { public void removeMarkers() {
for (BasicBlock block : blocks) { for (BasicBlock block : blocks) {
block.mark = 0; block.mark = 0;
@ -82,6 +56,7 @@ public class ControlFlowGraph implements CodeConstants {
} }
public String toString() { public String toString() {
if (blocks == null) return "Empty";
String new_line_separator = DecompilerContext.getNewLineSeparator(); String new_line_separator = DecompilerContext.getNewLineSeparator();
@ -93,12 +68,11 @@ public class ControlFlowGraph implements CodeConstants {
buf.append("----- Edges -----").append(new_line_separator); buf.append("----- Edges -----").append(new_line_separator);
List<BasicBlock> suc = block.getSuccs(); List<BasicBlock> suc = block.getSuccs();
for (int j = 0; j < suc.size(); j++) { for (BasicBlock aSuc : suc) {
buf.append(">>>>>>>>(regular) Block ").append(suc.get(j).id).append(new_line_separator); buf.append(">>>>>>>>(regular) Block ").append(aSuc.id).append(new_line_separator);
} }
suc = block.getSuccExceptions(); suc = block.getSuccExceptions();
for (int j = 0; j < suc.size(); j++) { for (BasicBlock handler : suc) {
BasicBlock handler = suc.get(j);
ExceptionRangeCFG range = getExceptionRange(handler, block); ExceptionRangeCFG range = getExceptionRange(handler, block);
if (range == null) { if (range == null) {
@ -123,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants {
return buf.toString(); return buf.toString();
} }
public void inlineJsr(StructMethod mt) { public void inlineJsr(StructClass cl, StructMethod mt) {
processJsr(); processJsr();
removeJsr(mt); removeJsr(cl, mt);
removeMarkers(); removeMarkers();
@ -169,13 +143,7 @@ public class ControlFlowGraph implements CodeConstants {
} }
} }
Iterator<Entry<BasicBlock, BasicBlock>> it = subroutines.entrySet().iterator(); subroutines.entrySet().removeIf(ent -> ent.getKey() == block || ent.getValue() == block);
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) { public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) {
@ -224,7 +192,7 @@ public class ControlFlowGraph implements CodeConstants {
short[] states = findStartInstructions(instrseq); short[] states = findStartInstructions(instrseq);
Map<Integer, BasicBlock> mapInstrBlocks = new HashMap<Integer, BasicBlock>(); Map<Integer, BasicBlock> mapInstrBlocks = new HashMap<>();
VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks); VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks);
blocks = colBlocks; blocks = colBlocks;
@ -243,7 +211,7 @@ public class ControlFlowGraph implements CodeConstants {
int len = seq.length(); int len = seq.length();
short[] inststates = new short[len]; short[] inststates = new short[len];
Set<Integer> excSet = new HashSet<Integer>(); Set<Integer> excSet = new HashSet<>();
for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) {
excSet.add(handler.from_instr); excSet.add(handler.from_instr);
@ -255,7 +223,7 @@ public class ControlFlowGraph implements CodeConstants {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
// exception blocks // exception blocks
if (excSet.contains(new Integer(i))) { if (excSet.contains(i)) {
inststates[i] = 1; inststates[i] = 1;
} }
@ -274,7 +242,7 @@ public class ControlFlowGraph implements CodeConstants {
for (int j = dests.length - 1; j >= 0; j--) { for (int j = dests.length - 1; j >= 0; j--) {
inststates[dests[j]] = 1; inststates[dests[j]] = 1;
} }
inststates[swinstr.getDefaultdest()] = 1; inststates[swinstr.getDefaultDestination()] = 1;
if (i + 1 < len) { if (i + 1 < len) {
inststates[i + 1] = 1; inststates[i + 1] = 1;
} }
@ -292,10 +260,10 @@ public class ControlFlowGraph implements CodeConstants {
InstructionSequence instrseq, InstructionSequence instrseq,
Map<Integer, BasicBlock> mapInstrBlocks) { Map<Integer, BasicBlock> mapInstrBlocks) {
VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<BasicBlock, Integer>(); VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<>();
InstructionSequence currseq = null; InstructionSequence currseq = null;
ArrayList<Integer> lstOffs = null; List<Integer> lstOffs = null;
int len = startblock.length; int len = startblock.length;
short counter = 0; short counter = 0;
@ -305,14 +273,11 @@ public class ControlFlowGraph implements CodeConstants {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (startblock[i] == 1) { if (startblock[i] == 1) {
currentBlock = new BasicBlock(); currentBlock = new BasicBlock(++counter);
currentBlock.id = ++counter;
currseq = new SimpleInstructionSequence(); currseq = currentBlock.getSeq();
lstOffs = new ArrayList<Integer>(); lstOffs = currentBlock.getInstrOldOffsets();
currentBlock.setSeq(currseq);
currentBlock.setInstrOldOffsets(lstOffs);
col.addWithKey(currentBlock, currentBlock.id); col.addWithKey(currentBlock, currentBlock.id);
blockoffset = instrseq.getOffset(i); blockoffset = instrseq.getOffset(i);
@ -338,7 +303,7 @@ public class ControlFlowGraph implements CodeConstants {
BasicBlock block = lstbb.get(i); BasicBlock block = lstbb.get(i);
Instruction instr = block.getLastInstruction(); Instruction instr = block.getLastInstruction();
boolean fallthrough = instr.canFallthrough(); boolean fallthrough = instr.canFallThrough();
BasicBlock bTemp; BasicBlock bTemp;
switch (instr.group) { switch (instr.group) {
@ -352,10 +317,10 @@ public class ControlFlowGraph implements CodeConstants {
SwitchInstruction sinstr = (SwitchInstruction)instr; SwitchInstruction sinstr = (SwitchInstruction)instr;
int[] dests = sinstr.getDestinations(); int[] dests = sinstr.getDestinations();
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest()); bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultDestination());
block.addSuccessor(bTemp); block.addSuccessor(bTemp);
for (int j = 0; j < dests.length; j++) { for (int dest1 : dests) {
bTemp = mapInstrBlocks.get(dests[j]); bTemp = mapInstrBlocks.get(dest1);
block.addSuccessor(bTemp); block.addSuccessor(bTemp);
} }
} }
@ -369,9 +334,9 @@ public class ControlFlowGraph implements CodeConstants {
private void setExceptionEdges(InstructionSequence instrseq, Map<Integer, BasicBlock> instrBlocks) { private void setExceptionEdges(InstructionSequence instrseq, Map<Integer, BasicBlock> instrBlocks) {
exceptions = new ArrayList<ExceptionRangeCFG>(); exceptions = new ArrayList<>();
Map<String, ExceptionRangeCFG> mapRanges = new HashMap<String, ExceptionRangeCFG>(); Map<String, ExceptionRangeCFG> mapRanges = new HashMap<>();
for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) { for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) {
@ -387,7 +352,7 @@ public class ControlFlowGraph implements CodeConstants {
} }
else { else {
List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); List<BasicBlock> protectedRange = new ArrayList<>();
for (int j = from.id; j < to.id; j++) { for (int j = from.id; j < to.id; j++) {
BasicBlock block = blocks.getWithKey(j); BasicBlock block = blocks.getWithKey(j);
protectedRange.add(block); protectedRange.add(block);
@ -396,7 +361,7 @@ public class ControlFlowGraph implements CodeConstants {
ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null
? null ? null
: Arrays.asList(handler.exceptionClass)); : Collections.singletonList(handler.exceptionClass));
mapRanges.put(key, range); mapRanges.put(key, range);
exceptions.add(range); exceptions.add(range);
@ -406,19 +371,19 @@ public class ControlFlowGraph implements CodeConstants {
private void setSubroutineEdges() { private void setSubroutineEdges() {
final Map<BasicBlock, BasicBlock> subroutines = new HashMap<BasicBlock, BasicBlock>(); final Map<BasicBlock, BasicBlock> subroutines = new LinkedHashMap<>();
for (BasicBlock block : blocks) { for (BasicBlock block : blocks) {
if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) { if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); LinkedList<BasicBlock> stack = new LinkedList<>();
LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<LinkedList<BasicBlock>>(); LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<>();
Set<BasicBlock> setVisited = new HashSet<BasicBlock>(); Set<BasicBlock> setVisited = new HashSet<>();
stack.add(block); stack.add(block);
stackJsrStacks.add(new LinkedList<BasicBlock>()); stackJsrStacks.add(new LinkedList<>());
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
@ -451,7 +416,7 @@ public class ControlFlowGraph implements CodeConstants {
for (BasicBlock succ : node.getSuccs()) { for (BasicBlock succ : node.getSuccs()) {
if (!setVisited.contains(succ)) { if (!setVisited.contains(succ)) {
stack.add(succ); stack.add(succ);
stackJsrStacks.add(new LinkedList<BasicBlock>(jsrstack)); stackJsrStacks.add(new LinkedList<>(jsrstack));
} }
} }
} }
@ -468,7 +433,7 @@ public class ControlFlowGraph implements CodeConstants {
} }
} }
private static class JsrRecord { private static final class JsrRecord {
private final BasicBlock jsr; private final BasicBlock jsr;
private final Set<BasicBlock> range; private final Set<BasicBlock> range;
private final BasicBlock ret; private final BasicBlock ret;
@ -482,7 +447,7 @@ public class ControlFlowGraph implements CodeConstants {
private int processJsrRanges() { private int processJsrRanges() {
List<JsrRecord> lstJsrAll = new ArrayList<JsrRecord>(); List<JsrRecord> lstJsrAll = new ArrayList<>();
// get all jsr ranges // get all jsr ranges
for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) { for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) {
@ -494,7 +459,7 @@ public class ControlFlowGraph implements CodeConstants {
// sort ranges // sort ranges
// FIXME: better sort order // FIXME: better sort order
List<JsrRecord> lstJsr = new ArrayList<JsrRecord>(); List<JsrRecord> lstJsr = new ArrayList<>();
for (JsrRecord arr : lstJsrAll) { for (JsrRecord arr : lstJsrAll) {
int i = 0; int i = 0;
for (; i < lstJsr.size(); i++) { for (; i < lstJsr.size(); i++) {
@ -516,7 +481,7 @@ public class ControlFlowGraph implements CodeConstants {
Set<BasicBlock> set1 = arr1.range; Set<BasicBlock> set1 = arr1.range;
if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa
Set<BasicBlock> setc = new HashSet<BasicBlock>(set); Set<BasicBlock> setc = new HashSet<>(set);
setc.retainAll(set1); setc.retainAll(set1);
if (!setc.isEmpty()) { if (!setc.isEmpty()) {
@ -532,9 +497,9 @@ public class ControlFlowGraph implements CodeConstants {
private Set<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) { private Set<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) {
Set<BasicBlock> blocks = new HashSet<BasicBlock>(); Set<BasicBlock> blocks = new HashSet<>();
List<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); List<BasicBlock> lstNodes = new LinkedList<>();
lstNodes.add(jsr); lstNodes.add(jsr);
BasicBlock dom = jsr.getSuccs().get(0); BasicBlock dom = jsr.getSuccs().get(0);
@ -596,8 +561,8 @@ public class ControlFlowGraph implements CodeConstants {
private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set<BasicBlock> common_blocks) { private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set<BasicBlock> common_blocks) {
List<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); List<BasicBlock> lstNodes = new LinkedList<>();
Map<Integer, BasicBlock> mapNewNodes = new HashMap<Integer, BasicBlock>(); Map<Integer, BasicBlock> mapNewNodes = new HashMap<>();
lstNodes.add(jsr); lstNodes.add(jsr);
mapNewNodes.put(jsr.id, jsr); mapNewNodes.put(jsr.id, jsr);
@ -633,9 +598,8 @@ public class ControlFlowGraph implements CodeConstants {
node.replaceSuccessor(child, mapNewNodes.get(childid)); node.replaceSuccessor(child, mapNewNodes.get(childid));
} }
else if (common_blocks.contains(child)) { else if (common_blocks.contains(child)) {
// make a copy of the current block // make a copy of the current block
BasicBlock copy = (BasicBlock)child.clone(); BasicBlock copy = child.clone();
copy.id = ++last_id; copy.id = ++last_id;
// copy all successors // copy all successors
if (copy.getLastInstruction().opcode == CodeConstants.opc_ret && if (copy.getLastInstruction().opcode == CodeConstants.opc_ret &&
@ -682,14 +646,14 @@ public class ControlFlowGraph implements CodeConstants {
ExceptionRangeCFG range = exceptions.get(i); ExceptionRangeCFG range = exceptions.get(i);
List<BasicBlock> lstRange = range.getProtectedRange(); List<BasicBlock> lstRange = range.getProtectedRange();
HashSet<BasicBlock> setBoth = new HashSet<BasicBlock>(common_blocks); HashSet<BasicBlock> setBoth = new HashSet<>(common_blocks);
setBoth.retainAll(lstRange); setBoth.retainAll(lstRange);
if (setBoth.size() > 0) { if (setBoth.size() > 0) {
List<BasicBlock> lstNewRange; List<BasicBlock> lstNewRange;
if (setBoth.size() == lstRange.size()) { if (setBoth.size() == lstRange.size()) {
lstNewRange = new ArrayList<BasicBlock>(); lstNewRange = new ArrayList<>();
ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange, ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange,
mapNewNodes.get(range.getHandler().id), range.getExceptionTypes()); mapNewNodes.get(range.getHandler().id), range.getExceptionTypes());
exceptions.add(newRange); exceptions.add(newRange);
@ -705,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants {
} }
} }
private void removeJsr(StructMethod mt) { private void removeJsr(StructClass cl, StructMethod mt) {
removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt));
} }
private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {
@ -753,7 +717,7 @@ public class ControlFlowGraph implements CodeConstants {
if (suc.mark != 1) { if (suc.mark != 1) {
DataPoint point = new DataPoint(); DataPoint point = new DataPoint();
point.setLocalVariables(new ArrayList<VarType>(data.getLocalVariables())); point.setLocalVariables(new ArrayList<>(data.getLocalVariables()));
point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null));
removeJsrInstructions(pool, suc, point); removeJsrInstructions(pool, suc, point);
@ -765,9 +729,7 @@ public class ControlFlowGraph implements CodeConstants {
first = blocks.get(0); first = blocks.get(0);
last = new BasicBlock(); last = new BasicBlock(++last_id);
last.id = ++last_id;
last.setSeq(new SimpleInstructionSequence());
for (BasicBlock block : blocks) { for (BasicBlock block : blocks) {
if (block.getSuccs().isEmpty()) { if (block.getSuccs().isEmpty()) {
@ -778,18 +740,18 @@ public class ControlFlowGraph implements CodeConstants {
public List<BasicBlock> getReversePostOrder() { public List<BasicBlock> getReversePostOrder() {
List<BasicBlock> res = new LinkedList<BasicBlock>(); List<BasicBlock> res = new LinkedList<>();
addToReversePostOrderListIterative(first, res); addToReversePostOrderListIterative(first, res);
return res; return res;
} }
private static void addToReversePostOrderListIterative(BasicBlock root, List<BasicBlock> lst) { private static void addToReversePostOrderListIterative(BasicBlock root, List<? super BasicBlock> lst) {
LinkedList<BasicBlock> stackNode = new LinkedList<BasicBlock>(); LinkedList<BasicBlock> stackNode = new LinkedList<>();
LinkedList<Integer> stackIndex = new LinkedList<Integer>(); LinkedList<Integer> stackIndex = new LinkedList<>();
Set<BasicBlock> setVisited = new HashSet<BasicBlock>(); Set<BasicBlock> setVisited = new HashSet<>();
stackNode.add(root); stackNode.add(root);
stackIndex.add(0); stackIndex.add(0);
@ -801,7 +763,7 @@ public class ControlFlowGraph implements CodeConstants {
setVisited.add(node); setVisited.add(node);
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(node.getSuccs()); List<BasicBlock> lstSuccs = new ArrayList<>(node.getSuccs());
lstSuccs.addAll(node.getSuccExceptions()); lstSuccs.addAll(node.getSuccExceptions());
for (; index < lstSuccs.size(); index++) { for (; index < lstSuccs.size(); index++) {
@ -834,10 +796,6 @@ public class ControlFlowGraph implements CodeConstants {
return blocks; return blocks;
} }
public void setBlocks(VBStyleCollection<BasicBlock, Integer> blocks) {
this.blocks = blocks;
}
public BasicBlock getFirst() { public BasicBlock getFirst() {
return first; return first;
} }
@ -846,39 +804,15 @@ public class ControlFlowGraph implements CodeConstants {
this.first = first; this.first = first;
} }
public List<BasicBlock> getEndBlocks() {
return last.getPreds();
}
public List<ExceptionRangeCFG> getExceptions() { public List<ExceptionRangeCFG> getExceptions() {
return exceptions; return exceptions;
} }
public void setExceptions(List<ExceptionRangeCFG> exceptions) {
this.exceptions = exceptions;
}
public BasicBlock getLast() { public BasicBlock getLast() {
return last; return last;
} }
public void setLast(BasicBlock last) {
this.last = last;
}
public Map<BasicBlock, BasicBlock> getSubroutines() {
return subroutines;
}
public void setSubroutines(Map<BasicBlock, BasicBlock> subroutines) {
this.subroutines = subroutines;
}
public Set<BasicBlock> getFinallyExits() { public Set<BasicBlock> getFinallyExits() {
return finallyExits; return finallyExits;
} }
}
public void setFinallyExits(HashSet<BasicBlock> finallyExits) {
this.finallyExits = finallyExits;
}
}

View File

@ -1,33 +1,15 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.cfg; package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.stream.Collectors;
public class ExceptionRangeCFG { public class ExceptionRangeCFG {
private final List<BasicBlock> protectedRange; // FIXME: replace with set
private List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); // FIXME: replace with set
private BasicBlock handler; private BasicBlock handler;
private List<String> exceptionTypes; private List<String> exceptionTypes;
public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) { public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) {
@ -35,7 +17,7 @@ public class ExceptionRangeCFG {
this.handler = handler; this.handler = handler;
if (exceptionType != null) { if (exceptionType != null) {
this.exceptionTypes = new ArrayList<String>(exceptionType); this.exceptionTypes = new ArrayList<>(exceptionType);
} }
} }
@ -43,22 +25,28 @@ public class ExceptionRangeCFG {
return protectedRange.contains(handler); return protectedRange.contains(handler);
} }
@Override
public String toString() { public String toString() {
String new_line_separator = DecompilerContext.getNewLineSeparator(); String new_line_separator = DecompilerContext.getNewLineSeparator();
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("exceptionType:"); buf.append("exceptionType:");
for (String exception_type : exceptionTypes) {
buf.append(" ").append(exception_type); if (exceptionTypes == null) {
buf.append(" null");
} }
else {
for (String exception_type : exceptionTypes) {
buf.append(" ").append(exception_type);
}
}
buf.append(new_line_separator); buf.append(new_line_separator);
buf.append("handler: ").append(handler.id).append(new_line_separator); buf.append("handler: ").append(handler.id).append(new_line_separator);
buf.append("range: "); buf.append("range: ");
for (int i = 0; i < protectedRange.size(); i++) { for (BasicBlock block : protectedRange) {
buf.append(protectedRange.get(i).id).append(" "); buf.append(block.id).append(" ");
} }
buf.append(new_line_separator); buf.append(new_line_separator);
@ -77,16 +65,11 @@ public class ExceptionRangeCFG {
return protectedRange; return protectedRange;
} }
public void setProtectedRange(List<BasicBlock> protectedRange) {
this.protectedRange = protectedRange;
}
public List<String> getExceptionTypes() { public List<String> getExceptionTypes() {
return this.exceptionTypes; return this.exceptionTypes;
} }
public void addExceptionType(String exceptionType) { public void addExceptionType(String exceptionType) {
if (this.exceptionTypes == null) { if (this.exceptionTypes == null) {
return; return;
} }
@ -100,30 +83,6 @@ public class ExceptionRangeCFG {
} }
public String getUniqueExceptionsString() { public String getUniqueExceptionsString() {
return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(":")) : null;
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;
// }
}

View File

@ -1,31 +1,18 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.interpreter; package org.jetbrains.java.decompiler.code.interpreter;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.consts.LinkConstant; import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.DataPoint; import org.jetbrains.java.decompiler.struct.gen.DataPoint;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.ListStack; import org.jetbrains.java.decompiler.util.ListStack;
public class InstructionImpact { public final class InstructionImpact {
// {read, write} // {read, write}
private static final int[][][] stack_impact = { private static final int[][][] stack_impact = {
@ -338,7 +325,6 @@ public class InstructionImpact {
public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) { public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) {
ListStack<VarType> stack = data.getStack(); ListStack<VarType> stack = data.getStack();
int[][] arr = stack_impact[instr.opcode]; int[][] arr = stack_impact[instr.opcode];
@ -350,8 +336,7 @@ public class InstructionImpact {
if (read != null) { if (read != null) {
int depth = 0; int depth = 0;
for (int i = 0; i < read.length; i++) { for (int type : read) {
int type = read[i];
depth++; depth++;
if (type == CodeConstants.TYPE_LONG || if (type == CodeConstants.TYPE_LONG ||
type == CodeConstants.TYPE_DOUBLE) { type == CodeConstants.TYPE_DOUBLE) {
@ -363,8 +348,7 @@ public class InstructionImpact {
} }
if (write != null) { if (write != null) {
for (int i = 0; i < write.length; i++) { for (int type : write) {
int type = write[i];
stack.push(new VarType(type)); stack.push(new VarType(type));
if (type == CodeConstants.TYPE_LONG || if (type == CodeConstants.TYPE_LONG ||
type == CodeConstants.TYPE_DOUBLE) { type == CodeConstants.TYPE_DOUBLE) {
@ -394,8 +378,8 @@ public class InstructionImpact {
case CodeConstants.opc_ldc: case CodeConstants.opc_ldc:
case CodeConstants.opc_ldc_w: case CodeConstants.opc_ldc_w:
case CodeConstants.opc_ldc2_w: case CodeConstants.opc_ldc2_w:
cn = pool.getPrimitiveConstant(instr.getOperand(0)); PooledConstant constant = pool.getConstant(instr.operand(0));
switch (cn.type) { switch (constant.type) {
case CodeConstants.CONSTANT_Integer: case CodeConstants.CONSTANT_Integer:
stack.push(new VarType(CodeConstants.TYPE_INT)); stack.push(new VarType(CodeConstants.TYPE_INT));
break; break;
@ -416,10 +400,13 @@ public class InstructionImpact {
case CodeConstants.CONSTANT_Class: case CodeConstants.CONSTANT_Class:
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class")); stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"));
break; break;
case CodeConstants.CONSTANT_MethodHandle:
stack.push(new VarType(((LinkConstant)constant).descriptor));
break;
} }
break; break;
case CodeConstants.opc_aload: case CodeConstants.opc_aload:
var1 = data.getVariable(instr.getOperand(0)); var1 = data.getVariable(instr.operand(0));
if (var1 != null) { if (var1 != null) {
stack.push(var1); stack.push(var1);
} }
@ -429,10 +416,10 @@ public class InstructionImpact {
break; break;
case CodeConstants.opc_aaload: case CodeConstants.opc_aaload:
var1 = stack.pop(2); var1 = stack.pop(2);
stack.push(new VarType(var1.type, var1.arraydim - 1, var1.value)); stack.push(new VarType(var1.type, var1.arrayDim - 1, var1.value));
break; break;
case CodeConstants.opc_astore: case CodeConstants.opc_astore:
data.setVariable(instr.getOperand(0), stack.pop()); data.setVariable(instr.operand(0), stack.pop());
break; break;
case CodeConstants.opc_dup: case CodeConstants.opc_dup:
case CodeConstants.opc_dup_x1: case CodeConstants.opc_dup_x1:
@ -454,19 +441,19 @@ public class InstructionImpact {
case CodeConstants.opc_getfield: case CodeConstants.opc_getfield:
stack.pop(); stack.pop();
case CodeConstants.opc_getstatic: case CodeConstants.opc_getstatic:
ck = pool.getLinkConstant(instr.getOperand(0)); ck = pool.getLinkConstant(instr.operand(0));
var1 = new VarType(ck.descriptor); var1 = new VarType(ck.descriptor);
stack.push(var1); stack.push(var1);
if (var1.stack_size == 2) { if (var1.stackSize == 2) {
stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));
} }
break; break;
case CodeConstants.opc_putfield: case CodeConstants.opc_putfield:
stack.pop(); stack.pop();
case CodeConstants.opc_putstatic: case CodeConstants.opc_putstatic:
ck = pool.getLinkConstant(instr.getOperand(0)); ck = pool.getLinkConstant(instr.operand(0));
var1 = new VarType(ck.descriptor); var1 = new VarType(ck.descriptor);
stack.pop(var1.stack_size); stack.pop(var1.stackSize);
break; break;
case CodeConstants.opc_invokevirtual: case CodeConstants.opc_invokevirtual:
case CodeConstants.opc_invokespecial: case CodeConstants.opc_invokespecial:
@ -474,29 +461,27 @@ public class InstructionImpact {
stack.pop(); stack.pop();
case CodeConstants.opc_invokestatic: case CodeConstants.opc_invokestatic:
case CodeConstants.opc_invokedynamic: case CodeConstants.opc_invokedynamic:
if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
ck = pool.getLinkConstant(instr.getOperand(0)); ck = pool.getLinkConstant(instr.operand(0));
MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor);
for (int i = 0; i < md.params.length; i++) { for (int i = 0; i < md.params.length; i++) {
stack.pop(md.params[i].stack_size); stack.pop(md.params[i].stackSize);
} }
if (md.ret.type != CodeConstants.TYPE_VOID) { if (md.ret.type != CodeConstants.TYPE_VOID) {
stack.push(md.ret); stack.push(md.ret);
if (md.ret.stack_size == 2) { if (md.ret.stackSize == 2) {
stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));
} }
} }
} }
break; break;
case CodeConstants.opc_new: case CodeConstants.opc_new:
cn = pool.getPrimitiveConstant(instr.getOperand(0)); cn = pool.getPrimitiveConstant(instr.operand(0));
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));
break; break;
case CodeConstants.opc_newarray: case CodeConstants.opc_newarray:
stack.pop(); stack.pop();
var1 = new VarType(arr_type[instr.getOperand(0) - 4]); stack.push(new VarType(arr_type[instr.operand(0) - 4], 1).resizeArrayDim(1));
var1.arraydim = 1;
stack.push(var1);
break; break;
case CodeConstants.opc_athrow: case CodeConstants.opc_athrow:
var1 = stack.pop(); var1 = stack.pop();
@ -506,17 +491,17 @@ public class InstructionImpact {
case CodeConstants.opc_checkcast: case CodeConstants.opc_checkcast:
case CodeConstants.opc_instanceof: case CodeConstants.opc_instanceof:
stack.pop(); stack.pop();
cn = pool.getPrimitiveConstant(instr.getOperand(0)); cn = pool.getPrimitiveConstant(instr.operand(0));
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));
break; break;
case CodeConstants.opc_anewarray: case CodeConstants.opc_anewarray:
case CodeConstants.opc_multianewarray: case CodeConstants.opc_multianewarray:
int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.getOperand(1); int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1);
stack.pop(dimensions); stack.pop(dimensions);
cn = pool.getPrimitiveConstant(instr.getOperand(0)); cn = pool.getPrimitiveConstant(instr.operand(0));
if (cn.isArray) { if (cn.isArray) {
var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()); var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString());
var1.arraydim += dimensions; var1 = var1.resizeArrayDim(var1.arrayDim + dimensions);
stack.push(var1); stack.push(var1);
} }
else { else {

View File

@ -1,286 +0,0 @@
/*
* 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.interpreter;
import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.struct.StructClass;
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];
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,48 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,34 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,60 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,46 +0,0 @@
/*
* 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.JumpInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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.JumpInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}

View File

@ -1,43 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,55 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,35 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,35 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,55 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,46 +0,0 @@
/*
* 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.JumpInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* 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.JumpInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,55 +0,0 @@
/*
* 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;
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]);
}
}
public int length() {
int index = getOperand(0);
if (index > 3) {
return wide ? 4 : 2;
}
else {
return 1;
}
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.SwitchInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}

View File

@ -1,55 +0,0 @@
/*
* 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;
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;
}
}
}

View File

@ -1,34 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,41 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,33 +0,0 @@
/*
* 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;
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;
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.SwitchInstruction;
import java.io.DataOutputStream;
import java.io.IOException;
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;
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -35,13 +21,13 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class AssertProcessor { public final class AssertProcessor {
private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError"); private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError");
public static void buildAssertions(ClassNode node) { public static void buildAssertions(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructField field = findAssertionField(node); StructField field = findAssertionField(node);
@ -67,7 +53,7 @@ public class AssertProcessor {
private static StructField findAssertionField(ClassNode node) { private static StructField findAssertionField(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
@ -89,7 +75,7 @@ public class AssertProcessor {
if (initializer.type == Exprent.EXPRENT_FUNCTION) { if (initializer.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)initializer; FunctionExprent fexpr = (FunctionExprent)initializer;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && if (fexpr.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&
fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) { fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0); InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);
@ -101,11 +87,11 @@ public class AssertProcessor {
invexpr.getLstParameters().isEmpty()) { invexpr.getLstParameters().isEmpty()) {
ConstExprent cexpr = (ConstExprent)invexpr.getInstance(); ConstExprent cexpr = (ConstExprent)invexpr.getInstance();
if (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) { if (VarType.VARTYPE_CLASS.equals(cexpr.getConstType())) {
ClassNode nd = node; ClassNode nd = node;
while (nd != null) { while (nd != null) {
if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) { if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) {
break; break;
} }
nd = nd.parent; nd = nd.parent;
@ -157,26 +143,42 @@ public class AssertProcessor {
private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) { private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {
boolean throwInIf = true;
Statement ifstat = stat.getIfstat(); Statement ifstat = stat.getIfstat();
InvocationExprent throwError = isAssertionError(ifstat); InvocationExprent throwError = isAssertionError(ifstat);
if (throwError == null) { if (throwError == null) {
return false; //check else:
Statement elsestat = stat.getElsestat();
throwError = isAssertionError(elsestat);
if (throwError == null) {
return false;
}
else {
throwInIf = false;
}
} }
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key);
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key, throwInIf);
if (!(Boolean)exprres[1]) { if (!(Boolean)exprres[1]) {
return false; return false;
} }
List<Exprent> lstParams = new ArrayList<Exprent>(); List<Exprent> lstParams = new ArrayList<>();
Exprent ascond = null, retcond = null; Exprent ascond = null, retcond = null;
if (exprres[0] != null) { if (throwInIf) {
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, if (exprres[0] != null) {
Arrays.asList(new Exprent[]{(Exprent)exprres[0]})); ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond); retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
}
} }
else {
ascond = (Exprent) exprres[0];
retcond = ascond;
}
lstParams.add(retcond == null ? ascond : retcond); lstParams.add(retcond == null ? ascond : retcond);
if (!throwError.getLstParameters().isEmpty()) { if (!throwError.getLstParameters().isEmpty()) {
@ -197,13 +199,18 @@ public class AssertProcessor {
first.removeSuccessor(stat.getIfEdge()); first.removeSuccessor(stat.getIfEdge());
first.removeSuccessor(stat.getElseEdge()); first.removeSuccessor(stat.getElseEdge());
List<Statement> lstStatements = new ArrayList<Statement>(); List<Statement> lstStatements = new ArrayList<>();
if (first.getExprents() != null && !first.getExprents().isEmpty()) { if (first.getExprents() != null && !first.getExprents().isEmpty()) {
lstStatements.add(first); lstStatements.add(first);
} }
lstStatements.add(newstat); lstStatements.add(newstat);
if (stat.iftype == IfStatement.IFTYPE_IFELSE) { if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
lstStatements.add(stat.getElsestat()); if (throwInIf) {
lstStatements.add(stat.getElsestat());
}
else {
lstStatements.add(stat.getIfstat());
}
} }
SequenceStatement sequence = new SequenceStatement(lstStatements); SequenceStatement sequence = new SequenceStatement(lstStatements);
@ -214,10 +221,16 @@ public class AssertProcessor {
sequence.getStats().get(i), sequence.getStats().get(i + 1))); sequence.getStats().get(i), sequence.getStats().get(i + 1)));
} }
if (stat.iftype == IfStatement.IFTYPE_IFELSE) { if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) {
Statement ifelse = stat.getElsestat(); Statement stmts;
if (throwInIf) {
stmts = stat.getElsestat();
}
else {
stmts = stat.getIfstat();
}
List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges(); List<StatEdge> lstSuccs = stmts.getAllSuccessorEdges();
if (!lstSuccs.isEmpty()) { if (!lstSuccs.isEmpty()) {
StatEdge endedge = lstSuccs.get(0); StatEdge endedge = lstSuccs.get(0);
if (endedge.closure == stat) { if (endedge.closure == stat) {
@ -245,9 +258,9 @@ public class AssertProcessor {
if (expr.type == Exprent.EXPRENT_EXIT) { if (expr.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)expr; ExitExprent exexpr = (ExitExprent)expr;
if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)exexpr.getValue(); NewExprent nexpr = (NewExprent)exexpr.getValue();
if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) { if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewType()) && nexpr.getConstructor() != null) {
return nexpr.getConstructor(); return nexpr.getConstructor();
} }
} }
@ -256,16 +269,21 @@ public class AssertProcessor {
return null; return null;
} }
private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) { private static Object[] getAssertionExprent(Exprent exprent, String classname, String key, boolean throwInIf) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) { if (exprent.type == Exprent.EXPRENT_FUNCTION) {
int desiredOperation = FunctionExprent.FUNCTION_CADD;
if (!throwInIf) {
desiredOperation = FunctionExprent.FUNCTION_COR;
}
FunctionExprent fexpr = (FunctionExprent)exprent; FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) { if (fexpr.getFuncType() == desiredOperation) {
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
Exprent param = fexpr.getLstOperands().get(i); Exprent param = fexpr.getLstOperands().get(i);
if (isAssertionField(param, classname, key)) { if (isAssertionField(param, classname, key, throwInIf)) {
return new Object[]{fexpr.getLstOperands().get(1 - i), true}; return new Object[]{fexpr.getLstOperands().get(1 - i), true};
} }
} }
@ -273,7 +291,7 @@ public class AssertProcessor {
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
Exprent param = fexpr.getLstOperands().get(i); Exprent param = fexpr.getLstOperands().get(i);
Object[] res = getAssertionExprent(param, classname, key); Object[] res = getAssertionExprent(param, classname, key, throwInIf);
if ((Boolean)res[1]) { if ((Boolean)res[1]) {
if (param != res[0]) { if (param != res[0]) {
fexpr.getLstOperands().set(i, (Exprent)res[0]); fexpr.getLstOperands().set(i, (Exprent)res[0]);
@ -282,7 +300,7 @@ public class AssertProcessor {
} }
} }
} }
else if (isAssertionField(fexpr, classname, key)) { else if (isAssertionField(fexpr, classname, key, throwInIf)) {
// assert false; // assert false;
return new Object[]{null, true}; return new Object[]{null, true};
} }
@ -291,20 +309,25 @@ public class AssertProcessor {
return new Object[]{exprent, false}; return new Object[]{exprent, false};
} }
private static boolean isAssertionField(Exprent exprent, String classname, String key) { private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) {
if (throwInIf) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) { if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fparam = (FunctionExprent)exprent; FunctionExprent fparam = (FunctionExprent)exprent;
if (fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
if (classname.equals(fdparam.getClassname()) return classname.equals(fdparam.getClassname()) &&
&& key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) { key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
return true;
} }
} }
} }
else {
if (exprent.type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent) exprent;
return classname.equals(fdparam.getClassname()) &&
key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
}
}
return false; return false;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -36,79 +22,48 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public class ClassReference14Processor { public final class ClassReference14Processor {
private static final ExitExprent BODY_EXPR;
private static final ExitExprent HANDLER_EXPR;
public ExitExprent bodyexprent; static {
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(Collections.singletonList(new VarExprent(0, VarType.VARTYPE_STRING, null)));
BODY_EXPR = new ExitExprent(ExitExprent.EXIT_RETURN, invFor, VarType.VARTYPE_CLASS, null);
public ExitExprent handlerexprent; InvocationExprent ctor = new InvocationExprent();
ctor.setName(CodeConstants.INIT_NAME);
ctor.setClassname("java/lang/NoClassDefFoundError");
public ClassReference14Processor() { ctor.setStringDescriptor("()V");
ctor.setFunctype(InvocationExprent.TYP_INIT);
InvocationExprent invfor = new InvocationExprent(); ctor.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
invfor.setName("forName"); NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<>(), null);
invfor.setClassname("java/lang/Class"); newExpr.setConstructor(ctor);
invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); InvocationExprent invCause = new InvocationExprent();
invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); invCause.setName("initCause");
invfor.setStatic(true); invCause.setClassname("java/lang/NoClassDefFoundError");
invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)})); invCause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
invCause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"));
bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, invCause.setInstance(newExpr);
invfor, invCause.setLstParameters(
VarType.VARTYPE_CLASS); Collections.singletonList(new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)));
HANDLER_EXPR = new ExitExprent(ExitExprent.EXIT_THROW, invCause, null, null);
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 static void processClassReferences(ClassNode node) {
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 // find the synthetic method Class class$(String) if present
HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>(); Map<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<>();
mapClassMethods(node, mapClassMeths); mapClassMethods(node, mapClassMeths);
if (mapClassMeths.isEmpty()) { if (mapClassMeths.isEmpty()) {
return; return;
} }
HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>(); Set<ClassWrapper> setFound = new HashSet<>();
processClassRec(node, mapClassMeths, setFound); processClassRec(node, mapClassMeths, setFound);
if (!setFound.isEmpty()) { if (!setFound.isEmpty()) {
@ -119,29 +74,21 @@ public class ClassReference14Processor {
} }
} }
private static void processClassRec(ClassNode node, private static void processClassRec(ClassNode node, Map<ClassWrapper, MethodWrapper> mapClassMeths, Set<? super ClassWrapper> setFound) {
final HashMap<ClassWrapper, MethodWrapper> mapClassMeths, ClassWrapper wrapper = node.getWrapper();
final HashSet<ClassWrapper> setFound) {
final ClassWrapper wrapper = node.wrapper;
// search code // search code
for (MethodWrapper meth : wrapper.getMethods()) { for (MethodWrapper meth : wrapper.getMethods()) {
RootStatement root = meth.root; RootStatement root = meth.root;
if (root != null) { if (root != null) {
DirectGraph graph = meth.getOrBuildGraph(); DirectGraph graph = meth.getOrBuildGraph();
graph.iterateExprents(exprent -> {
graph.iterateExprents(new DirectGraph.ExprentIterator() { for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
public int processExprent(Exprent exprent) { if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { setFound.add(ent.getKey());
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
setFound.add(ent.getKey());
}
} }
return 0;
} }
return 0;
}); });
} }
} }
@ -160,7 +107,7 @@ public class ClassReference14Processor {
String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue()); String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue());
if (cl != null) { if (cl != null) {
initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), exprent.bytecode));
setFound.add(ent.getKey()); setFound.add(ent.getKey());
} }
} }
@ -173,10 +120,10 @@ public class ClassReference14Processor {
} }
} }
private void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) { private static void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
for (MethodWrapper method : wrapper.getMethods()) { for (MethodWrapper method : wrapper.getMethods()) {
StructMethod mt = method.methodStruct; StructMethod mt = method.methodStruct;
@ -190,14 +137,14 @@ public class ClassReference14Processor {
CatchStatement cst = (CatchStatement)root.getFirst(); CatchStatement cst = (CatchStatement)root.getFirst();
if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK && if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK &&
cst.getStats().get(1).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"))) { cst.getVars().get(0).getVarType().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) {
BasicBlockStatement body = (BasicBlockStatement)cst.getFirst(); BasicBlockStatement body = (BasicBlockStatement)cst.getFirst();
BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1); BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1);
if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) { if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) {
if (bodyexprent.equals(body.getExprents().get(0)) && if (BODY_EXPR.equals(body.getExprents().get(0)) &&
handlerexprent.equals(handler.getExprents().get(0))) { HANDLER_EXPR.equals(handler.getExprents().get(0))) {
map.put(wrapper, method); map.put(wrapper, method);
break; break;
} }
@ -213,19 +160,16 @@ public class ClassReference14Processor {
} }
} }
private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
boolean res = false; boolean res = false;
while (true) { while (true) {
boolean found = false; boolean found = false;
for (Exprent expr : exprent.getAllExprents()) { for (Exprent expr : exprent.getAllExprents()) {
String cl = isClass14Invocation(expr, wrapper, meth); String cl = isClass14Invocation(expr, wrapper, meth);
if (cl != null) { if (cl != null) {
exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), expr.bytecode));
found = true; found = true;
res = true; res = true;
break; break;
@ -242,18 +186,16 @@ public class ClassReference14Processor {
return res; return res;
} }
private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) { if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)exprent; FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) { if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) {
if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0); FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0);
if (headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) { if (headexpr.getFuncType() == FunctionExprent.FUNCTION_EQ) {
if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD && if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD &&
headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST && headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST &&
((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) { ((ConstExprent)headexpr.getLstOperands().get(1)).getConstType().equals(VarType.VARTYPE_NULL)) {
FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0); FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0);
ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname()); ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname());
@ -293,4 +235,4 @@ public class ClassReference14Processor {
return null; return null;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,10 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; 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.main.collectors.BytecodeSourceMapper; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
@ -27,109 +14,103 @@ import org.jetbrains.java.decompiler.main.rels.LambdaProcessor;
import org.jetbrains.java.decompiler.main.rels.NestedClassProcessor; import org.jetbrains.java.decompiler.main.rels.NestedClassProcessor;
import org.jetbrains.java.decompiler.main.rels.NestedMemberAccess; import org.jetbrains.java.decompiler.main.rels.NestedMemberAccess;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute; import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public class ClassesProcessor { public class ClassesProcessor implements CodeConstants {
public static final int AVERAGE_CLASS_SIZE = 16 * 1024; public static final int AVERAGE_CLASS_SIZE = 16 * 1024;
private Map<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>(); private final StructContext context;
private final Map<String, ClassNode> mapRootClasses = new HashMap<>();
private static class Inner {
private String simpleName;
private int type;
private int accessFlags;
private static boolean equal(Inner o1, Inner o2) {
return o1.type == o2.type && o1.accessFlags == o2.accessFlags && InterpreterUtil.equalObjects(o1.simpleName, o2.simpleName);
}
}
public ClassesProcessor(StructContext context) { public ClassesProcessor(StructContext context) {
this.context = context;
}
HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>(); public void loadClasses(IIdentifierRenamer renamer) {
HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>(); Map<String, Inner> mapInnerClasses = new HashMap<>();
HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>(); Map<String, Set<String>> mapNestedClassReferences = new HashMap<>();
HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>(); Map<String, Set<String>> mapEnclosingClassReferences = new HashMap<>();
Map<String, String> mapNewSimpleNames = new HashMap<>();
boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER); boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);
boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES);
// create class nodes // create class nodes
for (StructClass cl : context.getClasses().values()) { for (StructClass cl : context.getClasses().values()) {
if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) { if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {
if (bDecompileInner) { if (bDecompileInner) {
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses"); StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
if (inner != null) { if (inner != null) {
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
for (int i = 0; i < inner.getClassEntries().size(); i++) { String innerName = entry.innerName;
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 // original simple name
String simpleName = strentry[2]; String simpleName = entry.simpleName;
String savedName = mapNewSimpleNames.get(innername); String savedName = mapNewSimpleNames.get(innerName);
if (savedName != null) { if (savedName != null) {
simpleName = savedName; simpleName = savedName;
} }
else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { else if (simpleName != null &&
IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper(); renamer != null &&
if (renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) { renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) {
simpleName = renamer.getNextClassname(innername, simpleName); simpleName = renamer.getNextClassName(innerName, simpleName);
mapNewSimpleNames.put(innername, simpleName); mapNewSimpleNames.put(innerName, simpleName);
}
} }
arr[1] = simpleName; Inner rec = new Inner();
rec.simpleName = simpleName;
// original access flags rec.type = entry.simpleNameIdx == 0 ? ClassNode.CLASS_ANONYMOUS : entry.outerNameIdx == 0 ? ClassNode.CLASS_LOCAL : ClassNode.CLASS_MEMBER;
arr[3] = entry[3]; rec.accessFlags = entry.accessFlags;
// enclosing class // enclosing class
String enclClassName; String enclClassName = entry.outerNameIdx != 0 ? entry.enclosingName : cl.qualifiedName;
if (entry[1] != 0) { if (enclClassName == null || innerName.equals(enclClassName)) {
enclClassName = strentry[1]; continue; // invalid name or self reference
} }
else { if (rec.type == ClassNode.CLASS_MEMBER && !innerName.equals(enclClassName + '$' + entry.simpleName)) {
enclClassName = cl.qualifiedName; continue; // not a real inner class
} }
if (!innername.equals(enclClassName)) { // self reference StructClass enclosingClass = context.getClasses().get(enclClassName);
StructClass enclosing_class = context.getClasses().get(enclClassName); if (enclosingClass != null && enclosingClass.isOwn()) { // own classes only
if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only Inner existingRec = mapInnerClasses.get(innerName);
if (existingRec == null) {
Object[] arrold = mapInnerClasses.get(innername); mapInnerClasses.put(innerName, rec);
if (arrold == null) {
mapInnerClasses.put(innername, arr);
}
else if (!InterpreterUtil.equalObjectArrays(arrold, arr)) {
String message = "Inconsistent inner class entries for " + innername + "!";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
}
// 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);
} }
else if (!Inner.equal(existingRec, rec)) {
String message = "Inconsistent inner class entries for " + innerName + "!";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
}
// reference to the nested class
mapNestedClassReferences.computeIfAbsent(enclClassName, k -> new HashSet<>()).add(innerName);
// reference to the enclosing class
mapEnclosingClassReferences.computeIfAbsent(innerName, k -> new HashSet<>()).add(enclClassName);
} }
} }
} }
@ -142,30 +123,32 @@ public class ClassesProcessor {
} }
if (bDecompileInner) { if (bDecompileInner) {
// connect nested classes // connect nested classes
for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) { for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) {
// root class? // root class?
if (!mapInnerClasses.containsKey(ent.getKey())) { if (!mapInnerClasses.containsKey(ent.getKey())) {
Set<String> setVisited = new HashSet<>();
HashSet<String> setVisited = new HashSet<String>(); LinkedList<String> stack = new LinkedList<>();
LinkedList<String> stack = new LinkedList<String>();
stack.add(ent.getKey()); stack.add(ent.getKey());
setVisited.add(ent.getKey()); setVisited.add(ent.getKey());
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
String superClass = stack.removeFirst(); String superClass = stack.removeFirst();
ClassNode supernode = mapRootClasses.get(superClass); ClassNode superNode = mapRootClasses.get(superClass);
HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass); Set<String> setNestedClasses = mapNestedClassReferences.get(superClass);
if (setNestedClasses != null) { if (setNestedClasses != null) {
StructClass scl = superNode.classStruct;
StructInnerClassesAttribute inner = scl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
StructClass scl = supernode.classStruct; if (inner == null || inner.getEntries().isEmpty()) {
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses"); DecompilerContext.getLogger().writeMessage(superClass + " does not contain inner classes!", IFernflowerLogger.Severity.WARN);
for (int i = 0; i < inner.getStringEntries().size(); i++) { continue;
String nestedClass = inner.getStringEntries().get(i)[0]; }
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
String nestedClass = entry.innerName;
if (!setNestedClasses.contains(nestedClass)) { if (!setNestedClasses.contains(nestedClass)) {
continue; continue;
} }
@ -174,52 +157,49 @@ public class ClassesProcessor {
continue; continue;
} }
ClassNode nestednode = mapRootClasses.get(nestedClass); ClassNode nestedNode = mapRootClasses.get(nestedClass);
if (nestednode == null) { if (nestedNode == null) {
DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.Severity.WARN); DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.Severity.WARN);
continue; continue;
} }
Object[] arr = mapInnerClasses.get(nestedClass); Inner rec = mapInnerClasses.get(nestedClass);
//if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) { //if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) {
// FIXME: check for consistent naming // FIXME: check for consistent naming
//} //}
nestednode.type = (Integer)arr[2]; nestedNode.simpleName = rec.simpleName;
nestednode.simpleName = (String)arr[1]; nestedNode.type = rec.type;
nestednode.access = (Integer)arr[3]; nestedNode.access = rec.accessFlags;
if (nestednode.type == ClassNode.CLASS_ANONYMOUS) { // sanity checks of the class supposed to be anonymous
StructClass cl = nestednode.classStruct; if (verifyAnonymousClasses && nestedNode.type == ClassNode.CLASS_ANONYMOUS && !isAnonymous(nestedNode.classStruct, scl)) {
nestedNode.type = ClassNode.CLASS_LOCAL;
}
// remove static if anonymous class if (nestedNode.type == ClassNode.CLASS_ANONYMOUS) {
// a common compiler bug StructClass cl = nestedNode.classStruct;
nestednode.access &= ~CodeConstants.ACC_STATIC; // remove static if anonymous class (a common compiler bug)
nestedNode.access &= ~CodeConstants.ACC_STATIC;
int[] interfaces = cl.getInterfaces(); int[] interfaces = cl.getInterfaces();
if (interfaces.length > 0) { if (interfaces.length > 0) {
if (interfaces.length > 1) { nestedNode.anonymousClassType = new VarType(cl.getInterface(0), true);
String message = "Inconsistent anonymous class definition: " + cl.qualifiedName;
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
}
nestednode.anonymousClassType = new VarType(cl.getInterface(0), true);
} }
else { else {
nestednode.anonymousClassType = new VarType(cl.superClass.getString(), true); nestedNode.anonymousClassType = new VarType(cl.superClass.getString(), true);
} }
} }
else if (nestednode.type == ClassNode.CLASS_LOCAL) { else if (nestedNode.type == ClassNode.CLASS_LOCAL) {
// only abstract and final are permitted // only abstract and final are permitted (a common compiler bug)
// a common compiler bug nestedNode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
} }
supernode.nested.add(nestednode); superNode.nested.add(nestedNode);
nestednode.parent = supernode; nestedNode.parent = superNode;
nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass)); nestedNode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
stack.add(nestedClass); stack.add(nestedClass);
} }
@ -230,64 +210,158 @@ public class ClassesProcessor {
} }
} }
private static boolean isAnonymous(StructClass cl, StructClass enclosingCl) {
// checking super class and interfaces
int[] interfaces = cl.getInterfaces();
if (interfaces.length > 0) {
boolean hasNonTrivialSuperClass = cl.superClass != null && !VarType.VARTYPE_OBJECT.equals(new VarType(cl.superClass.getString(), true));
if (hasNonTrivialSuperClass || interfaces.length > 1) { // can't have multiple 'sources'
String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Multiple interfaces and/or super class defined.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
return false;
}
}
else if (cl.superClass == null) { // neither interface nor super class defined
String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Neither interface nor super class defined.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
return false;
}
// FIXME: check constructors
// FIXME: check enclosing class/method
ConstantPool pool = enclosingCl.getPool();
int refCounter = 0;
boolean refNotNew = false;
StructEnclosingMethodAttribute attribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD);
String enclosingMethod = attribute != null ? attribute.getMethodName() : null;
// checking references in the enclosing class
for (StructMethod mt : enclosingCl.getMethods()) {
if (enclosingMethod != null && !enclosingMethod.equals(mt.getName())) {
continue;
}
try {
mt.expandData(enclosingCl);
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null) {
int len = seq.length();
for (int i = 0; i < len; i++) {
Instruction instr = seq.getInstr(i);
switch (instr.opcode) {
case opc_checkcast:
case opc_instanceof:
if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {
refCounter++;
refNotNew = true;
}
break;
case opc_new:
case opc_anewarray:
case opc_multianewarray:
if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {
refCounter++;
}
break;
case opc_getstatic:
case opc_putstatic:
if (cl.qualifiedName.equals(pool.getLinkConstant(instr.operand(0)).classname)) {
refCounter++;
refNotNew = true;
}
}
}
}
mt.releaseResources();
}
catch (IOException ex) {
String message = "Could not read method while checking anonymous class definition: '" + enclosingCl.qualifiedName + "', '" +
InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()) + "'";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
return false;
}
if (refCounter > 1 || refNotNew) {
String message = "Inconsistent references to the class '" + cl.qualifiedName + "' which is supposed to be anonymous";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
return false;
}
}
return true;
}
public void writeClass(StructClass cl, TextBuffer buffer) throws IOException { public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {
ClassNode root = mapRootClasses.get(cl.qualifiedName); ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.CLASS_ROOT) { if (root.type != ClassNode.CLASS_ROOT) {
return; return;
} }
boolean packageInfo = cl.isSynthetic() && "package-info".equals(root.simpleName);
boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
DecompilerContext.getLogger().startReadingClass(cl.qualifiedName); DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try { try {
ImportCollector importCollector = new ImportCollector(root); ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.setImportCollector(importCollector); DecompilerContext.startClass(importCollector);
DecompilerContext.setCounterContainer(new CounterContainer());
DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper());
new LambdaProcessor().processClass(root); if (packageInfo) {
ClassWriter.packageInfoToJava(cl, buffer);
// add simple class names to implicit import importCollector.writeImports(buffer, false);
addClassnameToImport(root, importCollector);
// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);
new NestedClassProcessor().processClass(root, root);
new NestedMemberAccess().propagateMemberAccess(root);
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
new ClassWriter().classToJava(root, classBuffer, 0);
String lineSeparator = DecompilerContext.getNewLineSeparator();
int total_offset_lines = 0;
int index = cl.qualifiedName.lastIndexOf("/");
if (index >= 0) {
total_offset_lines+=2;
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ");
buffer.append(packageName);
buffer.append(";");
buffer.append(lineSeparator);
buffer.append(lineSeparator);
} }
else if (moduleInfo) {
TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
ClassWriter.moduleInfoToJava(cl, moduleBuffer);
int import_lines_written = importCollector.writeImports(buffer); importCollector.writeImports(buffer, true);
if (import_lines_written > 0) {
buffer.append(lineSeparator); buffer.append(moduleBuffer);
total_offset_lines += import_lines_written + 1;
} }
//buffer.append(lineSeparator); else {
new LambdaProcessor().processClass(root);
buffer.append(classBuffer); // add simple class names to implicit import
addClassNameToImport(root, importCollector);
if(DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { // build wrappers for all nested classes (that's where actual processing takes place)
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); initWrappers(root);
mapper.addTotalOffset(total_offset_lines);
buffer.append(lineSeparator); new NestedClassProcessor().processClass(root, root);
mapper.dumpMapping(buffer);
new NestedMemberAccess().propagateMemberAccess(root);
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
new ClassWriter().classToJava(root, classBuffer, 0, null);
int index = cl.qualifiedName.lastIndexOf('/');
if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}
importCollector.writeImports(buffer, true);
int offsetLines = buffer.countLines();
buffer.append(classBuffer);
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
mapper.addTotalOffset(offsetLines);
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {
buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());
}
if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) {
buffer.appendLineSeparator();
mapper.dumpMapping(buffer, true);
}
}
} }
} }
finally { finally {
@ -296,8 +370,7 @@ public class ClassesProcessor {
} }
} }
private static void initWrappers(ClassNode node) throws IOException { private static void initWrappers(ClassNode node) {
if (node.type == ClassNode.CLASS_LAMBDA) { if (node.type == ClassNode.CLASS_LAMBDA) {
return; return;
} }
@ -312,19 +385,17 @@ public class ClassesProcessor {
} }
} }
private static void addClassnameToImport(ClassNode node, ImportCollector imp) { private static void addClassNameToImport(ClassNode node, ImportCollector imp) {
if (node.simpleName != null && node.simpleName.length() > 0) { if (node.simpleName != null && node.simpleName.length() > 0) {
imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false); imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);
} }
for (ClassNode nd : node.nested) { for (ClassNode nd : node.nested) {
addClassnameToImport(nd, imp); addClassNameToImport(nd, imp);
} }
} }
private static void destroyWrappers(ClassNode node) { private static void destroyWrappers(ClassNode node) {
node.wrapper = null; node.wrapper = null;
node.classStruct.releaseResources(); node.classStruct.releaseResources();
@ -339,7 +410,6 @@ public class ClassesProcessor {
public static class ClassNode { public static class ClassNode {
public static final int CLASS_ROOT = 0; public static final int CLASS_ROOT = 0;
public static final int CLASS_MEMBER = 1; public static final int CLASS_MEMBER = 1;
public static final int CLASS_ANONYMOUS = 2; public static final int CLASS_ANONYMOUS = 2;
@ -347,30 +417,18 @@ public class ClassesProcessor {
public static final int CLASS_LAMBDA = 8; public static final int CLASS_LAMBDA = 8;
public int type; public int type;
public int access; public int access;
public String simpleName; public String simpleName;
public final StructClass classStruct;
public StructClass classStruct; private ClassWrapper wrapper;
public ClassWrapper wrapper;
public String enclosingMethod; public String enclosingMethod;
public InvocationExprent superInvocation; public InvocationExprent superInvocation;
public final Map<String, VarVersionPair> mapFieldsToVars = new HashMap<>();
public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>();
public VarType anonymousClassType; public VarType anonymousClassType;
public final List<ClassNode> nested = new ArrayList<>();
public List<ClassNode> nested = new ArrayList<ClassNode>(); public final Set<String> enclosingClasses = new HashSet<>();
public Set<String> enclosingClasses = new HashSet<String>();
public ClassNode parent; public ClassNode parent;
public LambdaInformation lambdaInformation;
public LambdaInformation lambda_information;
public ClassNode(String content_class_name, public ClassNode(String content_class_name,
String content_method_name, String content_method_name,
@ -383,19 +441,18 @@ public class ClassesProcessor {
this.type = CLASS_LAMBDA; this.type = CLASS_LAMBDA;
this.classStruct = classStruct; // 'parent' class containing the static function this.classStruct = classStruct; // 'parent' class containing the static function
lambda_information = new LambdaInformation(); lambdaInformation = new LambdaInformation();
lambda_information.class_name = lambda_class_name; lambdaInformation.method_name = lambda_method_name;
lambda_information.method_name = lambda_method_name; lambdaInformation.method_descriptor = lambda_method_descriptor;
lambda_information.method_descriptor = lambda_method_descriptor;
lambda_information.content_class_name = content_class_name; lambdaInformation.content_class_name = content_class_name;
lambda_information.content_method_name = content_method_name; lambdaInformation.content_method_name = content_method_name;
lambda_information.content_method_descriptor = content_method_descriptor; lambdaInformation.content_method_descriptor = content_method_descriptor;
lambda_information.content_method_invocation_type = content_method_invocation_type; lambdaInformation.content_method_invocation_type = content_method_invocation_type;
lambda_information.content_method_key = lambdaInformation.content_method_key =
InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor); InterpreterUtil.makeUniqueKey(lambdaInformation.content_method_name, lambdaInformation.content_method_descriptor);
anonymousClassType = new VarType(lambda_class_name, true); anonymousClassType = new VarType(lambda_class_name, true);
@ -405,9 +462,9 @@ public class ClassesProcessor {
is_method_reference = !mt.isSynthetic(); // if not synthetic -> method reference is_method_reference = !mt.isSynthetic(); // if not synthetic -> method reference
} }
lambda_information.is_method_reference = is_method_reference; lambdaInformation.is_method_reference = is_method_reference;
lambda_information.is_content_method_static = lambdaInformation.is_content_method_static =
(lambda_information.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant? (lambdaInformation.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
} }
public ClassNode(int type, StructClass classStruct) { public ClassNode(int type, StructClass classStruct) {
@ -426,8 +483,15 @@ public class ClassesProcessor {
return null; return null;
} }
public ClassWrapper getWrapper() {
ClassNode node = this;
while (node.type == CLASS_LAMBDA) {
node = node.parent;
}
return node.wrapper;
}
public static class LambdaInformation { public static class LambdaInformation {
public String class_name;
public String method_name; public String method_name;
public String method_descriptor; public String method_descriptor;
@ -435,11 +499,10 @@ public class ClassesProcessor {
public String content_method_name; public String content_method_name;
public String content_method_descriptor; public String content_method_descriptor;
public int content_method_invocation_type; // values from CONSTANT_MethodHandle_REF_* public int content_method_invocation_type; // values from CONSTANT_MethodHandle_REF_*
public String content_method_key; public String content_method_key;
public boolean is_method_reference; public boolean is_method_reference;
public boolean is_content_method_static; public boolean is_content_method_static;
} }
} }
} }

View File

@ -1,64 +1,57 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
public class DecompilerContext { public class DecompilerContext {
public static final String CURRENT_CLASS = "CURRENT_CLASS"; public static final String CURRENT_CLASS = "CURRENT_CLASS";
public static final String CURRENT_CLASS_WRAPPER = "CURRENT_CLASS_WRAPPER";
public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE"; public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE";
public static final String CURRENT_METHOD = "CURRENT_METHOD";
public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR";
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR";
private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>();
private final Map<String, Object> properties; private final Map<String, Object> properties;
private StructContext structContext; private final IFernflowerLogger logger;
private final StructContext structContext;
private final ClassesProcessor classProcessor;
private final PoolInterceptor poolInterceptor;
private ImportCollector importCollector; private ImportCollector importCollector;
private VarNamesCollector varNamescollector; private VarProcessor varProcessor;
private CounterContainer counterContainer; private CounterContainer counterContainer;
private ClassesProcessor classProcessor;
private PoolInterceptor poolInterceptor;
private IFernflowerLogger logger;
private BytecodeSourceMapper bytecodeSourceMapper; private BytecodeSourceMapper bytecodeSourceMapper;
private DecompilerContext(Map<String, Object> properties) { public DecompilerContext(Map<String, Object> properties,
IFernflowerLogger logger,
StructContext structContext,
ClassesProcessor classProcessor,
PoolInterceptor interceptor) {
Objects.requireNonNull(properties);
Objects.requireNonNull(logger);
Objects.requireNonNull(structContext);
Objects.requireNonNull(classProcessor);
this.properties = properties; this.properties = properties;
this.logger = logger;
this.structContext = structContext;
this.classProcessor = classProcessor;
this.poolInterceptor = interceptor;
this.counterContainer = new CounterContainer();
} }
public static void initContext(Map<String, Object> propertiesCustom) { // *****************************************************************************
Map<String, Object> properties = new HashMap<String, Object>(IFernflowerPreferences.DEFAULTS); // context setup and update
if (propertiesCustom != null) { // *****************************************************************************
properties.putAll(propertiesCustom);
} private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
currentContext.set(new DecompilerContext(properties));
}
public static DecompilerContext getCurrentContext() { public static DecompilerContext getCurrentContext() {
return currentContext.get(); return currentContext.get();
@ -68,93 +61,69 @@ public class DecompilerContext {
currentContext.set(context); currentContext.set(context);
} }
public static Object getProperty(String key) {
return getCurrentContext().properties.get(key);
}
public static void setProperty(String key, Object value) { public static void setProperty(String key, Object value) {
getCurrentContext().properties.put(key, value); getCurrentContext().properties.put(key, value);
} }
public static void startClass(ImportCollector importCollector) {
DecompilerContext context = getCurrentContext();
context.importCollector = importCollector;
context.counterContainer = new CounterContainer();
context.bytecodeSourceMapper = new BytecodeSourceMapper();
}
public static void startMethod(VarProcessor varProcessor) {
DecompilerContext context = getCurrentContext();
context.varProcessor = varProcessor;
context.counterContainer = new CounterContainer();
}
// *****************************************************************************
// context access
// *****************************************************************************
public static Object getProperty(String key) {
return getCurrentContext().properties.get(key);
}
public static boolean getOption(String key) { public static boolean getOption(String key) {
return "1".equals(getCurrentContext().properties.get(key)); return "1".equals(getProperty(key));
} }
public static ImportCollector getImportCollector() { public static String getNewLineSeparator() {
return getCurrentContext().importCollector; return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
} IFernflowerPreferences.LINE_SEPARATOR_UNX : IFernflowerPreferences.LINE_SEPARATOR_WIN;
public static void setImportCollector(ImportCollector importCollector) {
getCurrentContext().importCollector = importCollector;
}
public static VarNamesCollector getVarNamesCollector() {
return getCurrentContext().varNamescollector;
}
public static void setVarNamesCollector(VarNamesCollector varNamesCollector) {
getCurrentContext().varNamescollector = varNamesCollector;
}
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 BytecodeSourceMapper getBytecodeSourceMapper() {
return getCurrentContext().bytecodeSourceMapper;
}
public static void setBytecodeSourceMapper(BytecodeSourceMapper bytecodeSourceMapper) {
getCurrentContext().bytecodeSourceMapper = bytecodeSourceMapper;
} }
public static IFernflowerLogger getLogger() { public static IFernflowerLogger getLogger() {
return getCurrentContext().logger; return getCurrentContext().logger;
} }
public static void setLogger(IFernflowerLogger logger) { public static StructContext getStructContext() {
if (logger != null) { return getCurrentContext().structContext;
String level = (String)getProperty(IFernflowerPreferences.LOG_LEVEL);
if (level != null) {
try {
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException ignore) { }
}
}
getCurrentContext().logger = logger;
} }
public static String getNewLineSeparator() { public static ClassesProcessor getClassProcessor() {
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ? return getCurrentContext().classProcessor;
IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN;
} }
}
public static PoolInterceptor getPoolInterceptor() {
return getCurrentContext().poolInterceptor;
}
public static ImportCollector getImportCollector() {
return getCurrentContext().importCollector;
}
public static VarProcessor getVarProcessor() {
return getCurrentContext().varProcessor;
}
public static CounterContainer getCounterContainer() {
return getCurrentContext().counterContainer;
}
public static BytecodeSourceMapper getBytecodeSourceMapper() {
return getCurrentContext().bytecodeSourceMapper;
}
}

View File

@ -1,34 +1,19 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; 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.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;
import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
public class EnumProcessor { public final class EnumProcessor {
public static void clearEnum(ClassWrapper wrapper) { public static void clearEnum(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
@ -48,13 +33,13 @@ public class EnumProcessor {
wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor)); wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor));
} }
} }
else if ("<init>".equals(name)) { else if (CodeConstants.INIT_NAME.equals(name)) {
Statement firstData = findFirstData(method.root); Statement firstData = Statements.findFirstData(method.root);
if (firstData != null && !firstData.getExprents().isEmpty()) { if (firstData != null && !firstData.getExprents().isEmpty()) {
Exprent exprent = firstData.getExprents().get(0); Exprent exprent = firstData.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) { if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent; InvocationExprent invExpr = (InvocationExprent)exprent;
if (isInvocationSuperConstructor(invexpr, method, wrapper)) { if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) {
firstData.getExprents().remove(0); firstData.getExprents().remove(0);
} }
} }
@ -70,49 +55,4 @@ public class EnumProcessor {
} }
} }
} }
}
// 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;
}
}

View File

@ -1,55 +1,88 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.*;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.IDecompiledData; import org.jetbrains.java.decompiler.struct.IDecompiledData;
import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.File;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
public class Fernflower implements IDecompiledData { public class Fernflower implements IDecompiledData {
private final StructContext structContext;
private final ClassesProcessor classProcessor;
private final IIdentifierRenamer helper;
private final IdentifierConverter converter;
private StructContext structContext; public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> customProperties, IFernflowerLogger logger) {
private ClassesProcessor classesProcessor; Map<String, Object> properties = new HashMap<>(IFernflowerPreferences.DEFAULTS);
if (customProperties != null) {
properties.putAll(customProperties);
}
String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL);
if (level != null) {
try {
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.ENGLISH)));
}
catch (IllegalArgumentException ignore) { }
}
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
structContext = new StructContext(saver, this, new LazyLoader(provider)); structContext = new StructContext(saver, this, new LazyLoader(provider));
DecompilerContext.initContext(options); classProcessor = new ClassesProcessor(structContext);
DecompilerContext.setCounterContainer(new CounterContainer());
DecompilerContext.setLogger(logger); PoolInterceptor interceptor = null;
if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) {
helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger);
interceptor = new PoolInterceptor();
converter = new IdentifierConverter(structContext, helper, interceptor);
}
else {
helper = null;
converter = null;
}
DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
DecompilerContext.setCurrentContext(context);
}
private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) {
if (className != null) {
try {
Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);
return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e);
}
}
return new ConverterHelper();
}
public void addSource(File source) {
structContext.addSpace(source, true);
}
public void addLibrary(File library) {
structContext.addSpace(library, false);
} }
public void decompileContext() { public void decompileContext() {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { if (converter != null) {
new IdentifierConverter().rename(structContext); converter.rename();
} }
classesProcessor = new ClassesProcessor(structContext); classProcessor.loadClasses(helper);
DecompilerContext.setClassProcessor(classesProcessor);
DecompilerContext.setStructContext(structContext);
structContext.saveContext(); structContext.saveContext();
} }
@ -58,24 +91,18 @@ public class Fernflower implements IDecompiledData {
DecompilerContext.setCurrentContext(null); DecompilerContext.setCurrentContext(null);
} }
public StructContext getStructContext() {
return structContext;
}
@Override @Override
public String getClassEntryName(StructClass cl, String entryName) { public String getClassEntryName(StructClass cl, String entryName) {
ClassNode node = classesProcessor.getMapRootClasses().get(cl.qualifiedName); ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName);
if (node.type != ClassNode.CLASS_ROOT) { if (node.type != ClassNode.CLASS_ROOT) {
return null; return null;
} }
else if (converter != null) {
String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);
return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simpleClassName + ".java";
}
else { else {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java";
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";
}
} }
} }
@ -84,12 +111,12 @@ public class Fernflower implements IDecompiledData {
try { try {
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE); TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString()); buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());
classesProcessor.writeClass(cl, buffer); classProcessor.writeClass(cl, buffer);
return org.spigotmc.fernflower.EclipseFormatter.format(buffer.toString()); // Spigot return buffer.toString();
} }
catch (Throwable ex) { catch (Throwable t) {
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex); DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t);
return null; return null;
} }
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -23,7 +9,8 @@ import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*; 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.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
@ -31,14 +18,11 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public final class InitializerProcessor {
public class InitializerProcessor {
public static void extractInitializers(ClassWrapper wrapper) { public static void extractInitializers(ClassWrapper wrapper) {
MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V");
MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V"); if (method != null && method.root != null) { // successfully decompiled static constructor
if (meth != null && meth.root != null) { // successfully decompiled static constructor extractStaticInitializers(wrapper, method);
extractStaticInitializers(wrapper, meth);
} }
extractDynamicInitializers(wrapper); extractDynamicInitializers(wrapper);
@ -52,30 +36,26 @@ public class InitializerProcessor {
} }
} }
private static void liftConstructor(ClassWrapper wrapper) { private static void liftConstructor(ClassWrapper wrapper) {
for (MethodWrapper method : wrapper.getMethods()) {
for (MethodWrapper meth : wrapper.getMethods()) { if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { Statement firstData = Statements.findFirstData(method.root);
Statement firstdata = findFirstData(meth.root); if (firstData == null) {
if (firstdata == null) {
return; return;
} }
int index = 0; int index = 0;
List<Exprent> lstExprents = firstdata.getExprents(); List<Exprent> lstExprents = firstData.getExprents();
for (Exprent exprent : lstExprents) { for (Exprent exprent : lstExprents) {
int action = 0; int action = 0;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent; AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD && assignExpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) { if (fExpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString); StructField structField = wrapper.getClassStruct().getField(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) { if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) {
action = 1; action = 1;
} }
@ -83,7 +63,7 @@ public class InitializerProcessor {
} }
} }
else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION && else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION &&
isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) { Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, true)) {
// this() or super() // this() or super()
lstExprents.add(0, lstExprents.remove(index)); lstExprents.add(0, lstExprents.remove(index));
action = 2; action = 2;
@ -99,52 +79,50 @@ public class InitializerProcessor {
} }
} }
private static void hideEmptySuper(ClassWrapper wrapper) { private static void hideEmptySuper(ClassWrapper wrapper) {
for (MethodWrapper method : wrapper.getMethods()) {
for (MethodWrapper meth : wrapper.getMethods()) { if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { Statement firstData = Statements.findFirstData(method.root);
Statement firstdata = findFirstData(meth.root); if (firstData == null || firstData.getExprents().isEmpty()) {
if (firstdata == null || firstdata.getExprents().isEmpty()) {
return; return;
} }
Exprent exprent = firstdata.getExprents().get(0); Exprent exprent = firstData.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) { if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent; InvocationExprent invExpr = (InvocationExprent)exprent;
if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) { if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false) && invExpr.getLstParameters().isEmpty()) {
firstdata.getExprents().remove(0); firstData.getExprents().remove(0);
} }
} }
} }
} }
} }
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) { private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper method) {
RootStatement root = method.root;
RootStatement root = meth.root;
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
Statement firstData = Statements.findFirstData(root);
if (firstData != null) {
boolean inlineInitializers = cl.hasModifier(CodeConstants.ACC_INTERFACE) || cl.hasModifier(CodeConstants.ACC_ENUM);
Statement firstdata = findFirstData(root); while (!firstData.getExprents().isEmpty()) {
if (firstdata != null) { Exprent exprent = firstData.getExprents().get(0);
while (!firstdata.getExprents().isEmpty()) {
Exprent exprent = firstdata.getExprents().get(0);
boolean found = false; boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent; AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && if (fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) {
if (true || isExprentIndependent(asexpr.getRight(), meth)) { // Spigot // interfaces fields should always be initialized inline
if (inlineInitializers || isExprentIndependent(assignExpr.getRight(), method)) {
String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); String keyField = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) { if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) {
wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField); wrapper.getStaticFieldInitializers().addWithKey(assignExpr.getRight(), keyField);
firstdata.getExprents().remove(0); firstData.getExprents().remove(0);
found = true; found = true;
} }
} }
@ -160,27 +138,26 @@ public class InitializerProcessor {
} }
private static void extractDynamicInitializers(ClassWrapper wrapper) { private static void extractDynamicInitializers(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS; boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;
List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>(); List<List<Exprent>> lstFirst = new ArrayList<>();
List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>(); List<MethodWrapper> lstMethodWrappers = new ArrayList<>();
for (MethodWrapper meth : wrapper.getMethods()) { for (MethodWrapper method : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { // successfully decompiled constructor
Statement firstdata = findFirstData(meth.root); Statement firstData = Statements.findFirstData(method.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) { if (firstData == null || firstData.getExprents().isEmpty()) {
return; return;
} }
lstFirst.add(firstdata.getExprents()); lstFirst.add(firstData.getExprents());
lstMethWrappers.add(meth); lstMethodWrappers.add(method);
Exprent exprent = firstdata.getExprents().get(0); Exprent exprent = firstData.getExprents().get(0);
if (!isAnonymous) { // FIXME: doesn't make sense if (!isAnonymous) { // FIXME: doesn't make sense
if (exprent.type != Exprent.EXPRENT_INVOCATION || if (exprent.type != Exprent.EXPRENT_INVOCATION ||
!isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) { !Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, false)) {
return; return;
} }
} }
@ -192,12 +169,10 @@ public class InitializerProcessor {
} }
while (true) { while (true) {
String fieldWithDescr = null; String fieldWithDescr = null;
Exprent value = null; Exprent value = null;
for (int i = 0; i < lstFirst.size(); i++) { for (int i = 0; i < lstFirst.size(); i++) {
List<Exprent> lst = lstFirst.get(i); List<Exprent> lst = lstFirst.get(i);
if (lst.size() < (isAnonymous ? 1 : 2)) { if (lst.size() < (isAnonymous ? 1 : 2)) {
@ -209,22 +184,21 @@ public class InitializerProcessor {
boolean found = false; boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent; AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && if (!fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) { if (isExprentIndependent(assignExpr.getRight(), lstMethodWrappers.get(i))) {
String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); String fieldKey = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (fieldWithDescr == null) { if (fieldWithDescr == null) {
fieldWithDescr = fieldKey; fieldWithDescr = fieldKey;
value = asexpr.getRight(); value = assignExpr.getRight();
} }
else { else {
if (!fieldWithDescr.equals(fieldKey) || if (!fieldWithDescr.equals(fieldKey) ||
!value.equals(asexpr.getRight())) { !value.equals(assignExpr.getRight())) {
return; return;
} }
} }
@ -252,19 +226,17 @@ public class InitializerProcessor {
} }
} }
private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) { private static boolean isExprentIndependent(Exprent exprent, MethodWrapper method) {
List<Exprent> lst = exprent.getAllExprents(true); List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent); lst.add(exprent);
for (Exprent expr : lst) { for (Exprent expr : lst) {
switch (expr.type) { switch (expr.type) {
case Exprent.EXPRENT_VAR: case Exprent.EXPRENT_VAR:
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr); VarVersionPair varPair = new VarVersionPair((VarExprent)expr);
if (!meth.varproc.getExternvars().contains(varpaar)) { if (!method.varproc.getExternalVars().contains(varPair)) {
String varname = meth.varproc.getVarName(varpaar); String varName = method.varproc.getVarName(varPair);
if (!varName.equals("this") && !varName.endsWith(".this")) { // FIXME: remove direct comparison with strings
if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings
return false; return false;
} }
} }
@ -276,48 +248,4 @@ public class InitializerProcessor {
return true; 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;
}
}

View File

@ -1,187 +0,0 @@
/*
* 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;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Allows to connect text with resulting lines
*
* @author egor
*/
public class TextBuffer {
private final String myLineSeparator = DecompilerContext.getNewLineSeparator();
private final String myIndent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING);
private final StringBuilder myStringBuilder;
private Map<Integer, Integer> myLineToOffsetMapping = null;
public TextBuffer() {
myStringBuilder = new StringBuilder();
}
public TextBuffer(int size) {
myStringBuilder = new StringBuilder(size);
}
public void setCurrentLine(int line) {
if (line >= 0) {
checkMapCreated();
myLineToOffsetMapping.put(line, myStringBuilder.length()+1);
}
}
public TextBuffer append(String str) {
myStringBuilder.append(str);
return this;
}
public TextBuffer append(char ch) {
myStringBuilder.append(ch);
return this;
}
public TextBuffer appendLineSeparator() {
myStringBuilder.append(myLineSeparator);
return this;
}
public TextBuffer appendIndent(int length) {
while (length-- > 0) {
append(myIndent);
}
return this;
}
public TextBuffer addBanner(String banner) {
myStringBuilder.insert(0, banner);
if (myLineToOffsetMapping != null) {
for (Integer line : myLineToOffsetMapping.keySet()) {
myLineToOffsetMapping.put(line, myLineToOffsetMapping.get(line) + banner.length());
}
}
return this;
}
@Override
public String toString() {
String original = myStringBuilder.toString();
if (myLineToOffsetMapping == null || myLineToOffsetMapping.isEmpty()) {
return original;
}
else {
StringBuilder res = new StringBuilder();
String[] srcLines = original.split(myLineSeparator);
int currentLineStartOffset = 0;
int currentLine = 0;
int previousMarkLine = 0;
int dumpedLines = 0;
ArrayList<Integer> linesWithMarks = new ArrayList<Integer>(myLineToOffsetMapping.keySet());
Collections.sort(linesWithMarks);
for (Integer markLine : linesWithMarks) {
Integer markOffset = myLineToOffsetMapping.get(markLine);
while (currentLine < srcLines.length) {
String line = srcLines[currentLine];
int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length();
if (markOffset >= currentLineStartOffset && markOffset <= lineEnd) {
int requiredLinesNumber = markLine - dumpedLines;
dumpedLines = markLine;
appendLines(res, srcLines, previousMarkLine, currentLine, requiredLinesNumber);
previousMarkLine = currentLine;
break;
}
currentLineStartOffset = lineEnd;
currentLine++;
}
}
if (previousMarkLine < srcLines.length) {
appendLines(res, srcLines, previousMarkLine, srcLines.length, srcLines.length - previousMarkLine);
}
return res.toString();
}
}
private void appendLines(StringBuilder res, String[] srcLines, int from, int to, int requiredLineNumber) {
if (to - from > requiredLineNumber) {
int separatorsRequired = to - from - requiredLineNumber - 1;
for (int i = from; i < to; i++) {
res.append(srcLines[i]);
if (separatorsRequired-- > 0) {
res.append(myLineSeparator);
}
}
res.append(myLineSeparator);
}
else if (to - from <= requiredLineNumber) {
for (int i = from; i < to; i++) {
res.append(srcLines[i]).append(myLineSeparator);
}
for (int i = 0; i < requiredLineNumber - to + from; i++) {
res.append(myLineSeparator);
}
}
}
public int length() {
return myStringBuilder.length();
}
public String substring(int start) {
return myStringBuilder.substring(start);
}
public void setLength(int position) {
myStringBuilder.setLength(position);
}
public TextBuffer append(TextBuffer buffer) {
if (buffer.myLineToOffsetMapping != null && !buffer.myLineToOffsetMapping.isEmpty()) {
checkMapCreated();
for (Map.Entry<Integer, Integer> entry : buffer.myLineToOffsetMapping.entrySet()) {
myLineToOffsetMapping.put(entry.getKey(), entry.getValue() + myStringBuilder.length());
}
}
myStringBuilder.append(buffer.myStringBuilder);
return this;
}
private void checkMapCreated() {
if (myLineToOffsetMapping == null) {
myLineToOffsetMapping = new HashMap<Integer, Integer>();
}
}
public void insert(int offset, String s) {
if (myLineToOffsetMapping != null) {
throw new IllegalStateException("insert not yet supported with Line mapping");
}
myStringBuilder.insert(offset, s);
}
public int count(String substring, int from) {
int count = 0, length = substring.length(), p = from;
while ((p = myStringBuilder.indexOf(substring, p)) > 0) {
++count;
p += length;
}
return count;
}
}

View File

@ -1,70 +1,103 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.main.collectors; package org.jetbrains.java.decompiler.main.collectors;
import java.util.HashMap; import org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute;
import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
public class BytecodeMappingTracer { public class BytecodeMappingTracer {
public static final BytecodeMappingTracer DUMMY = new BytecodeMappingTracer();
private int current_sourceline; private int currentSourceLine;
private StructLineNumberTableAttribute lineNumberTable = null;
private final Map<Integer, Integer> mapping = new HashMap<>(); // bytecode offset, source line
// bytecode offset, source line public BytecodeMappingTracer() { }
private HashMap<Integer, Integer> mapping = new HashMap<Integer, Integer>();
public BytecodeMappingTracer() {}
public BytecodeMappingTracer(int initial_source_line) { public BytecodeMappingTracer(int initial_source_line) {
current_sourceline = initial_source_line; currentSourceLine = initial_source_line;
} }
public void incrementCurrentSourceLine() { public void incrementCurrentSourceLine() {
current_sourceline++; currentSourceLine++;
} }
public void incrementCurrentSourceLine(int number_lines) { public void incrementCurrentSourceLine(int number_lines) {
current_sourceline += number_lines; currentSourceLine += number_lines;
}
public void shiftSourceLines(int shift) {
for(Entry<Integer, Integer> entry : mapping.entrySet()) {
entry.setValue(entry.getValue() + shift);
}
} }
public void addMapping(int bytecode_offset) { public void addMapping(int bytecode_offset) {
if(!mapping.containsKey(bytecode_offset)) { mapping.putIfAbsent(bytecode_offset, currentSourceLine);
mapping.put(bytecode_offset, current_sourceline);
}
} }
public void addMapping(Set<Integer> bytecode_offsets) { public void addMapping(Set<Integer> bytecode_offsets) {
if(bytecode_offsets != null) { if (bytecode_offsets != null) {
for(Integer bytecode_offset : bytecode_offsets) { for (Integer bytecode_offset : bytecode_offsets) {
addMapping(bytecode_offset); addMapping(bytecode_offset);
} }
} }
} }
public void addTracer(BytecodeMappingTracer tracer) { public void addTracer(BytecodeMappingTracer tracer) {
if(tracer != null) { if (tracer != null) {
for(Entry<Integer, Integer> entry : tracer.mapping.entrySet()) { for (Entry<Integer, Integer> entry : tracer.mapping.entrySet()) {
if(!mapping.containsKey(entry.getKey())) { mapping.putIfAbsent(entry.getKey(), entry.getValue());
mapping.put(entry.getKey(), entry.getValue());
}
} }
} }
} }
public HashMap<Integer, Integer> getMapping() { public Map<Integer, Integer> getMapping() {
return mapping; return mapping;
} }
public int getCurrentSourceLine() { public int getCurrentSourceLine() {
return current_sourceline; return currentSourceLine;
} }
public void setCurrentSourceLine(int current_sourceline) { public void setCurrentSourceLine(int currentSourceLine) {
this.current_sourceline = current_sourceline; this.currentSourceLine = currentSourceLine;
} }
} public void setLineNumberTable(StructLineNumberTableAttribute lineNumberTable) {
this.lineNumberTable = lineNumberTable;
}
private final Set<Integer> unmappedLines = new HashSet<>();
public Set<Integer> getUnmappedLines() {
return unmappedLines;
}
public Map<Integer, Integer> getOriginalLinesMapping() {
if (lineNumberTable == null) {
return Collections.emptyMap();
}
Map<Integer, Integer> res = new HashMap<>();
// first match offsets from line number table
int[] data = lineNumberTable.getRawData();
for (int i = 0; i < data.length; i += 2) {
int originalOffset = data[i];
int originalLine = data[i + 1];
Integer newLine = mapping.get(originalOffset);
if (newLine != null) {
res.put(originalLine, newLine);
}
else {
unmappedLines.add(originalLine);
}
}
// now match offsets from decompiler mapping
for (Entry<Integer, Integer> entry : mapping.entrySet()) {
int originalLine = lineNumberTable.findLineNumber(entry.getKey());
if (originalLine > -1 && !res.containsKey(originalLine)) {
res.put(originalLine, entry.getValue());
unmappedLines.remove(originalLine);
}
}
return res;
}
}

View File

@ -1,81 +1,109 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.main.collectors; package org.jetbrains.java.decompiler.main.collectors;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.util.TextBuffer;
import java.util.HashMap; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public class BytecodeSourceMapper { public class BytecodeSourceMapper {
private int offset_total; private int offset_total;
// class, method, bytecode offset, source line // class, method, bytecode offset, source line
private HashMap<String, HashMap<String, HashMap<Integer, Integer>>> mapping = new HashMap<String, HashMap<String, HashMap<Integer, Integer>>>(); private final Map<String, Map<String, Map<Integer, Integer>>> mapping = new LinkedHashMap<>();
public void addMapping(String classname, String methodname, int bytecode_offset, int source_line) { // original line to decompiled line
private final Map<Integer, Integer> linesMapping = new HashMap<>();
private final Set<Integer> unmappedLines = new TreeSet<>();
HashMap<String, HashMap<Integer, Integer>> class_mapping = mapping.get(classname); public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) {
if(class_mapping == null) { Map<String, Map<Integer, Integer>> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order
mapping.put(classname, class_mapping = new HashMap<String, HashMap<Integer, Integer>>()); Map<Integer, Integer> method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>());
}
HashMap<Integer, Integer> method_mapping = class_mapping.get(methodname);
if(method_mapping == null) {
class_mapping.put(methodname, method_mapping = new HashMap<Integer, Integer>());
}
// don't overwrite // don't overwrite
if(!method_mapping.containsKey(bytecode_offset)) { method_mapping.putIfAbsent(bytecodeOffset, sourceLine);
method_mapping.put(bytecode_offset, source_line);
}
} }
public void addTracer(String classname, String methodname, BytecodeMappingTracer tracer) { public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) {
for(Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) { for (Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
addMapping(classname, methodname, entry.getKey(), entry.getValue()); addMapping(className, methodName, entry.getKey(), entry.getValue());
} }
linesMapping.putAll(tracer.getOriginalLinesMapping());
unmappedLines.addAll(tracer.getUnmappedLines());
} }
public void dumpMapping(TextBuffer buffer) { public void dumpMapping(TextBuffer buffer, boolean offsetsToHex) {
if (mapping.isEmpty() && linesMapping.isEmpty()) {
return;
}
String lineSeparator = DecompilerContext.getNewLineSeparator(); String lineSeparator = DecompilerContext.getNewLineSeparator();
for(Entry<String, HashMap<String, HashMap<Integer, Integer>>> class_entry : mapping.entrySet()) { for (Entry<String, Map<String, Map<Integer, Integer>>> class_entry : mapping.entrySet()) {
HashMap<String, HashMap<Integer, Integer>> class_mapping = class_entry.getValue(); Map<String, Map<Integer, Integer>> class_mapping = class_entry.getValue();
buffer.append("class " + class_entry.getKey() + "{" + lineSeparator); buffer.append("class '" + class_entry.getKey() + "' {" + lineSeparator);
boolean is_first_method = true; boolean is_first_method = true;
for (Entry<String, Map<Integer, Integer>> method_entry : class_mapping.entrySet()) {
Map<Integer, Integer> method_mapping = method_entry.getValue();
for(Entry<String, HashMap<Integer, Integer>> method_entry : class_mapping.entrySet()) { if (!is_first_method) {
HashMap<Integer, Integer> method_mapping = method_entry.getValue();
if(!is_first_method) {
buffer.appendLineSeparator(); buffer.appendLineSeparator();
} }
buffer.appendIndent(1).append("method " + method_entry.getKey() + "{" + lineSeparator);
for(Entry<Integer, Integer> line : method_mapping.entrySet()) { buffer.appendIndent(1).append("method '" + method_entry.getKey() + "' {" + lineSeparator);
buffer.appendIndent(2).append(line.getKey().toString()).appendIndent(2).append((line.getValue() + offset_total) + lineSeparator);
List<Integer> lstBytecodeOffsets = new ArrayList<>(method_mapping.keySet());
Collections.sort(lstBytecodeOffsets);
for (Integer offset : lstBytecodeOffsets) {
Integer line = method_mapping.get(offset);
String strOffset = offsetsToHex ? Integer.toHexString(offset) : line.toString();
buffer.appendIndent(2).append(strOffset).appendIndent(2).append((line + offset_total) + lineSeparator);
} }
buffer.appendIndent(1).append("}").appendLineSeparator(); buffer.appendIndent(1).append("}").appendLineSeparator();
is_first_method = false; is_first_method = false;
} }
buffer.append("}").appendLineSeparator();
buffer.append("}").appendLineSeparator().appendLineSeparator();
} }
}
public int getTotalOffset() { // lines mapping
return offset_total; buffer.append("Lines mapping:").appendLineSeparator();
} Map<Integer, Integer> sorted = new TreeMap<>(linesMapping);
for (Entry<Integer, Integer> entry : sorted.entrySet()) {
buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue() + offset_total + 1).appendLineSeparator();
}
public void setTotalOffset(int offset_total) { if (!unmappedLines.isEmpty()) {
this.offset_total = offset_total; buffer.append("Not mapped:").appendLineSeparator();
for (Integer line : unmappedLines) {
if (!linesMapping.containsKey(line)) {
buffer.append(line).appendLineSeparator();
}
}
}
} }
public void addTotalOffset(int offset_total) { public void addTotalOffset(int offset_total) {
this.offset_total += offset_total; this.offset_total += offset_total;
} }
/**
} * Original to decompiled line mapping.
*/
public int[] getOriginalLinesMapping() {
int[] res = new int[linesMapping.size() * 2];
int i = 0;
for (Entry<Integer, Integer> entry : linesMapping.entrySet()) {
res[i] = entry.getKey();
unmappedLines.remove(entry.getKey());
res[i + 1] = entry.getValue() + offset_total + 1; // make it 1 based
i += 2;
}
return res;
}
}

View File

@ -1,27 +1,12 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.collectors; package org.jetbrains.java.decompiler.main.collectors;
public class CounterContainer { public class CounterContainer {
public static final int STATEMENT_COUNTER = 0; public static final int STATEMENT_COUNTER = 0;
public static final int EXPRENT_COUNTER = 1; public static final int EXPRESSION_COUNTER = 1;
public static final int VAR_COUNTER = 2; public static final int VAR_COUNTER = 2;
private int[] values = new int[]{1, 1, 1}; private final int[] values = new int[]{1, 1, 1};
public void setCounter(int counter, int value) { public void setCounter(int counter, int value) {
values[counter] = value; values[counter] = value;
@ -34,4 +19,4 @@ public class CounterContainer {
public int getCounterAndIncrement(int counter) { public int getCounterAndIncrement(int counter) {
return values[counter]++; return values[counter]++;
} }
} }

View File

@ -1,155 +1,186 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.collectors; package org.jetbrains.java.decompiler.main.collectors;
import org.jetbrains.java.decompiler.main.ClassesProcessor;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.StructField;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.stream.Collectors;
public class ImportCollector { public class ImportCollector {
private static final String JAVA_LANG_PACKAGE = "java.lang"; private static final String JAVA_LANG_PACKAGE = "java.lang";
private Map<String, String> mapSimpleNames = new HashMap<String, String>(); private final Map<String, String> mapSimpleNames = new HashMap<>();
private Set<String> setNotImportedNames = new HashSet<String>(); private final Set<String> setNotImportedNames = new HashSet<>();
private String currentPackageSlash = ""; // set of field names in this class and all its predecessors.
private String currentPackagePoint = ""; private final Set<String> setFieldNames = new HashSet<>();
private final Set<String> setInnerClassNames = new HashSet<>();
private final String currentPackageSlash;
private final String currentPackagePoint;
public ImportCollector(ClassNode root) { public ImportCollector(ClassNode root) {
String clName = root.classStruct.qualifiedName;
String clname = root.classStruct.qualifiedName; int index = clName.lastIndexOf('/');
int index = clname.lastIndexOf("/");
if (index >= 0) { if (index >= 0) {
currentPackageSlash = clname.substring(0, index); String packageName = clName.substring(0, index);
currentPackagePoint = currentPackageSlash.replace('/', '.'); currentPackageSlash = packageName + '/';
currentPackageSlash += "/"; currentPackagePoint = packageName.replace('/', '.');
}
else {
currentPackageSlash = "";
currentPackagePoint = "";
}
Map<String, StructClass> classes = DecompilerContext.getStructContext().getClasses();
LinkedList<String> queue = new LinkedList<>();
Set<StructClass> processedClasses = new HashSet<>();
StructClass currentClass = root.classStruct;
while (currentClass != null) {
processedClasses.add(currentClass);
if (currentClass.superClass != null) {
queue.add(currentClass.superClass.getString());
}
Collections.addAll(queue, currentClass.getInterfaceNames());
// all field names for the current class ..
for (StructField f : currentClass.getFields()) {
setFieldNames.add(f.getName());
}
// .. all inner classes for the current class ..
StructInnerClassesAttribute attribute = currentClass.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
if (attribute != null) {
for (StructInnerClassesAttribute.Entry entry : attribute.getEntries()) {
if (entry.enclosingName != null && entry.enclosingName.equals(currentClass.qualifiedName)) {
setInnerClassNames.add(entry.simpleName);
}
}
}
// .. and traverse through parent.
do {
currentClass = queue.isEmpty() ? null : classes.get(queue.removeFirst());
if (currentClass != null && processedClasses.contains(currentClass)) {
// Class already processed, skipping.
// This may be sign of circularity in the class hierarchy but in most cases this mean that same interface
// are listed as implemented several times in the class hierarchy.
currentClass = null;
}
} while (currentClass == null && !queue.isEmpty());
} }
} }
public String getShortName(String fullname) { /**
return getShortName(fullname, true); * Check whether the package-less name ClassName is shaded by variable in a context of
* the decompiled class
* @param classToName - pkg.name.ClassName - class to find shortname for
* @return ClassName if the name is not shaded by local field, pkg.name.ClassName otherwise
*/
public String getShortNameInClassContext(String classToName) {
String shortName = getShortName(classToName);
if (setFieldNames.contains(shortName)) {
return classToName;
}
else {
return shortName;
}
} }
public String getShortName(String fullname, boolean imported) { public String getShortName(String fullName) {
return getShortName(fullName, true);
}
ClassesProcessor clproc = DecompilerContext.getClassProcessor(); public String getShortName(String fullName, boolean imported) {
ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/')); ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/')); //todo[r.sh] anonymous classes?
String retname = null;
String result = null;
if (node != null && node.classStruct.isOwn()) { if (node != null && node.classStruct.isOwn()) {
result = node.simpleName;
retname = node.simpleName;
while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) { while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
retname = node.parent.simpleName + "." + retname; //noinspection StringConcatenationInLoop
result = node.parent.simpleName + '.' + result;
node = node.parent; node = node.parent;
} }
if (node.type == ClassNode.CLASS_ROOT) { if (node.type == ClassNode.CLASS_ROOT) {
fullname = node.classStruct.qualifiedName; fullName = node.classStruct.qualifiedName;
fullname = fullname.replace('/', '.'); fullName = fullName.replace('/', '.');
} }
else { else {
return retname; return result;
} }
} }
else { else {
fullname = fullname.replace('$', '.'); fullName = fullName.replace('$', '.');
} }
String nshort = fullname; String shortName = fullName;
String npackage = ""; String packageName = "";
int lastpoint = fullname.lastIndexOf("."); int lastDot = fullName.lastIndexOf('.');
if (lastDot >= 0) {
if (lastpoint >= 0) { shortName = fullName.substring(lastDot + 1);
nshort = fullname.substring(lastpoint + 1); packageName = fullName.substring(0, lastDot);
npackage = fullname.substring(0, lastpoint);
} }
StructContext context = DecompilerContext.getStructContext(); StructContext context = DecompilerContext.getStructContext();
boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null // check for another class which could 'shadow' this one. Three cases:
&& !npackage.equals(currentPackagePoint)) // current package // 1) class with the same short name in the current package
|| (context.getClass(nshort) != null); // default package // 2) class with the same short name in the default package
// 3) inner class with the same short name in the current class, a super class, or an implemented interface
boolean existsDefaultClass =
(context.getClass(currentPackageSlash + shortName) != null && !packageName.equals(currentPackagePoint)) || // current package
(context.getClass(shortName) != null && !currentPackagePoint.isEmpty()) || // default package
setInnerClassNames.contains(shortName); // inner class
if (existsDefaultClass || if (existsDefaultClass ||
(mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) { (mapSimpleNames.containsKey(shortName) && !packageName.equals(mapSimpleNames.get(shortName)))) {
return fullname; // don't return full name because if the class is a inner class, full name refers to the parent full name, not the child full name
return result == null ? fullName : (packageName + "." + result);
} }
else if (!mapSimpleNames.containsKey(nshort)) { else if (!mapSimpleNames.containsKey(shortName)) {
mapSimpleNames.put(nshort, npackage); mapSimpleNames.put(shortName, packageName);
if (!imported) { if (!imported) {
setNotImportedNames.add(nshort); setNotImportedNames.add(shortName);
} }
} }
return retname == null ? nshort : retname; return result == null ? shortName : result;
} }
public int writeImports(TextBuffer buffer) { public void writeImports(TextBuffer buffer, boolean addSeparator) {
int importlines_written = 0;
String new_line_separator = DecompilerContext.getNewLineSeparator();
List<String> imports = packImports(); List<String> imports = packImports();
for (String line : imports) {
for (String s : imports) { buffer.append("import ").append(line).append(';').appendLineSeparator();
buffer.append("import "); }
buffer.append(s); if (addSeparator && !imports.isEmpty()) {
buffer.append(";"); buffer.appendLineSeparator();
buffer.append(new_line_separator);
importlines_written++;
} }
return importlines_written;
} }
private List<String> packImports() { private List<String> packImports() {
List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet()); return mapSimpleNames.entrySet().stream()
.filter(ent ->
Collections.sort(lst, new Comparator<Entry<String, String>>() { // exclude the current class or one of the nested ones
public int compare(Entry<String, String> par0, Entry<String, String> par1) { // empty, java.lang and the current packages
int res = par0.getValue().compareTo(par1.getValue()); !setNotImportedNames.contains(ent.getKey()) &&
if (res == 0) { !ent.getValue().isEmpty() &&
res = par0.getKey().compareTo(par1.getKey()); !JAVA_LANG_PACKAGE.equals(ent.getValue()) &&
} !ent.getValue().equals(currentPackagePoint)
return res; )
} .sorted(Map.Entry.<String, String>comparingByValue().thenComparing(Map.Entry.comparingByKey()))
}); .map(ent -> ent.getValue() + "." + ent.getKey())
.collect(Collectors.toList());
List<String> res = new ArrayList<String>();
for (Entry<String, String> ent : lst) {
// exclude a current class or one of the nested ones, java.lang and empty packages
if (!setNotImportedNames.contains(ent.getKey()) &&
!JAVA_LANG_PACKAGE.equals(ent.getValue()) &&
!ent.getValue().isEmpty() && !ent.getValue().equals(this.currentPackagePoint)) { // Spigot: Remove same package imports
res.add(ent.getValue() + "." + ent.getKey());
}
}
return res;
} }
} }

View File

@ -1,29 +1,20 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.collectors; package org.jetbrains.java.decompiler.main.collectors;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
public class VarNamesCollector { public class VarNamesCollector {
private HashSet<String> usedNames = new HashSet<String>(); private final Set<String> usedNames = new HashSet<>();
public VarNamesCollector() { public VarNamesCollector() { }
}
public VarNamesCollector(Collection<String> setNames) { public VarNamesCollector(Collection<String> setNames) {
usedNames.addAll(setNames); usedNames.addAll(setNames);
@ -33,20 +24,140 @@ public class VarNamesCollector {
usedNames.add(value); usedNames.add(value);
} }
public String getFreeName(int index) {
return getFreeName("var" + index);
}
public String getFreeName(String proposition) { public static String removePrefix(String str, String prefix)
{
while (usedNames.contains(proposition)) { if (str.toLowerCase().startsWith(prefix))
proposition += "x"; {
return str.substring(prefix.length());
}
else
{
return str;
} }
usedNames.add(proposition);
return proposition;
} }
public HashSet<String> getUsedNames() { public String getTypeLabel(VarType type, int index) {
return usedNames; return getTypeLabel(type, index, true);
}
public String getTypeLabel(VarType type, int index, boolean rand) {
String typeName = "tl_var" + index;
// nasty hack
if (type == null)
{
}
else if (type.type == CodeConstants.TYPE_OBJECT)
{
typeName = ExprProcessor.getTypeName(type, true);
typeName = removePrefix(typeName,"abstract");
typeName = removePrefix(typeName,"client");
typeName = Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1);
switch (typeName)
{
case "class":
{
typeName = "clazz";
break;
}
case "integer":
{
typeName = "i";
break;
}
case "long":
{
typeName = "l";
break;
}
case "int":
{
typeName = "i";
break;
}
case "boolean":
{
typeName = "bool";
break;
}
case "float":
{
typeName = "f";
break;
}
case "double":
{
typeName = "d";
break;
}
case "short":
{
typeName = "s";
break;
}
case "byte":
{
typeName = "b";
break;
}
case "enum":
{
typeName = "e";
break;
}
}
}
else if (type.type == CodeConstants.TYPE_NULL)
{
typeName = "null";
}
else
{
typeName = String.valueOf(VarType.getTypeReverse(type)).toLowerCase();
}
if (typeName.contains("."))
{
typeName = typeName.substring(0, typeName.indexOf('.'));
}
if (typeName.equals("void"))
typeName = "v";
if (typeName.equals("enum"))
typeName = "e";
if (!rand)
{
return typeName;
}
if (usedNames.contains(typeName))
{
return getFreeName(typeName);
}
addName(typeName);
return typeName;
}
public String getFreeName(String proposition) {
int i = 2;
String actual = proposition;
while (usedNames.contains(actual)) {
actual = proposition + i;
i++;
}
usedNames.add(actual);
return actual;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.decompiler; package org.jetbrains.java.decompiler.main.decompiler;
import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.Fernflower;
@ -21,27 +7,30 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Map; import java.util.Map;
@SuppressWarnings("unused")
public class BaseDecompiler { public class BaseDecompiler {
private final Fernflower engine;
private final Fernflower fernflower;
public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) { public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
fernflower = new Fernflower(provider, saver, options, logger); engine = new Fernflower(provider, saver, options, logger);
} }
public void addSpace(File file, boolean isOwn) throws IOException { public void addSource(File source) {
fernflower.getStructContext().addSpace(file, isOwn); engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
} }
public void decompileContext() { public void decompileContext() {
try { try {
fernflower.decompileContext(); engine.decompileContext();
} }
finally { finally {
fernflower.clearContext(); engine.clearContext();
} }
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.decompiler; package org.jetbrains.java.decompiler.main.decompiler;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -23,6 +9,7 @@ import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import java.util.jar.Manifest; import java.util.jar.Manifest;
@ -31,7 +18,6 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
@SuppressWarnings("UseOfSystemOutOrSystemErr") @SuppressWarnings("UseOfSystemOutOrSystemErr")
public static void main(String[] args) { public static void main(String[] args) {
if (args.length < 2) { if (args.length < 2) {
@ -41,21 +27,20 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return; return;
} }
Map<String, Object> mapOptions = new HashMap<String, Object>(); Map<String, Object> mapOptions = new HashMap<>();
List<String> lstSources = new ArrayList<String>(); List<File> sources = new ArrayList<>();
List<String> lstLibraries = new ArrayList<String>(); List<File> libraries = new ArrayList<>();
boolean isOption = true; boolean isOption = true;
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
String arg = args[i]; String arg = args[i];
if (isOption && arg.startsWith("-") && if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') {
arg.length() > 5 && arg.charAt(4) == '=') { String value = arg.substring(5);
String value = arg.substring(5).toUpperCase(Locale.US); if ("true".equalsIgnoreCase(value)) {
if ("TRUE".equals(value)) {
value = "1"; value = "1";
} }
else if ("FALSE".equals(value)) { else if ("false".equalsIgnoreCase(value)) {
value = "0"; value = "0";
} }
@ -65,15 +50,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
isOption = false; isOption = false;
if (arg.startsWith("-e=")) { if (arg.startsWith("-e=")) {
lstLibraries.add(arg.substring(3)); addPath(libraries, arg.substring(3));
} }
else { else {
lstSources.add(arg); addPath(sources, arg);
} }
} }
} }
if (lstSources.isEmpty()) { if (sources.isEmpty()) {
System.out.println("error: no sources given"); System.out.println("error: no sources given");
return; return;
} }
@ -87,45 +72,55 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
PrintStreamLogger logger = new PrintStreamLogger(System.out); PrintStreamLogger logger = new PrintStreamLogger(System.out);
ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger); ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);
for (String source : lstSources) { for (File library : libraries) {
decompiler.addSpace(new File(source), true); decompiler.addLibrary(library);
} }
for (String library : lstLibraries) { for (File source : sources) {
decompiler.addSpace(new File(library), false); decompiler.addSource(source);
} }
decompiler.decompileContext(); decompiler.decompileContext();
} }
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private static void addPath(List<? super File> list, String path) {
File file = new File(path);
if (file.exists()) {
list.add(file);
}
else {
System.out.println("warn: missing '" + path + "', ignored");
}
}
// ******************************************************************* // *******************************************************************
// Implementation // Implementation
// ******************************************************************* // *******************************************************************
private final File root; private final File root;
private final Fernflower fernflower; private final Fernflower engine;
private Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
private Map<String, Set<String>> mapArchiveEntries = new HashMap<String, Set<String>>(); private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public ConsoleDecompiler(File destination, Map<String, Object> options) {
this(destination, options, new PrintStreamLogger(System.out));
}
protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) { protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
root = destination; root = destination;
fernflower = new Fernflower(this, this, options, logger); engine = new Fernflower(this, this, options, logger);
} }
public void addSpace(File file, boolean isOwn) { public void addSource(File source) {
fernflower.getStructContext().addSpace(file, isOwn); engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
} }
public void decompileContext() { public void decompileContext() {
try { try {
fernflower.decompileContext(); engine.decompileContext();
} }
finally { finally {
fernflower.clearContext(); engine.clearContext();
} }
} }
@ -140,17 +135,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return InterpreterUtil.getBytes(file); return InterpreterUtil.getBytes(file);
} }
else { else {
ZipFile archive = new ZipFile(file); try (ZipFile archive = new ZipFile(file)) {
try {
ZipEntry entry = archive.getEntry(internalPath); ZipEntry entry = archive.getEntry(internalPath);
if (entry == null) { if (entry == null) throw new IOException("Entry not found: " + internalPath);
throw new IOException("Entry not found: " + internalPath);
}
return InterpreterUtil.getBytes(archive, entry); return InterpreterUtil.getBytes(archive, entry);
} }
finally {
archive.close();
}
} }
} }
@ -181,16 +170,10 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
} }
@Override @Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content) { public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
File file = new File(getAbsolutePath(path), entryName); File file = new File(getAbsolutePath(path), entryName);
try { try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8"); out.write(content);
try {
out.write(content);
}
finally {
out.close();
}
} }
catch (IOException ex) { catch (IOException ex) {
DecompilerContext.getLogger().writeMessage("Cannot write class file " + file, ex); DecompilerContext.getLogger().writeMessage("Cannot write class file " + file, ex);
@ -206,7 +189,6 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
} }
FileOutputStream fileStream = new FileOutputStream(file); FileOutputStream fileStream = new FileOutputStream(file);
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream); ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream);
mapArchiveStreams.put(file.getPath(), zipStream); mapArchiveStreams.put(file.getPath(), zipStream);
} }
@ -228,21 +210,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return; return;
} }
try { try (ZipFile srcArchive = new ZipFile(new File(source))) {
ZipFile srcArchive = new ZipFile(new File(source)); ZipEntry entry = srcArchive.getEntry(entryName);
try { if (entry != null) {
ZipEntry entry = srcArchive.getEntry(entryName); try (InputStream in = srcArchive.getInputStream(entry)) {
if (entry != null) {
InputStream in = srcArchive.getInputStream(entry);
ZipOutputStream out = mapArchiveStreams.get(file); ZipOutputStream out = mapArchiveStreams.get(file);
out.putNextEntry(new ZipEntry(entryName)); out.putNextEntry(new ZipEntry(entryName));
InterpreterUtil.copyStream(in, out); InterpreterUtil.copyStream(in, out);
in.close();
} }
} }
finally {
srcArchive.close();
}
} }
catch (IOException ex) { catch (IOException ex) {
String message = "Cannot copy entry " + entryName + " from " + source + " to " + file; String message = "Cannot copy entry " + entryName + " from " + source + " to " + file;
@ -262,7 +238,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
ZipOutputStream out = mapArchiveStreams.get(file); ZipOutputStream out = mapArchiveStreams.get(file);
out.putNextEntry(new ZipEntry(entryName)); out.putNextEntry(new ZipEntry(entryName));
if (content != null) { if (content != null) {
out.write(content.getBytes("UTF-8")); out.write(content.getBytes(StandardCharsets.UTF_8));
} }
} }
catch (IOException ex) { catch (IOException ex) {
@ -272,10 +248,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
} }
private boolean checkEntry(String entryName, String file) { private boolean checkEntry(String entryName, String file) {
Set<String> set = mapArchiveEntries.get(file); Set<String> set = mapArchiveEntries.computeIfAbsent(file, k -> new HashSet<>());
if (set == null) {
mapArchiveEntries.put(file, set = new HashSet<String>());
}
boolean added = set.add(entryName); boolean added = set.add(entryName);
if (!added) { if (!added) {
@ -296,4 +269,4 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN); DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN);
} }
} }
} }

View File

@ -1,22 +1,8 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.decompiler; package org.jetbrains.java.decompiler.main.decompiler;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.TextUtil;
import java.io.PrintStream; import java.io.PrintStream;
@ -33,62 +19,79 @@ public class PrintStreamLogger extends IFernflowerLogger {
@Override @Override
public void writeMessage(String message, Severity severity) { public void writeMessage(String message, Severity severity) {
if (accepts(severity)) { if (accepts(severity)) {
stream.println(severity.prefix + InterpreterUtil.getIndentString(indent) + message); stream.println(severity.prefix + TextUtil.getIndentString(indent) + message);
} }
} }
@Override @Override
public void writeMessage(String message, Throwable t) { public void writeMessage(String message, Severity severity, Throwable t) {
writeMessage(message, Severity.ERROR); if (accepts(severity)) {
if (accepts(Severity.ERROR)) { writeMessage(message, severity);
t.printStackTrace(stream); t.printStackTrace(stream);
} }
} }
@Override @Override
public void startReadingClass(String className) { public void startReadingClass(String className) {
writeMessage("Decompiling class " + className, Severity.INFO); if (accepts(Severity.INFO)) {
++indent; writeMessage("Decompiling class " + className, Severity.INFO);
++indent;
}
} }
@Override @Override
public void endReadingClass() { public void endReadingClass() {
--indent; if (accepts(Severity.INFO)) {
writeMessage("... done", Severity.INFO); --indent;
writeMessage("... done", Severity.INFO);
}
} }
@Override @Override
public void startClass(String className) { public void startClass(String className) {
writeMessage("Processing class " + className, Severity.TRACE); if (accepts(Severity.INFO)) {
++indent; writeMessage("Processing class " + className, Severity.TRACE);
++indent;
}
} }
@Override @Override
public void endClass() { public void endClass() {
--indent; if (accepts(Severity.INFO)) {
writeMessage("... proceeded", Severity.TRACE); --indent;
writeMessage("... proceeded", Severity.TRACE);
}
} }
@Override @Override
public void startMethod(String methodName) { public void startMethod(String methodName) {
writeMessage("Processing method " + methodName, Severity.TRACE); if (accepts(Severity.INFO)) {
++indent; writeMessage("Processing method " + methodName, Severity.TRACE);
++indent;
}
} }
@Override
public void endMethod() { public void endMethod() {
--indent; if (accepts(Severity.INFO)) {
writeMessage("... proceeded", Severity.TRACE); --indent;
writeMessage("... proceeded", Severity.TRACE);
}
} }
@Override @Override
public void startWriteClass(String className) { public void startWriteClass(String className) {
writeMessage("Writing class " + className, Severity.TRACE); if (accepts(Severity.INFO)) {
++indent; writeMessage("Writing class " + className, Severity.TRACE);
++indent;
}
} }
@Override @Override
public void endWriteClass() { public void endWriteClass() {
--indent; if (accepts(Severity.INFO)) {
writeMessage("... written", Severity.TRACE); --indent;
writeMessage("... written", Severity.TRACE);
}
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.extern;
import java.io.IOException; import java.io.IOException;

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.extern;
public abstract class IFernflowerLogger { public abstract class IFernflowerLogger {
@ -39,7 +25,11 @@ public abstract class IFernflowerLogger {
public abstract void writeMessage(String message, Severity severity); public abstract void writeMessage(String message, Severity severity);
public abstract void writeMessage(String message, Throwable t); public abstract void writeMessage(String message, Severity severity, Throwable t);
public void writeMessage(String message, Throwable t) {
writeMessage(message, Severity.ERROR, t);
}
public void startReadingClass(String className) { } public void startReadingClass(String className) { }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.extern;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
@ -31,6 +17,7 @@ public interface IFernflowerPreferences {
String HIDE_DEFAULT_CONSTRUCTOR = "hdc"; String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
String DECOMPILE_GENERIC_SIGNATURES = "dgs"; String DECOMPILE_GENERIC_SIGNATURES = "dgs";
String NO_EXCEPTIONS_RETURN = "ner"; String NO_EXCEPTIONS_RETURN = "ner";
String ENSURE_SYNCHRONIZED_MONITOR = "esm";
String DECOMPILE_ENUM = "den"; String DECOMPILE_ENUM = "den";
String REMOVE_GET_CLASS_NEW = "rgn"; String REMOVE_GET_CLASS_NEW = "rgn";
String LITERALS_AS_IS = "lit"; String LITERALS_AS_IS = "lit";
@ -39,13 +26,14 @@ public interface IFernflowerPreferences {
String SYNTHETIC_NOT_SET = "nns"; String SYNTHETIC_NOT_SET = "nns";
String UNDEFINED_PARAM_TYPE_OBJECT = "uto"; String UNDEFINED_PARAM_TYPE_OBJECT = "uto";
String USE_DEBUG_VAR_NAMES = "udv"; String USE_DEBUG_VAR_NAMES = "udv";
String USE_METHOD_PARAMETERS = "ump";
String REMOVE_EMPTY_RANGES = "rer"; String REMOVE_EMPTY_RANGES = "rer";
String FINALLY_DEINLINE = "fdi"; String FINALLY_DEINLINE = "fdi";
String IDEA_NOT_NULL_ANNOTATION = "inn"; String IDEA_NOT_NULL_ANNOTATION = "inn";
String LAMBDA_TO_ANONYMOUS_CLASS = "lac"; String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
String BYTECODE_SOURCE_MAPPING = "bsm"; String BYTECODE_SOURCE_MAPPING = "bsm";
String USE_DEBUG_LINE_NUMBERS = "udl"; String IGNORE_INVALID_BYTECODE = "iib";
String VERIFY_ANONYMOUS_CLASSES = "vac";
String LOG_LEVEL = "log"; String LOG_LEVEL = "log";
String MAX_PROCESSING_METHOD = "mpm"; String MAX_PROCESSING_METHOD = "mpm";
@ -53,44 +41,55 @@ public interface IFernflowerPreferences {
String USER_RENAMER_CLASS = "urc"; String USER_RENAMER_CLASS = "urc";
String NEW_LINE_SEPARATOR = "nls"; String NEW_LINE_SEPARATOR = "nls";
String INDENT_STRING = "ind"; String INDENT_STRING = "ind";
String BANNER = "ban"; String BANNER = "ban";
String DUMP_ORIGINAL_LINES = "__dump_original_lines__";
String UNIT_TEST_MODE = "__unit_test_mode__";
String LINE_SEPARATOR_WIN = "\r\n"; String LINE_SEPARATOR_WIN = "\r\n";
String LINE_SEPARATOR_LIN = "\n"; String LINE_SEPARATOR_UNX = "\n";
Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>() {{ Map<String, Object> DEFAULTS = getDefaults();
put(REMOVE_BRIDGE, "1");
put(REMOVE_SYNTHETIC, "0");
put(DECOMPILE_INNER, "1");
put(DECOMPILE_CLASS_1_4, "1");
put(DECOMPILE_ASSERTIONS, "1");
put(HIDE_EMPTY_SUPER, "1");
put(HIDE_DEFAULT_CONSTRUCTOR, "1");
put(DECOMPILE_GENERIC_SIGNATURES, "0");
put(NO_EXCEPTIONS_RETURN, "1");
put(DECOMPILE_ENUM, "1");
put(REMOVE_GET_CLASS_NEW, "1");
put(LITERALS_AS_IS, "0");
put(BOOLEAN_TRUE_ONE, "1");
put(ASCII_STRING_CHARACTERS, "0");
put(SYNTHETIC_NOT_SET, "1");
put(UNDEFINED_PARAM_TYPE_OBJECT, "1");
put(USE_DEBUG_VAR_NAMES, "1");
put(REMOVE_EMPTY_RANGES, "1");
put(FINALLY_DEINLINE, "1");
put(IDEA_NOT_NULL_ANNOTATION, "1");
put(LAMBDA_TO_ANONYMOUS_CLASS, "0");
put(BYTECODE_SOURCE_MAPPING, "0"); static Map<String, Object> getDefaults() {
put(USE_DEBUG_LINE_NUMBERS, "0"); Map<String, Object> defaults = new HashMap<>();
put(BANNER, ""); defaults.put(REMOVE_BRIDGE, "1");
defaults.put(REMOVE_SYNTHETIC, "0");
defaults.put(DECOMPILE_INNER, "1");
defaults.put(DECOMPILE_CLASS_1_4, "1");
defaults.put(DECOMPILE_ASSERTIONS, "1");
defaults.put(HIDE_EMPTY_SUPER, "1");
defaults.put(HIDE_DEFAULT_CONSTRUCTOR, "1");
defaults.put(DECOMPILE_GENERIC_SIGNATURES, "0");
defaults.put(NO_EXCEPTIONS_RETURN, "1");
defaults.put(ENSURE_SYNCHRONIZED_MONITOR, "1");
defaults.put(DECOMPILE_ENUM, "1");
defaults.put(REMOVE_GET_CLASS_NEW, "1");
defaults.put(LITERALS_AS_IS, "0");
defaults.put(BOOLEAN_TRUE_ONE, "1");
defaults.put(ASCII_STRING_CHARACTERS, "0");
defaults.put(SYNTHETIC_NOT_SET, "0");
defaults.put(UNDEFINED_PARAM_TYPE_OBJECT, "1");
defaults.put(USE_DEBUG_VAR_NAMES, "1");
defaults.put(USE_METHOD_PARAMETERS, "1");
defaults.put(REMOVE_EMPTY_RANGES, "1");
defaults.put(FINALLY_DEINLINE, "1");
defaults.put(IDEA_NOT_NULL_ANNOTATION, "1");
defaults.put(LAMBDA_TO_ANONYMOUS_CLASS, "0");
defaults.put(BYTECODE_SOURCE_MAPPING, "0");
defaults.put(IGNORE_INVALID_BYTECODE, "0");
defaults.put(VERIFY_ANONYMOUS_CLASSES, "0");
put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name()); defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name());
put(MAX_PROCESSING_METHOD, "0"); defaults.put(MAX_PROCESSING_METHOD, "0");
put(RENAME_ENTITIES, "0"); defaults.put(RENAME_ENTITIES, "0");
put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1")); defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1"));
put(INDENT_STRING, " "); defaults.put(INDENT_STRING, " ");
}}); defaults.put(BANNER, "");
} defaults.put(UNIT_TEST_MODE, "0");
defaults.put(DUMP_ORIGINAL_LINES, "0");
return Collections.unmodifiableMap(defaults);
}
}

View File

@ -1,35 +1,15 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.extern;
public interface IIdentifierRenamer { public interface IIdentifierRenamer {
int ELEMENT_CLASS = 1; enum Type {ELEMENT_CLASS, ELEMENT_FIELD, ELEMENT_METHOD}
int ELEMENT_FIELD = 2; boolean toBeRenamed(Type elementType, String className, String element, String descriptor);
int ELEMENT_METHOD = 3; String getNextClassName(String fullName, String shortName);
String getNextFieldName(String className, String field, String descriptor);
boolean toBeRenamed(int element_type, String classname, String element, String descriptor); String getNextMethodName(String className, String method, String descriptor);
String getNextClassname(String fullname, String shortname);
String getNextFieldname(String classname, String field, String descriptor);
String getNextMethodname(String classname, String method, String descriptor);
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.extern;
import java.util.jar.Manifest; import java.util.jar.Manifest;
@ -22,7 +8,7 @@ public interface IResultSaver {
void copyFile(String source, String path, String entryName); void copyFile(String source, String path, String entryName);
void saveClassFile(String path, String qualifiedName, String entryName, String content); void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping);
void createArchive(String path, String archiveName, Manifest manifest); void createArchive(String path, String archiveName, Manifest manifest);

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -22,64 +8,49 @@ import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass; 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.StructMethod;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection; import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
public class ClassWrapper { public class ClassWrapper {
private final StructClass classStruct;
private StructClass classStruct; private final Set<String> hiddenMembers = new HashSet<>();
private Set<String> hiddenMembers = new HashSet<String>(); private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<>();
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>(); private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<>();
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>(); private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<>();
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
public ClassWrapper(StructClass classStruct) { public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct; this.classStruct = classStruct;
} }
public void init() throws IOException { public void init() {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName); DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
// collect field names int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
HashSet<String> setFieldNames = new HashSet<String>(); boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);
for (StructField fd : classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
int maxsec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
for (StructMethod mt : classStruct.getMethods()) { for (StructMethod mt : classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor()); DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
VarNamesCollector vc = new VarNamesCollector(); MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
DecompilerContext.setVarNamesCollector(vc); VarProcessor varProc = new VarProcessor(mt, md);
DecompilerContext.startMethod(varProc);
CounterContainer counter = new CounterContainer(); VarNamesCollector vc = varProc.getVarNamesCollector();
DecompilerContext.setCounterContainer(counter); CounterContainer counter = DecompilerContext.getCounterContainer();
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);
RootStatement root = null; RootStatement root = null;
@ -87,86 +58,113 @@ public class ClassWrapper {
try { try {
if (mt.containsCode()) { if (mt.containsCode()) {
if (maxSec == 0 || testMode) {
if (maxsec == 0) { // blocking wait root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);
root = MethodProcessorThread.codeToJava(mt, varproc);
} }
else { else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext()); MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtthread = new Thread(mtproc);
long stopAt = System.currentTimeMillis() + maxsec * 1000;
mtthread.start(); Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000L;
while (mtthread.isAlive()) { mtThread.start();
synchronized (mtproc.lock) { while (!mtProc.isFinished()) {
mtproc.lock.wait(100); try {
synchronized (mtProc.lock) {
mtProc.lock.wait(200);
}
}
catch (InterruptedException e) {
killThread(mtThread);
throw e;
} }
if (System.currentTimeMillis() >= stopAt) { if (System.currentTimeMillis() >= stopAt) {
String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted."; String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR); DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR);
killThread(mtthread); killThread(mtThread);
isError = true; isError = true;
break; break;
} }
} }
if (!isError) { if (!isError) {
root = mtproc.getResult(); root = mtProc.getResult();
} }
} }
} }
else { else {
boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0; int paramCount = 0;
if (thisvar) { if (thisVar) {
varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName); varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName);
paramcount = 1; paramCount = 1;
} }
paramcount += md.params.length; paramCount += md.params.length;
int varindex = 0; int varIndex = 0;
for (int i = 0; i < paramcount; i++) { for (int i = 0; i < paramCount; i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0, classStruct.qualifiedName, false), vc.getFreeName(varindex)); varProc.setVarName(new VarVersionPair(varIndex, 0), "tbd");//, vc.getFreeName(varIndex));
if (thisvar) { if (thisVar) {
if (i == 0) { if (i == 0) {
varindex++; varIndex++;
} }
else { else {
varindex += md.params[i - 1].stack_size; varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getTypeLabel(md.params[i - 1], varIndex));
varIndex += md.params[i - 1].stackSize;
} }
} }
else { else {
varindex += md.params[i].stack_size; varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getTypeLabel(md.params[i], varIndex));
varIndex += md.params[i].stackSize;
} }
} }
} }
} }
catch (Throwable ex) { catch (Throwable t) {
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex); String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
isError = true; isError = true;
} }
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter); MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter);
meth.decompiledWithErrors = isError; methodWrapper.decompiledWithErrors = isError;
methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
// rename vars so that no one has the same name as a field if (!isError) {
varproc.refreshVarNames(new VarNamesCollector(setFieldNames)); // rename vars so that no one has the same name as a field
VarNamesCollector namesCollector = new VarNamesCollector();
// well, actually, no -reece
//classStruct.getFields().forEach(f -> namesCollector.addName(f.getName()));
varProc.refreshVarNames(namesCollector);
// if debug information present and should be used // if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey( StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); if (attr != null) {
// only param names here
varProc.setDebugVarNames(attr.getMapParamNames());
if (attr != null) { // the rest is here
varproc.setDebugVarNames(attr.getMapVarNames()); methodWrapper.getOrBuildGraph().iterateExprents(exprent -> {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
lst.stream()
.filter(e -> e.type == Exprent.EXPRENT_VAR)
.forEach(e -> {
VarExprent varExprent = (VarExprent)e;
String name = varExprent.getDebugName(mt);
if (name != null) {
varProc.setVarName(varExprent.getVarVersionPair(), name);
}
});
return 0;
});
}
} }
} }
@ -204,4 +202,9 @@ public class ClassWrapper {
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() { public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers; return dynamicFieldInitializers;
} }
}
@Override
public String toString() {
return classStruct.qualifiedName;
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -35,36 +21,28 @@ import java.io.IOException;
import java.util.*; import java.util.*;
public class LambdaProcessor { public class LambdaProcessor {
@SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory"; @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
@SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_METHOD = "metafactory"; @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_METHOD = "metafactory";
@SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_ALT_METHOD = "altMetafactory"; @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_ALT_METHOD = "altMetafactory";
public void processClass(ClassNode node) throws IOException { public void processClass(ClassNode node) throws IOException {
for (ClassNode child : node.nested) { for (ClassNode child : node.nested) {
processClass(child); processClass(child);
} }
hasLambda(node); ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();
}
public boolean hasLambda(ClassNode node) throws IOException {
ClassesProcessor clprocessor = DecompilerContext.getClassProcessor();
StructClass cl = node.classStruct; StructClass cl = node.classStruct;
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8 if (!cl.isVersion8()) { // lambda beginning with Java 8
return false; return;
} }
StructBootstrapMethodsAttribute bootstrap = StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return false; // no bootstrap constants in pool return; // no bootstrap constants in pool
} }
BitSet lambda_methods = new BitSet(); BitSet lambdaMethods = new BitSet();
// find lambda bootstrap constants // find lambda bootstrap constants
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
@ -73,19 +51,19 @@ public class LambdaProcessor {
// FIXME: extend for Eclipse etc. at some point // FIXME: extend for Eclipse etc. at some point
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
(JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) { (JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) {
lambda_methods.set(i); lambdaMethods.set(i);
} }
} }
if (lambda_methods.isEmpty()) { if (lambdaMethods.isEmpty()) {
return false; // no lambda bootstrap constant found return; // no lambda bootstrap constant found
} }
Map<String, String> mapMethodsLambda = new HashMap<String, String>(); Map<String, String> mapMethodsLambda = new HashMap<>();
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes. // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for (StructMethod mt : cl.getMethods()) { for (StructMethod mt : cl.getMethods()) {
mt.expandData(); mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence(); InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) { if (seq != null && seq.length() > 0) {
@ -95,9 +73,9 @@ public class LambdaProcessor {
Instruction instr = seq.getInstr(i); Instruction instr = seq.getInstr(i);
if (instr.opcode == CodeConstants.opc_invokedynamic) { if (instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0)); LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0));
if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
@ -117,9 +95,9 @@ public class LambdaProcessor {
node.nested.add(node_lambda); node.nested.add(node_lambda);
node_lambda.parent = node; node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda); clProcessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
if (!node_lambda.lambda_information.is_method_reference) { if (!node_lambda.lambdaInformation.is_method_reference) {
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName); mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName);
} }
} }
} }
@ -134,7 +112,7 @@ public class LambdaProcessor {
if (nd.type == ClassNode.CLASS_LAMBDA) { if (nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod); String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if (parent_class_name != null) { if (parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name); ClassNode parent_class = clProcessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd); parent_class.nested.add(nd);
nd.parent = parent_class; nd.parent = parent_class;
@ -143,7 +121,5 @@ public class LambdaProcessor {
} }
// FIXME: mixed hierarchy? // FIXME: mixed hierarchy?
return false;
} }
} }

View File

@ -1,20 +1,7 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -28,81 +15,73 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.io.IOException; import java.io.IOException;
public class MethodProcessorThread implements Runnable { public class MethodProcessorRunnable implements Runnable {
public final Object lock = new Object(); public final Object lock = new Object();
private final StructClass klass;
private final StructMethod method; private final StructMethod method;
private final VarProcessor varproc; private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc;
private final DecompilerContext parentContext; private final DecompilerContext parentContext;
private volatile RootStatement root; private volatile RootStatement root;
private volatile Throwable error; private volatile Throwable error;
private volatile boolean finished = false;
public MethodProcessorThread(StructMethod method, VarProcessor varproc, DecompilerContext parentContext) { public MethodProcessorRunnable(StructClass klass,
StructMethod method,
MethodDescriptor methodDescriptor,
VarProcessor varProc,
DecompilerContext parentContext) {
this.klass = klass;
this.method = method; this.method = method;
this.varproc = varproc; this.methodDescriptor = methodDescriptor;
this.varProc = varProc;
this.parentContext = parentContext; this.parentContext = parentContext;
} }
@Override
public void run() { public void run() {
DecompilerContext.setCurrentContext(parentContext);
error = null; error = null;
root = null; root = null;
try { try {
root = codeToJava(method, varproc); DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(klass, method, methodDescriptor, varProc);
}
catch (Throwable t) {
error = t;
}
finally {
DecompilerContext.setCurrentContext(null);
}
synchronized (lock) { finished = true;
lock.notifyAll(); synchronized (lock) {
} lock.notifyAll();
}
catch (ThreadDeath ex) {
throw ex;
}
catch (Throwable ex) {
error = ex;
} }
} }
public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException { public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
StructClass cl = mt.getClassStruct(); mt.expandData(cl);
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence(); InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq); 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); DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt); graph.inlineJsr(cl, mt);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
// TODO: move to the start, before jsr inlining // TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph); DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph); DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph); ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
ExceptionDeobfuscator.restorePopRanges(graph); ExceptionDeobfuscator.restorePopRanges(graph);
@ -110,15 +89,16 @@ public class MethodProcessorThread implements Runnable {
ExceptionDeobfuscator.removeEmptyRanges(graph); ExceptionDeobfuscator.removeEmptyRanges(graph);
} }
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); if (DecompilerContext.getOption(IFernflowerPreferences.ENSURE_SYNCHRONIZED_MONITOR)) {
// special case: search for 'synchronized' ranges w/o monitorexit instruction (as generated by Kotlin and Scala)
DeadCodeHelper.extendSynchronizedRangeToMonitorexit(graph);
}
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range // special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph); DeadCodeHelper.incorporateValueReturns(graph);
} }
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
// ExceptionDeobfuscator.restorePopRanges(graph); // ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
@ -126,22 +106,18 @@ public class MethodProcessorThread implements Runnable {
DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); 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)) { if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN);
if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {
DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN);
}
ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion());
} }
RootStatement root = DomHelper.parseGraph(graph); RootStatement root = DomHelper.parseGraph(graph);
FinallyProcessor fproc = new FinallyProcessor(varproc); FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fproc.iterateGraph(mt, root, graph)) { while (fProc.iterateGraph(cl, 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); root = DomHelper.parseGraph(graph);
} }
@ -149,72 +125,43 @@ public class MethodProcessorThread implements Runnable {
// not until now because of comparison between synchronized statements in the finally cycle // not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root); DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); // LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root); SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root); ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor(); ExprProcessor proc = new ExprProcessor(md, varProc);
proc.processStatement(root, cl); proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); SequenceHelper.condenseSequences(root);
// System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); StackVarsProcessor stackProc = new StackVarsProcessor();
while (true) { do {
StackVarsProcessor stackproc = new StackVarsProcessor(); stackProc.simplifyStackVars(root, mt, cl);
stackproc.simplifyStackVars(root, mt, cl); varProc.setVarVersions(root);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (!new PPandMMHelper().findPPandMM(root)) {
break;
}
} }
while (new PPandMMHelper().findPPandMM(root));
while (true) { while (true) {
LabelHelper.cleanUpEdges(root); LabelHelper.cleanUpEdges(root);
while (true) { do {
MergeHelper.enhanceLoops(root); MergeHelper.enhanceLoops(root);
if (LoopExtractHelper.extractLoops(root)) {
continue;
}
if (!IfHelper.mergeAllIfs(root)) {
break;
}
} }
while (LoopExtractHelper.extractLoops(root) || IfHelper.mergeAllIfs(root));
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root); SequenceHelper.condenseSequences(root);
stackProc.simplifyStackVars(root, mt, cl);
StackVarsProcessor stackproc = new StackVarsProcessor(); varProc.setVarVersions(root);
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
} }
} }
LabelHelper.identifyLabels(root); LabelHelper.identifyLabels(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) { if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue; continue;
} }
@ -225,16 +172,16 @@ public class MethodProcessorThread implements Runnable {
} }
// FIXME: !! // FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) { //if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break; // break;
// } //}
} }
ExitHelper.removeRedundantReturns(root); ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root); SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);
varproc.setVarDefinitions(root); varProc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent // must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed // FIXME: new edge type needed
@ -242,8 +189,6 @@ public class MethodProcessorThread implements Runnable {
mt.releaseResources(); mt.releaseResources();
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root; return root;
} }
@ -253,7 +198,7 @@ public class MethodProcessorThread implements Runnable {
return root; return root;
} }
public Throwable getError() { public boolean isFinished() {
return error; return finished;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
@ -20,31 +6,24 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
public class MethodWrapper { public class MethodWrapper {
public final RootStatement root;
public RootStatement root; public final VarProcessor varproc;
public final StructMethod methodStruct;
public VarProcessor varproc; public final CounterContainer counter;
public final Set<String> setOuterVarNames = new HashSet<>();
public StructMethod methodStruct;
public CounterContainer counter;
public DirectGraph graph; public DirectGraph graph;
public List<VarVersionPair> synthParameters;
public List<VarVersionPaar> signatureFields;
public boolean decompiledWithErrors; public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) { public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root; this.root = root;
this.varproc = varproc; this.varproc = varproc;
@ -54,9 +33,13 @@ public class MethodWrapper {
public DirectGraph getOrBuildGraph() { public DirectGraph getOrBuildGraph() {
if (graph == null && root != null) { if (graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); graph = new FlattenStatementsHelper().buildDirectGraph(root);
graph = flatthelper.buildDirectGraph(root);
} }
return graph; return graph;
} }
}
@Override
public String toString() {
return methodStruct.getName();
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -24,25 +10,19 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*; 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.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.HashMap; import java.util.*;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
public class NestedMemberAccess { public class NestedMemberAccess {
private static final int METHOD_ACCESS_NORMAL = 1; private enum MethodAccess {NORMAL, FIELD_GET, FIELD_SET, METHOD, FUNCTION}
private static final int METHOD_ACCESS_FIELD_GET = 2;
private static final int METHOD_ACCESS_FIELD_SET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
private boolean noSynthFlag; private boolean noSynthFlag;
private Map<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>(); private final Map<MethodWrapper, MethodAccess> mapMethodType = new HashMap<>();
public void propagateMemberAccess(ClassNode root) { public void propagateMemberAccess(ClassNode root) {
@ -67,13 +47,13 @@ public class NestedMemberAccess {
computeMethodTypes(nd); computeMethodTypes(nd);
} }
for (MethodWrapper method : node.wrapper.getMethods()) { for (MethodWrapper method : node.getWrapper().getMethods()) {
computeMethodType(node, method); computeMethodType(node, method);
} }
} }
private void computeMethodType(ClassNode node, MethodWrapper method) { private void computeMethodType(ClassNode node, MethodWrapper method) {
int type = METHOD_ACCESS_NORMAL; MethodAccess type = MethodAccess.NORMAL;
if (method.root != null) { if (method.root != null) {
DirectGraph graph = method.getOrBuildGraph(); DirectGraph graph = method.getOrBuildGraph();
@ -91,7 +71,7 @@ public class NestedMemberAccess {
if (exprent.type == Exprent.EXPRENT_EXIT) { if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent; ExitExprent exexpr = (ExitExprent)exprent;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue(); exprCore = exexpr.getValue();
} }
} }
@ -104,7 +84,7 @@ public class NestedMemberAccess {
if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpr.isStatic() || if (fexpr.isStatic() ||
(fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) { (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELD_GET; type = MethodAccess.FIELD_GET;
} }
} }
} }
@ -113,13 +93,23 @@ public class NestedMemberAccess {
if (parcount == 1) { if (parcount == 1) {
// this or final variable // this or final variable
if (((VarExprent)exprCore).getIndex() != 0) { if (((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELD_GET; type = MethodAccess.FIELD_GET;
} }
} }
break;
case Exprent.EXPRENT_FUNCTION:
// for now detect only increment/decrement
FunctionExprent functionExprent = (FunctionExprent)exprCore;
if (functionExprent.getFuncType() >= FunctionExprent.FUNCTION_IMM &&
functionExprent.getFuncType() <= FunctionExprent.FUNCTION_PPI) {
if (functionExprent.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
type = MethodAccess.FUNCTION;
}
}
break; break;
case Exprent.EXPRENT_INVOCATION: case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD; type = MethodAccess.METHOD;
break; break;
case Exprent.EXPRENT_ASSIGNMENT: case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore; AssignmentExprent asexpr = (AssignmentExprent)exprCore;
@ -131,7 +121,7 @@ public class NestedMemberAccess {
if (fexpras.isStatic() || if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELD_SET; type = MethodAccess.FIELD_SET;
} }
} }
} }
@ -139,30 +129,31 @@ public class NestedMemberAccess {
} }
} }
if (type == MethodAccess.METHOD) { // FIXME: check for private flag of the method
if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method type = MethodAccess.NORMAL;
type = METHOD_ACCESS_NORMAL;
InvocationExprent invexpr = (InvocationExprent)exprCore; InvocationExprent invexpr = (InvocationExprent)exprCore;
if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || boolean isStatic = invexpr.isStatic();
(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR if ((isStatic && invexpr.getLstParameters().size() == parcount) ||
(!isStatic && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) { && ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) {
boolean equalpars = true; boolean equalpars = true;
int index = isStatic ? 0 : 1;
for (int i = 0; i < invexpr.getLstParameters().size(); i++) { for (int i = 0; i < invexpr.getLstParameters().size(); i++) {
Exprent parexpr = invexpr.getLstParameters().get(i); Exprent parexpr = invexpr.getLstParameters().get(i);
if (parexpr.type != Exprent.EXPRENT_VAR || if (parexpr.type != Exprent.EXPRENT_VAR || ((VarExprent)parexpr).getIndex() != index) {
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) {
equalpars = false; equalpars = false;
break; break;
} }
index += mtdesc.params[i + (isStatic ? 0 : 1)].stackSize;
} }
if (equalpars) { if (equalpars) {
type = METHOD_ACCESS_METHOD; type = MethodAccess.METHOD;
} }
} }
} }
@ -188,10 +179,10 @@ public class NestedMemberAccess {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond; ExitExprent exexpr = (ExitExprent)exprentSecond;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if (exexpr.getValue().type == Exprent.EXPRENT_VAR && if (exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { ((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELD_SET; type = MethodAccess.FIELD_SET;
} }
} }
} }
@ -205,7 +196,7 @@ public class NestedMemberAccess {
} }
} }
if (type != METHOD_ACCESS_NORMAL) { if (type != MethodAccess.NORMAL) {
mapMethodType.put(method, type); mapMethodType.put(method, type);
} }
else { else {
@ -220,7 +211,7 @@ public class NestedMemberAccess {
return; return;
} }
for (MethodWrapper meth : node.wrapper.getMethods()) { for (MethodWrapper meth : node.getWrapper().getMethods()) {
if (meth.root != null) { if (meth.root != null) {
@ -228,8 +219,8 @@ public class NestedMemberAccess {
DirectGraph graph = meth.getOrBuildGraph(); DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); HashSet<DirectNode> setVisited = new HashSet<>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); LinkedList<DirectNode> stack = new LinkedList<>();
stack.add(graph.first); stack.add(graph.first);
while (!stack.isEmpty()) { // TODO: replace with interface iterator? while (!stack.isEmpty()) { // TODO: replace with interface iterator?
@ -256,9 +247,7 @@ public class NestedMemberAccess {
} }
} }
for (DirectNode ndx : nd.succs) { stack.addAll(nd.succs);
stack.add(ndx);
}
} }
if (replaced) { if (replaced) {
@ -323,12 +312,11 @@ public class NestedMemberAccess {
} }
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) { private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname()); ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null; MethodWrapper methsource = null;
if (node != null && node.wrapper != null) { if (node != null && node.getWrapper() != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); methsource = node.getWrapper().getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
} }
if (methsource == null || !mapMethodType.containsKey(methsource)) { if (methsource == null || !mapMethodType.containsKey(methsource)) {
@ -343,10 +331,10 @@ public class NestedMemberAccess {
return null; return null;
} }
int type = mapMethodType.get(methsource); MethodAccess type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map // // FIXME: impossible case. MethodAccess.NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) { // if(type == MethodAccess.NORMAL) {
// return null; // return null;
// } // }
@ -360,11 +348,11 @@ public class NestedMemberAccess {
Exprent retexprent = null; Exprent retexprent = null;
switch (type) { switch (type) {
case METHOD_ACCESS_FIELD_GET: case FIELD_GET:
ExitExprent exsource = (ExitExprent)source; ExitExprent exsource = (ExitExprent)source;
if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue(); VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var)); String varname = methsource.varproc.getVarName(new VarVersionPair(var));
if (!methdest.setOuterVarNames.contains(varname)) { if (!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector(); VarNamesCollector vnc = new VarNamesCollector();
@ -375,8 +363,8 @@ public class NestedMemberAccess {
} }
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER); int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc); VarExprent ret = new VarExprent(index, var.getVarType(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname); methdest.varproc.setVarName(new VarVersionPair(index, 0), varname);
retexprent = ret; retexprent = ret;
} }
@ -388,7 +376,7 @@ public class NestedMemberAccess {
retexprent = ret; retexprent = ret;
} }
break; break;
case METHOD_ACCESS_FIELD_SET: case FIELD_SET:
AssignmentExprent ret; AssignmentExprent ret;
if (source.type == Exprent.EXPRENT_EXIT) { if (source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source; ExitExprent extex = (ExitExprent)source;
@ -406,9 +394,17 @@ public class NestedMemberAccess {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1)); ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0)); fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
} }
// do not use copied bytecodes
ret.getLeft().bytecode = null;
ret.getRight().bytecode = null;
retexprent = ret; retexprent = ret;
break; break;
case METHOD_ACCESS_METHOD: case FUNCTION:
retexprent = replaceFunction(invexpr, source);
break;
case METHOD:
if (source.type == Exprent.EXPRENT_EXIT) { if (source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue(); source = ((ExitExprent)source).getValue();
} }
@ -430,6 +426,10 @@ public class NestedMemberAccess {
if (retexprent != null) { if (retexprent != null) {
// preserve original bytecodes
retexprent.bytecode = null;
retexprent.addBytecodeOffsets(invexpr.bytecode);
// hide synthetic access method // hide synthetic access method
boolean hide = true; boolean hide = true;
@ -440,10 +440,31 @@ public class NestedMemberAccess {
} }
} }
if (hide) { if (hide) {
node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
} }
} }
return retexprent; return retexprent;
} }
private static Exprent replaceFunction(final InvocationExprent invexpr, final Exprent source) {
FunctionExprent functionExprent = (FunctionExprent)((ExitExprent)source).getValue().copy();
List<Exprent> lstParameters = invexpr.getLstParameters();
FieldExprent fieldExprent = (FieldExprent)functionExprent.getLstOperands().get(0);
if (fieldExprent.isStatic()) {
if (!lstParameters.isEmpty()) {
return null;
}
return functionExprent;
}
if (lstParameters.size() != 1) {
return null;
}
fieldExprent.replaceExprent(fieldExprent.getInstance(), lstParameters.get(0));
return functionExprent;
}
} }

View File

@ -1,23 +1,10 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* 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.code; package org.jetbrains.java.decompiler.modules.code;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
@ -26,12 +13,12 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import java.util.*; import java.util.*;
public class DeadCodeHelper { public final class DeadCodeHelper {
public static void removeDeadBlocks(ControlFlowGraph graph) { public static void removeDeadBlocks(ControlFlowGraph graph) {
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); LinkedList<BasicBlock> stack = new LinkedList<>();
HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>(); HashSet<BasicBlock> setStacked = new HashSet<>();
stack.add(graph.getFirst()); stack.add(graph.getFirst());
setStacked.add(graph.getFirst()); setStacked.add(graph.getFirst());
@ -39,7 +26,7 @@ public class DeadCodeHelper {
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
BasicBlock block = stack.removeFirst(); BasicBlock block = stack.removeFirst();
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
lstSuccs.addAll(block.getSuccExceptions()); lstSuccs.addAll(block.getSuccExceptions());
for (BasicBlock succ : lstSuccs) { for (BasicBlock succ : lstSuccs) {
@ -50,7 +37,7 @@ public class DeadCodeHelper {
} }
} }
HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks()); HashSet<BasicBlock> setAllBlocks = new HashSet<>(graph.getBlocks());
setAllBlocks.removeAll(setStacked); setAllBlocks.removeAll(setStacked);
for (BasicBlock block : setAllBlocks) { for (BasicBlock block : setAllBlocks) {
@ -94,7 +81,7 @@ public class DeadCodeHelper {
} }
} }
HashSet<BasicBlock> setExits = new HashSet<BasicBlock>(graph.getLast().getPreds()); HashSet<BasicBlock> setExits = new HashSet<>(graph.getLast().getPreds());
if (block.getPredExceptions().isEmpty() && if (block.getPredExceptions().isEmpty() &&
(!setExits.contains(block) || block.getPreds().size() == 1)) { (!setExits.contains(block) || block.getPreds().size() == 1)) {
@ -109,15 +96,15 @@ public class DeadCodeHelper {
} }
} }
HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds()); HashSet<BasicBlock> setPreds = new HashSet<>(block.getPreds());
HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs()); HashSet<BasicBlock> setSuccs = new HashSet<>(block.getSuccs());
// collect common exception ranges of predecessors and successors // collect common exception ranges of predecessors and successors
HashSet<BasicBlock> setCommonExceptionHandlers = null; HashSet<BasicBlock> setCommonExceptionHandlers = null;
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
for (BasicBlock pred : i == 0 ? setPreds : setSuccs) { for (BasicBlock pred : i == 0 ? setPreds : setSuccs) {
if (setCommonExceptionHandlers == null) { if (setCommonExceptionHandlers == null) {
setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions()); setCommonExceptionHandlers = new HashSet<>(pred.getSuccExceptions());
} }
else { else {
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
@ -159,7 +146,7 @@ public class DeadCodeHelper {
BasicBlock pred = block.getPreds().get(0); BasicBlock pred = block.getPreds().get(0);
pred.removeSuccessor(block); pred.removeSuccessor(block);
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
for (BasicBlock succ : lstSuccs) { for (BasicBlock succ : lstSuccs) {
block.removeSuccessor(succ); block.removeSuccessor(succ);
pred.addSuccessor(succ); pred.addSuccessor(succ);
@ -205,13 +192,13 @@ public class DeadCodeHelper {
public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {
HashSet<BasicBlock> marked = new HashSet<BasicBlock>(); HashSet<BasicBlock> marked = new HashSet<>();
if (block == dom) { if (block == dom) {
return true; return true;
} }
LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); LinkedList<BasicBlock> lstNodes = new LinkedList<>();
lstNodes.add(block); lstNodes.add(block);
while (!lstNodes.isEmpty()) { while (!lstNodes.isEmpty()) {
@ -230,14 +217,14 @@ public class DeadCodeHelper {
for (int i = 0; i < node.getPreds().size(); i++) { for (int i = 0; i < node.getPreds().size(); i++) {
BasicBlock pred = node.getPreds().get(i); BasicBlock pred = node.getPreds().get(i);
if (!marked.contains(pred) && pred != dom) { if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred); lstNodes.add(pred);
} }
} }
for (int i = 0; i < node.getPredExceptions().size(); i++) { for (int i = 0; i < node.getPredExceptions().size(); i++) {
BasicBlock pred = node.getPredExceptions().get(i); BasicBlock pred = node.getPredExceptions().get(i);
if (!marked.contains(pred) && pred != dom) { if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred); lstNodes.add(pred);
} }
} }
@ -252,7 +239,7 @@ public class DeadCodeHelper {
Instruction instr = block.getLastInstruction(); Instruction instr = block.getLastInstruction();
if (instr != null && instr.opcode == CodeConstants.opc_goto) { if (instr != null && instr.opcode == CodeConstants.opc_goto) {
block.getSeq().removeInstruction(block.getSeq().length() - 1); block.getSeq().removeLast();
} }
} }
@ -262,12 +249,161 @@ public class DeadCodeHelper {
public static void connectDummyExitBlock(ControlFlowGraph graph) { public static void connectDummyExitBlock(ControlFlowGraph graph) {
BasicBlock exit = graph.getLast(); BasicBlock exit = graph.getLast();
for (BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) { for (BasicBlock block : new HashSet<>(exit.getPreds())) {
exit.removePredecessor(block); exit.removePredecessor(block);
block.addSuccessor(exit); block.addSuccessor(exit);
} }
} }
public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) {
while(true) {
boolean range_extended = false;
for (ExceptionRangeCFG range : graph.getExceptions()) {
Set<BasicBlock> setPreds = new HashSet<>();
for (BasicBlock block : range.getProtectedRange()) {
setPreds.addAll(block.getPreds());
}
for (BasicBlock basicBlock : range.getProtectedRange()) {
setPreds.remove(basicBlock);
}
if(setPreds.size() != 1) {
continue; // multiple predecessors, obfuscated range
}
BasicBlock predBlock = setPreds.iterator().next();
InstructionSequence predSeq = predBlock.getSeq();
if(predSeq.isEmpty() || predSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) {
continue; // not a synchronized range
}
boolean monitorexit_in_range = false;
Set<BasicBlock> setProtectedBlocks = new HashSet<>(range.getProtectedRange());
setProtectedBlocks.add(range.getHandler());
for (BasicBlock block : setProtectedBlocks) {
InstructionSequence blockSeq = block.getSeq();
for (int i = 0; i < blockSeq.length(); i++) {
if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
monitorexit_in_range = true;
break;
}
}
if(monitorexit_in_range) {
break;
}
}
if(monitorexit_in_range) {
continue; // protected range already contains monitorexit
}
Set<BasicBlock> setSuccs = new HashSet<>();
for (BasicBlock block : range.getProtectedRange()) {
setSuccs.addAll(block.getSuccs());
}
for (BasicBlock basicBlock : range.getProtectedRange()) {
setSuccs.remove(basicBlock);
}
if(setSuccs.size() != 1) {
continue; // non-unique successor
}
BasicBlock succBlock = setSuccs.iterator().next();
InstructionSequence succSeq = succBlock.getSeq();
int succ_monitorexit_index = -1;
for (int i = 0; i < succSeq.length(); i++) {
if (succSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
succ_monitorexit_index = i;
break;
}
}
if(succ_monitorexit_index < 0) {
continue; // monitorexit not found in the single successor block
}
BasicBlock handlerBlock = range.getHandler();
if(handlerBlock.getSuccs().size() != 1) {
continue; // non-unique handler successor
}
BasicBlock succHandler = handlerBlock.getSuccs().get(0);
InstructionSequence succHandlerSeq = succHandler.getSeq();
if(succHandlerSeq.isEmpty() || succHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) {
continue; // not a standard synchronized range
}
int handler_monitorexit_index = -1;
for (int i = 0; i < succHandlerSeq.length(); i++) {
if (succHandlerSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
handler_monitorexit_index = i;
break;
}
}
if(handler_monitorexit_index < 0) {
continue; // monitorexit not found in the handler successor block
}
// checks successful, prerequisites satisfied, now extend the range
if(succ_monitorexit_index < succSeq.length() - 1) { // split block
SimpleInstructionSequence seq = new SimpleInstructionSequence();
for(int counter = 0; counter < succ_monitorexit_index; counter++) {
seq.addInstruction(succSeq.getInstr(0), -1);
succSeq.removeInstruction(0);
}
// build a separate block
BasicBlock newblock = new BasicBlock(++graph.last_id);
newblock.setSeq(seq);
// insert new block
for (BasicBlock block : succBlock.getPreds()) {
block.replaceSuccessor(succBlock, newblock);
}
newblock.addSuccessor(succBlock);
graph.getBlocks().addWithKey(newblock, newblock.id);
succBlock = newblock;
}
// copy exception edges and extend protected ranges (successor block)
BasicBlock rangeExitBlock = succBlock.getPreds().get(0);
for (int j = 0; j < rangeExitBlock.getSuccExceptions().size(); j++) {
BasicBlock hd = rangeExitBlock.getSuccExceptions().get(j);
succBlock.addSuccessorException(hd);
ExceptionRangeCFG rng = graph.getExceptionRange(hd, rangeExitBlock);
rng.getProtectedRange().add(succBlock);
}
// copy instructions (handler successor block)
InstructionSequence handlerSeq = handlerBlock.getSeq();
for(int counter = 0; counter < handler_monitorexit_index; counter++) {
handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1);
succHandlerSeq.removeInstruction(0);
}
range_extended = true;
break;
}
if(!range_extended) {
break;
}
}
}
public static void incorporateValueReturns(ControlFlowGraph graph) { public static void incorporateValueReturns(ControlFlowGraph graph) {
for (BasicBlock block : graph.getBlocks()) { for (BasicBlock block : graph.getBlocks()) {
@ -311,8 +447,8 @@ public class DeadCodeHelper {
if (!block.getPreds().isEmpty()) { if (!block.getPreds().isEmpty()) {
HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>(); HashSet<BasicBlock> setPredHandlersUnion = new HashSet<>();
HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>(); HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<>();
boolean firstpred = true; boolean firstpred = true;
for (BasicBlock pred : block.getPreds()) { for (BasicBlock pred : block.getPreds()) {
@ -328,7 +464,9 @@ public class DeadCodeHelper {
} }
// add exception ranges from predecessors // add exception ranges from predecessors
setPredHandlersIntersection.removeAll(block.getSuccExceptions()); for (BasicBlock basicBlock : block.getSuccExceptions()) {
setPredHandlersIntersection.remove(basicBlock);
}
BasicBlock predecessor = block.getPreds().get(0); BasicBlock predecessor = block.getPreds().get(0);
for (BasicBlock handler : setPredHandlersIntersection) { for (BasicBlock handler : setPredHandlersIntersection) {
@ -339,7 +477,7 @@ public class DeadCodeHelper {
} }
// remove redundant ranges // remove redundant ranges
HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions()); HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<>(block.getSuccExceptions());
setRangesToBeRemoved.removeAll(setPredHandlersUnion); setRangesToBeRemoved.removeAll(setPredHandlersUnion);
for (BasicBlock handler : setRangesToBeRemoved) { for (BasicBlock handler : setRangesToBeRemoved) {
@ -369,7 +507,7 @@ public class DeadCodeHelper {
} }
// remove superfluous ranges from successors // remove superfluous ranges from successors
for (BasicBlock succ : new HashSet<BasicBlock>(block.getSuccExceptions())) { for (BasicBlock succ : new HashSet<>(block.getSuccExceptions())) {
if (!bpred.getSuccExceptions().contains(succ)) { if (!bpred.getSuccExceptions().contains(succ)) {
ExceptionRangeCFG range = graph.getExceptionRange(succ, block); ExceptionRangeCFG range = graph.getExceptionRange(succ, block);
@ -416,6 +554,7 @@ public class DeadCodeHelper {
if (sameRanges) { if (sameRanges) {
seq.addSequence(next.getSeq()); seq.addSequence(next.getSeq());
block.getInstrOldOffsets().addAll(next.getInstrOldOffsets());
next.getSeq().clear(); next.getSeq().clear();
removeEmptyBlock(graph, next, true); removeEmptyBlock(graph, next, true);

View File

@ -0,0 +1,72 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public final class ClasspathHelper {
private static final Map<String, Method> METHOD_CACHE = Collections.synchronizedMap(new HashMap<>());
public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) {
String targetClass = classname.replace('/', '.');
String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor);
Method method;
if (METHOD_CACHE.containsKey(methodSignature)) {
method = METHOD_CACHE.get(methodSignature);
}
else {
method = findMethodOnClasspath(targetClass, methodSignature);
METHOD_CACHE.put(methodSignature, method);
}
return method;
}
private static Method findMethodOnClasspath(String targetClass, String methodSignature) {
try {
// use bootstrap classloader to only provide access to JRE classes
Class cls = new ClassLoader(null) {}.loadClass(targetClass);
for (Method mtd : cls.getMethods()) {
// use contains() to ignore access modifiers and thrown exceptions
if (mtd.toString().contains(methodSignature)) {
return mtd;
}
}
}
catch (Exception e) {
// ignore
}
return null;
}
private static String buildMethodSignature(String name, MethodDescriptor md) {
StringBuilder sb = new StringBuilder();
appendType(sb, md.ret);
sb.append(' ').append(name).append('(');
for (VarType param : md.params) {
appendType(sb, param);
sb.append(',');
}
if (sb.charAt(sb.length() - 1) == ',') {
sb.setLength(sb.length() - 1);
}
sb.append(')');
return sb.toString();
}
private static void appendType(StringBuilder sb, VarType type) {
sb.append(type.value.replace('/', '.'));
for (int i = 0; i < type.arrayDim; i++) {
sb.append("[]");
}
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
@ -21,11 +7,11 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.LinkedList; import java.util.LinkedList;
public class ClearStructHelper { public final class ClearStructHelper {
public static void clearStatements(RootStatement root) { public static void clearStatements(RootStatement root) {
LinkedList<Statement> stack = new LinkedList<Statement>(); LinkedList<Statement> stack = new LinkedList<>();
stack.add(root); stack.add(root);
while (!stack.isEmpty()) { while (!stack.isEmpty()) {

View File

@ -1,29 +1,19 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
public class ConcatenationHelper { public final class ConcatenationHelper {
private static final String builderClass = "java/lang/StringBuilder"; private static final String builderClass = "java/lang/StringBuilder";
private static final String bufferClass = "java/lang/StringBuffer"; private static final String bufferClass = "java/lang/StringBuffer";
@ -52,6 +42,12 @@ public class ConcatenationHelper {
exprTmp = iex.getInstance(); exprTmp = iex.getInstance();
} }
} }
else if ("makeConcatWithConstants".equals(iex.getName())) { // java 9 style
List<Exprent> parameters = extractParameters(iex.getBootstrapArguments(), iex);
if (parameters.size() >= 2) {
return createConcatExprent(parameters, expr.bytecode);
}
}
} }
if (exprTmp == null) { if (exprTmp == null) {
@ -60,7 +56,7 @@ public class ConcatenationHelper {
// iterate in depth, collecting possible operands // iterate in depth, collecting possible operands
List<Exprent> lstOperands = new ArrayList<Exprent>(); List<Exprent> lstOperands = new ArrayList<>();
while (true) { while (true) {
@ -104,7 +100,7 @@ public class ConcatenationHelper {
} }
if (first2str == 0) { if (first2str == 0) {
lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "")); lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "", expr.bytecode));
} }
// remove redundant String.valueOf // remove redundant String.valueOf
@ -125,20 +121,69 @@ public class ConcatenationHelper {
lstOperands.set(i, rep); lstOperands.set(i, rep);
} }
} }
return createConcatExprent(lstOperands, expr.bytecode);
}
private static Exprent createConcatExprent(List<Exprent> lstOperands, Set<Integer> bytecode) {
// build exprent to return // build exprent to return
Exprent func = lstOperands.get(0); Exprent func = lstOperands.get(0);
for (int i = 1; i < lstOperands.size(); i++) { for (int i = 1; i < lstOperands.size(); i++) {
List<Exprent> lstTmp = new ArrayList<Exprent>(); func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode);
lstTmp.add(func);
lstTmp.add(lstOperands.get(i));
func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp);
} }
return func; return func;
} }
// See StringConcatFactory in jdk sources
private static final char TAG_ARG = '\u0001';
private static final char TAG_CONST = '\u0002';
private static List<Exprent> extractParameters(List<PooledConstant> bootstrapArguments, InvocationExprent expr) {
List<Exprent> parameters = expr.getLstParameters();
if (bootstrapArguments != null) {
PooledConstant constant = bootstrapArguments.get(0);
if (constant.type == CodeConstants.CONSTANT_String) {
String recipe = ((PrimitiveConstant)constant).getString();
List<Exprent> res = new ArrayList<>();
StringBuilder acc = new StringBuilder();
int parameterId = 0;
for (int i = 0; i < recipe.length(); i++) {
char c = recipe.charAt(i);
if (c == TAG_CONST || c == TAG_ARG) {
// Detected a special tag, flush all accumulated characters
// as a constant first:
if (acc.length() > 0) {
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
acc.setLength(0);
}
if (c == TAG_CONST) {
// skip for now
}
if (c == TAG_ARG) {
res.add(parameters.get(parameterId++));
}
}
else {
// Not a special characters, this is a constant embedded into
// the recipe itself.
acc.append(c);
}
}
// Flush the remaining characters as constant:
if (acc.length() > 0) {
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
}
return res;
}
}
return new ArrayList<>(parameters);
}
private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) { private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {
if ("append".equals(expr.getName())) { if ("append".equals(expr.getName())) {
@ -167,13 +212,9 @@ public class ConcatenationHelper {
} }
private static boolean isNewConcat(NewExprent expr, VarType cltype) { private static boolean isNewConcat(NewExprent expr, VarType cltype) {
if (expr.getNewType().equals(cltype)) {
if (expr.getNewtype().equals(cltype)) {
VarType[] params = expr.getConstructor().getDescriptor().params; VarType[] params = expr.getConstructor().getDescriptor().params;
if (params.length == 0 || (params.length == 1 && return params.length == 0 || params.length == 1 && params[0].equals(VarType.VARTYPE_STRING);
params[0].equals(VarType.VARTYPE_STRING))) {
return true;
}
} }
return false; return false;

View File

@ -1,33 +1,22 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.*; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DecHelper { public final class DecHelper {
public static boolean checkStatementExceptions(List<Statement> lst) { public static boolean checkStatementExceptions(List<? extends Statement> lst) {
Set<Statement> all = new HashSet<Statement>(lst); Set<Statement> all = new HashSet<>(lst);
Set<Statement> handlers = new HashSet<Statement>(); Set<Statement> handlers = new HashSet<>();
Set<Statement> intersection = null; Set<Statement> intersection = null;
for (Statement stat : lst) { for (Statement stat : lst) {
@ -37,7 +26,7 @@ public class DecHelper {
intersection = setNew; intersection = setNew;
} }
else { else {
HashSet<Statement> interclone = new HashSet<Statement>(intersection); HashSet<Statement> interclone = new HashSet<>(intersection);
interclone.removeAll(setNew); interclone.removeAll(setNew);
intersection.retainAll(setNew); intersection.retainAll(setNew);
@ -66,7 +55,7 @@ public class DecHelper {
return true; return true;
} }
public static boolean isChoiceStatement(Statement head, List<Statement> lst) { public static boolean isChoiceStatement(Statement head, List<? super Statement> lst) {
Statement post = null; Statement post = null;
@ -159,7 +148,7 @@ public class DecHelper {
if (head == statd) { if (head == statd) {
return false; return false;
} }
if (!setDest.contains(statd) && post != statd) { if (post != statd && !setDest.contains(statd)) {
if (post != null) { if (post != null) {
return false; return false;
} }
@ -194,25 +183,17 @@ public class DecHelper {
return true; return true;
} }
public static Set<Statement> getUniquePredExceptions(Statement head) {
public static HashSet<Statement> getUniquePredExceptions(Statement head) { Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
setHandlers.removeIf(statement -> statement.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1);
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; return setHandlers;
} }
public static List<Exprent> copyExprentList(List<Exprent> lst) { public static List<Exprent> copyExprentList(List<? extends Exprent> lst) {
List<Exprent> ret = new ArrayList<Exprent>(); List<Exprent> ret = new ArrayList<>();
for (Exprent expr : lst) { for (Exprent expr : lst) {
ret.add(expr.copy()); ret.add(expr.copy());
} }
return ret; return ret;
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
@ -30,12 +16,12 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*; import java.util.*;
public class DomHelper { public final class DomHelper {
private static RootStatement graphToStatement(ControlFlowGraph graph) { private static RootStatement graphToStatement(ControlFlowGraph graph) {
VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();
VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks(); VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks();
for (BasicBlock block : blocks) { for (BasicBlock block : blocks) {
@ -46,8 +32,7 @@ public class DomHelper {
// head statement // head statement
Statement firstst = stats.getWithKey(firstblock.id); Statement firstst = stats.getWithKey(firstblock.id);
// dummy exit statement // dummy exit statement
Statement dummyexit = new Statement(); DummyExitStatement dummyexit = new DummyExitStatement();
dummyexit.type = Statement.TYPE_DUMMYEXIT;
Statement general; Statement general;
if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block
@ -104,14 +89,14 @@ public class DomHelper {
public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) { public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) {
HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>(); HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<>();
StrongConnectivityHelper schelper = new StrongConnectivityHelper(container); StrongConnectivityHelper schelper = new StrongConnectivityHelper(container);
List<List<Statement>> components = schelper.getComponents(); List<List<Statement>> components = schelper.getComponents();
List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components)); List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components));
FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats); FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<>(lstStats);
FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet(); FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet();
setFlagNodes.setAllElements(); setFlagNodes.setAllElements();
@ -177,26 +162,22 @@ public class DomHelper {
} }
while (!setFlagNodes.isEmpty()); while (!setFlagNodes.isEmpty());
VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>(); VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<>();
List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial! List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial!
final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>(); final HashMap<Integer, Integer> mapSortOrder = new HashMap<>();
for (int i = 0; i < lstRevPost.size(); i++) { for (int i = 0; i < lstRevPost.size(); i++) {
mapSortOrder.put(lstRevPost.get(i).id, i); mapSortOrder.put(lstRevPost.get(i).id, i);
} }
for (Statement st : lstStats) { for (Statement st : lstStats) {
List<Integer> lstPosts = new ArrayList<Integer>(); List<Integer> lstPosts = new ArrayList<>();
for (Statement stt : lists.get(st)) { for (Statement stt : lists.get(st)) {
lstPosts.add(stt.id); lstPosts.add(stt.id);
} }
Collections.sort(lstPosts, new Comparator<Integer>() { lstPosts.sort(Comparator.comparing(mapSortOrder::get));
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) { if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) {
lstPosts.add(lstPosts.remove(0)); lstPosts.add(lstPosts.remove(0));
@ -212,11 +193,17 @@ public class DomHelper {
RootStatement root = graphToStatement(graph); RootStatement root = graphToStatement(graph);
if (!processStatement(root, new HashMap<Integer, Set<Integer>>())) { if (!processStatement(root, new HashMap<>())) {
// try {
// DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot"));
// } catch (Exception ex) {
// ex.printStackTrace();
// }
throw new RuntimeException("parsing failure!"); throw new RuntimeException("parsing failure!");
} }
LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); LabelHelper.lowContinueLabels(root, new HashSet<>());
SequenceHelper.condenseSequences(root); SequenceHelper.condenseSequences(root);
root.buildMonitorFlags(); root.buildMonitorFlags();
@ -286,7 +273,7 @@ public class DomHelper {
SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler()); SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler());
sync.setAllParent(); sync.setAllParent();
for (StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) { for (StatEdge edge : new HashSet<>(ca.getLabelEdges())) {
sync.addLabeledEdge(edge); sync.addLabeledEdge(edge);
} }
@ -356,7 +343,7 @@ public class DomHelper {
// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
// } catch(Exception ex) {ex.printStackTrace();} // } catch(Exception ex) {ex.printStackTrace();}
mapExtPost = new HashMap<Integer, Set<Integer>>(); mapExtPost = new HashMap<>();
mapRefreshed = true; mapRefreshed = true;
} }
@ -377,7 +364,7 @@ public class DomHelper {
Statement stat = findGeneralStatement(general, forceall, mapExtPost); Statement stat = findGeneralStatement(general, forceall, mapExtPost);
if (stat != null) { if (stat != null) {
boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<Integer, Set<Integer>>()); boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<>());
if (complete) { if (complete) {
// replace general purpose statement with simple one // replace general purpose statement with simple one
@ -387,7 +374,7 @@ public class DomHelper {
return false; return false;
} }
mapExtPost = new HashMap<Integer, Set<Integer>>(); mapExtPost = new HashMap<>();
mapRefreshed = true; mapRefreshed = true;
reducibility = 0; reducibility = 0;
} }
@ -408,7 +395,7 @@ public class DomHelper {
break; break;
} }
else { else {
mapExtPost = new HashMap<Integer, Set<Integer>>(); mapExtPost = new HashMap<>();
} }
} }
@ -426,13 +413,13 @@ public class DomHelper {
} }
if (forceall) { if (forceall) {
vbPost = new VBStyleCollection<List<Integer>, Integer>(); vbPost = new VBStyleCollection<>();
List<Statement> lstAll = stat.getPostReversePostOrderList(); List<Statement> lstAll = stat.getPostReversePostOrderList();
for (Statement st : lstAll) { for (Statement st : lstAll) {
Set<Integer> set = mapExtPost.get(st.id); Set<Integer> set = mapExtPost.get(st.id);
if (set != null) { if (set != null) {
vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!! vbPost.addWithKey(new ArrayList<>(set), st.id); // FIXME: sort order!!
} }
} }
@ -442,7 +429,7 @@ public class DomHelper {
for (Integer id : setFirst) { for (Integer id : setFirst) {
List<Integer> lst = vbPost.getWithKey(id); List<Integer> lst = vbPost.getWithKey(id);
if (lst == null) { if (lst == null) {
vbPost.addWithKey(lst = new ArrayList<Integer>(), id); vbPost.addWithKey(lst = new ArrayList<>(), id);
} }
lst.add(id); lst.add(id);
} }
@ -466,14 +453,12 @@ public class DomHelper {
Set<Integer> setExtPosts = mapExtPost.get(headid); Set<Integer> setExtPosts = mapExtPost.get(headid);
for (int i = 0; i < posts.size(); i++) { for (Integer postId : posts) {
if (!postId.equals(headid) && !setExtPosts.contains(postId)) {
Integer postid = posts.get(i);
if (!postid.equals(headid) && !setExtPosts.contains(postid)) {
continue; continue;
} }
Statement post = stats.getWithKey(postid); Statement post = stats.getWithKey(postId);
if (post == null) { // possible in case of an inherited postdominance set if (post == null) { // possible in case of an inherited postdominance set
continue; continue;
@ -481,11 +466,11 @@ public class DomHelper {
boolean same = (post == head); boolean same = (post == head);
HashSet<Statement> setNodes = new HashSet<Statement>(); HashSet<Statement> setNodes = new HashSet<>();
HashSet<Statement> setPreds = new HashSet<Statement>(); HashSet<Statement> setPreds = new HashSet<>();
// collect statement nodes // collect statement nodes
HashSet<Statement> setHandlers = new HashSet<Statement>(); HashSet<Statement> setHandlers = new HashSet<>();
setHandlers.add(head); setHandlers.add(head);
while (true) { while (true) {
@ -503,7 +488,7 @@ public class DomHelper {
} }
if (addhd) { if (addhd) {
LinkedList<Statement> lstStack = new LinkedList<Statement>(); LinkedList<Statement> lstStack = new LinkedList<>();
lstStack.add(handler); lstStack.add(handler);
while (!lstStack.isEmpty()) { while (!lstStack.isEmpty()) {
@ -552,14 +537,14 @@ public class DomHelper {
// build statement and return // build statement and return
if (excok) { if (excok) {
Statement res = null; Statement res;
setPreds.removeAll(setNodes); setPreds.removeAll(setNodes);
if (setPreds.size() == 0) { if (setPreds.size() == 0) {
if ((setNodes.size() > 1 || if ((setNodes.size() > 1 ||
head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head)) head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head))
&& setNodes.size() < stats.size()) { && setNodes.size() < stats.size()) {
if (checkSynchronizedCompleteness(head, setNodes)) { if (checkSynchronizedCompleteness(setNodes)) {
res = new GeneralStatement(head, setNodes, same ? null : post); res = new GeneralStatement(head, setNodes, same ? null : post);
stat.collapseNodesToStatement(res); stat.collapseNodesToStatement(res);
@ -574,8 +559,7 @@ public class DomHelper {
return null; return null;
} }
private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) { private static boolean checkSynchronizedCompleteness(Set<Statement> setNodes) {
// check exit nodes // check exit nodes
for (Statement stat : setNodes) { for (Statement stat : setNodes) {
if (stat.isMonitorEnter()) { if (stat.isMonitorEnter()) {
@ -617,26 +601,21 @@ public class DomHelper {
// update the postdominator map // update the postdominator map
if (!mapExtPost.isEmpty()) { if (!mapExtPost.isEmpty()) {
HashSet<Integer> setOldNodes = new HashSet<Integer>(); HashSet<Integer> setOldNodes = new HashSet<>();
for (Statement old : result.getStats()) { for (Statement old : result.getStats()) {
setOldNodes.add(old.id); setOldNodes.add(old.id);
} }
Integer newid = result.id; Integer newid = result.id;
for (Integer key : new ArrayList<Integer>(mapExtPost.keySet())) { for (Integer key : new ArrayList<>(mapExtPost.keySet())) {
Set<Integer> set = mapExtPost.get(key); Set<Integer> set = mapExtPost.get(key);
int oldsize = set.size(); int oldsize = set.size();
set.removeAll(setOldNodes); set.removeAll(setOldNodes);
if (setOldNodes.contains(key)) { if (setOldNodes.contains(key)) {
Set<Integer> setNew = mapExtPost.get(newid); mapExtPost.computeIfAbsent(newid, k -> new HashSet<>()).addAll(set);
if (setNew == null) {
mapExtPost.put(newid, setNew = new HashSet<Integer>());
}
setNew.addAll(set);
mapExtPost.remove(key); mapExtPost.remove(key);
} }
else { else {

View File

@ -1,214 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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);
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
@ -21,40 +7,30 @@ 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.ExitExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public class ExitHelper { public final class ExitHelper {
public static boolean condenseExits(RootStatement root) { public static boolean condenseExits(RootStatement root) {
int changed = integrateExits(root); int changed = integrateExits(root);
if (changed > 0) { if (changed > 0) {
cleanUpUnreachableBlocks(root); cleanUpUnreachableBlocks(root);
SequenceHelper.condenseSequences(root); SequenceHelper.condenseSequences(root);
} }
return (changed > 0); return (changed > 0);
} }
private static void cleanUpUnreachableBlocks(Statement stat) { private static void cleanUpUnreachableBlocks(Statement stat) {
boolean found; boolean found;
do { do {
found = false; found = false;
for (int i = 0; i < stat.getStats().size(); i++) { for (int i = 0; i < stat.getStats().size(); i++) {
Statement st = stat.getStats().get(i); Statement st = stat.getStats().get(i);
cleanUpUnreachableBlocks(st); cleanUpUnreachableBlocks(st);
@ -71,7 +47,7 @@ public class ExitHelper {
set.remove(secondlast); set.remove(secondlast);
if (set.isEmpty()) { if (set.isEmpty()) {
last.setExprents(new ArrayList<Exprent>()); last.setExprents(new ArrayList<>());
found = true; found = true;
break; break;
} }
@ -83,16 +59,12 @@ public class ExitHelper {
while (found); while (found);
} }
private static int integrateExits(Statement stat) { private static int integrateExits(Statement stat) {
int ret = 0; int ret = 0;
Statement dest = null; Statement dest;
if (stat.getExprents() == null) { if (stat.getExprents() == null) {
while (true) { while (true) {
int changed = 0; int changed = 0;
for (Statement st : stat.getStats()) { for (Statement st : stat.getStats()) {
@ -108,33 +80,31 @@ public class ExitHelper {
} }
} }
if (stat.type == 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()));
switch (stat.type) { ifst.getFirst().removeSuccessor(ifedge);
case Statement.TYPE_IF: StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
IfStatement ifst = (IfStatement)stat; ifst.getFirst().addSuccessor(newedge);
if (ifst.getIfstat() == null) { ifst.setIfEdge(newedge);
StatEdge ifedge = ifst.getIfEdge(); ifst.setIfstat(bstat);
dest = isExitEdge(ifedge); ifst.getStats().addWithKey(bstat, bstat.id);
if (dest != null) { bstat.setParent(ifst);
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
ifst.getFirst().removeSuccessor(ifedge); StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
ifst.getFirst().addSuccessor(newedge); bstat.addSuccessor(newexitedge);
ifst.setIfEdge(newedge); oldexitedge.closure.addLabeledEdge(newexitedge);
ifst.setIfstat(bstat); ret = 1;
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;
}
} }
}
} }
} }
@ -168,15 +138,12 @@ public class ExitHelper {
// LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());
// do it by hand // do it by hand
for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
block.removePredecessor(prededge); block.removePredecessor(prededge);
prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat); prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat);
stat.addPredecessor(prededge); stat.addPredecessor(prededge);
stat.addLabeledEdge(prededge); stat.addLabeledEdge(prededge);
} }
stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat)); stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat));
for (StatEdge edge : dest.getAllPredecessorEdges()) { for (StatEdge edge : dest.getAllPredecessorEdges()) {
@ -202,11 +169,9 @@ public class ExitHelper {
} }
private static Statement isExitEdge(StatEdge edge) { private static Statement isExitEdge(StatEdge edge) {
Statement dest = edge.getDestination(); Statement dest = edge.getDestination();
if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK && edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
&& edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
List<Exprent> data = dest.getExprents(); List<Exprent> data = dest.getExprents();
if (data != null && data.size() == 1) { if (data != null && data.size() == 1) {
@ -220,7 +185,6 @@ public class ExitHelper {
} }
private static boolean isOnlyEdge(StatEdge edge) { private static boolean isOnlyEdge(StatEdge edge) {
Statement stat = edge.getDestination(); Statement stat = edge.getDestination();
for (StatEdge ed : stat.getAllPredecessorEdges()) { for (StatEdge ed : stat.getAllPredecessorEdges()) {
@ -243,11 +207,10 @@ public class ExitHelper {
return true; return true;
} }
public static boolean removeRedundantReturns(RootStatement root) { public static void removeRedundantReturns(RootStatement root) {
DummyExitStatement dummyExit = root.getDummyExit();
boolean res = false; for (StatEdge edge : dummyExit.getAllPredecessorEdges()) {
for (StatEdge edge : root.getDummyExit().getAllPredecessorEdges()) {
if (!edge.explicit) { if (!edge.explicit) {
Statement source = edge.getSource(); Statement source = edge.getSource();
List<Exprent> lstExpr = source.getExprents(); List<Exprent> lstExpr = source.getExprents();
@ -255,91 +218,14 @@ public class ExitHelper {
Exprent expr = lstExpr.get(lstExpr.size() - 1); Exprent expr = lstExpr.get(lstExpr.size() - 1);
if (expr.type == Exprent.EXPRENT_EXIT) { if (expr.type == Exprent.EXPRENT_EXIT) {
ExitExprent ex = (ExitExprent)expr; ExitExprent ex = (ExitExprent)expr;
if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { if (ex.getExitType() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {
// remove redundant return // remove redundant return
dummyExit.addBytecodeOffsets(ex.bytecode);
lstExpr.remove(lstExpr.size() - 1); 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(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;
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -20,7 +6,7 @@ import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.util.TextBuffer;
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*; 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.DirectGraph;
@ -38,152 +24,115 @@ import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.TextUtil;
import java.util.*; import java.util.*;
import org.jetbrains.java.decompiler.util.SortUtil;
import org.jetbrains.java.decompiler.util.Util;
public class ExprProcessor implements CodeConstants { public class ExprProcessor implements CodeConstants {
public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>"; public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>";
public static final String UNKNOWN_TYPE_STRING = "<unknown>"; public static final String UNKNOWN_TYPE_STRING = "<unknown>";
public static final String NULL_TYPE_STRING = "<null>"; public static final String NULL_TYPE_STRING = "<null>";
private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>(); private static final Map<Integer, Integer> mapConsts = new HashMap<>();
static { static {
mapConsts.put(opc_arraylength, FunctionExprent.FUNCTION_ARRAY_LENGTH);
// mapConsts.put(new Integer(opc_i2l), new mapConsts.put(opc_checkcast, FunctionExprent.FUNCTION_CAST);
// Integer(FunctionExprent.FUNCTION_I2L)); mapConsts.put(opc_instanceof, FunctionExprent.FUNCTION_INSTANCEOF);
// 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 = private static final VarType[] consts = {
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_STRING
VarType.VARTYPE_STRING}; };
private static final VarType[] vartypes = private static final VarType[] varTypes = {
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT}; VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT
};
private static final VarType[] arrtypes = private static final VarType[] arrTypes = {
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT, VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT}; VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT
};
private static final int[] func1 = private static final int[] func1 = {
new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV, FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
FunctionExprent.FUNCTION_REM}; FunctionExprent.FUNCTION_REM
};
private static final int[] func2 = {
FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,
FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR
};
private static final int[] func3 = {
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 = {
FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
FunctionExprent.FUNCTION_DCMPG
};
private static final int[] func5 = {
IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE
};
private static final int[] func6 = {
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 = {IfExprent.IF_NULL, IfExprent.IF_NONNULL};
private static final int[] func8 = {MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};
private static final int[] func2 = private static final int[] arrTypeIds = {
new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND, CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,
FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR}; CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG
};
private static final int[] func3 = private static final int[] negIfs = {
new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I, IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,
FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L, IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,
FunctionExprent.FUNCTION_F2D, IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, IfExprent.IF_ACMPEQ
FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B, };
FunctionExprent.FUNCTION_I2C,
FunctionExprent.FUNCTION_I2S};
private static final int[] func4 = private static final String[] typeNames = {"byte", "char", "double", "float", "int", "long", "short", "boolean"};
new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
FunctionExprent.FUNCTION_DCMPG};
private static final int[] func5 = private final MethodDescriptor methodDescriptor;
new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE}; private final VarProcessor varProcessor;
private static final int[] func6 = public ExprProcessor(MethodDescriptor md, VarProcessor varProc) {
new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE, methodDescriptor = md;
IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE}; varProcessor = varProc;
}
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) { public void processStatement(RootStatement root, StructClass cl) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root); 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 // collect finally entry points
Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>(); Set<String> setFinallyShortRangeEntryPoints = new HashSet<>();
for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) { for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) {
for (FinallyPathWrapper finwrap : lst) { for (FinallyPathWrapper finwrap : lst) {
setFinallyShortRangeEntryPoints.add(finwrap.entry); setFinallyShortRangeEntryPoints.add(finwrap.entry);
} }
} }
Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>(); Set<String> setFinallyLongRangeEntryPaths = new HashSet<>();
for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) { for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) {
for (FinallyPathWrapper finwrap : lst) { for (FinallyPathWrapper finwrap : lst) {
setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry); setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry);
} }
} }
Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>(); Map<String, VarExprent> mapCatch = new HashMap<>();
collectCatchVars(root, flatthelper, mapCatch); collectCatchVars(root, flatthelper, mapCatch);
Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<DirectNode, Map<String, PrimitiveExprsList>>(); Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); LinkedList<DirectNode> stack = new LinkedList<>();
LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>(); LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<>();
stack.add(dgraph.first); stack.add(dgraph.first);
stackEntryPoint.add(new LinkedList<String>()); stackEntryPoint.add(new LinkedList<>());
Map<String, PrimitiveExprsList> map = new HashMap<String, PrimitiveExprsList>(); Map<String, PrimitiveExprsList> map = new HashMap<>();
map.put(null, new PrimitiveExprsList()); map.put(null, new PrimitiveExprsList());
mapData.put(dgraph.first, map); mapData.put(dgraph.first, map);
@ -222,13 +171,8 @@ public class ExprProcessor implements CodeConstants {
} }
if (isSuccessor) { if (isSuccessor) {
Map<String, PrimitiveExprsList> mapSucc = mapData.computeIfAbsent(nd, k -> new HashMap<>());
Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd); LinkedList<String> ndentrypoints = new LinkedList<>(entrypoints);
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)) { if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) {
ndentrypoints.addLast(node.id); ndentrypoints.addLast(node.id);
@ -279,10 +223,14 @@ public class ExprProcessor implements CodeConstants {
private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) { private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) {
ExprentStack stack = data.getStack(); ExprentStack stack = data.getStack();
copyEntries(stack);
return data;
}
public static void copyEntries(List<Exprent> stack) {
for (int i = 0; i < stack.size(); i++) { for (int i = 0; i < stack.size(); i++) {
stack.set(i, stack.get(i).copy()); stack.set(i, stack.get(i).copy());
} }
return data;
} }
private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) { private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) {
@ -321,8 +269,7 @@ public class ExprProcessor implements CodeConstants {
public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) { public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) {
ConstantPool pool = cl.getPool(); ConstantPool pool = cl.getPool();
StructBootstrapMethodsAttribute bootstrap = StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
BasicBlock block = stat.getBlock(); BasicBlock block = stat.getBlock();
@ -335,40 +282,47 @@ public class ExprProcessor implements CodeConstants {
Instruction instr = seq.getInstr(i); Instruction instr = seq.getInstr(i);
Integer bytecode_offset = block.getOldOffset(i); Integer bytecode_offset = block.getOldOffset(i);
Set<Integer> bytecode_offsets = bytecode_offset >= 0 ? Collections.singleton(bytecode_offset) : null;
switch (instr.opcode) { switch (instr.opcode) {
case opc_aconst_null: case opc_aconst_null:
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null)); pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null, bytecode_offsets));
break; break;
case opc_bipush: case opc_bipush:
case opc_sipush: case opc_sipush:
pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true)); pushEx(stack, exprlist, new ConstExprent(instr.operand(0), true, bytecode_offsets));
break; break;
case opc_lconst_0: case opc_lconst_0:
case opc_lconst_1: case opc_lconst_1:
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0))); pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, (long)(instr.opcode - opc_lconst_0), bytecode_offsets));
break; break;
case opc_fconst_0: case opc_fconst_0:
case opc_fconst_1: case opc_fconst_1:
case opc_fconst_2: case opc_fconst_2:
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0))); pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, (float)(instr.opcode - opc_fconst_0), bytecode_offsets));
break; break;
case opc_dconst_0: case opc_dconst_0:
case opc_dconst_1: case opc_dconst_1:
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0))); pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, (double)(instr.opcode - opc_dconst_0), bytecode_offsets));
break; break;
case opc_ldc: case opc_ldc:
case opc_ldc_w: case opc_ldc_w:
case opc_ldc2_w: case opc_ldc2_w:
PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0)); PooledConstant cn = pool.getConstant(instr.operand(0));
pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value)); if (cn instanceof PrimitiveConstant) {
pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], ((PrimitiveConstant)cn).value, bytecode_offsets));
}
else if (cn instanceof LinkConstant) {
//TODO: for now treat Links as Strings
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_STRING, ((LinkConstant)cn).elementname, bytecode_offsets));
}
break; break;
case opc_iload: case opc_iload:
case opc_lload: case opc_lload:
case opc_fload: case opc_fload:
case opc_dload: case opc_dload:
case opc_aload: case opc_aload:
pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor)); pushEx(stack, exprlist, new VarExprent(instr.operand(0), varTypes[instr.opcode - opc_iload], varProcessor, bytecode_offset));
break; break;
case opc_iaload: case opc_iaload:
case opc_laload: case opc_laload:
@ -389,17 +343,17 @@ public class ExprProcessor implements CodeConstants {
case opc_daload: case opc_daload:
vartype = VarType.VARTYPE_DOUBLE; vartype = VarType.VARTYPE_DOUBLE;
} }
pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload]), vartype); pushEx(stack, exprlist, new ArrayExprent(arr, index, arrTypes[instr.opcode - opc_iaload], bytecode_offsets), vartype);
break; break;
case opc_istore: case opc_istore:
case opc_lstore: case opc_lstore:
case opc_fstore: case opc_fstore:
case opc_dstore: case opc_dstore:
case opc_astore: case opc_astore:
Exprent top = stack.pop(); Exprent expr = stack.pop();
int varindex = instr.getOperand(0); int varindex = instr.operand(0);
AssignmentExprent assign = AssignmentExprent assign = new AssignmentExprent(
new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top); new VarExprent(varindex, varTypes[instr.opcode - opc_istore], varProcessor, nextMeaningfulOffset(block, i)), expr, bytecode_offsets);
exprlist.add(assign); exprlist.add(assign);
break; break;
case opc_iastore: case opc_iastore:
@ -414,7 +368,8 @@ public class ExprProcessor implements CodeConstants {
Exprent index_store = stack.pop(); Exprent index_store = stack.pop();
Exprent arr_store = stack.pop(); Exprent arr_store = stack.pop();
AssignmentExprent arrassign = AssignmentExprent arrassign =
new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value); new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrTypes[instr.opcode - opc_iastore], bytecode_offsets), value,
bytecode_offsets);
exprlist.add(arrassign); exprlist.add(arrassign);
break; break;
case opc_iadd: case opc_iadd:
@ -437,7 +392,7 @@ public class ExprProcessor implements CodeConstants {
case opc_lrem: case opc_lrem:
case opc_frem: case opc_frem:
case opc_drem: case opc_drem:
pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack)); pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack, bytecode_offsets));
break; break;
case opc_ishl: case opc_ishl:
case opc_lshl: case opc_lshl:
@ -451,19 +406,20 @@ public class ExprProcessor implements CodeConstants {
case opc_lor: case opc_lor:
case opc_ixor: case opc_ixor:
case opc_lxor: case opc_lxor:
pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack)); pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack, bytecode_offsets));
break; break;
case opc_ineg: case opc_ineg:
case opc_lneg: case opc_lneg:
case opc_fneg: case opc_fneg:
case opc_dneg: case opc_dneg:
pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack)); pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack, bytecode_offsets));
break; break;
case opc_iinc: case opc_iinc:
VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor); VarExprent vevar = new VarExprent(instr.operand(0), VarType.VARTYPE_INT, varProcessor);
exprlist.add(new AssignmentExprent(vevar, new FunctionExprent( exprlist.add(new AssignmentExprent(vevar, new FunctionExprent(
instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays instr.operand(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))))})))); .asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.operand(1)), null)),
bytecode_offsets), bytecode_offsets));
break; break;
case opc_i2l: case opc_i2l:
case opc_i2f: case opc_i2f:
@ -480,14 +436,14 @@ public class ExprProcessor implements CodeConstants {
case opc_i2b: case opc_i2b:
case opc_i2c: case opc_i2c:
case opc_i2s: case opc_i2s:
pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack)); pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack, bytecode_offsets));
break; break;
case opc_lcmp: case opc_lcmp:
case opc_fcmpl: case opc_fcmpl:
case opc_fcmpg: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpl:
case opc_dcmpg: case opc_dcmpg:
pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack)); pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack, bytecode_offsets));
break; break;
case opc_ifeq: case opc_ifeq:
case opc_ifne: case opc_ifne:
@ -495,7 +451,7 @@ public class ExprProcessor implements CodeConstants {
case opc_ifge: case opc_ifge:
case opc_ifgt: case opc_ifgt:
case opc_ifle: case opc_ifle:
exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack)); exprlist.add(new IfExprent(negIfs[func5[instr.opcode - opc_ifeq]], stack, bytecode_offsets));
break; break;
case opc_if_icmpeq: case opc_if_icmpeq:
case opc_if_icmpne: case opc_if_icmpne:
@ -505,15 +461,15 @@ public class ExprProcessor implements CodeConstants {
case opc_if_icmple: case opc_if_icmple:
case opc_if_acmpeq: case opc_if_acmpeq:
case opc_if_acmpne: case opc_if_acmpne:
exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack)); exprlist.add(new IfExprent(negIfs[func6[instr.opcode - opc_if_icmpeq]], stack, bytecode_offsets));
break; break;
case opc_ifnull: case opc_ifnull:
case opc_ifnonnull: case opc_ifnonnull:
exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack)); exprlist.add(new IfExprent(negIfs[func7[instr.opcode - opc_ifnull]], stack, bytecode_offsets));
break; break;
case opc_tableswitch: case opc_tableswitch:
case opc_lookupswitch: case opc_lookupswitch:
exprlist.add(new SwitchExprent(stack.pop())); exprlist.add(new SwitchExprent(stack.pop(), bytecode_offsets));
break; break;
case opc_ireturn: case opc_ireturn:
case opc_lreturn: case opc_lreturn:
@ -524,51 +480,47 @@ public class ExprProcessor implements CodeConstants {
case opc_athrow: case opc_athrow:
exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN, exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN,
instr.opcode == opc_return ? null : stack.pop(), instr.opcode == opc_return ? null : stack.pop(),
instr.opcode == opc_athrow instr.opcode == opc_athrow ? null : methodDescriptor.ret,
? null bytecode_offsets));
: ((MethodDescriptor)DecompilerContext
.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret));
break; break;
case opc_monitorenter: case opc_monitorenter:
case opc_monitorexit: case opc_monitorexit:
exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop())); exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop(), bytecode_offsets));
break; break;
case opc_checkcast: case opc_checkcast:
case opc_instanceof: case opc_instanceof:
stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null)); stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true), null, null));
case opc_arraylength: case opc_arraylength:
pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack)); pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode), stack, bytecode_offsets));
break; break;
case opc_getstatic: case opc_getstatic:
case opc_getfield: case opc_getfield:
pushEx(stack, exprlist, pushEx(stack, exprlist,
new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop())); new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_getstatic ? null : stack.pop(),
bytecode_offsets));
break; break;
case opc_putstatic: case opc_putstatic:
case opc_putfield: case opc_putfield:
Exprent valfield = stack.pop(); Exprent valfield = stack.pop();
Exprent exprfield = Exprent exprfield =
new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop()); new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_putstatic ? null : stack.pop(),
exprlist.add(new AssignmentExprent(exprfield, valfield)); bytecode_offsets);
exprlist.add(new AssignmentExprent(exprfield, valfield, bytecode_offsets));
break; break;
case opc_invokevirtual: case opc_invokevirtual:
case opc_invokespecial: case opc_invokespecial:
case opc_invokestatic: case opc_invokestatic:
case opc_invokeinterface: case opc_invokeinterface:
case opc_invokedynamic: case opc_invokedynamic:
if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { if (instr.opcode != opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
LinkConstant invoke_constant = pool.getLinkConstant(instr.operand(0));
LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
int dynamic_invokation_type = -1;
List<PooledConstant> bootstrap_arguments = null;
if (instr.opcode == opc_invokedynamic && bootstrap != null) { if (instr.opcode == opc_invokedynamic && bootstrap != null) {
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1); 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); InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, bootstrap_arguments, stack, bytecode_offsets);
if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) {
exprlist.add(exprinv); exprlist.add(exprinv);
} }
@ -580,15 +532,15 @@ public class ExprProcessor implements CodeConstants {
case opc_new: case opc_new:
case opc_anewarray: case opc_anewarray:
case opc_multianewarray: case opc_multianewarray:
int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1); int dimensions = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.operand(1);
VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true); VarType arrType = new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true);
if (instr.opcode != opc_multianewarray) { if (instr.opcode != opc_multianewarray) {
arrtype.arraydim += arrdims; arrType = arrType.resizeArrayDim(arrType.arrayDim + dimensions);
} }
pushEx(stack, exprlist, new NewExprent(arrtype, stack, arrdims)); pushEx(stack, exprlist, new NewExprent(arrType, stack, dimensions, bytecode_offsets));
break; break;
case opc_newarray: case opc_newarray:
pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1)); pushEx(stack, exprlist, new NewExprent(new VarType(arrTypeIds[instr.operand(0) - 4], 1), stack, 1, bytecode_offsets));
break; break;
case opc_dup: case opc_dup:
pushEx(stack, exprlist, stack.getByOffset(-1).copy()); pushEx(stack, exprlist, stack.getByOffset(-1).copy());
@ -597,7 +549,7 @@ public class ExprProcessor implements CodeConstants {
insertByOffsetEx(-2, stack, exprlist, -1); insertByOffsetEx(-2, stack, exprlist, -1);
break; break;
case opc_dup_x2: case opc_dup_x2:
if (stack.getByOffset(-2).getExprType().stack_size == 2) { if (stack.getByOffset(-2).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1); insertByOffsetEx(-2, stack, exprlist, -1);
} }
else { else {
@ -605,7 +557,7 @@ public class ExprProcessor implements CodeConstants {
} }
break; break;
case opc_dup2: case opc_dup2:
if (stack.getByOffset(-1).getExprType().stack_size == 2) { if (stack.getByOffset(-1).getExprType().stackSize == 2) {
pushEx(stack, exprlist, stack.getByOffset(-1).copy()); pushEx(stack, exprlist, stack.getByOffset(-1).copy());
} }
else { else {
@ -614,7 +566,7 @@ public class ExprProcessor implements CodeConstants {
} }
break; break;
case opc_dup2_x1: case opc_dup2_x1:
if (stack.getByOffset(-1).getExprType().stack_size == 2) { if (stack.getByOffset(-1).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1); insertByOffsetEx(-2, stack, exprlist, -1);
} }
else { else {
@ -623,8 +575,8 @@ public class ExprProcessor implements CodeConstants {
} }
break; break;
case opc_dup2_x2: case opc_dup2_x2:
if (stack.getByOffset(-1).getExprType().stack_size == 2) { if (stack.getByOffset(-1).getExprType().stackSize == 2) {
if (stack.getByOffset(-2).getExprType().stack_size == 2) { if (stack.getByOffset(-2).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1); insertByOffsetEx(-2, stack, exprlist, -1);
} }
else { else {
@ -632,7 +584,7 @@ public class ExprProcessor implements CodeConstants {
} }
} }
else { else {
if (stack.getByOffset(-3).getExprType().stack_size == 2) { if (stack.getByOffset(-3).getExprType().stackSize == 2) {
insertByOffsetEx(-3, stack, exprlist, -2); insertByOffsetEx(-3, stack, exprlist, -2);
insertByOffsetEx(-3, stack, exprlist, -1); insertByOffsetEx(-3, stack, exprlist, -1);
} }
@ -647,12 +599,38 @@ public class ExprProcessor implements CodeConstants {
stack.pop(); stack.pop();
break; break;
case opc_pop: case opc_pop:
case opc_pop2:
stack.pop(); stack.pop();
break;
case opc_pop2:
if (stack.getByOffset(-1).getExprType().stackSize == 1) {
// Since value at the top of the stack is a value of category 1 (JVMS9 2.11.1)
// we should remove one more item from the stack.
// See JVMS9 pop2 chapter.
stack.pop();
}
stack.pop();
break;
} }
} }
} }
private static int nextMeaningfulOffset(BasicBlock block, int index) {
InstructionSequence seq = block.getSeq();
while (++index < seq.length()) {
switch (seq.getInstr(index).opcode) {
case opc_nop:
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
continue;
}
return block.getOldOffset(index);
}
return -1;
}
private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) { private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) {
pushEx(stack, exprlist, exprent, null); pushEx(stack, exprlist, exprent, null);
} }
@ -662,7 +640,7 @@ public class ExprProcessor implements CodeConstants {
VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor); VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor);
var.setStack(true); var.setStack(true);
exprlist.add(new AssignmentExprent(var, exprent)); exprlist.add(new AssignmentExprent(var, exprent, null));
stack.push(var.copy()); stack.push(var.copy());
} }
@ -670,20 +648,20 @@ public class ExprProcessor implements CodeConstants {
int base = VarExprent.STACK_BASE + stack.size(); int base = VarExprent.STACK_BASE + stack.size();
LinkedList<VarExprent> lst = new LinkedList<VarExprent>(); LinkedList<VarExprent> lst = new LinkedList<>();
for (int i = -1; i >= offset; i--) { for (int i = -1; i >= offset; i--) {
Exprent varex = stack.pop(); Exprent varex = stack.pop();
VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor); VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor);
varnew.setStack(true); varnew.setStack(true);
exprlist.add(new AssignmentExprent(varnew, varex)); exprlist.add(new AssignmentExprent(varnew, varex, null));
lst.add(0, (VarExprent)varnew.copy()); lst.add(0, (VarExprent)varnew.copy());
} }
Exprent exprent = lst.get(lst.size() + copyoffset).copy(); Exprent exprent = lst.get(lst.size() + copyoffset).copy();
VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor); VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor);
var.setStack(true); var.setStack(true);
exprlist.add(new AssignmentExprent(var, exprent)); exprlist.add(new AssignmentExprent(var, exprent, null));
lst.add(0, (VarExprent)var.copy()); lst.add(0, (VarExprent)var.copy());
for (VarExprent expr : lst) { for (VarExprent expr : lst) {
@ -696,7 +674,6 @@ public class ExprProcessor implements CodeConstants {
} }
public static String getTypeName(VarType type, boolean getShort) { public static String getTypeName(VarType type, boolean getShort) {
int tp = type.type; int tp = type.type;
if (tp <= CodeConstants.TYPE_BOOLEAN) { if (tp <= CodeConstants.TYPE_BOOLEAN) {
return typeNames[tp]; return typeNames[tp];
@ -731,12 +708,9 @@ public class ExprProcessor implements CodeConstants {
} }
public static String getCastTypeName(VarType type, boolean getShort) { public static String getCastTypeName(VarType type, boolean getShort) {
String s = getTypeName(type, getShort); StringBuilder s = new StringBuilder(getTypeName(type, getShort));
int dim = type.arraydim; TextUtil.append(s, "[]", type.arrayDim);
while (dim-- > 0) { return s.toString();
s += "[]";
}
return s;
} }
public static PrimitiveExprsList getExpressionData(VarExprent var) { public static PrimitiveExprsList getExpressionData(VarExprent var) {
@ -744,61 +718,63 @@ public class ExprProcessor implements CodeConstants {
VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor()); VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor());
vartmp.setStack(true); vartmp.setStack(true);
prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy())); prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy(), null));
prlst.getStack().push(vartmp.copy()); prlst.getStack().push(vartmp.copy());
return prlst; return prlst;
} }
public static boolean endsWithSemikolon(Exprent expr) { public static boolean endsWithSemicolon(Exprent expr) {
int type = expr.type; int type = expr.type;
return !(type == Exprent.EXPRENT_SWITCH || return !(type == Exprent.EXPRENT_SWITCH ||
type == Exprent.EXPRENT_MONITOR || type == Exprent.EXPRENT_MONITOR ||
type == Exprent.EXPRENT_IF || type == Exprent.EXPRENT_IF ||
(type == Exprent.EXPRENT_VAR && ((VarExprent)expr) (type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()));
.isClassdef()));
} }
public static String jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) { private static void addDeletedGotoInstructionMapping(Statement stat, BytecodeMappingTracer tracer) {
String new_line_separator = DecompilerContext.getNewLineSeparator(); if (stat instanceof BasicBlockStatement) {
BasicBlock block = ((BasicBlockStatement)stat).getBlock();
List<Integer> offsets = block.getInstrOldOffsets();
if (!offsets.isEmpty() &&
offsets.size() > block.getSeq().length()) { // some instructions have been deleted, but we still have offsets
tracer.addMapping(offsets.get(offsets.size() - 1)); // add the last offset
}
}
}
StringBuilder buf = new StringBuilder(); public static TextBuffer jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) {
String content = stat.toJava(indent, tracer); TextBuffer buf = stat.toJava(indent, tracer);
if (!(stat instanceof IfStatement) && !(stat instanceof DoStatement) && !(stat instanceof SequenceStatement)) {
content = Util.rtrim(content);
}
buf.append(content);
if (!(stat instanceof IfStatement) && !(stat instanceof DoStatement) && !(stat instanceof SequenceStatement) && buf.length() != 0) {
buf.append(new_line_separator);
}
List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
if (lstSuccs.size() == 1) { if (lstSuccs.size() == 1) {
StatEdge edge = lstSuccs.get(0); StatEdge edge = lstSuccs.get(0);
if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
buf.append(InterpreterUtil.getIndentString(indent)); buf.appendIndent(indent);
switch (edge.getType()) { switch (edge.getType()) {
case StatEdge.TYPE_BREAK: case StatEdge.TYPE_BREAK:
addDeletedGotoInstructionMapping(stat, tracer);
buf.append("break"); buf.append("break");
break; break;
case StatEdge.TYPE_CONTINUE: case StatEdge.TYPE_CONTINUE:
addDeletedGotoInstructionMapping(stat, tracer);
buf.append("continue"); buf.append("continue");
} }
if (edge.labeled) { if (edge.labeled) {
buf.append(" label").append(edge.closure.id); buf.append(" label").append(edge.closure.id.toString());
} }
buf.append(";").append(new_line_separator); buf.append(";").appendLineSeparator();
tracer.incrementCurrentSourceLine(); tracer.incrementCurrentSourceLine();
} }
} }
if (buf.length() == 0 && semicolon) { if (buf.length() == 0 && semicolon) {
buf.append(InterpreterUtil.getIndentString(indent)).append(";").append(new_line_separator); buf.appendIndent(indent).append(";").appendLineSeparator();
tracer.incrementCurrentSourceLine(); tracer.incrementCurrentSourceLine();
} }
return buf.toString(); return buf;
} }
public static String buildJavaClassName(String name) { public static String buildJavaClassName(String name) {
@ -815,92 +791,59 @@ public class ExprProcessor implements CodeConstants {
return res; return res;
} }
public static String listToJava(List<Exprent> lst, int indent, BytecodeMappingTracer tracer) { public static TextBuffer listToJava(List<? extends Exprent> lst, int indent, BytecodeMappingTracer tracer) {
if (lst == null || lst.isEmpty()) { if (lst == null || lst.isEmpty()) {
return ""; return new TextBuffer();
} }
String indstr = InterpreterUtil.getIndentString(indent); TextBuffer buf = new TextBuffer();
String new_line_separator = DecompilerContext.getNewLineSeparator();
for (Exprent expr : lst) {
if (buf.length() > 0 && expr.type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()) {
// separates local class definition from previous statements
buf.appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
TextBuffer content = expr.toJava(indent, tracer);
StringBuilder buf = new StringBuilder();
// Spigot Start
boolean inVar = false;
boolean bloodySpecialCaseForNewAssignments = false;
for (Iterator<Exprent> iter = SortUtil.sortIndexed(lst.iterator()); iter.hasNext();) {
Exprent expr = iter.next();
// Spigot End
String content = expr.toJava(indent, tracer);
if (content.length() > 0) { if (content.length() > 0) {
if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) {
if (expr instanceof VarExprent || expr instanceof AssignmentExprent) { buf.appendIndent(indent);
inVar = true;
boolean wasASpecialLittleFlower = bloodySpecialCaseForNewAssignments;
bloodySpecialCaseForNewAssignments = expr instanceof VarExprent;
if (expr instanceof AssignmentExprent && ((AssignmentExprent) expr).getLeft() instanceof VarExprent) {
AssignmentExprent assignmentExprent = (AssignmentExprent) expr;
VarExprent var = (VarExprent) assignmentExprent.getLeft();
bloodySpecialCaseForNewAssignments = var.isDefinition();
}
if (wasASpecialLittleFlower && !bloodySpecialCaseForNewAssignments) {
buf.append(new_line_separator);
}
} else if (inVar) {
inVar = false;
if (!(expr instanceof InvocationExprent
|| expr instanceof ExitExprent
|| expr instanceof FunctionExprent
)
|| bloodySpecialCaseForNewAssignments) {
bloodySpecialCaseForNewAssignments = false;
buf.append(new_line_separator);
}
}
if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassdef()) {
buf.append(indstr);
} }
buf.append(content); buf.append(content);
if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMontype() == MonitorExprent.MONITOR_ENTER) { if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMonType() == MonitorExprent.MONITOR_ENTER) {
buf.append("{}"); // empty synchronized block buf.append("{}"); // empty synchronized block
} }
if (endsWithSemikolon(expr)) { if (endsWithSemicolon(expr)) {
buf.append(";"); buf.append(";");
} }
buf.append(new_line_separator); buf.appendLineSeparator();
tracer.incrementCurrentSourceLine(); tracer.incrementCurrentSourceLine();
} }
} }
return buf.toString(); return buf;
} }
public static ConstExprent getDefaultArrayValue(VarType arrtype) { public static ConstExprent getDefaultArrayValue(VarType arrType) {
ConstExprent defaultVal;
ConstExprent defaultval; if (arrType.type == CodeConstants.TYPE_OBJECT || arrType.arrayDim > 0) {
if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) { defaultVal = new ConstExprent(VarType.VARTYPE_NULL, null, null);
defaultval = new ConstExprent(VarType.VARTYPE_NULL, null);
} }
else if (arrtype.type == CodeConstants.TYPE_FLOAT) { else if (arrType.type == CodeConstants.TYPE_FLOAT) {
defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); defaultVal = new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null);
} }
else if (arrtype.type == CodeConstants.TYPE_LONG) { else if (arrType.type == CodeConstants.TYPE_LONG) {
defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); defaultVal = new ConstExprent(VarType.VARTYPE_LONG, 0L, null);
} }
else if (arrtype.type == CodeConstants.TYPE_DOUBLE) { else if (arrType.type == CodeConstants.TYPE_DOUBLE) {
defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); defaultVal = new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null);
} }
else { // integer types else { // integer types
defaultval = new ConstExprent(0, true); defaultVal = new ConstExprent(0, true, null);
} }
return defaultVal;
return defaultval;
}
public static boolean getCastedExprent(Exprent exprent, VarType leftType, TextBuffer buffer, int indent,
boolean castNull, BytecodeMappingTracer tracer) {
return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, tracer);
} }
public static boolean getCastedExprent(Exprent exprent, public static boolean getCastedExprent(Exprent exprent,
@ -908,44 +851,89 @@ public class ExprProcessor implements CodeConstants {
TextBuffer buffer, TextBuffer buffer,
int indent, int indent,
boolean castNull, boolean castNull,
boolean castAlways, BytecodeMappingTracer tracer) { BytecodeMappingTracer tracer) {
return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, false, false, tracer);
}
public static boolean getCastedExprent(Exprent exprent,
VarType leftType,
TextBuffer buffer,
int indent,
boolean castNull,
boolean castAlways,
boolean castNarrowing,
boolean unbox,
BytecodeMappingTracer tracer) {
if (unbox) {
// "unbox" invocation parameters, e.g. 'byteSet.add((byte)123)' or 'new ShortContainer((short)813)'
if (exprent.type == Exprent.EXPRENT_INVOCATION && ((InvocationExprent)exprent).isBoxingCall()) {
InvocationExprent invocationExprent = (InvocationExprent)exprent;
exprent = invocationExprent.getLstParameters().get(0);
int paramType = invocationExprent.getDescriptor().params[0].type;
if (exprent.type == Exprent.EXPRENT_CONST && ((ConstExprent)exprent).getConstType().type != paramType) {
leftType = new VarType(paramType);
}
}
}
boolean ret = false;
VarType rightType = exprent.getExprType(); VarType rightType = exprent.getExprType();
String res = exprent.toJava(indent, tracer);
boolean cast = boolean cast =
!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT); castAlways ||
cast |= castAlways && !leftType.equals(rightType); // Spigot (!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) ||
(castNull && rightType.type == CodeConstants.TYPE_NULL && !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType))) ||
(castNarrowing && isIntConstant(exprent) && isNarrowedIntType(leftType));
if (!cast && castNull && rightType.type == CodeConstants.TYPE_NULL) { boolean quote = cast && exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST);
// 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) { // cast instead to 'byte' / 'short' when int constant is used as a value for 'Byte' / 'Short'
if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { if (castNarrowing && exprent.type == Exprent.EXPRENT_CONST && !((ConstExprent) exprent).isNull()) {
res = "(" + res + ")"; if (leftType.equals(VarType.VARTYPE_BYTE_OBJ)) {
leftType = VarType.VARTYPE_BYTE;
}
else if (leftType.equals(VarType.VARTYPE_SHORT_OBJ)) {
leftType = VarType.VARTYPE_SHORT;
} }
res = "(" + getCastTypeName(leftType) + ") " + res;
ret = true;
} }
buffer.append(res); String name = getCastTypeName(leftType);
return ret; if (exprent instanceof NewExprent)
{
if (((NewExprent) exprent).isVarArgParam())
{
quote = cast = false;
}
}
if (name.equals("Object"))
{
quote = cast = false;
}
if ((leftType.type == CodeConstants.TYPE_NULL))
{
quote = cast = false;
}
if (cast) buffer.append('(').append(name).append(')');
if (quote) buffer.append('(');
if (exprent.type == Exprent.EXPRENT_CONST) {
((ConstExprent) exprent).adjustConstType(leftType);
}
buffer.append(exprent.toJava(indent, tracer));
if (quote) buffer.append(')');
return cast;
} }
private static boolean isIntConstant(Exprent exprent) { private static boolean isIntConstant(Exprent exprent) {
if (exprent.type == Exprent.EXPRENT_CONST) { if (exprent.type == Exprent.EXPRENT_CONST) {
ConstExprent cexpr = (ConstExprent)exprent; switch (((ConstExprent)exprent).getConstType().type) {
switch (cexpr.getConsttype().type) {
case CodeConstants.TYPE_BYTE: case CodeConstants.TYPE_BYTE:
case CodeConstants.TYPE_BYTECHAR: case CodeConstants.TYPE_BYTECHAR:
case CodeConstants.TYPE_SHORT: case CodeConstants.TYPE_SHORT:
@ -957,4 +945,9 @@ public class ExprProcessor implements CodeConstants {
return false; return false;
} }
}
private static boolean isNarrowedIntType(VarType type) {
return VarType.VARTYPE_INT.isStrictSuperset(type) ||
type.equals(VarType.VARTYPE_BYTE_OBJ) || type.equals(VarType.VARTYPE_SHORT_OBJ);
}
}

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
@ -28,17 +14,13 @@ public class ExprentStack extends ListStack<Exprent> {
pointer = list.getPointer(); pointer = list.getPointer();
} }
public Exprent push(Exprent item) { @Override
super.push(item);
return item;
}
public Exprent pop() { public Exprent pop() {
return this.remove(--pointer); return this.remove(--pointer);
} }
@Override
public ExprentStack clone() { public ExprentStack clone() {
return new ExprentStack(this); return new ExprentStack(this);
} }

View File

@ -1,21 +1,10 @@
/* // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.*; 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.SimpleInstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
@ -36,8 +25,10 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; 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.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
@ -45,30 +36,24 @@ import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public class FinallyProcessor { public class FinallyProcessor {
private final Map<Integer, Integer> finallyBlockIDs = new HashMap<>();
private final Map<Integer, Integer> catchallBlockIDs = new HashMap<>();
private Map<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>(); private final MethodDescriptor methodDescriptor;
private Map<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>(); private final VarProcessor varProcessor;
private VarProcessor varprocessor; public FinallyProcessor(MethodDescriptor md, VarProcessor varProc) {
methodDescriptor = md;
public FinallyProcessor(VarProcessor varprocessor) { varProcessor = varProc;
this.varprocessor = varprocessor;
} }
public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) {
// return processStatement(mt, root, graph, root); int bytecodeVersion = mt.getBytecodeVersion();
return processStatementEx(mt, root, graph);
}
private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { LinkedList<Statement> stack = new LinkedList<>();
int bytecode_version = mt.getClassStruct().getBytecodeVersion();
LinkedList<Statement> stack = new LinkedList<Statement>();
stack.add(root); stack.add(root);
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
Statement stat = stack.removeLast(); Statement stat = stack.removeLast();
Statement parent = stat.getParent(); Statement parent = stat.getParent();
@ -83,30 +68,26 @@ public class FinallyProcessor {
// do nothing // do nothing
} }
else if (finallyBlockIDs.containsKey(handler.id)) { else if (finallyBlockIDs.containsKey(handler.id)) {
fin.setFinally(true); fin.setFinally(true);
Integer var = finallyBlockIDs.get(handler.id); Integer var = finallyBlockIDs.get(handler.id);
fin.setMonitor(var == null ? null : new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));
} }
else { else {
Record inf = getFinallyInformation(cl, mt, root, fin);
Record inf = getFinallyInformation(mt, root, fin);
if (inf == null) { // inconsistent finally if (inf == null) { // inconsistent finally
catchallBlockIDs.put(handler.id, null); catchallBlockIDs.put(handler.id, null);
} }
else { else {
if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
finallyBlockIDs.put(handler.id, null); finallyBlockIDs.put(handler.id, null);
} }
else { else {
int varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varIndex, inf, bytecodeVersion);
int varindex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); finallyBlockIDs.put(handler.id, varIndex);
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.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally
@ -124,63 +105,7 @@ public class FinallyProcessor {
return false; return false;
} }
private static final class Record {
// 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 static class Record {
private final int firstCode; private final int firstCode;
private final Map<BasicBlock, Boolean> mapLast; private final Map<BasicBlock, Boolean> mapLast;
@ -190,10 +115,8 @@ public class FinallyProcessor {
} }
} }
private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) {
private static Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { Map<BasicBlock, Boolean> mapLast = new HashMap<>();
Map<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>();
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
BasicBlock firstBasicBlock = firstBlockStatement.getBlock(); BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
@ -209,26 +132,25 @@ public class FinallyProcessor {
firstcode = 2; firstcode = 2;
} }
ExprProcessor proc = new ExprProcessor(); ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
proc.processStatement(root, mt.getClassStruct()); proc.processStatement(root, cl);
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt); ssa.splitVariables(root, mt);
List<Exprent> lstExprents = firstBlockStatement.getExprents(); List<Exprent> lstExprents = firstBlockStatement.getExprents();
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft()); VarVersionPair varpaar = new VarVersionPair((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft());
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root); DirectGraph dgraph = flatthelper.buildDirectGraph(root);
LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); LinkedList<DirectNode> stack = new LinkedList<>();
stack.add(dgraph.first); stack.add(dgraph.first);
Set<DirectNode> setVisited = new HashSet<DirectNode>(); Set<DirectNode> setVisited = new HashSet<>();
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
DirectNode node = stack.removeFirst(); DirectNode node = stack.removeFirst();
if (setVisited.contains(node)) { if (setVisited.contains(node)) {
@ -259,7 +181,7 @@ public class FinallyProcessor {
boolean found = false; boolean found = false;
for (Exprent expr : lst) { for (Exprent expr : lst) {
if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) { if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPair((VarExprent)expr).equals(varpaar)) {
found = true; found = true;
break; break;
} }
@ -269,7 +191,7 @@ public class FinallyProcessor {
found = false; found = false;
if (exprent.type == Exprent.EXPRENT_EXIT) { if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent; ExitExprent exexpr = (ExitExprent)exprent;
if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) { if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) {
found = true; found = true;
} }
} }
@ -287,7 +209,7 @@ public class FinallyProcessor {
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent assexpr = (AssignmentExprent)exprent; AssignmentExprent assexpr = (AssignmentExprent)exprent;
if (assexpr.getRight().type == Exprent.EXPRENT_VAR && if (assexpr.getRight().type == Exprent.EXPRENT_VAR &&
new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) { new VarVersionPair((VarExprent)assexpr.getRight()).equals(varpaar)) {
Exprent next = null; Exprent next = null;
if (i == node.exprents.size() - 1) { if (i == node.exprents.size() - 1) {
@ -305,7 +227,7 @@ public class FinallyProcessor {
boolean found = false; boolean found = false;
if (next != null && next.type == Exprent.EXPRENT_EXIT) { if (next != null && next.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)next; ExitExprent exexpr = (ExitExprent)next;
if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR
&& assexpr.getLeft().equals(exexpr.getValue())) { && assexpr.getLeft().equals(exexpr.getValue())) {
found = true; found = true;
} }
@ -375,8 +297,7 @@ public class FinallyProcessor {
int var, int var,
Record information, Record information,
int bytecode_version) { int bytecode_version) {
Set<BasicBlock> setCopy = new HashSet<>(setTry);
Set<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry);
int finallytype = information.firstCode; int finallytype = information.firstCode;
Map<BasicBlock, Boolean> mapLast = information.mapLast; Map<BasicBlock, Boolean> mapLast = information.mapLast;
@ -394,21 +315,15 @@ public class FinallyProcessor {
// disable semaphore at statement exit points // disable semaphore at statement exit points
for (BasicBlock block : setTry) { for (BasicBlock block : setTry) {
List<BasicBlock> lstSucc = block.getSuccs(); List<BasicBlock> lstSucc = block.getSuccs();
for (BasicBlock dest : lstSucc) {
for (BasicBlock dest : lstSucc) {
// break out // break out
if (!setCopy.contains(dest) && dest != graph.getLast()) { if (dest != graph.getLast() && !setCopy.contains(dest)) {
// disable semaphore // disable semaphore
SimpleInstructionSequence seq = new SimpleInstructionSequence(); SimpleInstructionSequence seq = new SimpleInstructionSequence();
seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
seq.addInstruction(ConstantsUtil seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
.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 // build a separate block
BasicBlock newblock = new BasicBlock(++graph.last_id); BasicBlock newblock = new BasicBlock(++graph.last_id);
@ -435,14 +350,10 @@ public class FinallyProcessor {
} }
} }
// enable semaphor at the statement entrance // enable semaphore at the statement entrance
SimpleInstructionSequence seq = new SimpleInstructionSequence(); SimpleInstructionSequence seq = new SimpleInstructionSequence();
seq.addInstruction( seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), -1);
ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -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); BasicBlock newhead = new BasicBlock(++graph.last_id);
newhead.setSeq(seq); newhead.setSeq(seq);
@ -451,12 +362,8 @@ public class FinallyProcessor {
// initialize semaphor with false // initialize semaphor with false
seq = new SimpleInstructionSequence(); seq = new SimpleInstructionSequence();
seq.addInstruction( seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
-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); BasicBlock newheadinit = new BasicBlock(++graph.last_id);
newheadinit.setSeq(seq); newheadinit.setSeq(seq);
@ -466,7 +373,7 @@ public class FinallyProcessor {
setCopy.add(newhead); setCopy.add(newhead);
setCopy.add(newheadinit); setCopy.add(newheadinit);
for (BasicBlock hd : new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) { for (BasicBlock hd : new HashSet<>(newheadinit.getSuccExceptions())) {
ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit); ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit);
if (setCopy.containsAll(range.getProtectedRange())) { if (setCopy.containsAll(range.getProtectedRange())) {
@ -476,10 +383,8 @@ public class FinallyProcessor {
} }
} }
private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) { private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) {
List<BasicBlock> lstTemp = new ArrayList<>();
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
lstTemp.addAll(oldblock.getPreds()); lstTemp.addAll(oldblock.getPreds());
lstTemp.addAll(oldblock.getPredExceptions()); lstTemp.addAll(oldblock.getPredExceptions());
@ -510,9 +415,8 @@ public class FinallyProcessor {
} }
} }
private static HashSet<BasicBlock> getAllBasicBlocks(Statement stat) { private static Set<BasicBlock> getAllBasicBlocks(Statement stat) {
List<Statement> lst = new LinkedList<>();
List<Statement> lst = new LinkedList<Statement>();
lst.add(stat); lst.add(stat);
int index = 0; int index = 0;
@ -529,7 +433,7 @@ public class FinallyProcessor {
} }
while (index < lst.size()); while (index < lst.size());
HashSet<BasicBlock> res = new LinkedHashSet<BasicBlock>(); // Spigot: Fix determinism Set<BasicBlock> res = new HashSet<>();
for (Statement st : lst) { for (Statement st : lst) {
res.add(((BasicBlockStatement)st).getBlock()); res.add(((BasicBlockStatement)st).getBlock());
@ -538,11 +442,9 @@ public class FinallyProcessor {
return res; return res;
} }
private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) { private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) {
Set<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst()); Set<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
int finallytype = information.firstCode; int finallytype = information.firstCode;
Map<BasicBlock, Boolean> mapLast = information.mapLast; Map<BasicBlock, Boolean> mapLast = information.mapLast;
@ -571,7 +473,7 @@ public class FinallyProcessor {
} }
// identify start blocks // identify start blocks
HashSet<BasicBlock> startBlocks = new LinkedHashSet<BasicBlock>(); // Spigot: Fix determinism Set<BasicBlock> startBlocks = new HashSet<>();
for (BasicBlock block : tryBlocks) { for (BasicBlock block : tryBlocks) {
startBlocks.addAll(block.getSuccs()); startBlocks.addAll(block.getSuccs());
} }
@ -580,7 +482,7 @@ public class FinallyProcessor {
startBlocks.remove(graph.getLast()); startBlocks.remove(graph.getLast());
startBlocks.removeAll(tryBlocks); startBlocks.removeAll(tryBlocks);
List<Area> lstAreas = new ArrayList<Area>(); List<Area> lstAreas = new ArrayList<>();
for (BasicBlock start : startBlocks) { for (BasicBlock start : startBlocks) {
@ -620,7 +522,7 @@ public class FinallyProcessor {
return true; return true;
} }
private static class Area { private static final class Area {
private final BasicBlock start; private final BasicBlock start;
private final Set<BasicBlock> sample; private final Set<BasicBlock> sample;
private final BasicBlock next; private final BasicBlock next;
@ -634,33 +536,32 @@ public class FinallyProcessor {
private Area compareSubgraphsEx(ControlFlowGraph graph, private Area compareSubgraphsEx(ControlFlowGraph graph,
BasicBlock startSample, BasicBlock startSample,
HashSet<BasicBlock> catchBlocks, Set<BasicBlock> catchBlocks,
BasicBlock startCatch, BasicBlock startCatch,
int finallytype, int finallytype,
Map<BasicBlock, Boolean> mapLast, Map<BasicBlock, Boolean> mapLast,
boolean skippedFirst) { boolean skippedFirst) {
class BlockStackEntry { class BlockStackEntry {
public BasicBlock blockCatch; public final BasicBlock blockCatch;
public BasicBlock blockSample; public final BasicBlock blockSample;
// TODO: correct handling (merging) of multiple paths // TODO: correct handling (merging) of multiple paths
public List<int[]> lstStoreVars; public final List<int[]> lstStoreVars;
public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) { BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
this.blockCatch = blockCatch; this.blockCatch = blockCatch;
this.blockSample = blockSample; this.blockSample = blockSample;
this.lstStoreVars = new ArrayList<int[]>(lstStoreVars); this.lstStoreVars = new ArrayList<>(lstStoreVars);
} }
} }
List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>(); List<BlockStackEntry> stack = new LinkedList<>();
Set<BasicBlock> setSample = new HashSet<BasicBlock>(); Set<BasicBlock> setSample = new HashSet<>();
Map<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>(); Map<String, BasicBlock[]> mapNext = new HashMap<>();
stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>())); stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<>()));
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
@ -693,7 +594,6 @@ public class FinallyProcessor {
} }
} }
// exception successors // exception successors
if (isLastBlock && blockSample.getSeq().isEmpty()) { if (isLastBlock && blockSample.getSeq().isEmpty()) {
// do nothing, blockSample will be removed anyway // do nothing, blockSample will be removed anyway
@ -721,8 +621,8 @@ public class FinallyProcessor {
if (instrCatch.opcode == CodeConstants.opc_astore && if (instrCatch.opcode == CodeConstants.opc_astore &&
instrSample.opcode == CodeConstants.opc_astore) { instrSample.opcode == CodeConstants.opc_astore) {
lst = new ArrayList<int[]>(lst); lst = new ArrayList<>(lst);
lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)}); lst.add(new int[]{instrCatch.operand(0), instrSample.operand(0)});
} }
} }
@ -740,7 +640,7 @@ public class FinallyProcessor {
} }
if (isLastBlock) { if (isLastBlock) {
Set<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs()); Set<BasicBlock> setSuccs = new HashSet<>(blockSample.getSuccs());
setSuccs.removeAll(setSample); setSuccs.removeAll(setSample);
for (BlockStackEntry stackent : stack) { for (BlockStackEntry stackent : stack) {
@ -755,11 +655,10 @@ public class FinallyProcessor {
} }
} }
return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values()))); return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<>(mapNext.values())));
} }
private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) { private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) {
// precondition: there is at most one true exit path in a finally statement // precondition: there is at most one true exit path in a finally statement
BasicBlock next = null; BasicBlock next = null;
@ -800,13 +699,11 @@ public class FinallyProcessor {
Instruction instrNext = seqNext.getInstr(i); Instruction instrNext = seqNext.getInstr(i);
Instruction instrBlock = seqBlock.getInstr(i); Instruction instrBlock = seqBlock.getInstr(i);
if (instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide if (!Instruction.equals(instrNext, instrBlock)) {
|| instrNext.operandsCount() != instrBlock.operandsCount()) {
return null; return null;
} }
for (int j = 0; j < instrNext.operandsCount(); j++) {
for (int j = 0; j < instrNext.getOperands().length; j++) { if (instrNext.operand(j) != instrBlock.operand(j)) {
if (instrNext.getOperand(j) != instrBlock.getOperand(j)) {
return null; return null;
} }
} }
@ -848,7 +745,6 @@ public class FinallyProcessor {
int type, int type,
int finallytype, int finallytype,
List<int[]> lstStoreVars) { List<int[]> lstStoreVars) {
InstructionSequence seqPattern = pattern.getSeq(); InstructionSequence seqPattern = pattern.getSeq();
InstructionSequence seqSample = sample.getSeq(); InstructionSequence seqSample = sample.getSeq();
@ -863,11 +759,11 @@ public class FinallyProcessor {
if ((type & 2) > 0) { // last if ((type & 2) > 0) { // last
if (finallytype == 0 || finallytype == 2) { if (finallytype == 0 || finallytype == 2) {
seqPattern.removeInstruction(seqPattern.length() - 1); seqPattern.removeLast();
} }
if (finallytype == 2) { if (finallytype == 2) {
seqPattern.removeInstruction(seqPattern.length() - 1); seqPattern.removeLast();
} }
} }
} }
@ -887,18 +783,19 @@ public class FinallyProcessor {
} }
if (seqPattern.length() < seqSample.length()) { // split in two blocks if (seqPattern.length() < seqSample.length()) { // split in two blocks
SimpleInstructionSequence seq = new SimpleInstructionSequence(); SimpleInstructionSequence seq = new SimpleInstructionSequence();
LinkedList<Integer> oldOffsets = new LinkedList<>();
for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) { for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) {
seq.addInstruction(0, seqSample.getInstr(i), -1); seq.addInstruction(0, seqSample.getInstr(i), -1);
oldOffsets.addFirst(sample.getOldOffset(i));
seqSample.removeInstruction(i); seqSample.removeInstruction(i);
} }
BasicBlock newblock = new BasicBlock(++graph.last_id); BasicBlock newblock = new BasicBlock(++graph.last_id);
newblock.setSeq(seq); newblock.setSeq(seq);
newblock.getInstrOldOffsets().addAll(oldOffsets);
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); List<BasicBlock> lstTemp = new ArrayList<>(sample.getSuccs());
lstTemp.addAll(sample.getSuccs());
// move successors // move successors
for (BasicBlock suc : lstTemp) { for (BasicBlock suc : lstTemp) {
@ -930,19 +827,15 @@ public class FinallyProcessor {
} }
public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) { public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {
if (first.opcode != second.opcode || first.wide != second.wide if (!Instruction.equals(first, second)) {
|| first.operandsCount() != second.operandsCount()) {
return false; return false;
} }
if (first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison if (first.group != CodeConstants.GROUP_JUMP) { // FIXME: switch comparison
for (int i = 0; i < first.getOperands().length; i++) { for (int i = 0; i < first.operandsCount(); i++) {
int firstOp = first.operand(i);
int firstOp = first.getOperand(i); int secondOp = second.operand(i);
int secondOp = second.getOperand(i);
if (firstOp != secondOp) { if (firstOp != secondOp) {
// a-load/store instructions // a-load/store instructions
if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) { if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {
for (int[] arr : lstStoreVars) { for (int[] arr : lstStoreVars) {
@ -961,7 +854,6 @@ public class FinallyProcessor {
} }
private static void deleteArea(ControlFlowGraph graph, Area area) { private static void deleteArea(ControlFlowGraph graph, Area area) {
BasicBlock start = area.start; BasicBlock start = area.start;
BasicBlock next = area.next; BasicBlock next = area.next;
@ -975,14 +867,14 @@ public class FinallyProcessor {
} }
// collect common exception ranges of predecessors and successors // collect common exception ranges of predecessors and successors
Set<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions()); Set<BasicBlock> setCommonExceptionHandlers = new HashSet<>(next.getSuccExceptions());
for (BasicBlock pred : start.getPreds()) { for (BasicBlock pred : start.getPreds()) {
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
} }
boolean is_outside_range = false; boolean is_outside_range = false;
Set<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds()); Set<BasicBlock> setPredecessors = new HashSet<>(start.getPreds());
// replace start with next // replace start with next
for (BasicBlock pred : setPredecessors) { for (BasicBlock pred : setPredecessors) {
@ -1004,7 +896,7 @@ public class FinallyProcessor {
is_outside_range = true; is_outside_range = true;
} }
Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>(); Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<>();
for (BasicBlock handler : block.getSuccExceptions()) { for (BasicBlock handler : block.getSuccExceptions()) {
setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));
} }
@ -1019,7 +911,7 @@ public class FinallyProcessor {
// shift extern edges on splitted blocks // shift extern edges on splitted blocks
if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) { if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) {
BasicBlock succs = block.getSuccs().get(0); BasicBlock succs = block.getSuccs().get(0);
for (BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) { for (BasicBlock pred : new ArrayList<>(block.getPreds())) {
if (!setBlocks.contains(pred)) { if (!setBlocks.contains(pred)) {
pred.replaceSuccessor(block, succs); pred.replaceSuccessor(block, succs);
} }
@ -1035,18 +927,15 @@ public class FinallyProcessor {
} }
if (is_outside_range) { if (is_outside_range) {
// new empty block // new empty block
BasicBlock emptyblock = new BasicBlock(++graph.last_id); BasicBlock emptyblock = new BasicBlock(++graph.last_id);
emptyblock.setSeq(new SimpleInstructionSequence());
graph.getBlocks().addWithKey(emptyblock, emptyblock.id); graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
// add to ranges if necessary // add to ranges if necessary
if (setCommonRemovedExceptionRanges != null) { for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) { emptyblock.addSuccessorException(range.getHandler());
emptyblock.addSuccessorException(range.getHandler()); range.getProtectedRange().add(emptyblock);
range.getProtectedRange().add(emptyblock);
}
} }
// insert between predecessors and next // insert between predecessors and next
@ -1058,7 +947,6 @@ public class FinallyProcessor {
} }
private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) { private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) {
InstructionSequence seq = block.getSeq(); InstructionSequence seq = block.getSeq();
if (finallytype == 3) { // empty finally handler if (finallytype == 3) { // empty finally handler
@ -1075,11 +963,11 @@ public class FinallyProcessor {
if ((blocktype & 2) > 0) { // last if ((blocktype & 2) > 0) { // last
if (finallytype == 2 || finallytype == 0) { if (finallytype == 2 || finallytype == 0) {
seq.removeInstruction(seq.length() - 1); seq.removeLast();
} }
if (finallytype == 2) { // astore if (finallytype == 2) { // astore
seq.removeInstruction(seq.length() - 1); seq.removeLast();
} }
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -25,11 +11,10 @@ import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.List; import java.util.List;
public class IdeaNotNullHelper { public final class IdeaNotNullHelper {
public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {
@ -68,7 +53,7 @@ public class IdeaNotNullHelper {
// TODO: FUNCTION_NE also possible if reversed order (in theory) // TODO: FUNCTION_NE also possible if reversed order (in theory)
if (ifbranch != null && if (ifbranch != null &&
if_condition.type == Exprent.EXPRENT_FUNCTION && if_condition.type == Exprent.EXPRENT_FUNCTION &&
((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ && ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ &&
ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.type == Statement.TYPE_BASICBLOCK &&
ifbranch.getExprents().size() == 1 && ifbranch.getExprents().size() == 1 &&
ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {
@ -85,11 +70,10 @@ public class IdeaNotNullHelper {
boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
// parameter annotations // parameter annotations
StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes StructAnnotationParameterAttribute param_annotations =
.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
if (param_annotations != null) { if (param_annotations != null) {
List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations(); List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();
@ -107,8 +91,9 @@ public class IdeaNotNullHelper {
List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift); List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift);
for (AnnotationExprent ann : annotations) { for (AnnotationExprent ann : annotations) {
if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
is_notnull_check = true; is_notnull_check = true;
break;
} }
} }
} }
@ -116,7 +101,7 @@ public class IdeaNotNullHelper {
break; break;
} }
index += md.params[i].stack_size; index += md.params[i].stackSize;
} }
} }
} }
@ -127,7 +112,7 @@ public class IdeaNotNullHelper {
return false; return false;
} }
removeParameterCheck(stat, mt); removeParameterCheck(stat);
return true; return true;
} }
@ -135,7 +120,7 @@ public class IdeaNotNullHelper {
return false; return false;
} }
private static void removeParameterCheck(Statement stat, StructMethod mt) { private static void removeParameterCheck(Statement stat) {
Statement st = stat.getFirst(); Statement st = stat.getFirst();
while (st.type == Statement.TYPE_SEQUENCE) { while (st.type == Statement.TYPE_SEQUENCE) {
@ -144,11 +129,7 @@ public class IdeaNotNullHelper {
IfStatement ifstat = (IfStatement)st; IfStatement ifstat = (IfStatement)st;
if (ifstat.getElsestat() == null) { // if if (ifstat.getElsestat() != null) { // if - else
// TODO:
}
else { // if - else
StatEdge ifedge = ifstat.getIfEdge(); StatEdge ifedge = ifstat.getIfEdge();
StatEdge elseedge = ifstat.getElseEdge(); StatEdge elseedge = ifstat.getElseEdge();
@ -172,28 +153,22 @@ public class IdeaNotNullHelper {
private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) { private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {
VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
boolean is_notnull_check = false; boolean is_notnull_check = false;
// method annotation, refers to the return value // method annotation, refers to the return value
StructAnnotationAttribute attr = StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
(StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
if (attr != null) { if (attr != null) {
List<AnnotationExprent> annotations = attr.getAnnotations(); List<AnnotationExprent> annotations = attr.getAnnotations();
for (AnnotationExprent ann : annotations) { for (AnnotationExprent ann : annotations) {
if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
is_notnull_check = true; is_notnull_check = true;
break;
} }
} }
} }
if (is_notnull_check) { return is_notnull_check && removeReturnCheck(stat, mt);
return removeReturnCheck(stat, mt);
}
return false;
} }
@ -205,7 +180,7 @@ public class IdeaNotNullHelper {
Exprent exprent = stat.getExprents().get(0); Exprent exprent = stat.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_EXIT) { if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exit_exprent = (ExitExprent)exprent; ExitExprent exit_exprent = (ExitExprent)exprent;
if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {
Exprent exprent_value = exit_exprent.getValue(); Exprent exprent_value = exit_exprent.getValue();
//if(exprent_value.type == Exprent.EXPRENT_VAR) { //if(exprent_value.type == Exprent.EXPRENT_VAR) {
// VarExprent var_value = (VarExprent)exprent_value; // VarExprent var_value = (VarExprent)exprent_value;
@ -214,7 +189,7 @@ public class IdeaNotNullHelper {
Exprent if_condition = ifparent.getHeadexprent().getCondition(); Exprent if_condition = ifparent.getHeadexprent().getCondition();
if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION && if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION &&
((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
FunctionExprent func = (FunctionExprent)if_condition; FunctionExprent func = (FunctionExprent)if_condition;
Exprent first_param = func.getLstOperands().get(0); Exprent first_param = func.getLstOperands().get(0);
@ -268,7 +243,7 @@ public class IdeaNotNullHelper {
Exprent exprent = stat.getExprents().get(0); Exprent exprent = stat.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_EXIT) { if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exit_exprent = (ExitExprent)exprent; ExitExprent exit_exprent = (ExitExprent)exprent;
if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {
Exprent exprent_value = exit_exprent.getValue(); Exprent exprent_value = exit_exprent.getValue();
SequenceStatement sequence = (SequenceStatement)parent; SequenceStatement sequence = (SequenceStatement)parent;
@ -282,7 +257,7 @@ public class IdeaNotNullHelper {
Exprent if_condition = ifstat.getHeadexprent().getCondition(); Exprent if_condition = ifstat.getHeadexprent().getCondition();
if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION && 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)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)
FunctionExprent func = (FunctionExprent)if_condition; FunctionExprent func = (FunctionExprent)if_condition;
Exprent first_param = func.getLstOperands().get(0); Exprent first_param = func.getLstOperands().get(0);

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
@ -23,43 +9,30 @@ 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.SequenceStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
public class IfHelper {
public final class IfHelper {
public static boolean mergeAllIfs(RootStatement root) { public static boolean mergeAllIfs(RootStatement root) {
boolean res = mergeAllIfsRec(root, new HashSet<>());
boolean res = mergeAllIfsRec(root, new HashSet<Integer>());
if (res) { if (res) {
SequenceHelper.condenseSequences(root); SequenceHelper.condenseSequences(root);
} }
return res; return res;
} }
private static boolean mergeAllIfsRec(Statement stat, Set<? super Integer> setReorderedIfs) {
private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) {
boolean res = false; boolean res = false;
if (stat.getExprents() == null) { if (stat.getExprents() == null) {
while (true) { while (true) {
boolean changed = false; boolean changed = false;
for (Statement st : stat.getStats()) { for (Statement st : stat.getStats()) {
res |= mergeAllIfsRec(st, setReorderedIfs); res |= mergeAllIfsRec(st, setReorderedIfs);
// collapse composed if's // collapse composed if's
if (changed = mergeIfs(st, setReorderedIfs)) { if (mergeIfs(st, setReorderedIfs)) {
changed = true;
break; break;
} }
} }
@ -75,9 +48,7 @@ public class IfHelper {
return res; return res;
} }
public static boolean mergeIfs(Statement statement, Set<? super Integer> setReorderedIfs) {
public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) {
if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) { if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) {
return false; return false;
} }
@ -85,10 +56,9 @@ public class IfHelper {
boolean res = false; boolean res = false;
while (true) { while (true) {
boolean updated = false; boolean updated = false;
List<Statement> lst = new ArrayList<Statement>(); List<Statement> lst = new ArrayList<>();
if (statement.type == Statement.TYPE_IF) { if (statement.type == Statement.TYPE_IF) {
lst.add(statement); lst.add(statement);
} }
@ -99,7 +69,6 @@ public class IfHelper {
boolean stsingle = (lst.size() == 1); boolean stsingle = (lst.size() == 1);
for (Statement stat : lst) { for (Statement stat : lst) {
if (stat.type == Statement.TYPE_IF) { if (stat.type == Statement.TYPE_IF) {
IfNode rtnode = buildGraph((IfStatement)stat, stsingle); IfNode rtnode = buildGraph((IfStatement)stat, stsingle);
@ -107,22 +76,25 @@ public class IfHelper {
continue; continue;
} }
if (updated = collapseIfIf(rtnode)) { if (collapseIfIf(rtnode)) {
updated = true;
break; break;
} }
if (!setReorderedIfs.contains(stat.id)) { if (!setReorderedIfs.contains(stat.id)) {
if (collapseIfElse(rtnode)) {
if (updated = collapseIfElse(rtnode)) { updated = true;
break; break;
} }
if (updated = collapseElse(rtnode)) { if (collapseElse(rtnode)) {
updated = true;
break; break;
} }
} }
if (updated = reorderIf((IfStatement)stat)) { if (reorderIf((IfStatement)stat)) {
updated = true;
setReorderedIfs.add(stat.id); setReorderedIfs.add(stat.id);
break; break;
} }
@ -133,14 +105,13 @@ public class IfHelper {
break; break;
} }
res |= updated; res = true;
} }
return res; return res;
} }
private static boolean collapseIfIf(IfNode rtnode) { private static boolean collapseIfIf(IfNode rtnode) {
if (rtnode.edgetypes.get(0) == 0) { if (rtnode.edgetypes.get(0) == 0) {
IfNode ifbranch = rtnode.succs.get(0); IfNode ifbranch = rtnode.succs.get(0);
if (ifbranch.succs.size() == 2) { if (ifbranch.succs.size() == 2) {
@ -158,7 +129,7 @@ public class IfHelper {
ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0)); ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0));
ifparent.getStats().removeWithKey(ifchild.id); ifparent.getStats().removeWithKey(ifchild.id);
if (ifbranch.edgetypes.get(0).intValue() == 1) { // target null if (ifbranch.edgetypes.get(0) == 1) { // target null
ifparent.setIfstat(null); ifparent.setIfstat(null);
@ -196,11 +167,12 @@ public class IfHelper {
// merge if conditions // merge if conditions
IfExprent statexpr = ifparent.getHeadexprent(); IfExprent statexpr = ifparent.getHeadexprent();
List<Exprent> lstOperands = new ArrayList<Exprent>(); List<Exprent> lstOperands = new ArrayList<>();
lstOperands.add(statexpr.getCondition()); lstOperands.add(statexpr.getCondition());
lstOperands.add(ifchild.getHeadexprent().getCondition()); lstOperands.add(ifchild.getHeadexprent().getCondition());
statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null));
statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode);
return true; return true;
} }
@ -212,11 +184,9 @@ public class IfHelper {
} }
private static boolean collapseIfElse(IfNode rtnode) { private static boolean collapseIfElse(IfNode rtnode) {
if (rtnode.edgetypes.get(0) == 0) {
if (rtnode.edgetypes.get(0).intValue() == 0) {
IfNode ifbranch = rtnode.succs.get(0); IfNode ifbranch = rtnode.succs.get(0);
if (ifbranch.succs.size() == 2) { if (ifbranch.succs.size() == 2) {
// if-else branch // if-else branch
if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) { if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) {
@ -229,8 +199,8 @@ public class IfHelper {
ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());
ifparent.getStats().removeWithKey(ifchild.id); ifparent.getStats().removeWithKey(ifchild.id);
if (ifbranch.edgetypes.get(1).intValue() == 1 && if (ifbranch.edgetypes.get(1) == 1 &&
ifbranch.edgetypes.get(0).intValue() == 1) { // target null ifbranch.edgetypes.get(0) == 1) { // target null
ifparent.setIfstat(null); ifparent.setIfstat(null);
@ -249,11 +219,11 @@ public class IfHelper {
// merge if conditions // merge if conditions
IfExprent statexpr = ifparent.getHeadexprent(); IfExprent statexpr = ifparent.getHeadexprent();
List<Exprent> lstOperands = new ArrayList<Exprent>(); List<Exprent> lstOperands = new ArrayList<>();
lstOperands.add(statexpr.getCondition()); lstOperands.add(statexpr.getCondition());
lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, ifchild.getHeadexprent().getCondition(), null));
Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()}))); statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null));
statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode);
return true; return true;
} }
@ -264,10 +234,8 @@ public class IfHelper {
return false; return false;
} }
private static boolean collapseElse(IfNode rtnode) { private static boolean collapseElse(IfNode rtnode) {
if (rtnode.edgetypes.get(1) == 0) {
if (rtnode.edgetypes.get(1).intValue() == 0) {
IfNode elsebranch = rtnode.succs.get(1); IfNode elsebranch = rtnode.succs.get(1);
if (elsebranch.succs.size() == 2) { if (elsebranch.succs.size() == 2) {
@ -304,18 +272,17 @@ public class IfHelper {
// merge if conditions // merge if conditions
IfExprent statexpr = secondif.getHeadexprent(); IfExprent statexpr = secondif.getHeadexprent();
List<Exprent> lstOperands = new ArrayList<Exprent>(); List<Exprent> lstOperands = new ArrayList<>();
lstOperands.add(firstif.getHeadexprent().getCondition()); lstOperands.add(firstif.getHeadexprent().getCondition());
if (path == 2) { if (path == 2) {
lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstOperands.get(0), null));
Arrays.asList(new Exprent[]{lstOperands.get(0)})));
} }
lstOperands.add(statexpr.getCondition()); lstOperands.add(statexpr.getCondition());
statexpr statexpr
.setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands)); .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands, null));
if (secondif.getFirst().getExprents().isEmpty() && if (secondif.getFirst().getExprents().isEmpty() &&
!firstif.getFirst().getExprents().isEmpty()) { !firstif.getFirst().getExprents().isEmpty()) {
@ -328,9 +295,7 @@ public class IfHelper {
} }
} }
else if (elsebranch.succs.size() == 1) { else if (elsebranch.succs.size() == 1) {
if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) { if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) {
IfStatement firstif = (IfStatement)rtnode.value; IfStatement firstif = (IfStatement)rtnode.value;
Statement second = elsebranch.value; Statement second = elsebranch.value;
@ -359,7 +324,7 @@ public class IfHelper {
// negate the if condition // negate the if condition
IfExprent statexpr = firstif.getHeadexprent(); IfExprent statexpr = firstif.getHeadexprent();
statexpr statexpr
.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null));
return true; return true;
} }
@ -369,9 +334,7 @@ public class IfHelper {
return false; return false;
} }
private static IfNode buildGraph(IfStatement stat, boolean stsingle) { private static IfNode buildGraph(IfStatement stat, boolean stsingle) {
if (stat.iftype == IfStatement.IFTYPE_IFELSE) { if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
return null; return null;
} }
@ -433,16 +396,14 @@ public class IfHelper {
return res; return res;
} }
// FIXME: rewrite the entire method!!! keep in mind finally exits!! // FIXME: rewrite the entire method!!! keep in mind finally exits!!
private static boolean reorderIf(IfStatement ifstat) { private static boolean reorderIf(IfStatement ifstat) {
if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) { if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {
return false; return false;
} }
boolean ifdirect = false, elsedirect = false; boolean ifdirect, elsedirect;
boolean noifstat = false, noelsestat = false; boolean noifstat = false, noelsestat;
boolean ifdirectpath = false, elsedirectpath = false; boolean ifdirectpath = false, elsedirectpath = false;
Statement parent = ifstat.getParent(); Statement parent = ifstat.getParent();
@ -453,32 +414,20 @@ public class IfHelper {
if (ifstat.getIfstat() == null) { if (ifstat.getIfstat() == null) {
noifstat = true; noifstat = true;
if (ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) { ifdirect = ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT ||
ifdirect = true; MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());
}
else {
ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());
}
} }
else { else {
List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges(); List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges();
if (!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { ifdirect = !lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT ||
ifdirect = true; hasDirectEndEdge(ifstat.getIfstat(), from);
}
else {
ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from);
}
} }
Statement last = parent.type == Statement.TYPE_SEQUENCE ? parent.getStats().getLast() : ifstat; Statement last = parent.type == Statement.TYPE_SEQUENCE ? parent.getStats().getLast() : ifstat;
noelsestat = (last == ifstat); noelsestat = (last == ifstat);
if (!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { elsedirect = !last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT ||
elsedirect = true; hasDirectEndEdge(last, from);
}
else {
elsedirect = hasDirectEndEdge(last, from);
}
if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) { if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) {
return false; return false;
@ -496,10 +445,9 @@ public class IfHelper {
if (sttemp == ifstat) { if (sttemp == ifstat) {
break; break;
} }
else { else if (existsPath(sttemp, next)) {
if (elsedirectpath = existsPath(sttemp, next)) { elsedirectpath = true;
break; break;
}
} }
} }
} }
@ -509,7 +457,7 @@ public class IfHelper {
SequenceStatement sequence = (SequenceStatement)parent; SequenceStatement sequence = (SequenceStatement)parent;
// build and cut the new else statement // build and cut the new else statement
List<Statement> lst = new ArrayList<Statement>(); List<Statement> lst = new ArrayList<>();
for (int i = sequence.getStats().size() - 1; i >= 0; i--) { for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
Statement sttemp = sequence.getStats().get(i); Statement sttemp = sequence.getStats().get(i);
if (sttemp == ifstat) { if (sttemp == ifstat) {
@ -554,7 +502,7 @@ public class IfHelper {
// negate the if condition // negate the if condition
IfExprent statexpr = ifstat.getHeadexprent(); IfExprent statexpr = ifstat.getHeadexprent();
statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null));
if (noelsestat) { if (noelsestat) {
StatEdge ifedge = ifstat.getIfEdge(); StatEdge ifedge = ifstat.getIfEdge();
@ -597,7 +545,7 @@ public class IfHelper {
SequenceStatement sequence = (SequenceStatement)parent; SequenceStatement sequence = (SequenceStatement)parent;
// build and cut the new else statement // build and cut the new else statement
List<Statement> lst = new ArrayList<Statement>(); List<Statement> lst = new ArrayList<>();
for (int i = sequence.getStats().size() - 1; i >= 0; i--) { for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
Statement sttemp = sequence.getStats().get(i); Statement sttemp = sequence.getStats().get(i);
if (sttemp == ifstat) { if (sttemp == ifstat) {
@ -698,9 +646,7 @@ public class IfHelper {
return false; return false;
} }
private static Statement getNextStatement(Statement stat) { private static Statement getNextStatement(Statement stat) {
Statement parent = stat.getParent(); Statement parent = stat.getParent();
switch (parent.type) { switch (parent.type) {
case Statement.TYPE_ROOT: case Statement.TYPE_ROOT:
@ -722,7 +668,6 @@ public class IfHelper {
} }
private static boolean existsPath(Statement from, Statement to) { private static boolean existsPath(Statement from, Statement to) {
for (StatEdge edge : to.getAllPredecessorEdges()) { for (StatEdge edge : to.getAllPredecessorEdges()) {
if (from.containsStatementStrict(edge.getSource())) { if (from.containsStatementStrict(edge.getSource())) {
return true; return true;
@ -733,18 +678,17 @@ public class IfHelper {
} }
private static class IfNode { private static class IfNode {
public Statement value; public final Statement value;
public final List<IfNode> succs = new ArrayList<>();
public final List<Integer> edgetypes = new ArrayList<>();
public List<IfNode> succs = new ArrayList<IfNode>(); IfNode(Statement value) {
public List<Integer> edgetypes = new ArrayList<Integer>();
public IfNode(Statement value) {
this.value = value; this.value = value;
} }
public void addChild(IfNode child, int type) { public void addChild(IfNode child, int type) {
succs.add(child); succs.add(child);
edgetypes.add(new Integer(type)); edgetypes.add(type);
} }
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
@ -21,7 +7,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class InlineSingleBlockHelper { public final class InlineSingleBlockHelper {
public static boolean inlineSingleBlocks(RootStatement root) { public static boolean inlineSingleBlocks(RootStatement root) {
@ -68,7 +54,7 @@ public class InlineSingleBlockHelper {
Statement parent = source.getParent(); Statement parent = source.getParent();
source.removeSuccessor(edge); source.removeSuccessor(edge);
List<Statement> lst = new ArrayList<Statement>(); List<Statement> lst = new ArrayList<>();
for (int i = seq.getStats().size() - 1; i >= index; i--) { for (int i = seq.getStats().size() - 1; i >= index; i--) {
lst.add(0, seq.getStats().remove(i)); lst.add(0, seq.getStats().remove(i));
} }
@ -132,17 +118,14 @@ public class InlineSingleBlockHelper {
StatEdge edge = lst.get(0); StatEdge edge = lst.get(0);
if (sameCatchRanges(edge)) { if (sameCatchRanges(edge)) {
if (edge.explicit) { if (!edge.explicit) {
return true;
}
else {
for (int i = index; i < seq.getStats().size(); i++) { for (int i = index; i < seq.getStats().size(); i++) {
if (!noExitLabels(seq.getStats().get(i), seq)) { if (!noExitLabels(seq.getStats().get(i), seq)) {
return false; return false;
} }
} }
return true;
} }
return true;
} }
// FIXME: count labels properly // FIXME: count labels properly
} }
@ -200,21 +183,13 @@ public class InlineSingleBlockHelper {
} }
public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { public static boolean isBreakEdgeLabeled(Statement source, Statement closure) {
if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) {
Statement parent = source.getParent(); Statement parent = source.getParent();
return parent != closure &&
if (parent == closure) { (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || isBreakEdgeLabeled(parent, closure));
return false;
}
else {
return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH ||
isBreakEdgeLabeled(parent, closure);
}
} }
else { else {
return true; return true;
} }
} }
} }

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
@ -22,7 +8,7 @@ import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public class LabelHelper { public final class LabelHelper {
public static void cleanUpEdges(RootStatement root) { public static void cleanUpEdges(RootStatement root) {
@ -33,7 +19,7 @@ public class LabelHelper {
liftClosures(root); liftClosures(root);
lowContinueLabels(root, new HashSet<StatEdge>()); lowContinueLabels(root, new HashSet<>());
lowClosures(root); lowClosures(root);
} }
@ -63,7 +49,7 @@ public class LabelHelper {
if (dest.type != Statement.TYPE_DUMMYEXIT) { if (dest.type != Statement.TYPE_DUMMYEXIT) {
Statement parent = dest.getParent(); Statement parent = dest.getParent();
List<Statement> lst = new ArrayList<Statement>(); List<Statement> lst = new ArrayList<>();
if (parent.type == Statement.TYPE_SEQUENCE) { if (parent.type == Statement.TYPE_SEQUENCE) {
lst.addAll(parent.getStats()); lst.addAll(parent.getStats());
} }
@ -131,14 +117,14 @@ public class LabelHelper {
lowContinueLabels(st, edges); lowContinueLabels(st, edges);
} }
else { else {
lowContinueLabels(st, new HashSet<StatEdge>()); lowContinueLabels(st, new HashSet<>());
} }
} }
} }
public static void lowClosures(Statement stat) { public static void lowClosures(Statement stat) {
for (StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) { for (StatEdge edge : new ArrayList<>(stat.getLabelEdges())) {
if (edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ? if (edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ?
for (Statement st : stat.getStats()) { for (Statement st : stat.getStats()) {
@ -181,7 +167,7 @@ public class LabelHelper {
private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) { private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) {
HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>(); HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<>();
if (stat.getExprents() != null) { if (stat.getExprents() != null) {
return mapEdges; return mapEdges;
@ -279,7 +265,7 @@ public class LabelHelper {
Statement stlast = swst.getCaseStatements().get(last); Statement stlast = swst.getCaseStatements().get(last);
if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) { if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) {
StatEdge edge = stlast.getAllSuccessorEdges().get(0); StatEdge edge = stlast.getAllSuccessorEdges().get(0);
mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{edge}))); mapEdges.put(edge.getDestination(), new ArrayList<>(Collections.singletonList(edge)));
} }
else { else {
mapEdges = setExplicitEdges(stlast); mapEdges = setExplicitEdges(stlast);
@ -340,7 +326,7 @@ public class LabelHelper {
edge.explicit = false; edge.explicit = false;
} }
mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{newedge}))); mapEdges.put(newedge.getDestination(), new ArrayList<>(Collections.singletonList(newedge)));
} }
} }
} }
@ -388,7 +374,7 @@ public class LabelHelper {
} }
if (statedge != null) { if (statedge != null) {
mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[]{statedge}))); mapEdges.put(statedge.getDestination(), new ArrayList<>(Collections.singletonList(statedge)));
} }
} }
@ -420,24 +406,26 @@ public class LabelHelper {
} }
} }
private static HashSet<Statement>[] processStatementLabel(Statement stat) { private static class LabelSets {
private final Set<Statement> breaks = new HashSet<>();
private final Set<Statement> continues = new HashSet<>();
}
HashSet<Statement> setBreak = new HashSet<Statement>(); private static LabelSets processStatementLabel(Statement stat) {
HashSet<Statement> setContinue = new HashSet<Statement>(); LabelSets sets = new LabelSets();
if (stat.getExprents() == null) { if (stat.getExprents() == null) {
for (Statement st : stat.getStats()) { for (Statement st : stat.getStats()) {
HashSet<Statement>[] arr = processStatementLabel(st); LabelSets nested = processStatementLabel(st);
sets.breaks.addAll(nested.breaks);
setBreak.addAll(arr[0]); sets.continues.addAll(nested.continues);
setContinue.addAll(arr[1]);
} }
boolean shieldType = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH); boolean shieldType = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH);
if (shieldType) { if (shieldType) {
for (StatEdge edge : stat.getLabelEdges()) { for (StatEdge edge : stat.getLabelEdges()) {
if (edge.explicit && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) || if (edge.explicit && ((edge.getType() == StatEdge.TYPE_BREAK && sets.breaks.contains(edge.getSource())) ||
(edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) { (edge.getType() == StatEdge.TYPE_CONTINUE && sets.continues.contains(edge.getSource())))) {
edge.labeled = false; edge.labeled = false;
} }
} }
@ -445,16 +433,16 @@ public class LabelHelper {
switch (stat.type) { switch (stat.type) {
case Statement.TYPE_DO: case Statement.TYPE_DO:
setContinue.clear(); sets.continues.clear();
case Statement.TYPE_SWITCH: case Statement.TYPE_SWITCH:
setBreak.clear(); sets.breaks.clear();
} }
} }
setBreak.add(stat); sets.breaks.add(stat);
setContinue.add(stat); sets.continues.add(stat);
return new HashSet[] { setBreak, setContinue }; return sets;
} }
public static void replaceContinueWithBreak(Statement stat) { public static void replaceContinueWithBreak(Statement stat) {

View File

@ -1,18 +1,4 @@
/* // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler; package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
@ -25,7 +11,7 @@ import java.util.Arrays;
import java.util.Set; import java.util.Set;
public class LoopExtractHelper { public final class LoopExtractHelper {
public static boolean extractLoops(Statement root) { public static boolean extractLoops(Statement root) {
@ -72,9 +58,7 @@ public class LoopExtractHelper {
return res ? 1 : 0; return res ? 1 : 0;
} }
private static boolean extractLoop(DoStatement stat) { private static boolean extractLoop(DoStatement stat) {
if (stat.getLooptype() != DoStatement.LOOP_DO) { if (stat.getLooptype() != DoStatement.LOOP_DO) {
return false; return false;
} }
@ -85,12 +69,7 @@ public class LoopExtractHelper {
} }
} }
if (!extractLastIf(stat)) { return extractLastIf(stat) || extractFirstIf(stat);
return extractFirstIf(stat);
}
else {
return true;
}
} }
private static boolean extractLastIf(DoStatement stat) { private static boolean extractLastIf(DoStatement stat) {
@ -189,7 +168,7 @@ public class LoopExtractHelper {
loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target)); loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target));
for (StatEdge edge : new ArrayList<StatEdge>(block.getLabelEdges())) { for (StatEdge edge : new ArrayList<>(block.getLabelEdges())) {
if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) { if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) {
loop.addLabeledEdge(edge); loop.addLabeledEdge(edge);
} }

View File

@ -1,208 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.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) {
while (true) {
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) {
while (true) {
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);
}
while (true) {
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;
}
}

Some files were not shown because too many files have changed in this diff Show More