[*] 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>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>eclipse-formatter</artifactId>
<version>3.14.0-spigotmc-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>./src</sourceDirectory>
<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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
<plugin>

View File

@ -1,31 +1,22 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
@SuppressWarnings({"unused", "SpellCheckingInspection"})
public interface CodeConstants {
// ----------------------------------------------------------------------
// BYTECODE VERSIONS
// ----------------------------------------------------------------------
int BYTECODE_JAVA_LE_4 = 1;
int BYTECODE_JAVA_5 = 2;
int BYTECODE_JAVA_6 = 3;
int BYTECODE_JAVA_7 = 4;
int BYTECODE_JAVA_8 = 5;
int BYTECODE_JAVA_LE_4 = 48;
int BYTECODE_JAVA_5 = 49;
int BYTECODE_JAVA_6 = 50;
int BYTECODE_JAVA_7 = 51;
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
@ -63,20 +54,6 @@ public interface CodeConstants {
int TYPE_FAMILY_DOUBLE = 5;
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
// ----------------------------------------------------------------------
@ -87,6 +64,7 @@ public interface CodeConstants {
int ACC_STATIC = 0x0008;
int ACC_FINAL = 0x0010;
int ACC_SYNCHRONIZED = 0x0020;
int ACC_OPEN = 0x0020;
int ACC_NATIVE = 0x0100;
int ACC_ABSTRACT = 0x0400;
int ACC_STRICT = 0x0800;
@ -97,6 +75,8 @@ public interface CodeConstants {
int ACC_SYNTHETIC = 0x1000;
int ACC_ANNOTATION = 0x2000;
int ACC_ENUM = 0x4000;
int ACC_MANDATED = 0x8000;
int ACC_MODULE = 0x8000;
// ----------------------------------------------------------------------
// CLASS FLAGS
@ -105,17 +85,6 @@ public interface CodeConstants {
int ACC_SUPER = 0x0020;
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
// ----------------------------------------------------------------------
@ -145,6 +114,8 @@ public interface CodeConstants {
int CONSTANT_MethodHandle = 15;
int CONSTANT_MethodType = 16;
int CONSTANT_InvokeDynamic = 18;
int CONSTANT_Module = 19;
int CONSTANT_Package = 20;
// ----------------------------------------------------------------------
// MethodHandle reference_kind values
@ -351,7 +322,6 @@ public interface CodeConstants {
int opc_invokestatic = 184;
int opc_invokeinterface = 185;
int opc_invokedynamic = 186;
int opc_xxxunusedxxx = 186;
int opc_new = 187;
int opc_newarray = 188;
int opc_anewarray = 189;
@ -367,4 +337,7 @@ public interface CodeConstants {
int opc_ifnonnull = 199;
int opc_goto_w = 200;
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import java.io.DataOutputStream;
import java.io.IOException;
public class ExceptionHandler {
public int from = 0;
public int to = 0;
public int handler = 0;
@ -30,32 +12,12 @@ public class ExceptionHandler {
public int to_instr = 0;
public int handler_instr = 0;
public int class_index = 0;
public String exceptionClass = null;
public ExceptionHandler() {
}
public ExceptionHandler(int from_raw, int to_raw, int handler_raw, String exceptionClass) {
this.from = from_raw;
this.to = to_raw;
this.handler = handler_raw;
this.exceptionClass = exceptionClass;
}
public void writeToStream(DataOutputStream out) throws IOException {
out.writeShort(from);
out.writeShort(to);
out.writeShort(handler);
out.writeShort(class_index);
}
public String toString() {
String new_line_separator = DecompilerContext.getNewLineSeparator();
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 +
"exceptionClass: " + exceptionClass + new_line_separator;
}
}
}

View File

@ -1,58 +1,19 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.code.interpreter.Util;
import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ExceptionTable {
public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList());
private List<ExceptionHandler> handlers = new ArrayList<ExceptionHandler>();
public ExceptionTable() {
}
private final List<ExceptionHandler> handlers;
public ExceptionTable(List<ExceptionHandler> handlers) {
this.handlers = handlers;
}
public ExceptionHandler getHandlerByClass(StructContext context, int line, String valclass, boolean withany) {
ExceptionHandler res = null; // no handler found
for (ExceptionHandler handler : handlers) {
if (handler.from <= line && handler.to > line) {
String name = handler.exceptionClass;
if ((withany && name == null) || // any -> finally or synchronized handler
(name != null && Util.instanceOf(context, valclass, name))) {
res = handler;
break;
}
}
}
return res;
}
public List<ExceptionHandler> getHandlers() {
return handlers;
}
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
@ -25,7 +11,7 @@ public class FullInstructionSequence extends InstructionSequence {
// *****************************************************************************
public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) {
this.collinstr = collinstr;
super(collinstr);
this.exceptionTable = extable;
// 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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
import java.io.DataOutputStream;
import java.io.IOException;
import org.jetbrains.java.decompiler.util.TextUtil;
public class Instruction implements CodeConstants {
// *****************************************************************************
// public fields
// *****************************************************************************
public int opcode;
public int group = CodeConstants.GROUP_GENERAL;
public boolean wide = false;
public int bytecode_version = BYTECODE_JAVA_LE_4;
// *****************************************************************************
// private fields
// *****************************************************************************
private int[] operands = null;
// *****************************************************************************
// public methods
// *****************************************************************************
public Instruction() {
public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) {
if (opcode >= opc_ifeq && opcode <= opc_if_acmpne ||
opcode == opc_ifnull || opcode == opc_ifnonnull ||
opcode == opc_jsr || opcode == opc_jsr_w ||
opcode == opc_goto || opcode == opc_goto_w) {
return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands);
}
else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) {
return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands);
}
else {
return new Instruction(opcode, group, wide, bytecodeVersion, operands);
}
}
public int length() {
return 1;
public static boolean equals(Instruction i1, Instruction i2) {
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() {
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];
}
public Instruction clone() {
return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands == null ? null : operands.clone());
public boolean canFallThrough() {
return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret &&
!(opcode >= opc_ireturn && opcode <= opc_return) &&
opcode != opc_athrow &&
opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch;
}
@Override
public String toString() {
String res = wide ? "@wide " : "";
res += "@" + ConstantsUtil.getName(opcode);
StringBuilder res = new StringBuilder();
if (wide) res.append("@wide ");
res.append("@").append(TextUtil.getInstructionName(opcode));
int len = operandsCount();
for (int i = 0; i < len; i++) {
int op = operands[i];
if (op < 0) {
res += " -" + Integer.toHexString(-op);
res.append(" -").append(Integer.toHexString(-op));
}
else {
res += " " + Integer.toHexString(op);
res.append(" ").append(Integer.toHexString(op));
}
}
return res;
return res.toString();
}
public boolean canFallthrough() {
return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret &&
!(opcode >= opc_ireturn && opcode <= opc_return) && opcode != opc_athrow
&& opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch;
@Override
@SuppressWarnings("MethodDoesntCallSuperMethod")
public Instruction clone() {
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code;
import org.jetbrains.java.decompiler.code.interpreter.Util;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.TextUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public abstract class InstructionSequence {
// *****************************************************************************
// private fields
// *****************************************************************************
protected VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>();
protected final VBStyleCollection<Instruction, Integer> collinstr;
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
// *****************************************************************************
// to nbe overwritten
@Override
public InstructionSequence clone() {
return null;
}
@ -52,7 +38,7 @@ public abstract class InstructionSequence {
public void clear() {
collinstr.clear();
pointer = 0;
exceptionTable = new ExceptionTable();
exceptionTable = ExceptionTable.EMPTY;
}
public void addInstruction(Instruction inst, int offset) {
@ -73,8 +59,10 @@ public abstract class InstructionSequence {
collinstr.remove(index);
}
public Instruction getCurrentInstr() {
return collinstr.get(pointer);
public void removeLast() {
if (!collinstr.isEmpty()) {
collinstr.remove(collinstr.size() - 1);
}
}
public Instruction getInstr(int index) {
@ -85,16 +73,12 @@ public abstract class InstructionSequence {
return collinstr.getLast();
}
public int getCurrentOffset() {
return collinstr.getKey(pointer).intValue();
}
public int getOffset(int index) {
return collinstr.getKey(index).intValue();
public int getOffset(int index) {
return collinstr.getKey(index);
}
public int getPointerByAbsOffset(int offset) {
Integer absoffset = new Integer(offset);
Integer absoffset = offset;
if (collinstr.containsKey(absoffset)) {
return collinstr.getIndexByKey(absoffset);
}
@ -104,7 +88,7 @@ public int getOffset(int index) {
}
public int getPointerByRelOffset(int offset) {
Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset);
Integer absoffset = collinstr.getKey(pointer) + offset;
if (collinstr.containsKey(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() {
return collinstr.size();
}
@ -143,7 +120,7 @@ public int getOffset(int index) {
StringBuilder buf = new StringBuilder();
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(": ");
buf.append(collinstr.get(i).toString());
@ -153,58 +130,6 @@ public int getOffset(int index) {
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
// *****************************************************************************
@ -220,8 +145,4 @@ public int getOffset(int index) {
public ExceptionTable getExceptionTable() {
return exceptionTable;
}
public void setExceptionTable(ExceptionTable exceptionTable) {
this.exceptionTable = exceptionTable;
}
}
}

View File

@ -1,42 +1,22 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
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 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) {
destination = seq.getPointerByRelOffset(this.getOperand(0));
destination = seq.getPointerByRelOffset(this.operand(0));
}
@Override
public JumpInstruction clone() {
JumpInstruction newinstr = (JumpInstruction)super.clone();
newinstr.destination = destination;
return newinstr;
JumpInstruction copy = (JumpInstruction)super.clone();
copy.destination = destination;
return copy;
}
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
@ -23,17 +9,14 @@ public class SimpleInstructionSequence extends InstructionSequence {
}
public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {
this.collinstr = collinstr;
super(collinstr);
}
@Override
public SimpleInstructionSequence clone() {
SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone());
newseq.setPointer(this.getPointer());
return newseq;
}
public void removeInstruction(int index) {
collinstr.remove(index);
}
}

View File

@ -1,97 +1,61 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code;
/*
* opc_tableswitch, lookupswitch
*/
public class SwitchInstruction extends Instruction {
private int[] destinations;
private int[] values;
private int defaultDestination;
private int defaultdest;
public SwitchInstruction() {
public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
super(opcode, group, wide, bytecodeVersion, operands);
}
@Override
public void initInstruction(InstructionSequence seq) {
defaultDestination = seq.getPointerByRelOffset(operands[0]);
int pref = (opcode == CodeConstants.opc_tableswitch ? 3 : 2);
int len = this.getOperands().length - pref;
defaultdest = seq.getPointerByRelOffset(this.getOperand(0));
int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2;
int len = operands.length - prefix;
int low = 0;
if (opcode == CodeConstants.opc_lookupswitch) {
len /= 2;
}
else {
low = this.getOperand(1);
low = operands[1];
}
destinations = new int[len];
values = new int[len];
for (int i = 0, k = 0; i < len; i++, k++) {
if (opcode == CodeConstants.opc_lookupswitch) {
values[i] = this.getOperand(pref + k);
values[i] = operands[prefix + k];
k++;
}
else {
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() {
return destinations;
}
public void setDestinations(int[] destinations) {
this.destinations = destinations;
}
public int getDefaultdest() {
return defaultdest;
}
public void setDefaultdest(int defaultdest) {
this.defaultdest = defaultdest;
}
public int[] getValues() {
return values;
}
public void setValues(int[] values) {
this.values = values;
public int getDefaultDestination() {
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code.cfg;
import org.jetbrains.java.decompiler.code.Instruction;
@ -30,8 +16,7 @@ public class BasicBlock implements IGraphNode {
// public fields
// *****************************************************************************
public int id = 0;
public int id;
public int mark = 0;
// *****************************************************************************
@ -40,19 +25,11 @@ public class BasicBlock implements IGraphNode {
private InstructionSequence seq = new SimpleInstructionSequence();
private List<BasicBlock> preds = new ArrayList<BasicBlock>();
private List<BasicBlock> succs = new ArrayList<BasicBlock>();
private List<Integer> instrOldOffsets = new ArrayList<Integer>();
private List<BasicBlock> predExceptions = new ArrayList<BasicBlock>();
private List<BasicBlock> succExceptions = new ArrayList<BasicBlock>();
public BasicBlock() {
}
private final List<BasicBlock> preds = new ArrayList<>();
private final List<BasicBlock> succs = new ArrayList<>();
private final List<Integer> instrOldOffsets = new ArrayList<>();
private final List<BasicBlock> predExceptions = new ArrayList<>();
private final List<BasicBlock> succExceptions = new ArrayList<>();
public BasicBlock(int id) {
this.id = id;
@ -62,24 +39,17 @@ public class BasicBlock implements IGraphNode {
// 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.setInstrOldOffsets(new ArrayList<Integer>(instrOldOffsets));
block.instrOldOffsets.addAll(instrOldOffsets);
return block;
}
public void free() {
preds.clear();
succs.clear();
instrOldOffsets.clear();
succExceptions.clear();
seq = new SimpleInstructionSequence();
}
public Instruction getInstruction(int index) {
return seq.getInstr(index);
}
@ -110,7 +80,7 @@ public class BasicBlock implements IGraphNode {
}
public void removePredecessor(BasicBlock block) {
while (preds.remove(block)) ;
while (preds.remove(block)) /**/;
}
public void addSuccessor(BasicBlock block) {
@ -119,11 +89,11 @@ public class BasicBlock implements IGraphNode {
}
public void removeSuccessor(BasicBlock block) {
while (succs.remove(block)) ;
while (succs.remove(block)) /**/;
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) {
for (int i = 0; i < succs.size(); i++) {
if (succs.get(i).id == oldBlock.id) {
@ -147,7 +117,7 @@ public class BasicBlock implements IGraphNode {
}
public void removePredecessorException(BasicBlock block) {
while (predExceptions.remove(block)) ;
while (predExceptions.remove(block)) /**/;
}
public void addSuccessorException(BasicBlock block) {
@ -158,7 +128,7 @@ public class BasicBlock implements IGraphNode {
}
public void removeSuccessorException(BasicBlock block) {
while (succExceptions.remove(block)) ;
while (succExceptions.remove(block)) /**/;
block.removePredecessorException(this);
}
@ -173,27 +143,6 @@ public class BasicBlock implements IGraphNode {
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) {
for (BasicBlock succ : succs) {
if (succ.id == block.id) {
@ -203,15 +152,6 @@ public class BasicBlock implements IGraphNode {
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
// *****************************************************************************
@ -220,12 +160,9 @@ public class BasicBlock implements IGraphNode {
return instrOldOffsets;
}
public void setInstrOldOffsets(List<Integer> instrInds) {
this.instrOldOffsets = instrInds;
}
@Override
public List<? extends IGraphNode> getPredecessors() {
List<BasicBlock> lst = new ArrayList<BasicBlock>(preds);
List<BasicBlock> lst = new ArrayList<>(preds);
lst.addAll(predExceptions);
return lst;
}
@ -234,10 +171,6 @@ public class BasicBlock implements IGraphNode {
return preds;
}
public void setPreds(List<BasicBlock> preds) {
this.preds = preds;
}
public InstructionSequence getSeq() {
return seq;
}
@ -250,25 +183,11 @@ public class BasicBlock implements IGraphNode {
return succs;
}
public void setSuccs(List<BasicBlock> succs) {
this.succs = succs;
}
public List<BasicBlock> getSuccExceptions() {
return succExceptions;
}
public void setSuccExceptions(List<BasicBlock> succExceptions) {
this.succExceptions = succExceptions;
}
public List<BasicBlock> getPredExceptions() {
return predExceptions;
}
public void setPredExceptions(List<BasicBlock> predExceptions) {
this.predExceptions = predExceptions;
}
}
}

View File

@ -1,24 +1,11 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.DataPoint;
@ -47,7 +34,7 @@ public class ControlFlowGraph implements CodeConstants {
private Map<BasicBlock, BasicBlock> subroutines;
private Set<BasicBlock> finallyExits = new HashSet<BasicBlock>();
private final Set<BasicBlock> finallyExits = new HashSet<>();
// *****************************************************************************
// constructors
@ -62,19 +49,6 @@ public class ControlFlowGraph implements CodeConstants {
// public methods
// *****************************************************************************
public void free() {
for (BasicBlock block : blocks) {
block.free();
}
blocks.clear();
first = null;
last = null;
exceptions.clear();
finallyExits.clear();
}
public void removeMarkers() {
for (BasicBlock block : blocks) {
block.mark = 0;
@ -82,6 +56,7 @@ public class ControlFlowGraph implements CodeConstants {
}
public String toString() {
if (blocks == null) return "Empty";
String new_line_separator = DecompilerContext.getNewLineSeparator();
@ -93,12 +68,11 @@ public class ControlFlowGraph implements CodeConstants {
buf.append("----- Edges -----").append(new_line_separator);
List<BasicBlock> suc = block.getSuccs();
for (int j = 0; j < suc.size(); j++) {
buf.append(">>>>>>>>(regular) Block ").append(suc.get(j).id).append(new_line_separator);
for (BasicBlock aSuc : suc) {
buf.append(">>>>>>>>(regular) Block ").append(aSuc.id).append(new_line_separator);
}
suc = block.getSuccExceptions();
for (int j = 0; j < suc.size(); j++) {
BasicBlock handler = suc.get(j);
for (BasicBlock handler : suc) {
ExceptionRangeCFG range = getExceptionRange(handler, block);
if (range == null) {
@ -123,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants {
return buf.toString();
}
public void inlineJsr(StructMethod mt) {
public void inlineJsr(StructClass cl, StructMethod mt) {
processJsr();
removeJsr(mt);
removeJsr(cl, mt);
removeMarkers();
@ -169,13 +143,7 @@ public class ControlFlowGraph implements CodeConstants {
}
}
Iterator<Entry<BasicBlock, BasicBlock>> it = subroutines.entrySet().iterator();
while (it.hasNext()) {
Entry<BasicBlock, BasicBlock> ent = it.next();
if (ent.getKey() == block || ent.getValue() == block) {
it.remove();
}
}
subroutines.entrySet().removeIf(ent -> ent.getKey() == block || ent.getValue() == block);
}
public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) {
@ -224,7 +192,7 @@ public class ControlFlowGraph implements CodeConstants {
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);
blocks = colBlocks;
@ -243,7 +211,7 @@ public class ControlFlowGraph implements CodeConstants {
int len = seq.length();
short[] inststates = new short[len];
Set<Integer> excSet = new HashSet<Integer>();
Set<Integer> excSet = new HashSet<>();
for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) {
excSet.add(handler.from_instr);
@ -255,7 +223,7 @@ public class ControlFlowGraph implements CodeConstants {
for (int i = 0; i < len; i++) {
// exception blocks
if (excSet.contains(new Integer(i))) {
if (excSet.contains(i)) {
inststates[i] = 1;
}
@ -274,7 +242,7 @@ public class ControlFlowGraph implements CodeConstants {
for (int j = dests.length - 1; j >= 0; j--) {
inststates[dests[j]] = 1;
}
inststates[swinstr.getDefaultdest()] = 1;
inststates[swinstr.getDefaultDestination()] = 1;
if (i + 1 < len) {
inststates[i + 1] = 1;
}
@ -292,10 +260,10 @@ public class ControlFlowGraph implements CodeConstants {
InstructionSequence instrseq,
Map<Integer, BasicBlock> mapInstrBlocks) {
VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<BasicBlock, Integer>();
VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<>();
InstructionSequence currseq = null;
ArrayList<Integer> lstOffs = null;
List<Integer> lstOffs = null;
int len = startblock.length;
short counter = 0;
@ -305,14 +273,11 @@ public class ControlFlowGraph implements CodeConstants {
for (int i = 0; i < len; i++) {
if (startblock[i] == 1) {
currentBlock = new BasicBlock();
currentBlock.id = ++counter;
currentBlock = new BasicBlock(++counter);
currseq = new SimpleInstructionSequence();
lstOffs = new ArrayList<Integer>();
currseq = currentBlock.getSeq();
lstOffs = currentBlock.getInstrOldOffsets();
currentBlock.setSeq(currseq);
currentBlock.setInstrOldOffsets(lstOffs);
col.addWithKey(currentBlock, currentBlock.id);
blockoffset = instrseq.getOffset(i);
@ -338,7 +303,7 @@ public class ControlFlowGraph implements CodeConstants {
BasicBlock block = lstbb.get(i);
Instruction instr = block.getLastInstruction();
boolean fallthrough = instr.canFallthrough();
boolean fallthrough = instr.canFallThrough();
BasicBlock bTemp;
switch (instr.group) {
@ -352,10 +317,10 @@ public class ControlFlowGraph implements CodeConstants {
SwitchInstruction sinstr = (SwitchInstruction)instr;
int[] dests = sinstr.getDestinations();
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest());
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultDestination());
block.addSuccessor(bTemp);
for (int j = 0; j < dests.length; j++) {
bTemp = mapInstrBlocks.get(dests[j]);
for (int dest1 : dests) {
bTemp = mapInstrBlocks.get(dest1);
block.addSuccessor(bTemp);
}
}
@ -369,9 +334,9 @@ public class ControlFlowGraph implements CodeConstants {
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()) {
@ -387,7 +352,7 @@ public class ControlFlowGraph implements CodeConstants {
}
else {
List<BasicBlock> protectedRange = new ArrayList<BasicBlock>();
List<BasicBlock> protectedRange = new ArrayList<>();
for (int j = from.id; j < to.id; j++) {
BasicBlock block = blocks.getWithKey(j);
protectedRange.add(block);
@ -396,7 +361,7 @@ public class ControlFlowGraph implements CodeConstants {
ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null
? null
: Arrays.asList(handler.exceptionClass));
: Collections.singletonList(handler.exceptionClass));
mapRanges.put(key, range);
exceptions.add(range);
@ -406,19 +371,19 @@ public class ControlFlowGraph implements CodeConstants {
private void setSubroutineEdges() {
final Map<BasicBlock, BasicBlock> subroutines = new HashMap<BasicBlock, BasicBlock>();
final Map<BasicBlock, BasicBlock> subroutines = new LinkedHashMap<>();
for (BasicBlock block : blocks) {
if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<LinkedList<BasicBlock>>();
LinkedList<BasicBlock> stack = new LinkedList<>();
LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<>();
Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
Set<BasicBlock> setVisited = new HashSet<>();
stack.add(block);
stackJsrStacks.add(new LinkedList<BasicBlock>());
stackJsrStacks.add(new LinkedList<>());
while (!stack.isEmpty()) {
@ -451,7 +416,7 @@ public class ControlFlowGraph implements CodeConstants {
for (BasicBlock succ : node.getSuccs()) {
if (!setVisited.contains(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 Set<BasicBlock> range;
private final BasicBlock ret;
@ -482,7 +447,7 @@ public class ControlFlowGraph implements CodeConstants {
private int processJsrRanges() {
List<JsrRecord> lstJsrAll = new ArrayList<JsrRecord>();
List<JsrRecord> lstJsrAll = new ArrayList<>();
// get all jsr ranges
for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) {
@ -494,7 +459,7 @@ public class ControlFlowGraph implements CodeConstants {
// sort ranges
// FIXME: better sort order
List<JsrRecord> lstJsr = new ArrayList<JsrRecord>();
List<JsrRecord> lstJsr = new ArrayList<>();
for (JsrRecord arr : lstJsrAll) {
int i = 0;
for (; i < lstJsr.size(); i++) {
@ -516,7 +481,7 @@ public class ControlFlowGraph implements CodeConstants {
Set<BasicBlock> set1 = arr1.range;
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);
if (!setc.isEmpty()) {
@ -532,9 +497,9 @@ public class ControlFlowGraph implements CodeConstants {
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);
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) {
List<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
Map<Integer, BasicBlock> mapNewNodes = new HashMap<Integer, BasicBlock>();
List<BasicBlock> lstNodes = new LinkedList<>();
Map<Integer, BasicBlock> mapNewNodes = new HashMap<>();
lstNodes.add(jsr);
mapNewNodes.put(jsr.id, jsr);
@ -633,9 +598,8 @@ public class ControlFlowGraph implements CodeConstants {
node.replaceSuccessor(child, mapNewNodes.get(childid));
}
else if (common_blocks.contains(child)) {
// make a copy of the current block
BasicBlock copy = (BasicBlock)child.clone();
BasicBlock copy = child.clone();
copy.id = ++last_id;
// copy all successors
if (copy.getLastInstruction().opcode == CodeConstants.opc_ret &&
@ -682,14 +646,14 @@ public class ControlFlowGraph implements CodeConstants {
ExceptionRangeCFG range = exceptions.get(i);
List<BasicBlock> lstRange = range.getProtectedRange();
HashSet<BasicBlock> setBoth = new HashSet<BasicBlock>(common_blocks);
HashSet<BasicBlock> setBoth = new HashSet<>(common_blocks);
setBoth.retainAll(lstRange);
if (setBoth.size() > 0) {
List<BasicBlock> lstNewRange;
if (setBoth.size() == lstRange.size()) {
lstNewRange = new ArrayList<BasicBlock>();
lstNewRange = new ArrayList<>();
ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange,
mapNewNodes.get(range.getHandler().id), range.getExceptionTypes());
exceptions.add(newRange);
@ -705,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants {
}
}
private void removeJsr(StructMethod mt) {
removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt));
private void removeJsr(StructClass cl, StructMethod mt) {
removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt));
}
private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {
@ -753,7 +717,7 @@ public class ControlFlowGraph implements CodeConstants {
if (suc.mark != 1) {
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));
removeJsrInstructions(pool, suc, point);
@ -765,9 +729,7 @@ public class ControlFlowGraph implements CodeConstants {
first = blocks.get(0);
last = new BasicBlock();
last.id = ++last_id;
last.setSeq(new SimpleInstructionSequence());
last = new BasicBlock(++last_id);
for (BasicBlock block : blocks) {
if (block.getSuccs().isEmpty()) {
@ -778,18 +740,18 @@ public class ControlFlowGraph implements CodeConstants {
public List<BasicBlock> getReversePostOrder() {
List<BasicBlock> res = new LinkedList<BasicBlock>();
List<BasicBlock> res = new LinkedList<>();
addToReversePostOrderListIterative(first, 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<Integer> stackIndex = new LinkedList<Integer>();
LinkedList<BasicBlock> stackNode = new LinkedList<>();
LinkedList<Integer> stackIndex = new LinkedList<>();
Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
Set<BasicBlock> setVisited = new HashSet<>();
stackNode.add(root);
stackIndex.add(0);
@ -801,7 +763,7 @@ public class ControlFlowGraph implements CodeConstants {
setVisited.add(node);
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(node.getSuccs());
List<BasicBlock> lstSuccs = new ArrayList<>(node.getSuccs());
lstSuccs.addAll(node.getSuccExceptions());
for (; index < lstSuccs.size(); index++) {
@ -834,10 +796,6 @@ public class ControlFlowGraph implements CodeConstants {
return blocks;
}
public void setBlocks(VBStyleCollection<BasicBlock, Integer> blocks) {
this.blocks = blocks;
}
public BasicBlock getFirst() {
return first;
}
@ -846,39 +804,15 @@ public class ControlFlowGraph implements CodeConstants {
this.first = first;
}
public List<BasicBlock> getEndBlocks() {
return last.getPreds();
}
public List<ExceptionRangeCFG> getExceptions() {
return exceptions;
}
public void setExceptions(List<ExceptionRangeCFG> exceptions) {
this.exceptions = exceptions;
}
public BasicBlock getLast() {
return last;
}
public void setLast(BasicBlock last) {
this.last = last;
}
public Map<BasicBlock, BasicBlock> getSubroutines() {
return subroutines;
}
public void setSubroutines(Map<BasicBlock, BasicBlock> subroutines) {
this.subroutines = subroutines;
}
public Set<BasicBlock> getFinallyExits() {
return finallyExits;
}
public void setFinallyExits(HashSet<BasicBlock> finallyExits) {
this.finallyExits = finallyExits;
}
}
}

View File

@ -1,33 +1,15 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code.cfg;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class ExceptionRangeCFG {
private List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); // FIXME: replace with set
private final List<BasicBlock> protectedRange; // FIXME: replace with set
private BasicBlock handler;
private List<String> exceptionTypes;
public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) {
@ -35,7 +17,7 @@ public class ExceptionRangeCFG {
this.handler = handler;
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);
}
@Override
public String toString() {
String new_line_separator = DecompilerContext.getNewLineSeparator();
StringBuilder buf = new StringBuilder();
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("handler: ").append(handler.id).append(new_line_separator);
buf.append("range: ");
for (int i = 0; i < protectedRange.size(); i++) {
buf.append(protectedRange.get(i).id).append(" ");
for (BasicBlock block : protectedRange) {
buf.append(block.id).append(" ");
}
buf.append(new_line_separator);
@ -77,16 +65,11 @@ public class ExceptionRangeCFG {
return protectedRange;
}
public void setProtectedRange(List<BasicBlock> protectedRange) {
this.protectedRange = protectedRange;
}
public List<String> getExceptionTypes() {
return this.exceptionTypes;
}
public void addExceptionType(String exceptionType) {
if (this.exceptionTypes == null) {
return;
}
@ -100,30 +83,6 @@ public class ExceptionRangeCFG {
}
public String getUniqueExceptionsString() {
if (exceptionTypes == null) {
return null;
}
Set<String> setExceptionStrings = new HashSet<String>();
for (String exceptionType : exceptionTypes) { // normalize order
setExceptionStrings.add(exceptionType);
}
String ret = "";
for (String exception : setExceptionStrings) {
if (!ret.isEmpty()) {
ret += ":";
}
ret += exception;
}
return ret;
return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(":")) : null;
}
// public void setExceptionType(String exceptionType) {
// this.exceptionType = exceptionType;
// }
}
}

View File

@ -1,31 +1,18 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code.interpreter;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.DataPoint;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.ListStack;
public class InstructionImpact {
public final class InstructionImpact {
// {read, write}
private static final int[][][] stack_impact = {
@ -338,7 +325,6 @@ public class InstructionImpact {
public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) {
ListStack<VarType> stack = data.getStack();
int[][] arr = stack_impact[instr.opcode];
@ -350,8 +336,7 @@ public class InstructionImpact {
if (read != null) {
int depth = 0;
for (int i = 0; i < read.length; i++) {
int type = read[i];
for (int type : read) {
depth++;
if (type == CodeConstants.TYPE_LONG ||
type == CodeConstants.TYPE_DOUBLE) {
@ -363,8 +348,7 @@ public class InstructionImpact {
}
if (write != null) {
for (int i = 0; i < write.length; i++) {
int type = write[i];
for (int type : write) {
stack.push(new VarType(type));
if (type == CodeConstants.TYPE_LONG ||
type == CodeConstants.TYPE_DOUBLE) {
@ -394,8 +378,8 @@ public class InstructionImpact {
case CodeConstants.opc_ldc:
case CodeConstants.opc_ldc_w:
case CodeConstants.opc_ldc2_w:
cn = pool.getPrimitiveConstant(instr.getOperand(0));
switch (cn.type) {
PooledConstant constant = pool.getConstant(instr.operand(0));
switch (constant.type) {
case CodeConstants.CONSTANT_Integer:
stack.push(new VarType(CodeConstants.TYPE_INT));
break;
@ -416,10 +400,13 @@ public class InstructionImpact {
case CodeConstants.CONSTANT_Class:
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"));
break;
case CodeConstants.CONSTANT_MethodHandle:
stack.push(new VarType(((LinkConstant)constant).descriptor));
break;
}
break;
case CodeConstants.opc_aload:
var1 = data.getVariable(instr.getOperand(0));
var1 = data.getVariable(instr.operand(0));
if (var1 != null) {
stack.push(var1);
}
@ -429,10 +416,10 @@ public class InstructionImpact {
break;
case CodeConstants.opc_aaload:
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;
case CodeConstants.opc_astore:
data.setVariable(instr.getOperand(0), stack.pop());
data.setVariable(instr.operand(0), stack.pop());
break;
case CodeConstants.opc_dup:
case CodeConstants.opc_dup_x1:
@ -454,19 +441,19 @@ public class InstructionImpact {
case CodeConstants.opc_getfield:
stack.pop();
case CodeConstants.opc_getstatic:
ck = pool.getLinkConstant(instr.getOperand(0));
ck = pool.getLinkConstant(instr.operand(0));
var1 = new VarType(ck.descriptor);
stack.push(var1);
if (var1.stack_size == 2) {
if (var1.stackSize == 2) {
stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));
}
break;
case CodeConstants.opc_putfield:
stack.pop();
case CodeConstants.opc_putstatic:
ck = pool.getLinkConstant(instr.getOperand(0));
ck = pool.getLinkConstant(instr.operand(0));
var1 = new VarType(ck.descriptor);
stack.pop(var1.stack_size);
stack.pop(var1.stackSize);
break;
case CodeConstants.opc_invokevirtual:
case CodeConstants.opc_invokespecial:
@ -474,29 +461,27 @@ public class InstructionImpact {
stack.pop();
case CodeConstants.opc_invokestatic:
case CodeConstants.opc_invokedynamic:
if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
ck = pool.getLinkConstant(instr.getOperand(0));
if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
ck = pool.getLinkConstant(instr.operand(0));
MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor);
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) {
stack.push(md.ret);
if (md.ret.stack_size == 2) {
if (md.ret.stackSize == 2) {
stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));
}
}
}
break;
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()));
break;
case CodeConstants.opc_newarray:
stack.pop();
var1 = new VarType(arr_type[instr.getOperand(0) - 4]);
var1.arraydim = 1;
stack.push(var1);
stack.push(new VarType(arr_type[instr.operand(0) - 4], 1).resizeArrayDim(1));
break;
case CodeConstants.opc_athrow:
var1 = stack.pop();
@ -506,17 +491,17 @@ public class InstructionImpact {
case CodeConstants.opc_checkcast:
case CodeConstants.opc_instanceof:
stack.pop();
cn = pool.getPrimitiveConstant(instr.getOperand(0));
cn = pool.getPrimitiveConstant(instr.operand(0));
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));
break;
case CodeConstants.opc_anewarray:
case CodeConstants.opc_multianewarray:
int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.getOperand(1);
int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1);
stack.pop(dimensions);
cn = pool.getPrimitiveConstant(instr.getOperand(0));
cn = pool.getPrimitiveConstant(instr.operand(0));
if (cn.isArray) {
var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString());
var1.arraydim += dimensions;
var1 = var1.resizeArrayDim(var1.arrayDim + dimensions);
stack.push(var1);
}
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -35,13 +21,13 @@ import java.util.ArrayList;
import java.util.Arrays;
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");
public static void buildAssertions(ClassNode node) {
ClassWrapper wrapper = node.wrapper;
ClassWrapper wrapper = node.getWrapper();
StructField field = findAssertionField(node);
@ -67,7 +53,7 @@ public class AssertProcessor {
private static StructField findAssertionField(ClassNode node) {
ClassWrapper wrapper = node.wrapper;
ClassWrapper wrapper = node.getWrapper();
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
@ -89,7 +75,7 @@ public class AssertProcessor {
if (initializer.type == Exprent.EXPRENT_FUNCTION) {
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) {
InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);
@ -101,11 +87,11 @@ public class AssertProcessor {
invexpr.getLstParameters().isEmpty()) {
ConstExprent cexpr = (ConstExprent)invexpr.getInstance();
if (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) {
if (VarType.VARTYPE_CLASS.equals(cexpr.getConstType())) {
ClassNode nd = node;
while (nd != null) {
if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) {
if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) {
break;
}
nd = nd.parent;
@ -157,26 +143,42 @@ public class AssertProcessor {
private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {
boolean throwInIf = true;
Statement ifstat = stat.getIfstat();
InvocationExprent throwError = isAssertionError(ifstat);
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]) {
return false;
}
List<Exprent> lstParams = new ArrayList<Exprent>();
List<Exprent> lstParams = new ArrayList<>();
Exprent ascond = null, retcond = null;
if (exprres[0] != null) {
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT,
Arrays.asList(new Exprent[]{(Exprent)exprres[0]}));
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
if (throwInIf) {
if (exprres[0] != null) {
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
}
}
else {
ascond = (Exprent) exprres[0];
retcond = ascond;
}
lstParams.add(retcond == null ? ascond : retcond);
if (!throwError.getLstParameters().isEmpty()) {
@ -197,13 +199,18 @@ public class AssertProcessor {
first.removeSuccessor(stat.getIfEdge());
first.removeSuccessor(stat.getElseEdge());
List<Statement> lstStatements = new ArrayList<Statement>();
List<Statement> lstStatements = new ArrayList<>();
if (first.getExprents() != null && !first.getExprents().isEmpty()) {
lstStatements.add(first);
}
lstStatements.add(newstat);
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);
@ -214,10 +221,16 @@ public class AssertProcessor {
sequence.getStats().get(i), sequence.getStats().get(i + 1)));
}
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
Statement ifelse = stat.getElsestat();
if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) {
Statement stmts;
if (throwInIf) {
stmts = stat.getElsestat();
}
else {
stmts = stat.getIfstat();
}
List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges();
List<StatEdge> lstSuccs = stmts.getAllSuccessorEdges();
if (!lstSuccs.isEmpty()) {
StatEdge endedge = lstSuccs.get(0);
if (endedge.closure == stat) {
@ -245,9 +258,9 @@ public class AssertProcessor {
if (expr.type == Exprent.EXPRENT_EXIT) {
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();
if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) {
if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewType()) && nexpr.getConstructor() != null) {
return nexpr.getConstructor();
}
}
@ -256,16 +269,21 @@ public class AssertProcessor {
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) {
int desiredOperation = FunctionExprent.FUNCTION_CADD;
if (!throwInIf) {
desiredOperation = FunctionExprent.FUNCTION_COR;
}
FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) {
if (fexpr.getFuncType() == desiredOperation) {
for (int i = 0; i < 2; 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};
}
}
@ -273,7 +291,7 @@ public class AssertProcessor {
for (int i = 0; i < 2; 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 (param != 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;
return new Object[]{null, true};
}
@ -291,20 +309,25 @@ public class AssertProcessor {
return new Object[]{exprent, false};
}
private static boolean isAssertionField(Exprent exprent, String classname, String key) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fparam = (FunctionExprent)exprent;
if (fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT &&
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
if (classname.equals(fdparam.getClassname())
&& key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) {
return true;
private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) {
if (throwInIf) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fparam = (FunctionExprent)exprent;
if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
return classname.equals(fdparam.getClassname()) &&
key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
}
}
}
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;
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -36,79 +22,48 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
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;
public ClassReference14Processor() {
InvocationExprent invfor = new InvocationExprent();
invfor.setName("forName");
invfor.setClassname("java/lang/Class");
invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;");
invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"));
invfor.setStatic(true);
invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)}));
bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN,
invfor,
VarType.VARTYPE_CLASS);
InvocationExprent constr = new InvocationExprent();
constr.setName("<init>");
constr.setClassname("java/lang/NoClassDefFoundError");
constr.setStringDescriptor("()V");
constr.setFunctype(InvocationExprent.TYP_INIT);
constr.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
NewExprent newexpr =
new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>());
newexpr.setConstructor(constr);
InvocationExprent invcause = new InvocationExprent();
invcause.setName("initCause");
invcause.setClassname("java/lang/NoClassDefFoundError");
invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"));
invcause.setInstance(newexpr);
invcause.setLstParameters(
Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)}));
handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW,
invcause,
null);
InvocationExprent ctor = new InvocationExprent();
ctor.setName(CodeConstants.INIT_NAME);
ctor.setClassname("java/lang/NoClassDefFoundError");
ctor.setStringDescriptor("()V");
ctor.setFunctype(InvocationExprent.TYP_INIT);
ctor.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<>(), null);
newExpr.setConstructor(ctor);
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(
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);
}
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;
}
public static void processClassReferences(ClassNode node) {
// 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);
if (mapClassMeths.isEmpty()) {
return;
}
HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>();
Set<ClassWrapper> setFound = new HashSet<>();
processClassRec(node, mapClassMeths, setFound);
if (!setFound.isEmpty()) {
@ -119,29 +74,21 @@ public class ClassReference14Processor {
}
}
private static void processClassRec(ClassNode node,
final HashMap<ClassWrapper, MethodWrapper> mapClassMeths,
final HashSet<ClassWrapper> setFound) {
final ClassWrapper wrapper = node.wrapper;
private static void processClassRec(ClassNode node, Map<ClassWrapper, MethodWrapper> mapClassMeths, Set<? super ClassWrapper> setFound) {
ClassWrapper wrapper = node.getWrapper();
// search code
for (MethodWrapper meth : wrapper.getMethods()) {
RootStatement root = meth.root;
if (root != null) {
DirectGraph graph = meth.getOrBuildGraph();
graph.iterateExprents(new DirectGraph.ExprentIterator() {
public int processExprent(Exprent exprent) {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
setFound.add(ent.getKey());
}
graph.iterateExprents(exprent -> {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
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());
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());
}
}
@ -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);
ClassWrapper wrapper = node.wrapper;
ClassWrapper wrapper = node.getWrapper();
for (MethodWrapper method : wrapper.getMethods()) {
StructMethod mt = method.methodStruct;
@ -190,14 +137,14 @@ public class ClassReference14Processor {
CatchStatement cst = (CatchStatement)root.getFirst();
if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK &&
cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK &&
cst.getVars().get(0).getVartype().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) {
cst.getVars().get(0).getVarType().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) {
BasicBlockStatement body = (BasicBlockStatement)cst.getFirst();
BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1);
if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) {
if (bodyexprent.equals(body.getExprents().get(0)) &&
handlerexprent.equals(handler.getExprents().get(0))) {
if (BODY_EXPR.equals(body.getExprents().get(0)) &&
HANDLER_EXPR.equals(handler.getExprents().get(0))) {
map.put(wrapper, method);
break;
}
@ -213,19 +160,16 @@ public class ClassReference14Processor {
}
}
private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
boolean res = false;
while (true) {
boolean found = false;
for (Exprent expr : exprent.getAllExprents()) {
String cl = isClass14Invocation(expr, wrapper, meth);
if (cl != null) {
exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/')));
exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), expr.bytecode));
found = true;
res = true;
break;
@ -242,18 +186,16 @@ public class ClassReference14Processor {
return res;
}
private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent fexpr = (FunctionExprent)exprent;
if (fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) {
if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) {
if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0);
if (headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) {
if (headexpr.getFuncType() == FunctionExprent.FUNCTION_EQ) {
if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD &&
headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST &&
((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) {
((ConstExprent)headexpr.getLstOperands().get(1)).getConstType().equals(VarType.VARTYPE_NULL)) {
FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0);
ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname());
@ -293,4 +235,4 @@ public class ClassReference14Processor {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,10 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main;
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.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
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.NestedMemberAccess;
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.StructContext;
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.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
public class ClassesProcessor {
public class ClassesProcessor implements CodeConstants {
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) {
this.context = context;
}
HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>();
HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>();
HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>();
HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>();
public void loadClasses(IIdentifierRenamer renamer) {
Map<String, Inner> mapInnerClasses = new HashMap<>();
Map<String, Set<String>> mapNestedClassReferences = new HashMap<>();
Map<String, Set<String>> mapEnclosingClassReferences = new HashMap<>();
Map<String, String> mapNewSimpleNames = new HashMap<>();
boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);
boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES);
// create class nodes
for (StructClass cl : context.getClasses().values()) {
if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {
if (bDecompileInner) {
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses");
StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
if (inner != null) {
for (int i = 0; i < inner.getClassEntries().size(); i++) {
int[] entry = inner.getClassEntries().get(i);
String[] strentry = inner.getStringEntries().get(i);
Object[] arr = new Object[4]; // arr[0] not used
String innername = strentry[0];
// nested class type
arr[2] = entry[1] == 0 ? (entry[2] == 0 ? ClassNode.CLASS_ANONYMOUS : ClassNode.CLASS_LOCAL) : ClassNode.CLASS_MEMBER;
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
String innerName = entry.innerName;
// original simple name
String simpleName = strentry[2];
String savedName = mapNewSimpleNames.get(innername);
String simpleName = entry.simpleName;
String savedName = mapNewSimpleNames.get(innerName);
if (savedName != null) {
simpleName = savedName;
}
else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper();
if (renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) {
simpleName = renamer.getNextClassname(innername, simpleName);
mapNewSimpleNames.put(innername, simpleName);
}
else if (simpleName != null &&
renamer != null &&
renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) {
simpleName = renamer.getNextClassName(innerName, simpleName);
mapNewSimpleNames.put(innerName, simpleName);
}
arr[1] = simpleName;
// original access flags
arr[3] = entry[3];
Inner rec = new Inner();
rec.simpleName = simpleName;
rec.type = entry.simpleNameIdx == 0 ? ClassNode.CLASS_ANONYMOUS : entry.outerNameIdx == 0 ? ClassNode.CLASS_LOCAL : ClassNode.CLASS_MEMBER;
rec.accessFlags = entry.accessFlags;
// enclosing class
String enclClassName;
if (entry[1] != 0) {
enclClassName = strentry[1];
String enclClassName = entry.outerNameIdx != 0 ? entry.enclosingName : cl.qualifiedName;
if (enclClassName == null || innerName.equals(enclClassName)) {
continue; // invalid name or self reference
}
else {
enclClassName = cl.qualifiedName;
if (rec.type == ClassNode.CLASS_MEMBER && !innerName.equals(enclClassName + '$' + entry.simpleName)) {
continue; // not a real inner class
}
if (!innername.equals(enclClassName)) { // self reference
StructClass enclosing_class = context.getClasses().get(enclClassName);
if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only
Object[] arrold = mapInnerClasses.get(innername);
if (arrold == null) {
mapInnerClasses.put(innername, arr);
}
else if (!InterpreterUtil.equalObjectArrays(arrold, arr)) {
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);
StructClass enclosingClass = context.getClasses().get(enclClassName);
if (enclosingClass != null && enclosingClass.isOwn()) { // own classes only
Inner existingRec = mapInnerClasses.get(innerName);
if (existingRec == null) {
mapInnerClasses.put(innerName, rec);
}
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) {
// connect nested classes
for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) {
// root class?
if (!mapInnerClasses.containsKey(ent.getKey())) {
HashSet<String> setVisited = new HashSet<String>();
LinkedList<String> stack = new LinkedList<String>();
Set<String> setVisited = new HashSet<>();
LinkedList<String> stack = new LinkedList<>();
stack.add(ent.getKey());
setVisited.add(ent.getKey());
while (!stack.isEmpty()) {
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) {
StructClass scl = superNode.classStruct;
StructInnerClassesAttribute inner = scl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
StructClass scl = supernode.classStruct;
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses");
for (int i = 0; i < inner.getStringEntries().size(); i++) {
String nestedClass = inner.getStringEntries().get(i)[0];
if (inner == null || inner.getEntries().isEmpty()) {
DecompilerContext.getLogger().writeMessage(superClass + " does not contain inner classes!", IFernflowerLogger.Severity.WARN);
continue;
}
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
String nestedClass = entry.innerName;
if (!setNestedClasses.contains(nestedClass)) {
continue;
}
@ -174,52 +157,49 @@ public class ClassesProcessor {
continue;
}
ClassNode nestednode = mapRootClasses.get(nestedClass);
if (nestednode == null) {
ClassNode nestedNode = mapRootClasses.get(nestedClass);
if (nestedNode == null) {
DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.Severity.WARN);
continue;
}
Object[] arr = mapInnerClasses.get(nestedClass);
Inner rec = mapInnerClasses.get(nestedClass);
//if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) {
// FIXME: check for consistent naming
//}
nestednode.type = (Integer)arr[2];
nestednode.simpleName = (String)arr[1];
nestednode.access = (Integer)arr[3];
nestedNode.simpleName = rec.simpleName;
nestedNode.type = rec.type;
nestedNode.access = rec.accessFlags;
if (nestednode.type == ClassNode.CLASS_ANONYMOUS) {
StructClass cl = nestednode.classStruct;
// sanity checks of the class supposed to be anonymous
if (verifyAnonymousClasses && nestedNode.type == ClassNode.CLASS_ANONYMOUS && !isAnonymous(nestedNode.classStruct, scl)) {
nestedNode.type = ClassNode.CLASS_LOCAL;
}
// remove static if anonymous class
// a common compiler bug
nestednode.access &= ~CodeConstants.ACC_STATIC;
if (nestedNode.type == ClassNode.CLASS_ANONYMOUS) {
StructClass cl = nestedNode.classStruct;
// remove static if anonymous class (a common compiler bug)
nestedNode.access &= ~CodeConstants.ACC_STATIC;
int[] interfaces = cl.getInterfaces();
if (interfaces.length > 0) {
if (interfaces.length > 1) {
String message = "Inconsistent anonymous class definition: " + cl.qualifiedName;
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
}
nestednode.anonymousClassType = new VarType(cl.getInterface(0), true);
nestedNode.anonymousClassType = new VarType(cl.getInterface(0), true);
}
else {
nestednode.anonymousClassType = new VarType(cl.superClass.getString(), true);
nestedNode.anonymousClassType = new VarType(cl.superClass.getString(), true);
}
}
else if (nestednode.type == ClassNode.CLASS_LOCAL) {
// only abstract and final are permitted
// a common compiler bug
nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
else if (nestedNode.type == ClassNode.CLASS_LOCAL) {
// only abstract and final are permitted (a common compiler bug)
nestedNode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
}
supernode.nested.add(nestednode);
nestednode.parent = supernode;
superNode.nested.add(nestedNode);
nestedNode.parent = superNode;
nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
nestedNode.enclosingClasses.addAll(mapEnclosingClassReferences.get(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 {
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.CLASS_ROOT) {
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);
try {
ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.setImportCollector(importCollector);
DecompilerContext.setCounterContainer(new CounterContainer());
DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper());
DecompilerContext.startClass(importCollector);
new LambdaProcessor().processClass(root);
if (packageInfo) {
ClassWriter.packageInfoToJava(cl, buffer);
// add simple class names to implicit import
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);
importCollector.writeImports(buffer, false);
}
else if (moduleInfo) {
TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
ClassWriter.moduleInfoToJava(cl, moduleBuffer);
int import_lines_written = importCollector.writeImports(buffer);
if (import_lines_written > 0) {
buffer.append(lineSeparator);
total_offset_lines += import_lines_written + 1;
importCollector.writeImports(buffer, true);
buffer.append(moduleBuffer);
}
//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)) {
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
mapper.addTotalOffset(total_offset_lines);
// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);
buffer.append(lineSeparator);
mapper.dumpMapping(buffer);
new NestedClassProcessor().processClass(root, root);
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 {
@ -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) {
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) {
imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);
}
for (ClassNode nd : node.nested) {
addClassnameToImport(nd, imp);
addClassNameToImport(nd, imp);
}
}
private static void destroyWrappers(ClassNode node) {
node.wrapper = null;
node.classStruct.releaseResources();
@ -339,7 +410,6 @@ public class ClassesProcessor {
public static class ClassNode {
public static final int CLASS_ROOT = 0;
public static final int CLASS_MEMBER = 1;
public static final int CLASS_ANONYMOUS = 2;
@ -347,30 +417,18 @@ public class ClassesProcessor {
public static final int CLASS_LAMBDA = 8;
public int type;
public int access;
public String simpleName;
public StructClass classStruct;
public ClassWrapper wrapper;
public final StructClass classStruct;
private ClassWrapper wrapper;
public String enclosingMethod;
public InvocationExprent superInvocation;
public HashMap<String, VarVersionPaar> mapFieldsToVars = new HashMap<String, VarVersionPaar>();
public final Map<String, VarVersionPair> mapFieldsToVars = new HashMap<>();
public VarType anonymousClassType;
public List<ClassNode> nested = new ArrayList<ClassNode>();
public Set<String> enclosingClasses = new HashSet<String>();
public final List<ClassNode> nested = new ArrayList<>();
public final Set<String> enclosingClasses = new HashSet<>();
public ClassNode parent;
public LambdaInformation lambda_information;
public LambdaInformation lambdaInformation;
public ClassNode(String content_class_name,
String content_method_name,
@ -383,19 +441,18 @@ public class ClassesProcessor {
this.type = CLASS_LAMBDA;
this.classStruct = classStruct; // 'parent' class containing the static function
lambda_information = new LambdaInformation();
lambdaInformation = new LambdaInformation();
lambda_information.class_name = lambda_class_name;
lambda_information.method_name = lambda_method_name;
lambda_information.method_descriptor = lambda_method_descriptor;
lambdaInformation.method_name = lambda_method_name;
lambdaInformation.method_descriptor = lambda_method_descriptor;
lambda_information.content_class_name = content_class_name;
lambda_information.content_method_name = content_method_name;
lambda_information.content_method_descriptor = content_method_descriptor;
lambda_information.content_method_invocation_type = content_method_invocation_type;
lambdaInformation.content_class_name = content_class_name;
lambdaInformation.content_method_name = content_method_name;
lambdaInformation.content_method_descriptor = content_method_descriptor;
lambdaInformation.content_method_invocation_type = content_method_invocation_type;
lambda_information.content_method_key =
InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor);
lambdaInformation.content_method_key =
InterpreterUtil.makeUniqueKey(lambdaInformation.content_method_name, lambdaInformation.content_method_descriptor);
anonymousClassType = new VarType(lambda_class_name, true);
@ -405,9 +462,9 @@ public class ClassesProcessor {
is_method_reference = !mt.isSynthetic(); // if not synthetic -> method reference
}
lambda_information.is_method_reference = is_method_reference;
lambda_information.is_content_method_static =
(lambda_information.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
lambdaInformation.is_method_reference = is_method_reference;
lambdaInformation.is_content_method_static =
(lambdaInformation.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
}
public ClassNode(int type, StructClass classStruct) {
@ -426,8 +483,15 @@ public class ClassesProcessor {
return null;
}
public ClassWrapper getWrapper() {
ClassNode node = this;
while (node.type == CLASS_LAMBDA) {
node = node.parent;
}
return node.wrapper;
}
public static class LambdaInformation {
public String class_name;
public String method_name;
public String method_descriptor;
@ -435,11 +499,10 @@ public class ClassesProcessor {
public String content_method_name;
public String content_method_descriptor;
public int content_method_invocation_type; // values from CONSTANT_MethodHandle_REF_*
public String content_method_key;
public boolean is_method_reference;
public boolean is_content_method_static;
}
}
}
}

View File

@ -1,64 +1,57 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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;
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.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
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.struct.StructContext;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
public class DecompilerContext {
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_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_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR";
private static ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>();
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 VarNamesCollector varNamescollector;
private VarProcessor varProcessor;
private CounterContainer counterContainer;
private ClassesProcessor classProcessor;
private PoolInterceptor poolInterceptor;
private IFernflowerLogger logger;
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.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);
if (propertiesCustom != null) {
properties.putAll(propertiesCustom);
}
currentContext.set(new DecompilerContext(properties));
}
// *****************************************************************************
// context setup and update
// *****************************************************************************
private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
public static DecompilerContext getCurrentContext() {
return currentContext.get();
@ -68,93 +61,69 @@ public class DecompilerContext {
currentContext.set(context);
}
public static Object getProperty(String key) {
return getCurrentContext().properties.get(key);
}
public static void setProperty(String key, Object value) {
getCurrentContext().properties.put(key, value);
}
public static 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) {
return "1".equals(getCurrentContext().properties.get(key));
return "1".equals(getProperty(key));
}
public static ImportCollector getImportCollector() {
return getCurrentContext().importCollector;
}
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 String getNewLineSeparator() {
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
IFernflowerPreferences.LINE_SEPARATOR_UNX : IFernflowerPreferences.LINE_SEPARATOR_WIN;
}
public static IFernflowerLogger getLogger() {
return getCurrentContext().logger;
}
public static void setLogger(IFernflowerLogger logger) {
if (logger != null) {
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 StructContext getStructContext() {
return getCurrentContext().structContext;
}
public static String getNewLineSeparator() {
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN;
public static ClassesProcessor getClassProcessor() {
return getCurrentContext().classProcessor;
}
}
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
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.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.vars.VarVersionPaar;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;
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.util.InterpreterUtil;
public class EnumProcessor {
public final class EnumProcessor {
public static void clearEnum(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
@ -48,13 +33,13 @@ public class EnumProcessor {
wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor));
}
}
else if ("<init>".equals(name)) {
Statement firstData = findFirstData(method.root);
else if (CodeConstants.INIT_NAME.equals(name)) {
Statement firstData = Statements.findFirstData(method.root);
if (firstData != null && !firstData.getExprents().isEmpty()) {
Exprent exprent = firstData.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if (isInvocationSuperConstructor(invexpr, method, wrapper)) {
InvocationExprent invExpr = (InvocationExprent)exprent;
if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) {
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
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.main.extern.*;
import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper;
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.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext;
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;
public class Fernflower implements IDecompiledData {
private final StructContext structContext;
private final ClassesProcessor classProcessor;
private final IIdentifierRenamer helper;
private final IdentifierConverter converter;
private StructContext structContext;
private ClassesProcessor classesProcessor;
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> customProperties, IFernflowerLogger logger) {
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));
DecompilerContext.initContext(options);
DecompilerContext.setCounterContainer(new CounterContainer());
DecompilerContext.setLogger(logger);
classProcessor = new ClassesProcessor(structContext);
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() {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
new IdentifierConverter().rename(structContext);
if (converter != null) {
converter.rename();
}
classesProcessor = new ClassesProcessor(structContext);
DecompilerContext.setClassProcessor(classesProcessor);
DecompilerContext.setStructContext(structContext);
classProcessor.loadClasses(helper);
structContext.saveContext();
}
@ -58,24 +91,18 @@ public class Fernflower implements IDecompiledData {
DecompilerContext.setCurrentContext(null);
}
public StructContext getStructContext() {
return structContext;
}
@Override
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) {
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 {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);
return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simple_classname + ".java";
}
else {
return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java";
}
return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java";
}
}
@ -84,12 +111,12 @@ public class Fernflower implements IDecompiledData {
try {
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());
classesProcessor.writeClass(cl, buffer);
return org.spigotmc.fernflower.EclipseFormatter.format(buffer.toString()); // Spigot
classProcessor.writeClass(cl, buffer);
return buffer.toString();
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex);
catch (Throwable t) {
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t);
return null;
}
}
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.main;
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.stats.RootStatement;
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.StructField;
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.List;
public class InitializerProcessor {
public final class InitializerProcessor {
public static void extractInitializers(ClassWrapper wrapper) {
MethodWrapper meth = wrapper.getMethodWrapper("<clinit>", "()V");
if (meth != null && meth.root != null) { // successfully decompiled static constructor
extractStaticInitializers(wrapper, meth);
MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V");
if (method != null && method.root != null) { // successfully decompiled static constructor
extractStaticInitializers(wrapper, method);
}
extractDynamicInitializers(wrapper);
@ -52,30 +36,26 @@ public class InitializerProcessor {
}
}
private static void liftConstructor(ClassWrapper wrapper) {
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if (firstdata == null) {
for (MethodWrapper method : wrapper.getMethods()) {
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
Statement firstData = Statements.findFirstData(method.root);
if (firstData == null) {
return;
}
int index = 0;
List<Exprent> lstExprents = firstdata.getExprents();
List<Exprent> lstExprents = firstData.getExprents();
for (Exprent exprent : lstExprents) {
int action = 0;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString);
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD && assignExpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (fExpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
StructField structField = wrapper.getClassStruct().getField(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) {
action = 1;
}
@ -83,7 +63,7 @@ public class InitializerProcessor {
}
}
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()
lstExprents.add(0, lstExprents.remove(index));
action = 2;
@ -99,52 +79,50 @@ public class InitializerProcessor {
}
}
private static void hideEmptySuper(ClassWrapper wrapper) {
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) {
Statement firstdata = findFirstData(meth.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) {
for (MethodWrapper method : wrapper.getMethods()) {
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
Statement firstData = Statements.findFirstData(method.root);
if (firstData == null || firstData.getExprents().isEmpty()) {
return;
}
Exprent exprent = firstdata.getExprents().get(0);
Exprent exprent = firstData.getExprents().get(0);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)exprent;
if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) {
firstdata.getExprents().remove(0);
InvocationExprent invExpr = (InvocationExprent)exprent;
if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false) && invExpr.getLstParameters().isEmpty()) {
firstData.getExprents().remove(0);
}
}
}
}
}
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) {
RootStatement root = meth.root;
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper method) {
RootStatement root = method.root;
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);
if (firstdata != null) {
while (!firstdata.getExprents().isEmpty()) {
Exprent exprent = firstdata.getExprents().get(0);
while (!firstData.getExprents().isEmpty()) {
Exprent exprent = firstData.getExprents().get(0);
boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) {
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) {
if (true || isExprentIndependent(asexpr.getRight(), meth)) { // Spigot
String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
// interfaces fields should always be initialized inline
if (inlineInitializers || isExprentIndependent(assignExpr.getRight(), method)) {
String keyField = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) {
wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField);
firstdata.getExprents().remove(0);
wrapper.getStaticFieldInitializers().addWithKey(assignExpr.getRight(), keyField);
firstData.getExprents().remove(0);
found = true;
}
}
@ -160,27 +138,26 @@ public class InitializerProcessor {
}
private static void extractDynamicInitializers(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();
boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;
List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>();
List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>();
List<List<Exprent>> lstFirst = new ArrayList<>();
List<MethodWrapper> lstMethodWrappers = new ArrayList<>();
for (MethodWrapper meth : wrapper.getMethods()) {
if ("<init>".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor
Statement firstdata = findFirstData(meth.root);
if (firstdata == null || firstdata.getExprents().isEmpty()) {
for (MethodWrapper method : wrapper.getMethods()) {
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { // successfully decompiled constructor
Statement firstData = Statements.findFirstData(method.root);
if (firstData == null || firstData.getExprents().isEmpty()) {
return;
}
lstFirst.add(firstdata.getExprents());
lstMethWrappers.add(meth);
lstFirst.add(firstData.getExprents());
lstMethodWrappers.add(method);
Exprent exprent = firstdata.getExprents().get(0);
Exprent exprent = firstData.getExprents().get(0);
if (!isAnonymous) { // FIXME: doesn't make sense
if (exprent.type != Exprent.EXPRENT_INVOCATION ||
!isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) {
!Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, false)) {
return;
}
}
@ -192,12 +169,10 @@ public class InitializerProcessor {
}
while (true) {
String fieldWithDescr = null;
Exprent value = null;
for (int i = 0; i < lstFirst.size(); i++) {
List<Exprent> lst = lstFirst.get(i);
if (lst.size() < (isAnonymous ? 1 : 2)) {
@ -209,22 +184,21 @@ public class InitializerProcessor {
boolean found = false;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asexpr = (AssignmentExprent)exprent;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fexpr.getName(), fexpr
.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
if (!fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) {
String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
if (isExprentIndependent(assignExpr.getRight(), lstMethodWrappers.get(i))) {
String fieldKey = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (fieldWithDescr == null) {
fieldWithDescr = fieldKey;
value = asexpr.getRight();
value = assignExpr.getRight();
}
else {
if (!fieldWithDescr.equals(fieldKey) ||
!value.equals(asexpr.getRight())) {
!value.equals(assignExpr.getRight())) {
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);
lst.add(exprent);
for (Exprent expr : lst) {
switch (expr.type) {
case Exprent.EXPRENT_VAR:
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr);
if (!meth.varproc.getExternvars().contains(varpaar)) {
String varname = meth.varproc.getVarName(varpaar);
if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings
VarVersionPair varPair = new VarVersionPair((VarExprent)expr);
if (!method.varproc.getExternalVars().contains(varPair)) {
String varName = method.varproc.getVarName(varPair);
if (!varName.equals("this") && !varName.endsWith(".this")) { // FIXME: remove direct comparison with strings
return false;
}
}
@ -276,48 +248,4 @@ public class InitializerProcessor {
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;
import java.util.HashMap;
import org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
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
private HashMap<Integer, Integer> mapping = new HashMap<Integer, Integer>();
public BytecodeMappingTracer() {}
public BytecodeMappingTracer() { }
public BytecodeMappingTracer(int initial_source_line) {
current_sourceline = initial_source_line;
currentSourceLine = initial_source_line;
}
public void incrementCurrentSourceLine() {
current_sourceline++;
currentSourceLine++;
}
public void incrementCurrentSourceLine(int number_lines) {
current_sourceline += number_lines;
}
public void shiftSourceLines(int shift) {
for(Entry<Integer, Integer> entry : mapping.entrySet()) {
entry.setValue(entry.getValue() + shift);
}
currentSourceLine += number_lines;
}
public void addMapping(int bytecode_offset) {
if(!mapping.containsKey(bytecode_offset)) {
mapping.put(bytecode_offset, current_sourceline);
}
mapping.putIfAbsent(bytecode_offset, currentSourceLine);
}
public void addMapping(Set<Integer> bytecode_offsets) {
if(bytecode_offsets != null) {
for(Integer bytecode_offset : bytecode_offsets) {
if (bytecode_offsets != null) {
for (Integer bytecode_offset : bytecode_offsets) {
addMapping(bytecode_offset);
}
}
}
public void addTracer(BytecodeMappingTracer tracer) {
if(tracer != null) {
for(Entry<Integer, Integer> entry : tracer.mapping.entrySet()) {
if(!mapping.containsKey(entry.getKey())) {
mapping.put(entry.getKey(), entry.getValue());
}
if (tracer != null) {
for (Entry<Integer, Integer> entry : tracer.mapping.entrySet()) {
mapping.putIfAbsent(entry.getKey(), entry.getValue());
}
}
}
public HashMap<Integer, Integer> getMapping() {
public Map<Integer, Integer> getMapping() {
return mapping;
}
public int getCurrentSourceLine() {
return current_sourceline;
return currentSourceLine;
}
public void setCurrentSourceLine(int current_sourceline) {
this.current_sourceline = current_sourceline;
public void setCurrentSourceLine(int currentSourceLine) {
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;
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;
public class BytecodeSourceMapper {
private int offset_total;
// 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);
if(class_mapping == null) {
mapping.put(classname, class_mapping = new HashMap<String, HashMap<Integer, Integer>>());
}
HashMap<Integer, Integer> method_mapping = class_mapping.get(methodname);
if(method_mapping == null) {
class_mapping.put(methodname, method_mapping = new HashMap<Integer, Integer>());
}
public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) {
Map<String, Map<Integer, Integer>> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order
Map<Integer, Integer> method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>());
// don't overwrite
if(!method_mapping.containsKey(bytecode_offset)) {
method_mapping.put(bytecode_offset, source_line);
}
method_mapping.putIfAbsent(bytecodeOffset, sourceLine);
}
public void addTracer(String classname, String methodname, BytecodeMappingTracer tracer) {
for(Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
addMapping(classname, methodname, entry.getKey(), entry.getValue());
public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) {
for (Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
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();
for(Entry<String, HashMap<String, HashMap<Integer, Integer>>> class_entry : mapping.entrySet()) {
HashMap<String, HashMap<Integer, Integer>> class_mapping = class_entry.getValue();
buffer.append("class " + class_entry.getKey() + "{" + lineSeparator);
for (Entry<String, Map<String, Map<Integer, Integer>>> class_entry : mapping.entrySet()) {
Map<String, Map<Integer, Integer>> class_mapping = class_entry.getValue();
buffer.append("class '" + class_entry.getKey() + "' {" + lineSeparator);
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()) {
HashMap<Integer, Integer> method_mapping = method_entry.getValue();
if(!is_first_method) {
if (!is_first_method) {
buffer.appendLineSeparator();
}
buffer.appendIndent(1).append("method " + method_entry.getKey() + "{" + lineSeparator);
for(Entry<Integer, Integer> line : method_mapping.entrySet()) {
buffer.appendIndent(2).append(line.getKey().toString()).appendIndent(2).append((line.getValue() + offset_total) + lineSeparator);
buffer.appendIndent(1).append("method '" + method_entry.getKey() + "' {" + 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();
is_first_method = false;
}
buffer.append("}").appendLineSeparator();
buffer.append("}").appendLineSeparator().appendLineSeparator();
}
}
public int getTotalOffset() {
return offset_total;
}
// lines mapping
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) {
this.offset_total = offset_total;
if (!unmappedLines.isEmpty()) {
buffer.append("Not mapped:").appendLineSeparator();
for (Integer line : unmappedLines) {
if (!linesMapping.containsKey(line)) {
buffer.append(line).appendLineSeparator();
}
}
}
}
public void addTotalOffset(int 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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main.collectors;
public class CounterContainer {
public static final int STATEMENT_COUNTER = 0;
public static final int EXPRENT_COUNTER = 1;
public static final int EXPRESSION_COUNTER = 1;
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) {
values[counter] = value;
@ -34,4 +19,4 @@ public class CounterContainer {
public int getCounterAndIncrement(int counter) {
return values[counter]++;
}
}
}

View File

@ -1,155 +1,186 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
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.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.StructField;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class ImportCollector {
private static final String JAVA_LANG_PACKAGE = "java.lang";
private Map<String, String> mapSimpleNames = new HashMap<String, String>();
private Set<String> setNotImportedNames = new HashSet<String>();
private String currentPackageSlash = "";
private String currentPackagePoint = "";
private final Map<String, String> mapSimpleNames = new HashMap<>();
private final Set<String> setNotImportedNames = new HashSet<>();
// set of field names in this class and all its predecessors.
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) {
String clname = root.classStruct.qualifiedName;
int index = clname.lastIndexOf("/");
String clName = root.classStruct.qualifiedName;
int index = clName.lastIndexOf('/');
if (index >= 0) {
currentPackageSlash = clname.substring(0, index);
currentPackagePoint = currentPackageSlash.replace('/', '.');
currentPackageSlash += "/";
String packageName = clName.substring(0, index);
currentPackageSlash = packageName + '/';
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();
ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/'));
String retname = null;
public String getShortName(String fullName, boolean imported) {
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/')); //todo[r.sh] anonymous classes?
String result = null;
if (node != null && node.classStruct.isOwn()) {
retname = node.simpleName;
result = node.simpleName;
while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
retname = node.parent.simpleName + "." + retname;
//noinspection StringConcatenationInLoop
result = node.parent.simpleName + '.' + result;
node = node.parent;
}
if (node.type == ClassNode.CLASS_ROOT) {
fullname = node.classStruct.qualifiedName;
fullname = fullname.replace('/', '.');
fullName = node.classStruct.qualifiedName;
fullName = fullName.replace('/', '.');
}
else {
return retname;
return result;
}
}
else {
fullname = fullname.replace('$', '.');
fullName = fullName.replace('$', '.');
}
String nshort = fullname;
String npackage = "";
String shortName = fullName;
String packageName = "";
int lastpoint = fullname.lastIndexOf(".");
if (lastpoint >= 0) {
nshort = fullname.substring(lastpoint + 1);
npackage = fullname.substring(0, lastpoint);
int lastDot = fullName.lastIndexOf('.');
if (lastDot >= 0) {
shortName = fullName.substring(lastDot + 1);
packageName = fullName.substring(0, lastDot);
}
StructContext context = DecompilerContext.getStructContext();
boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null
&& !npackage.equals(currentPackagePoint)) // current package
|| (context.getClass(nshort) != null); // default package
// check for another class which could 'shadow' this one. Three cases:
// 1) class with the same short name in the current 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 ||
(mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) {
return fullname;
(mapSimpleNames.containsKey(shortName) && !packageName.equals(mapSimpleNames.get(shortName)))) {
// 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)) {
mapSimpleNames.put(nshort, npackage);
else if (!mapSimpleNames.containsKey(shortName)) {
mapSimpleNames.put(shortName, packageName);
if (!imported) {
setNotImportedNames.add(nshort);
setNotImportedNames.add(shortName);
}
}
return retname == null ? nshort : retname;
return result == null ? shortName : result;
}
public int writeImports(TextBuffer buffer) {
int importlines_written = 0;
String new_line_separator = DecompilerContext.getNewLineSeparator();
public void writeImports(TextBuffer buffer, boolean addSeparator) {
List<String> imports = packImports();
for (String s : imports) {
buffer.append("import ");
buffer.append(s);
buffer.append(";");
buffer.append(new_line_separator);
importlines_written++;
for (String line : imports) {
buffer.append("import ").append(line).append(';').appendLineSeparator();
}
if (addSeparator && !imports.isEmpty()) {
buffer.appendLineSeparator();
}
return importlines_written;
}
private List<String> packImports() {
List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet());
Collections.sort(lst, new Comparator<Entry<String, String>>() {
public int compare(Entry<String, String> par0, Entry<String, String> par1) {
int res = par0.getValue().compareTo(par1.getValue());
if (res == 0) {
res = par0.getKey().compareTo(par1.getKey());
}
return res;
}
});
List<String> res = new ArrayList<String>();
for (Entry<String, String> ent : lst) {
// 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;
return mapSimpleNames.entrySet().stream()
.filter(ent ->
// exclude the current class or one of the nested ones
// empty, java.lang and the current packages
!setNotImportedNames.contains(ent.getKey()) &&
!ent.getValue().isEmpty() &&
!JAVA_LANG_PACKAGE.equals(ent.getValue()) &&
!ent.getValue().equals(currentPackagePoint)
)
.sorted(Map.Entry.<String, String>comparingByValue().thenComparing(Map.Entry.comparingByKey()))
.map(ent -> ent.getValue() + "." + ent.getKey())
.collect(Collectors.toList());
}
}

View File

@ -1,29 +1,20 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
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.HashSet;
import java.util.Set;
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) {
usedNames.addAll(setNames);
@ -33,20 +24,140 @@ public class VarNamesCollector {
usedNames.add(value);
}
public String getFreeName(int index) {
return getFreeName("var" + index);
}
public String getFreeName(String proposition) {
while (usedNames.contains(proposition)) {
proposition += "x";
public static String removePrefix(String str, String prefix)
{
if (str.toLowerCase().startsWith(prefix))
{
return str.substring(prefix.length());
}
else
{
return str;
}
usedNames.add(proposition);
return proposition;
}
public HashSet<String> getUsedNames() {
return usedNames;
public String getTypeLabel(VarType type, int index) {
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.decompiler;
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 java.io.File;
import java.io.IOException;
import java.util.Map;
@SuppressWarnings("unused")
public class BaseDecompiler {
private final Fernflower fernflower;
private final Fernflower engine;
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 {
fernflower.getStructContext().addSpace(file, isOwn);
public void addSource(File source) {
engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
}
public void decompileContext() {
try {
fernflower.decompileContext();
engine.decompileContext();
}
finally {
fernflower.clearContext();
engine.clearContext();
}
}
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.decompiler;
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 java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
@ -31,7 +18,6 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public static void main(String[] args) {
if (args.length < 2) {
@ -41,21 +27,20 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return;
}
Map<String, Object> mapOptions = new HashMap<String, Object>();
List<String> lstSources = new ArrayList<String>();
List<String> lstLibraries = new ArrayList<String>();
Map<String, Object> mapOptions = new HashMap<>();
List<File> sources = new ArrayList<>();
List<File> libraries = new ArrayList<>();
boolean isOption = true;
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
String arg = args[i];
if (isOption && arg.startsWith("-") &&
arg.length() > 5 && arg.charAt(4) == '=') {
String value = arg.substring(5).toUpperCase(Locale.US);
if ("TRUE".equals(value)) {
if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') {
String value = arg.substring(5);
if ("true".equalsIgnoreCase(value)) {
value = "1";
}
else if ("FALSE".equals(value)) {
else if ("false".equalsIgnoreCase(value)) {
value = "0";
}
@ -65,15 +50,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
isOption = false;
if (arg.startsWith("-e=")) {
lstLibraries.add(arg.substring(3));
addPath(libraries, arg.substring(3));
}
else {
lstSources.add(arg);
addPath(sources, arg);
}
}
}
if (lstSources.isEmpty()) {
if (sources.isEmpty()) {
System.out.println("error: no sources given");
return;
}
@ -87,45 +72,55 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
PrintStreamLogger logger = new PrintStreamLogger(System.out);
ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);
for (String source : lstSources) {
decompiler.addSpace(new File(source), true);
for (File library : libraries) {
decompiler.addLibrary(library);
}
for (String library : lstLibraries) {
decompiler.addSpace(new File(library), false);
for (File source : sources) {
decompiler.addSource(source);
}
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
// *******************************************************************
private final File root;
private final Fernflower fernflower;
private Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>();
private Map<String, Set<String>> mapArchiveEntries = new HashMap<String, Set<String>>();
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public ConsoleDecompiler(File destination, Map<String, Object> options) {
this(destination, options, new PrintStreamLogger(System.out));
}
private final Fernflower engine;
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
root = destination;
fernflower = new Fernflower(this, this, options, logger);
engine = new Fernflower(this, this, options, logger);
}
public void addSpace(File file, boolean isOwn) {
fernflower.getStructContext().addSpace(file, isOwn);
public void addSource(File source) {
engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
}
public void decompileContext() {
try {
fernflower.decompileContext();
engine.decompileContext();
}
finally {
fernflower.clearContext();
engine.clearContext();
}
}
@ -140,17 +135,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return InterpreterUtil.getBytes(file);
}
else {
ZipFile archive = new ZipFile(file);
try {
try (ZipFile archive = new ZipFile(file)) {
ZipEntry entry = archive.getEntry(internalPath);
if (entry == null) {
throw new IOException("Entry not found: " + internalPath);
}
if (entry == null) throw new IOException("Entry not found: " + internalPath);
return InterpreterUtil.getBytes(archive, entry);
}
finally {
archive.close();
}
}
}
@ -181,16 +170,10 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
}
@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);
try {
Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8");
try {
out.write(content);
}
finally {
out.close();
}
try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
out.write(content);
}
catch (IOException 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);
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream);
mapArchiveStreams.put(file.getPath(), zipStream);
}
@ -228,21 +210,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return;
}
try {
ZipFile srcArchive = new ZipFile(new File(source));
try {
ZipEntry entry = srcArchive.getEntry(entryName);
if (entry != null) {
InputStream in = srcArchive.getInputStream(entry);
try (ZipFile srcArchive = new ZipFile(new File(source))) {
ZipEntry entry = srcArchive.getEntry(entryName);
if (entry != null) {
try (InputStream in = srcArchive.getInputStream(entry)) {
ZipOutputStream out = mapArchiveStreams.get(file);
out.putNextEntry(new ZipEntry(entryName));
InterpreterUtil.copyStream(in, out);
in.close();
}
}
finally {
srcArchive.close();
}
}
catch (IOException ex) {
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);
out.putNextEntry(new ZipEntry(entryName));
if (content != null) {
out.write(content.getBytes("UTF-8"));
out.write(content.getBytes(StandardCharsets.UTF_8));
}
}
catch (IOException ex) {
@ -272,10 +248,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
}
private boolean checkEntry(String entryName, String file) {
Set<String> set = mapArchiveEntries.get(file);
if (set == null) {
mapArchiveEntries.put(file, set = new HashSet<String>());
}
Set<String> set = mapArchiveEntries.computeIfAbsent(file, k -> new HashSet<>());
boolean added = set.add(entryName);
if (!added) {
@ -296,4 +269,4 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN);
}
}
}
}

View File

@ -1,22 +1,8 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.decompiler;
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;
@ -33,62 +19,79 @@ public class PrintStreamLogger extends IFernflowerLogger {
@Override
public void writeMessage(String message, Severity severity) {
if (accepts(severity)) {
stream.println(severity.prefix + InterpreterUtil.getIndentString(indent) + message);
stream.println(severity.prefix + TextUtil.getIndentString(indent) + message);
}
}
@Override
public void writeMessage(String message, Throwable t) {
writeMessage(message, Severity.ERROR);
if (accepts(Severity.ERROR)) {
public void writeMessage(String message, Severity severity, Throwable t) {
if (accepts(severity)) {
writeMessage(message, severity);
t.printStackTrace(stream);
}
}
@Override
public void startReadingClass(String className) {
writeMessage("Decompiling class " + className, Severity.INFO);
++indent;
if (accepts(Severity.INFO)) {
writeMessage("Decompiling class " + className, Severity.INFO);
++indent;
}
}
@Override
public void endReadingClass() {
--indent;
writeMessage("... done", Severity.INFO);
if (accepts(Severity.INFO)) {
--indent;
writeMessage("... done", Severity.INFO);
}
}
@Override
public void startClass(String className) {
writeMessage("Processing class " + className, Severity.TRACE);
++indent;
if (accepts(Severity.INFO)) {
writeMessage("Processing class " + className, Severity.TRACE);
++indent;
}
}
@Override
public void endClass() {
--indent;
writeMessage("... proceeded", Severity.TRACE);
if (accepts(Severity.INFO)) {
--indent;
writeMessage("... proceeded", Severity.TRACE);
}
}
@Override
public void startMethod(String methodName) {
writeMessage("Processing method " + methodName, Severity.TRACE);
++indent;
if (accepts(Severity.INFO)) {
writeMessage("Processing method " + methodName, Severity.TRACE);
++indent;
}
}
@Override
public void endMethod() {
--indent;
writeMessage("... proceeded", Severity.TRACE);
if (accepts(Severity.INFO)) {
--indent;
writeMessage("... proceeded", Severity.TRACE);
}
}
@Override
public void startWriteClass(String className) {
writeMessage("Writing class " + className, Severity.TRACE);
++indent;
if (accepts(Severity.INFO)) {
writeMessage("Writing class " + className, Severity.TRACE);
++indent;
}
}
@Override
public void endWriteClass() {
--indent;
writeMessage("... written", Severity.TRACE);
if (accepts(Severity.INFO)) {
--indent;
writeMessage("... written", Severity.TRACE);
}
}
}

View File

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

View File

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

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.extern;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
@ -31,6 +17,7 @@ public interface IFernflowerPreferences {
String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
String DECOMPILE_GENERIC_SIGNATURES = "dgs";
String NO_EXCEPTIONS_RETURN = "ner";
String ENSURE_SYNCHRONIZED_MONITOR = "esm";
String DECOMPILE_ENUM = "den";
String REMOVE_GET_CLASS_NEW = "rgn";
String LITERALS_AS_IS = "lit";
@ -39,13 +26,14 @@ public interface IFernflowerPreferences {
String SYNTHETIC_NOT_SET = "nns";
String UNDEFINED_PARAM_TYPE_OBJECT = "uto";
String USE_DEBUG_VAR_NAMES = "udv";
String USE_METHOD_PARAMETERS = "ump";
String REMOVE_EMPTY_RANGES = "rer";
String FINALLY_DEINLINE = "fdi";
String IDEA_NOT_NULL_ANNOTATION = "inn";
String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
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 MAX_PROCESSING_METHOD = "mpm";
@ -53,44 +41,55 @@ public interface IFernflowerPreferences {
String USER_RENAMER_CLASS = "urc";
String NEW_LINE_SEPARATOR = "nls";
String INDENT_STRING = "ind";
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_LIN = "\n";
String LINE_SEPARATOR_UNX = "\n";
Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>() {{
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");
Map<String, Object> DEFAULTS = getDefaults();
put(BYTECODE_SOURCE_MAPPING, "0");
put(USE_DEBUG_LINE_NUMBERS, "0");
static Map<String, Object> getDefaults() {
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());
put(MAX_PROCESSING_METHOD, "0");
put(RENAME_ENTITIES, "0");
put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1"));
put(INDENT_STRING, " ");
}});
}
defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name());
defaults.put(MAX_PROCESSING_METHOD, "0");
defaults.put(RENAME_ENTITIES, "0");
defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1"));
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main.extern;
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 getNextClassname(String fullname, String shortname);
String getNextFieldname(String classname, String field, String descriptor);
String getNextMethodname(String classname, String method, String descriptor);
String getNextMethodName(String className, String method, String descriptor);
}

View File

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

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main.rels;
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.IFernflowerPreferences;
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.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.StructField;
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.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ClassWrapper {
private StructClass classStruct;
private Set<String> hiddenMembers = new HashSet<String>();
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
private final StructClass classStruct;
private final Set<String> hiddenMembers = new HashSet<>();
private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<>();
private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<>();
private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<>();
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
public void init() throws IOException {
public void init() {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
// collect field names
HashSet<String> setFieldNames = new HashSet<String>();
for (StructField fd : classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
int maxsec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);
for (StructMethod mt : classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarNamesCollector(vc);
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
VarProcessor varProc = new VarProcessor(mt, md);
DecompilerContext.startMethod(varProc);
CounterContainer counter = new CounterContainer();
DecompilerContext.setCounterContainer(counter);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
VarProcessor varproc = new VarProcessor();
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc);
VarNamesCollector vc = varProc.getVarNamesCollector();
CounterContainer counter = DecompilerContext.getCounterContainer();
RootStatement root = null;
@ -87,86 +58,113 @@ public class ClassWrapper {
try {
if (mt.containsCode()) {
if (maxsec == 0) { // blocking wait
root = MethodProcessorThread.codeToJava(mt, varproc);
if (maxSec == 0 || testMode) {
root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);
}
else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext());
Thread mtthread = new Thread(mtproc);
long stopAt = System.currentTimeMillis() + maxsec * 1000;
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext());
mtthread.start();
Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000L;
while (mtthread.isAlive()) {
mtThread.start();
synchronized (mtproc.lock) {
mtproc.lock.wait(100);
while (!mtProc.isFinished()) {
try {
synchronized (mtProc.lock) {
mtProc.lock.wait(200);
}
}
catch (InterruptedException e) {
killThread(mtThread);
throw e;
}
if (System.currentTimeMillis() >= stopAt) {
String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR);
killThread(mtthread);
killThread(mtThread);
isError = true;
break;
}
}
if (!isError) {
root = mtproc.getResult();
root = mtProc.getResult();
}
}
}
else {
boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
int paramcount = 0;
if (thisvar) {
varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName);
paramcount = 1;
int paramCount = 0;
if (thisVar) {
varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName);
paramCount = 1;
}
paramcount += md.params.length;
paramCount += md.params.length;
int varindex = 0;
for (int i = 0; i < paramcount; i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0, classStruct.qualifiedName, false), vc.getFreeName(varindex));
int varIndex = 0;
for (int i = 0; i < paramCount; i++) {
varProc.setVarName(new VarVersionPair(varIndex, 0), "tbd");//, vc.getFreeName(varIndex));
if (thisvar) {
if (thisVar) {
if (i == 0) {
varindex++;
varIndex++;
}
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 {
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) {
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex);
catch (Throwable t) {
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
isError = true;
}
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter);
meth.decompiledWithErrors = isError;
MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter);
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
varproc.refreshVarNames(new VarNamesCollector(setFieldNames));
if (!isError) {
// 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 (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
// if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
if (attr != null) {
// only param names here
varProc.setDebugVarNames(attr.getMapParamNames());
if (attr != null) {
varproc.setDebugVarNames(attr.getMapVarNames());
// the rest is here
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() {
return dynamicFieldInitializers;
}
}
@Override
public String toString() {
return classStruct.qualifiedName;
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -35,36 +21,28 @@ import java.io.IOException;
import java.util.*;
public class LambdaProcessor {
@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_ALT_METHOD = "altMetafactory";
public void processClass(ClassNode node) throws IOException {
for (ClassNode child : node.nested) {
processClass(child);
}
hasLambda(node);
}
public boolean hasLambda(ClassNode node) throws IOException {
ClassesProcessor clprocessor = DecompilerContext.getClassProcessor();
ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();
StructClass cl = node.classStruct;
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
return false;
if (!cl.isVersion8()) { // lambda beginning with Java 8
return;
}
StructBootstrapMethodsAttribute bootstrap =
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
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
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
@ -73,19 +51,19 @@ public class LambdaProcessor {
// FIXME: extend for Eclipse etc. at some point
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
(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()) {
return false; // no lambda bootstrap constant found
if (lambdaMethods.isEmpty()) {
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.
for (StructMethod mt : cl.getMethods()) {
mt.expandData();
mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) {
@ -95,9 +73,9 @@ public class LambdaProcessor {
Instruction instr = seq.getInstr(i);
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);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
@ -117,9 +95,9 @@ public class LambdaProcessor {
node.nested.add(node_lambda);
node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
if (!node_lambda.lambda_information.is_method_reference) {
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
clProcessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
if (!node_lambda.lambdaInformation.is_method_reference) {
mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName);
}
}
}
@ -134,7 +112,7 @@ public class LambdaProcessor {
if (nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if (parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
ClassNode parent_class = clProcessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd);
nd.parent = parent_class;
@ -143,7 +121,5 @@ public class LambdaProcessor {
}
// FIXME: mixed hierarchy?
return false;
}
}

View File

@ -1,20 +1,7 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
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.cfg.ControlFlowGraph;
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.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.io.IOException;
public class MethodProcessorThread implements Runnable {
public class MethodProcessorRunnable implements Runnable {
public final Object lock = new Object();
private final StructClass klass;
private final StructMethod method;
private final VarProcessor varproc;
private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc;
private final DecompilerContext parentContext;
private volatile RootStatement root;
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.varproc = varproc;
this.methodDescriptor = methodDescriptor;
this.varProc = varProc;
this.parentContext = parentContext;
}
@Override
public void run() {
DecompilerContext.setCurrentContext(parentContext);
error = null;
root = null;
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) {
lock.notifyAll();
}
}
catch (ThreadDeath ex) {
throw ex;
}
catch (Throwable ex) {
error = ex;
finished = true;
synchronized (lock) {
lock.notifyAll();
}
}
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();
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
// System.out.println(graph.toString());
// if(mt.getName().endsWith("_getActiveServers")) {
// System.out.println();
// }
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
graph.inlineJsr(cl, mt);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
ExceptionDeobfuscator.restorePopRanges(graph);
@ -110,15 +89,16 @@ public class MethodProcessorThread implements Runnable {
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)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
@ -126,22 +106,18 @@ public class MethodProcessorThread implements Runnable {
DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
//System.out.println(graph.toString());
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.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);
FinallyProcessor fproc = new FinallyProcessor(varproc);
while (fproc.iterateGraph(mt, root, graph)) {
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true);
//System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(cl, mt, root, 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
DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor();
ExprProcessor proc = new ExprProcessor(md, varProc);
proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
SequenceHelper.condenseSequences(root);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
StackVarsProcessor stackProc = new StackVarsProcessor();
while (true) {
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (!new PPandMMHelper().findPPandMM(root)) {
break;
}
do {
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
}
while (new PPandMMHelper().findPPandMM(root));
while (true) {
LabelHelper.cleanUpEdges(root);
while (true) {
do {
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 (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
@ -225,16 +172,16 @@ public class MethodProcessorThread implements Runnable {
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
//if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
//}
}
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
// FIXME: new edge type needed
@ -242,8 +189,6 @@ public class MethodProcessorThread implements Runnable {
mt.releaseResources();
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root;
}
@ -253,7 +198,7 @@ public class MethodProcessorThread implements Runnable {
return root;
}
public Throwable getError() {
return error;
public boolean isFinished() {
return finished;
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package org.jetbrains.java.decompiler.main.rels;
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.stats.RootStatement;
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 java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MethodWrapper {
public RootStatement root;
public VarProcessor varproc;
public StructMethod methodStruct;
public CounterContainer counter;
public final RootStatement root;
public final VarProcessor varproc;
public final StructMethod methodStruct;
public final CounterContainer counter;
public final Set<String> setOuterVarNames = new HashSet<>();
public DirectGraph graph;
public List<VarVersionPaar> signatureFields;
public List<VarVersionPair> synthParameters;
public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root;
this.varproc = varproc;
@ -54,9 +33,13 @@ public class MethodWrapper {
public DirectGraph getOrBuildGraph() {
if (graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
graph = flatthelper.buildDirectGraph(root);
graph = new FlattenStatementsHelper().buildDirectGraph(root);
}
return graph;
}
}
@Override
public String toString() {
return methodStruct.getName();
}
}

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.rels;
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.sforms.DirectGraph;
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.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.*;
public class NestedMemberAccess {
private static final int METHOD_ACCESS_NORMAL = 1;
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 enum MethodAccess {NORMAL, FIELD_GET, FIELD_SET, METHOD, FUNCTION}
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) {
@ -67,13 +47,13 @@ public class NestedMemberAccess {
computeMethodTypes(nd);
}
for (MethodWrapper method : node.wrapper.getMethods()) {
for (MethodWrapper method : node.getWrapper().getMethods()) {
computeMethodType(node, method);
}
}
private void computeMethodType(ClassNode node, MethodWrapper method) {
int type = METHOD_ACCESS_NORMAL;
MethodAccess type = MethodAccess.NORMAL;
if (method.root != null) {
DirectGraph graph = method.getOrBuildGraph();
@ -91,7 +71,7 @@ public class NestedMemberAccess {
if (exprent.type == Exprent.EXPRENT_EXIT) {
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();
}
}
@ -104,7 +84,7 @@ public class NestedMemberAccess {
if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpr.isStatic() ||
(fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELD_GET;
type = MethodAccess.FIELD_GET;
}
}
}
@ -113,13 +93,23 @@ public class NestedMemberAccess {
if (parcount == 1) {
// this or final variable
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;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
type = MethodAccess.METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
@ -131,7 +121,7 @@ public class NestedMemberAccess {
if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
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 = METHOD_ACCESS_NORMAL;
type = MethodAccess.NORMAL;
InvocationExprent invexpr = (InvocationExprent)exprCore;
if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) ||
(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
boolean isStatic = invexpr.isStatic();
if ((isStatic && invexpr.getLstParameters().size() == parcount) ||
(!isStatic && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) {
boolean equalpars = true;
int index = isStatic ? 0 : 1;
for (int i = 0; i < invexpr.getLstParameters().size(); i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if (parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) {
if (parexpr.type != Exprent.EXPRENT_VAR || ((VarExprent)parexpr).getIndex() != index) {
equalpars = false;
break;
}
index += mtdesc.params[i + (isStatic ? 0 : 1)].stackSize;
}
if (equalpars) {
type = METHOD_ACCESS_METHOD;
type = MethodAccess.METHOD;
}
}
}
@ -188,10 +179,10 @@ public class NestedMemberAccess {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
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 &&
((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);
}
else {
@ -220,7 +211,7 @@ public class NestedMemberAccess {
return;
}
for (MethodWrapper meth : node.wrapper.getMethods()) {
for (MethodWrapper meth : node.getWrapper().getMethods()) {
if (meth.root != null) {
@ -228,8 +219,8 @@ public class NestedMemberAccess {
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
HashSet<DirectNode> setVisited = new HashSet<>();
LinkedList<DirectNode> stack = new LinkedList<>();
stack.add(graph.first);
while (!stack.isEmpty()) { // TODO: replace with interface iterator?
@ -256,9 +247,7 @@ public class NestedMemberAccess {
}
}
for (DirectNode ndx : nd.succs) {
stack.add(ndx);
}
stack.addAll(nd.succs);
}
if (replaced) {
@ -323,12 +312,11 @@ public class NestedMemberAccess {
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if (node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
if (node != null && node.getWrapper() != null) {
methsource = node.getWrapper().getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if (methsource == null || !mapMethodType.containsKey(methsource)) {
@ -343,10 +331,10 @@ public class NestedMemberAccess {
return null;
}
int type = mapMethodType.get(methsource);
MethodAccess type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// // FIXME: impossible case. MethodAccess.NORMAL is not saved in the map
// if(type == MethodAccess.NORMAL) {
// return null;
// }
@ -360,11 +348,11 @@ public class NestedMemberAccess {
Exprent retexprent = null;
switch (type) {
case METHOD_ACCESS_FIELD_GET:
case FIELD_GET:
ExitExprent exsource = (ExitExprent)source;
if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
String varname = methsource.varproc.getVarName(new VarVersionPair(var));
if (!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
@ -375,8 +363,8 @@ public class NestedMemberAccess {
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
VarExprent ret = new VarExprent(index, var.getVarType(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPair(index, 0), varname);
retexprent = ret;
}
@ -388,7 +376,7 @@ public class NestedMemberAccess {
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELD_SET:
case FIELD_SET:
AssignmentExprent ret;
if (source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
@ -406,9 +394,17 @@ public class NestedMemberAccess {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
// do not use copied bytecodes
ret.getLeft().bytecode = null;
ret.getRight().bytecode = null;
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
case FUNCTION:
retexprent = replaceFunction(invexpr, source);
break;
case METHOD:
if (source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
@ -430,6 +426,10 @@ public class NestedMemberAccess {
if (retexprent != null) {
// preserve original bytecodes
retexprent.bytecode = null;
retexprent.addBytecodeOffsets(invexpr.bytecode);
// hide synthetic access method
boolean hide = true;
@ -440,10 +440,31 @@ public class NestedMemberAccess {
}
}
if (hide) {
node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.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.ControlFlowGraph;
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
@ -26,12 +13,12 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import java.util.*;
public class DeadCodeHelper {
public final class DeadCodeHelper {
public static void removeDeadBlocks(ControlFlowGraph graph) {
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>();
LinkedList<BasicBlock> stack = new LinkedList<>();
HashSet<BasicBlock> setStacked = new HashSet<>();
stack.add(graph.getFirst());
setStacked.add(graph.getFirst());
@ -39,7 +26,7 @@ public class DeadCodeHelper {
while (!stack.isEmpty()) {
BasicBlock block = stack.removeFirst();
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
lstSuccs.addAll(block.getSuccExceptions());
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);
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() &&
(!setExits.contains(block) || block.getPreds().size() == 1)) {
@ -109,15 +96,15 @@ public class DeadCodeHelper {
}
}
HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds());
HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs());
HashSet<BasicBlock> setPreds = new HashSet<>(block.getPreds());
HashSet<BasicBlock> setSuccs = new HashSet<>(block.getSuccs());
// collect common exception ranges of predecessors and successors
HashSet<BasicBlock> setCommonExceptionHandlers = null;
for (int i = 0; i < 2; ++i) {
for (BasicBlock pred : i == 0 ? setPreds : setSuccs) {
if (setCommonExceptionHandlers == null) {
setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions());
setCommonExceptionHandlers = new HashSet<>(pred.getSuccExceptions());
}
else {
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
@ -159,7 +146,7 @@ public class DeadCodeHelper {
BasicBlock pred = block.getPreds().get(0);
pred.removeSuccessor(block);
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
for (BasicBlock succ : lstSuccs) {
block.removeSuccessor(succ);
pred.addSuccessor(succ);
@ -205,13 +192,13 @@ public class DeadCodeHelper {
public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {
HashSet<BasicBlock> marked = new HashSet<BasicBlock>();
HashSet<BasicBlock> marked = new HashSet<>();
if (block == dom) {
return true;
}
LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
LinkedList<BasicBlock> lstNodes = new LinkedList<>();
lstNodes.add(block);
while (!lstNodes.isEmpty()) {
@ -230,14 +217,14 @@ public class DeadCodeHelper {
for (int i = 0; i < node.getPreds().size(); i++) {
BasicBlock pred = node.getPreds().get(i);
if (!marked.contains(pred) && pred != dom) {
if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred);
}
}
for (int i = 0; i < node.getPredExceptions().size(); i++) {
BasicBlock pred = node.getPredExceptions().get(i);
if (!marked.contains(pred) && pred != dom) {
if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred);
}
}
@ -252,7 +239,7 @@ public class DeadCodeHelper {
Instruction instr = block.getLastInstruction();
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) {
BasicBlock exit = graph.getLast();
for (BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) {
for (BasicBlock block : new HashSet<>(exit.getPreds())) {
exit.removePredecessor(block);
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) {
for (BasicBlock block : graph.getBlocks()) {
@ -311,8 +447,8 @@ public class DeadCodeHelper {
if (!block.getPreds().isEmpty()) {
HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>();
HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>();
HashSet<BasicBlock> setPredHandlersUnion = new HashSet<>();
HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<>();
boolean firstpred = true;
for (BasicBlock pred : block.getPreds()) {
@ -328,7 +464,9 @@ public class DeadCodeHelper {
}
// add exception ranges from predecessors
setPredHandlersIntersection.removeAll(block.getSuccExceptions());
for (BasicBlock basicBlock : block.getSuccExceptions()) {
setPredHandlersIntersection.remove(basicBlock);
}
BasicBlock predecessor = block.getPreds().get(0);
for (BasicBlock handler : setPredHandlersIntersection) {
@ -339,7 +477,7 @@ public class DeadCodeHelper {
}
// remove redundant ranges
HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions());
HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<>(block.getSuccExceptions());
setRangesToBeRemoved.removeAll(setPredHandlersUnion);
for (BasicBlock handler : setRangesToBeRemoved) {
@ -369,7 +507,7 @@ public class DeadCodeHelper {
}
// 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)) {
ExceptionRangeCFG range = graph.getExceptionRange(succ, block);
@ -416,6 +554,7 @@ public class DeadCodeHelper {
if (sameRanges) {
seq.addSequence(next.getSeq());
block.getInstrOldOffsets().addAll(next.getInstrOldOffsets());
next.getSeq().clear();
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.modules.decompiler.stats.RootStatement;
@ -21,11 +7,11 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.LinkedList;
public class ClearStructHelper {
public final class ClearStructHelper {
public static void clearStatements(RootStatement root) {
LinkedList<Statement> stack = new LinkedList<Statement>();
LinkedList<Statement> stack = new LinkedList<>();
stack.add(root);
while (!stack.isEmpty()) {

View File

@ -1,29 +1,19 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code.CodeConstants;
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.VarType;
import java.util.ArrayList;
import java.util.Arrays;
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 bufferClass = "java/lang/StringBuffer";
@ -52,6 +42,12 @@ public class ConcatenationHelper {
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) {
@ -60,7 +56,7 @@ public class ConcatenationHelper {
// iterate in depth, collecting possible operands
List<Exprent> lstOperands = new ArrayList<Exprent>();
List<Exprent> lstOperands = new ArrayList<>();
while (true) {
@ -104,7 +100,7 @@ public class ConcatenationHelper {
}
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
@ -125,20 +121,69 @@ public class ConcatenationHelper {
lstOperands.set(i, rep);
}
}
return createConcatExprent(lstOperands, expr.bytecode);
}
private static Exprent createConcatExprent(List<Exprent> lstOperands, Set<Integer> bytecode) {
// build exprent to return
Exprent func = lstOperands.get(0);
for (int i = 1; i < lstOperands.size(); i++) {
List<Exprent> lstTmp = new ArrayList<Exprent>();
lstTmp.add(func);
lstTmp.add(lstOperands.get(i));
func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp);
func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode);
}
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) {
if ("append".equals(expr.getName())) {
@ -167,13 +212,9 @@ public class ConcatenationHelper {
}
private static boolean isNewConcat(NewExprent expr, VarType cltype) {
if (expr.getNewtype().equals(cltype)) {
if (expr.getNewType().equals(cltype)) {
VarType[] params = expr.getConstructor().getDescriptor().params;
if (params.length == 0 || (params.length == 1 &&
params[0].equals(VarType.VARTYPE_STRING))) {
return true;
}
return params.length == 0 || params.length == 1 && params[0].equals(VarType.VARTYPE_STRING);
}
return false;

View File

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

View File

@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.code.cfg.BasicBlock;
@ -30,12 +16,12 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
public class DomHelper {
public final class DomHelper {
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();
for (BasicBlock block : blocks) {
@ -46,8 +32,7 @@ public class DomHelper {
// head statement
Statement firstst = stats.getWithKey(firstblock.id);
// dummy exit statement
Statement dummyexit = new Statement();
dummyexit.type = Statement.TYPE_DUMMYEXIT;
DummyExitStatement dummyexit = new DummyExitStatement();
Statement general;
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) {
HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>();
HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<>();
StrongConnectivityHelper schelper = new StrongConnectivityHelper(container);
List<List<Statement>> components = schelper.getComponents();
List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components));
FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats);
FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<>(lstStats);
FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet();
setFlagNodes.setAllElements();
@ -177,26 +162,22 @@ public class DomHelper {
}
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!
final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>();
final HashMap<Integer, Integer> mapSortOrder = new HashMap<>();
for (int i = 0; i < lstRevPost.size(); i++) {
mapSortOrder.put(lstRevPost.get(i).id, i);
}
for (Statement st : lstStats) {
List<Integer> lstPosts = new ArrayList<Integer>();
List<Integer> lstPosts = new ArrayList<>();
for (Statement stt : lists.get(st)) {
lstPosts.add(stt.id);
}
Collections.sort(lstPosts, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2));
}
});
lstPosts.sort(Comparator.comparing(mapSortOrder::get));
if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) {
lstPosts.add(lstPosts.remove(0));
@ -212,11 +193,17 @@ public class DomHelper {
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!");
}
LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
LabelHelper.lowContinueLabels(root, new HashSet<>());
SequenceHelper.condenseSequences(root);
root.buildMonitorFlags();
@ -286,7 +273,7 @@ public class DomHelper {
SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler());
sync.setAllParent();
for (StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) {
for (StatEdge edge : new HashSet<>(ca.getLabelEdges())) {
sync.addLabeledEdge(edge);
}
@ -356,7 +343,7 @@ public class DomHelper {
// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
// } catch(Exception ex) {ex.printStackTrace();}
mapExtPost = new HashMap<Integer, Set<Integer>>();
mapExtPost = new HashMap<>();
mapRefreshed = true;
}
@ -377,7 +364,7 @@ public class DomHelper {
Statement stat = findGeneralStatement(general, forceall, mapExtPost);
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) {
// replace general purpose statement with simple one
@ -387,7 +374,7 @@ public class DomHelper {
return false;
}
mapExtPost = new HashMap<Integer, Set<Integer>>();
mapExtPost = new HashMap<>();
mapRefreshed = true;
reducibility = 0;
}
@ -408,7 +395,7 @@ public class DomHelper {
break;
}
else {
mapExtPost = new HashMap<Integer, Set<Integer>>();
mapExtPost = new HashMap<>();
}
}
@ -426,13 +413,13 @@ public class DomHelper {
}
if (forceall) {
vbPost = new VBStyleCollection<List<Integer>, Integer>();
vbPost = new VBStyleCollection<>();
List<Statement> lstAll = stat.getPostReversePostOrderList();
for (Statement st : lstAll) {
Set<Integer> set = mapExtPost.get(st.id);
if (set != null) {
vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!!
vbPost.addWithKey(new ArrayList<>(set), st.id); // FIXME: sort order!!
}
}
@ -442,7 +429,7 @@ public class DomHelper {
for (Integer id : setFirst) {
List<Integer> lst = vbPost.getWithKey(id);
if (lst == null) {
vbPost.addWithKey(lst = new ArrayList<Integer>(), id);
vbPost.addWithKey(lst = new ArrayList<>(), id);
}
lst.add(id);
}
@ -466,14 +453,12 @@ public class DomHelper {
Set<Integer> setExtPosts = mapExtPost.get(headid);
for (int i = 0; i < posts.size(); i++) {
Integer postid = posts.get(i);
if (!postid.equals(headid) && !setExtPosts.contains(postid)) {
for (Integer postId : posts) {
if (!postId.equals(headid) && !setExtPosts.contains(postId)) {
continue;
}
Statement post = stats.getWithKey(postid);
Statement post = stats.getWithKey(postId);
if (post == null) { // possible in case of an inherited postdominance set
continue;
@ -481,11 +466,11 @@ public class DomHelper {
boolean same = (post == head);
HashSet<Statement> setNodes = new HashSet<Statement>();
HashSet<Statement> setPreds = new HashSet<Statement>();
HashSet<Statement> setNodes = new HashSet<>();
HashSet<Statement> setPreds = new HashSet<>();
// collect statement nodes
HashSet<Statement> setHandlers = new HashSet<Statement>();
HashSet<Statement> setHandlers = new HashSet<>();
setHandlers.add(head);
while (true) {
@ -503,7 +488,7 @@ public class DomHelper {
}
if (addhd) {
LinkedList<Statement> lstStack = new LinkedList<Statement>();
LinkedList<Statement> lstStack = new LinkedList<>();
lstStack.add(handler);
while (!lstStack.isEmpty()) {
@ -552,14 +537,14 @@ public class DomHelper {
// build statement and return
if (excok) {
Statement res = null;
Statement res;
setPreds.removeAll(setNodes);
if (setPreds.size() == 0) {
if ((setNodes.size() > 1 ||
head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head))
&& setNodes.size() < stats.size()) {
if (checkSynchronizedCompleteness(head, setNodes)) {
if (checkSynchronizedCompleteness(setNodes)) {
res = new GeneralStatement(head, setNodes, same ? null : post);
stat.collapseNodesToStatement(res);
@ -574,8 +559,7 @@ public class DomHelper {
return null;
}
private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) {
private static boolean checkSynchronizedCompleteness(Set<Statement> setNodes) {
// check exit nodes
for (Statement stat : setNodes) {
if (stat.isMonitorEnter()) {
@ -617,26 +601,21 @@ public class DomHelper {
// update the postdominator map
if (!mapExtPost.isEmpty()) {
HashSet<Integer> setOldNodes = new HashSet<Integer>();
HashSet<Integer> setOldNodes = new HashSet<>();
for (Statement old : result.getStats()) {
setOldNodes.add(old.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);
int oldsize = set.size();
set.removeAll(setOldNodes);
if (setOldNodes.contains(key)) {
Set<Integer> setNew = mapExtPost.get(newid);
if (setNew == null) {
mapExtPost.put(newid, setNew = new HashSet<Integer>());
}
setNew.addAll(set);
mapExtPost.computeIfAbsent(newid, k -> new HashSet<>()).addAll(set);
mapExtPost.remove(key);
}
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.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.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class ExitHelper {
public final class ExitHelper {
public static boolean condenseExits(RootStatement root) {
int changed = integrateExits(root);
if (changed > 0) {
cleanUpUnreachableBlocks(root);
SequenceHelper.condenseSequences(root);
}
return (changed > 0);
}
private static void cleanUpUnreachableBlocks(Statement stat) {
boolean found;
do {
found = false;
for (int i = 0; i < stat.getStats().size(); i++) {
Statement st = stat.getStats().get(i);
cleanUpUnreachableBlocks(st);
@ -71,7 +47,7 @@ public class ExitHelper {
set.remove(secondlast);
if (set.isEmpty()) {
last.setExprents(new ArrayList<Exprent>());
last.setExprents(new ArrayList<>());
found = true;
break;
}
@ -83,16 +59,12 @@ public class ExitHelper {
while (found);
}
private static int integrateExits(Statement stat) {
int ret = 0;
Statement dest = null;
Statement dest;
if (stat.getExprents() == null) {
while (true) {
int changed = 0;
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) {
case Statement.TYPE_IF:
IfStatement ifst = (IfStatement)stat;
if (ifst.getIfstat() == null) {
StatEdge ifedge = ifst.getIfEdge();
dest = isExitEdge(ifedge);
if (dest != null) {
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
ifst.getFirst().removeSuccessor(ifedge);
StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
ifst.getFirst().addSuccessor(newedge);
ifst.setIfEdge(newedge);
ifst.setIfstat(bstat);
ifst.getStats().addWithKey(bstat, bstat.id);
bstat.setParent(ifst);
ifst.getFirst().removeSuccessor(ifedge);
StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
ifst.getFirst().addSuccessor(newedge);
ifst.setIfEdge(newedge);
ifst.setIfstat(bstat);
ifst.getStats().addWithKey(bstat, bstat.id);
bstat.setParent(ifst);
StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
bstat.addSuccessor(newexitedge);
oldexitedge.closure.addLabeledEdge(newexitedge);
ret = 1;
}
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>());
// do it by hand
for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
block.removePredecessor(prededge);
prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat);
stat.addPredecessor(prededge);
stat.addLabeledEdge(prededge);
}
stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat));
for (StatEdge edge : dest.getAllPredecessorEdges()) {
@ -202,11 +169,9 @@ public class ExitHelper {
}
private static Statement isExitEdge(StatEdge edge) {
Statement dest = edge.getDestination();
if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK
&& edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK && edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
List<Exprent> data = dest.getExprents();
if (data != null && data.size() == 1) {
@ -220,7 +185,6 @@ public class ExitHelper {
}
private static boolean isOnlyEdge(StatEdge edge) {
Statement stat = edge.getDestination();
for (StatEdge ed : stat.getAllPredecessorEdges()) {
@ -243,11 +207,10 @@ public class ExitHelper {
return true;
}
public static boolean removeRedundantReturns(RootStatement root) {
public static void removeRedundantReturns(RootStatement root) {
DummyExitStatement dummyExit = root.getDummyExit();
boolean res = false;
for (StatEdge edge : root.getDummyExit().getAllPredecessorEdges()) {
for (StatEdge edge : dummyExit.getAllPredecessorEdges()) {
if (!edge.explicit) {
Statement source = edge.getSource();
List<Exprent> lstExpr = source.getExprents();
@ -255,91 +218,14 @@ public class ExitHelper {
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) {
if (ex.getExitType() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {
// remove redundant return
dummyExit.addBytecodeOffsets(ex.bytecode);
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.modules.decompiler;
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.cfg.BasicBlock;
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.modules.decompiler.exps.*;
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.gen.MethodDescriptor;
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 org.jetbrains.java.decompiler.util.SortUtil;
import org.jetbrains.java.decompiler.util.Util;
public class ExprProcessor implements CodeConstants {
public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>";
public static final String UNKNOWN_TYPE_STRING = "<unknown>";
public static final String NULL_TYPE_STRING = "<null>";
private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>();
private static final Map<Integer, Integer> mapConsts = new HashMap<>();
static {
// mapConsts.put(new Integer(opc_i2l), new
// Integer(FunctionExprent.FUNCTION_I2L));
// mapConsts.put(new Integer(opc_i2f), new
// Integer(FunctionExprent.FUNCTION_I2F));
// mapConsts.put(new Integer(opc_i2d), new
// Integer(FunctionExprent.FUNCTION_I2D));
// mapConsts.put(new Integer(opc_l2i), new
// Integer(FunctionExprent.FUNCTION_L2I));
// mapConsts.put(new Integer(opc_l2f), new
// Integer(FunctionExprent.FUNCTION_L2F));
// mapConsts.put(new Integer(opc_l2d), new
// Integer(FunctionExprent.FUNCTION_L2D));
// mapConsts.put(new Integer(opc_f2i), new
// Integer(FunctionExprent.FUNCTION_F2I));
// mapConsts.put(new Integer(opc_f2l), new
// Integer(FunctionExprent.FUNCTION_F2L));
// mapConsts.put(new Integer(opc_f2d), new
// Integer(FunctionExprent.FUNCTION_F2D));
// mapConsts.put(new Integer(opc_d2i), new
// Integer(FunctionExprent.FUNCTION_D2I));
// mapConsts.put(new Integer(opc_d2l), new
// Integer(FunctionExprent.FUNCTION_D2L));
// mapConsts.put(new Integer(opc_d2f), new
// Integer(FunctionExprent.FUNCTION_D2F));
// mapConsts.put(new Integer(opc_i2b), new
// Integer(FunctionExprent.FUNCTION_I2B));
// mapConsts.put(new Integer(opc_i2c), new
// Integer(FunctionExprent.FUNCTION_I2C));
// mapConsts.put(new Integer(opc_i2s), new
// Integer(FunctionExprent.FUNCTION_I2S));
mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAYLENGTH));
mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST));
mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF));
mapConsts.put(opc_arraylength, FunctionExprent.FUNCTION_ARRAY_LENGTH);
mapConsts.put(opc_checkcast, FunctionExprent.FUNCTION_CAST);
mapConsts.put(opc_instanceof, FunctionExprent.FUNCTION_INSTANCEOF);
}
private static final VarType[] consts =
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS,
VarType.VARTYPE_STRING};
private static final VarType[] consts = {
VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_STRING
};
private static final VarType[] vartypes =
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT};
private static final VarType[] varTypes = {
VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT
};
private static final VarType[] arrtypes =
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT};
private static final VarType[] arrTypes = {
VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT
};
private static final int[] func1 =
new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
FunctionExprent.FUNCTION_REM};
private static final int[] func1 = {
FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
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 =
new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,
FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR};
private static final int[] arrTypeIds = {
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[] func3 =
new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I,
FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L,
FunctionExprent.FUNCTION_F2D,
FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B,
FunctionExprent.FUNCTION_I2C,
FunctionExprent.FUNCTION_I2S};
private static final int[] negIfs = {
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 int[] func4 =
new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
FunctionExprent.FUNCTION_DCMPG};
private static final String[] typeNames = {"byte", "char", "double", "float", "int", "long", "short", "boolean"};
private static final int[] func5 =
new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE};
private final MethodDescriptor methodDescriptor;
private final VarProcessor varProcessor;
private static final int[] func6 =
new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE,
IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE};
private static final int[] func7 = new int[]{IfExprent.IF_NULL, IfExprent.IF_NONNULL};
private static final int[] func8 = new int[]{MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};
private static final int[] arr_type =
new int[]{CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,
CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG};
private static final int[] negifs =
new int[]{IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,
IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,
IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE,
IfExprent.IF_ACMPEQ};
private static final String[] typeNames = new String[]{"byte", "char", "double", "float", "int", "long", "short", "boolean",};
private VarProcessor varProcessor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR);
public ExprProcessor(MethodDescriptor md, VarProcessor varProc) {
methodDescriptor = md;
varProcessor = varProc;
}
public void processStatement(RootStatement root, StructClass cl) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root);
// try {
// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot"));
// } catch (Exception ex) {
// ex.printStackTrace();
// }
// collect finally entry points
Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>();
Set<String> setFinallyShortRangeEntryPoints = new HashSet<>();
for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) {
for (FinallyPathWrapper finwrap : lst) {
setFinallyShortRangeEntryPoints.add(finwrap.entry);
}
}
Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>();
Set<String> setFinallyLongRangeEntryPaths = new HashSet<>();
for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) {
for (FinallyPathWrapper finwrap : lst) {
setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry);
}
}
Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>();
Map<String, VarExprent> mapCatch = new HashMap<>();
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<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>();
LinkedList<DirectNode> stack = new LinkedList<>();
LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<>();
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());
mapData.put(dgraph.first, map);
@ -222,13 +171,8 @@ public class ExprProcessor implements CodeConstants {
}
if (isSuccessor) {
Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd);
if (mapSucc == null) {
mapData.put(nd, mapSucc = new HashMap<String, PrimitiveExprsList>());
}
LinkedList<String> ndentrypoints = new LinkedList<String>(entrypoints);
Map<String, PrimitiveExprsList> mapSucc = mapData.computeIfAbsent(nd, k -> new HashMap<>());
LinkedList<String> ndentrypoints = new LinkedList<>(entrypoints);
if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) {
ndentrypoints.addLast(node.id);
@ -279,10 +223,14 @@ public class ExprProcessor implements CodeConstants {
private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) {
ExprentStack stack = data.getStack();
copyEntries(stack);
return data;
}
public static void copyEntries(List<Exprent> stack) {
for (int i = 0; i < stack.size(); i++) {
stack.set(i, stack.get(i).copy());
}
return data;
}
private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) {
@ -321,8 +269,7 @@ public class ExprProcessor implements CodeConstants {
public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) {
ConstantPool pool = cl.getPool();
StructBootstrapMethodsAttribute bootstrap =
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
BasicBlock block = stat.getBlock();
@ -335,40 +282,47 @@ public class ExprProcessor implements CodeConstants {
Instruction instr = seq.getInstr(i);
Integer bytecode_offset = block.getOldOffset(i);
Set<Integer> bytecode_offsets = bytecode_offset >= 0 ? Collections.singleton(bytecode_offset) : null;
switch (instr.opcode) {
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;
case opc_bipush:
case opc_sipush:
pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true));
pushEx(stack, exprlist, new ConstExprent(instr.operand(0), true, bytecode_offsets));
break;
case opc_lconst_0:
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;
case opc_fconst_0:
case opc_fconst_1:
case opc_fconst_2:
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0)));
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, (float)(instr.opcode - opc_fconst_0), bytecode_offsets));
break;
case opc_dconst_0:
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;
case opc_ldc:
case opc_ldc_w:
case opc_ldc2_w:
PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0));
pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value));
PooledConstant cn = pool.getConstant(instr.operand(0));
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;
case opc_iload:
case opc_lload:
case opc_fload:
case opc_dload:
case opc_aload:
pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor));
pushEx(stack, exprlist, new VarExprent(instr.operand(0), varTypes[instr.opcode - opc_iload], varProcessor, bytecode_offset));
break;
case opc_iaload:
case opc_laload:
@ -389,17 +343,17 @@ public class ExprProcessor implements CodeConstants {
case opc_daload:
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;
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
Exprent top = stack.pop();
int varindex = instr.getOperand(0);
AssignmentExprent assign =
new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top);
Exprent expr = stack.pop();
int varindex = instr.operand(0);
AssignmentExprent assign = new AssignmentExprent(
new VarExprent(varindex, varTypes[instr.opcode - opc_istore], varProcessor, nextMeaningfulOffset(block, i)), expr, bytecode_offsets);
exprlist.add(assign);
break;
case opc_iastore:
@ -414,7 +368,8 @@ public class ExprProcessor implements CodeConstants {
Exprent index_store = stack.pop();
Exprent arr_store = stack.pop();
AssignmentExprent arrassign =
new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value);
new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrTypes[instr.opcode - opc_iastore], bytecode_offsets), value,
bytecode_offsets);
exprlist.add(arrassign);
break;
case opc_iadd:
@ -437,7 +392,7 @@ public class ExprProcessor implements CodeConstants {
case opc_lrem:
case opc_frem:
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;
case opc_ishl:
case opc_lshl:
@ -451,19 +406,20 @@ public class ExprProcessor implements CodeConstants {
case opc_lor:
case opc_ixor:
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;
case opc_ineg:
case opc_lneg:
case opc_fneg:
case opc_dneg:
pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack));
pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack, bytecode_offsets));
break;
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(
instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays
.asList(new Exprent[]{vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, new Integer(Math.abs(instr.getOperand(1))))}))));
instr.operand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays
.asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.operand(1)), null)),
bytecode_offsets), bytecode_offsets));
break;
case opc_i2l:
case opc_i2f:
@ -480,14 +436,14 @@ public class ExprProcessor implements CodeConstants {
case opc_i2b:
case opc_i2c:
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;
case opc_lcmp:
case opc_fcmpl:
case opc_fcmpg:
case opc_dcmpl:
case opc_dcmpg:
pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack));
pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack, bytecode_offsets));
break;
case opc_ifeq:
case opc_ifne:
@ -495,7 +451,7 @@ public class ExprProcessor implements CodeConstants {
case opc_ifge:
case opc_ifgt:
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;
case opc_if_icmpeq:
case opc_if_icmpne:
@ -505,15 +461,15 @@ public class ExprProcessor implements CodeConstants {
case opc_if_icmple:
case opc_if_acmpeq:
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;
case opc_ifnull:
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;
case opc_tableswitch:
case opc_lookupswitch:
exprlist.add(new SwitchExprent(stack.pop()));
exprlist.add(new SwitchExprent(stack.pop(), bytecode_offsets));
break;
case opc_ireturn:
case opc_lreturn:
@ -524,51 +480,47 @@ public class ExprProcessor implements CodeConstants {
case opc_athrow:
exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN,
instr.opcode == opc_return ? null : stack.pop(),
instr.opcode == opc_athrow
? null
: ((MethodDescriptor)DecompilerContext
.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret));
instr.opcode == opc_athrow ? null : methodDescriptor.ret,
bytecode_offsets));
break;
case opc_monitorenter:
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;
case opc_checkcast:
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:
pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack));
pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode), stack, bytecode_offsets));
break;
case opc_getstatic:
case opc_getfield:
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;
case opc_putstatic:
case opc_putfield:
Exprent valfield = stack.pop();
Exprent exprfield =
new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop());
exprlist.add(new AssignmentExprent(exprfield, valfield));
new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_putstatic ? null : stack.pop(),
bytecode_offsets);
exprlist.add(new AssignmentExprent(exprfield, valfield, bytecode_offsets));
break;
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_invokedynamic:
if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
int dynamic_invokation_type = -1;
if (instr.opcode != opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
LinkConstant invoke_constant = pool.getLinkConstant(instr.operand(0));
List<PooledConstant> bootstrap_arguments = null;
if (instr.opcode == opc_invokedynamic && bootstrap != null) {
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
dynamic_invokation_type = content_method_handle.index1;
bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.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) {
exprlist.add(exprinv);
}
@ -580,15 +532,15 @@ public class ExprProcessor implements CodeConstants {
case opc_new:
case opc_anewarray:
case opc_multianewarray:
int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1);
VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true);
int dimensions = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.operand(1);
VarType arrType = new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true);
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;
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;
case opc_dup:
pushEx(stack, exprlist, stack.getByOffset(-1).copy());
@ -597,7 +549,7 @@ public class ExprProcessor implements CodeConstants {
insertByOffsetEx(-2, stack, exprlist, -1);
break;
case opc_dup_x2:
if (stack.getByOffset(-2).getExprType().stack_size == 2) {
if (stack.getByOffset(-2).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1);
}
else {
@ -605,7 +557,7 @@ public class ExprProcessor implements CodeConstants {
}
break;
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());
}
else {
@ -614,7 +566,7 @@ public class ExprProcessor implements CodeConstants {
}
break;
case opc_dup2_x1:
if (stack.getByOffset(-1).getExprType().stack_size == 2) {
if (stack.getByOffset(-1).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1);
}
else {
@ -623,8 +575,8 @@ public class ExprProcessor implements CodeConstants {
}
break;
case opc_dup2_x2:
if (stack.getByOffset(-1).getExprType().stack_size == 2) {
if (stack.getByOffset(-2).getExprType().stack_size == 2) {
if (stack.getByOffset(-1).getExprType().stackSize == 2) {
if (stack.getByOffset(-2).getExprType().stackSize == 2) {
insertByOffsetEx(-2, stack, exprlist, -1);
}
else {
@ -632,7 +584,7 @@ public class ExprProcessor implements CodeConstants {
}
}
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, -1);
}
@ -647,12 +599,38 @@ public class ExprProcessor implements CodeConstants {
stack.pop();
break;
case opc_pop:
case opc_pop2:
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) {
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);
var.setStack(true);
exprlist.add(new AssignmentExprent(var, exprent));
exprlist.add(new AssignmentExprent(var, exprent, null));
stack.push(var.copy());
}
@ -670,20 +648,20 @@ public class ExprProcessor implements CodeConstants {
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--) {
Exprent varex = stack.pop();
VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor);
varnew.setStack(true);
exprlist.add(new AssignmentExprent(varnew, varex));
exprlist.add(new AssignmentExprent(varnew, varex, null));
lst.add(0, (VarExprent)varnew.copy());
}
Exprent exprent = lst.get(lst.size() + copyoffset).copy();
VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor);
var.setStack(true);
exprlist.add(new AssignmentExprent(var, exprent));
exprlist.add(new AssignmentExprent(var, exprent, null));
lst.add(0, (VarExprent)var.copy());
for (VarExprent expr : lst) {
@ -696,7 +674,6 @@ public class ExprProcessor implements CodeConstants {
}
public static String getTypeName(VarType type, boolean getShort) {
int tp = type.type;
if (tp <= CodeConstants.TYPE_BOOLEAN) {
return typeNames[tp];
@ -731,12 +708,9 @@ public class ExprProcessor implements CodeConstants {
}
public static String getCastTypeName(VarType type, boolean getShort) {
String s = getTypeName(type, getShort);
int dim = type.arraydim;
while (dim-- > 0) {
s += "[]";
}
return s;
StringBuilder s = new StringBuilder(getTypeName(type, getShort));
TextUtil.append(s, "[]", type.arrayDim);
return s.toString();
}
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());
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());
return prlst;
}
public static boolean endsWithSemikolon(Exprent expr) {
public static boolean endsWithSemicolon(Exprent expr) {
int type = expr.type;
return !(type == Exprent.EXPRENT_SWITCH ||
type == Exprent.EXPRENT_MONITOR ||
type == Exprent.EXPRENT_IF ||
(type == Exprent.EXPRENT_VAR && ((VarExprent)expr)
.isClassdef()));
(type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()));
}
public static String jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) {
String new_line_separator = DecompilerContext.getNewLineSeparator();
private static void addDeletedGotoInstructionMapping(Statement stat, BytecodeMappingTracer tracer) {
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();
String content = 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);
}
public static TextBuffer jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) {
TextBuffer buf = stat.toJava(indent, tracer);
List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL);
if (lstSuccs.size() == 1) {
StatEdge edge = lstSuccs.get(0);
if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
buf.append(InterpreterUtil.getIndentString(indent));
buf.appendIndent(indent);
switch (edge.getType()) {
case StatEdge.TYPE_BREAK:
addDeletedGotoInstructionMapping(stat, tracer);
buf.append("break");
break;
case StatEdge.TYPE_CONTINUE:
addDeletedGotoInstructionMapping(stat, tracer);
buf.append("continue");
}
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();
}
}
if (buf.length() == 0 && semicolon) {
buf.append(InterpreterUtil.getIndentString(indent)).append(";").append(new_line_separator);
buf.appendIndent(indent).append(";").appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
return buf.toString();
return buf;
}
public static String buildJavaClassName(String name) {
@ -815,92 +791,59 @@ public class ExprProcessor implements CodeConstants {
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()) {
return "";
return new TextBuffer();
}
String indstr = InterpreterUtil.getIndentString(indent);
String new_line_separator = DecompilerContext.getNewLineSeparator();
TextBuffer buf = new TextBuffer();
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 (expr instanceof VarExprent || expr instanceof AssignmentExprent) {
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);
if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) {
buf.appendIndent(indent);
}
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
}
if (endsWithSemikolon(expr)) {
if (endsWithSemicolon(expr)) {
buf.append(";");
}
buf.append(new_line_separator);
buf.appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
}
return buf.toString();
return buf;
}
public static ConstExprent getDefaultArrayValue(VarType arrtype) {
ConstExprent defaultval;
if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) {
defaultval = new ConstExprent(VarType.VARTYPE_NULL, null);
public static ConstExprent getDefaultArrayValue(VarType arrType) {
ConstExprent defaultVal;
if (arrType.type == CodeConstants.TYPE_OBJECT || arrType.arrayDim > 0) {
defaultVal = new ConstExprent(VarType.VARTYPE_NULL, null, null);
}
else if (arrtype.type == CodeConstants.TYPE_FLOAT) {
defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0));
else if (arrType.type == CodeConstants.TYPE_FLOAT) {
defaultVal = new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null);
}
else if (arrtype.type == CodeConstants.TYPE_LONG) {
defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0));
else if (arrType.type == CodeConstants.TYPE_LONG) {
defaultVal = new ConstExprent(VarType.VARTYPE_LONG, 0L, null);
}
else if (arrtype.type == CodeConstants.TYPE_DOUBLE) {
defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0));
else if (arrType.type == CodeConstants.TYPE_DOUBLE) {
defaultVal = new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null);
}
else { // integer types
defaultval = new ConstExprent(0, true);
defaultVal = new ConstExprent(0, true, null);
}
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);
return defaultVal;
}
public static boolean getCastedExprent(Exprent exprent,
@ -908,44 +851,89 @@ public class ExprProcessor implements CodeConstants {
TextBuffer buffer,
int indent,
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();
String res = exprent.toJava(indent, tracer);
boolean cast =
!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT);
cast |= castAlways && !leftType.equals(rightType); // Spigot
castAlways ||
(!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) {
// check for a nameless anonymous class
cast = !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType));
}
if (!cast) {
cast = isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType);
}
boolean quote = cast && exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST);
if (cast) {
if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
res = "(" + res + ")";
// cast instead to 'byte' / 'short' when int constant is used as a value for 'Byte' / 'Short'
if (castNarrowing && exprent.type == Exprent.EXPRENT_CONST && !((ConstExprent) exprent).isNull()) {
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) {
if (exprent.type == Exprent.EXPRENT_CONST) {
ConstExprent cexpr = (ConstExprent)exprent;
switch (cexpr.getConsttype().type) {
switch (((ConstExprent)exprent).getConstType().type) {
case CodeConstants.TYPE_BYTE:
case CodeConstants.TYPE_BYTECHAR:
case CodeConstants.TYPE_SHORT:
@ -957,4 +945,9 @@ public class ExprProcessor implements CodeConstants {
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-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
@ -28,17 +14,13 @@ public class ExprentStack extends ListStack<Exprent> {
pointer = list.getPointer();
}
public Exprent push(Exprent item) {
super.push(item);
return item;
}
@Override
public Exprent pop() {
return this.remove(--pointer);
}
@Override
public ExprentStack clone() {
return new ExprentStack(this);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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