├── .gitignore ├── .idea ├── vcs.xml ├── .gitignore ├── misc.xml ├── compiler.xml └── jarRepositories.xml ├── README.md ├── src ├── main │ └── java │ │ └── org │ │ └── jetbrains │ │ └── java │ │ └── decompiler │ │ ├── ClassNameConstants.java │ │ ├── struct │ │ ├── gen │ │ │ ├── NewClassNameBuilder.java │ │ │ ├── generics │ │ │ │ ├── GenericFieldDescriptor.java │ │ │ │ ├── GenericClassDescriptor.java │ │ │ │ └── GenericMethodDescriptor.java │ │ │ ├── Type.java │ │ │ ├── FieldDescriptor.java │ │ │ ├── DataPoint.java │ │ │ └── MethodDescriptor.java │ │ ├── IDecompiledData.java │ │ ├── consts │ │ │ ├── PooledConstant.java │ │ │ ├── PrimitiveConstant.java │ │ │ └── LinkConstant.java │ │ ├── attr │ │ │ ├── StructConstantValueAttribute.java │ │ │ ├── StructGenericSignatureAttribute.java │ │ │ ├── StructAnnDefaultAttribute.java │ │ │ ├── StructPermittedSubclassesAttribute.java │ │ │ ├── StructExceptionsAttribute.java │ │ │ ├── StructLocalVariableTypeTableAttribute.java │ │ │ ├── StructAnnotationParameterAttribute.java │ │ │ ├── StructEnclosingMethodAttribute.java │ │ │ ├── StructRecordAttribute.java │ │ │ ├── StructCodeAttribute.java │ │ │ ├── StructLineNumberTableAttribute.java │ │ │ ├── StructMethodParametersAttribute.java │ │ │ ├── StructBootstrapMethodsAttribute.java │ │ │ ├── StructInnerClassesAttribute.java │ │ │ ├── StructLocalVariableTableAttribute.java │ │ │ └── StructTypeAnnotationAttribute.java │ │ ├── match │ │ │ ├── IMatchable.java │ │ │ └── MatchNode.java │ │ ├── StructRecordComponent.java │ │ ├── StructTypePathEntry.java │ │ ├── StructField.java │ │ └── lazy │ │ │ └── LazyLoader.java │ │ ├── main │ │ ├── extern │ │ │ ├── IBytecodeProvider.java │ │ │ ├── ClassFormatException.java │ │ │ ├── IIdentifierRenamer.java │ │ │ ├── IResultSaver.java │ │ │ ├── IFernflowerLogger.java │ │ │ └── IFernflowerPreferences.java │ │ ├── collectors │ │ │ ├── CounterContainer.java │ │ │ ├── VarNamesCollector.java │ │ │ ├── BytecodeMappingTracer.java │ │ │ └── BytecodeSourceMapper.java │ │ ├── decompiler │ │ │ ├── shxjia │ │ │ │ ├── CustomeBytecodeProvider.java │ │ │ │ └── CustomeResultSaver.java │ │ │ ├── BaseDecompiler.java │ │ │ └── PrintStreamLogger.java │ │ ├── rels │ │ │ └── MethodWrapper.java │ │ ├── EnumProcessor.java │ │ └── Fernflower.java │ │ ├── modules │ │ ├── decompiler │ │ │ ├── decompose │ │ │ │ ├── IGraphNode.java │ │ │ │ ├── IGraph.java │ │ │ │ ├── DominatorEngine.java │ │ │ │ └── GenericDominatorEngine.java │ │ │ ├── ExpressionStack.java │ │ │ ├── ExprentStack.java │ │ │ ├── ClearStructHelper.java │ │ │ ├── stats │ │ │ │ ├── DummyExitStatement.java │ │ │ │ ├── RootStatement.java │ │ │ │ ├── Statements.java │ │ │ │ ├── GeneralStatement.java │ │ │ │ ├── BasicBlockStatement.java │ │ │ │ └── SequenceStatement.java │ │ │ ├── PrimitiveExpressionList.java │ │ │ ├── PrimitiveExprsList.java │ │ │ ├── exps │ │ │ │ ├── AssertExprent.java │ │ │ │ ├── TypeAnnotation.java │ │ │ │ ├── MonitorExprent.java │ │ │ │ ├── ExprUtil.java │ │ │ │ ├── AnnotationExprent.java │ │ │ │ ├── SwitchExprent.java │ │ │ │ ├── ArrayExprent.java │ │ │ │ └── IfExprent.java │ │ │ ├── vars │ │ │ │ ├── VarVersionEdge.java │ │ │ │ ├── VarVersionPair.java │ │ │ │ ├── CheckTypesResult.java │ │ │ │ ├── VarVersionNode.java │ │ │ │ └── VarVersionsGraph.java │ │ │ ├── sforms │ │ │ │ ├── DirectNode.java │ │ │ │ └── DirectGraph.java │ │ │ ├── typeann │ │ │ │ ├── TypeAnnotationWriteHelper.java │ │ │ │ └── TypeAnnotation.java │ │ │ ├── ClasspathHelper.java │ │ │ ├── StrongConnectivityHelper.java │ │ │ └── PPandMMHelper.java │ │ └── renamer │ │ │ ├── PoolInterceptor.java │ │ │ └── ClassWrapperNode.java │ │ ├── code │ │ ├── ExceptionTable.java │ │ ├── SimpleInstructionSequence.java │ │ ├── JumpInstruction.java │ │ ├── ExceptionHandler.java │ │ ├── FullInstructionSequence.java │ │ ├── SwitchInstruction.java │ │ ├── cfg │ │ │ └── ExceptionRangeCFG.java │ │ ├── Instruction.java │ │ └── InstructionSequence.java │ │ └── util │ │ ├── DataInputFullStream.java │ │ ├── ListStack.java │ │ ├── InterpreterUtil.java │ │ ├── FilterClass.java │ │ └── VBStyleCollection.java └── test │ └── java │ └── Test.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /target/ -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fernflower 2 | wocao 3 | gansha? 4 | 5 | 6 | https://github.com/fesh0r/fernflower 7 | https://github.com/fesh0r/fernflower 8 | https://github.com/fesh0r/fernflower 9 | https://github.com/fesh0r/fernflower 10 | https://github.com/fesh0r/fernflower 11 | 说5遍 12 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | # Zeppelin ignored files 10 | /ZeppelinRemoteNotebooks/ 11 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/ClassNameConstants.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler; 3 | 4 | public interface ClassNameConstants { 5 | String JAVA_LANG_STRING = "java/lang/String"; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.gen; 3 | 4 | public interface NewClassNameBuilder { 5 | String buildNewClassname(String className); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/IDecompiledData.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct; 3 | 4 | public interface IDecompiledData { 5 | 6 | String getClassEntryName(StructClass cl, String entryname); 7 | 8 | String getClassContent(StructClass cl); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | import java.io.IOException; 5 | 6 | public interface IBytecodeProvider { 7 | byte[] getBytecode(String externalPath, String internalPath) throws IOException; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler.decompose; 3 | 4 | import java.util.List; 5 | 6 | public interface IGraphNode { 7 | List getPredecessorNodes(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/ClassFormatException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | public class ClassFormatException extends RuntimeException { 5 | public ClassFormatException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.gen.generics; 3 | 4 | public class GenericFieldDescriptor { 5 | public final GenericType type; 6 | 7 | public GenericFieldDescriptor(GenericType type) { 8 | this.type = type; 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.decompose; 3 | 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | public interface IGraph { 8 | 9 | List getReversePostOrderList(); 10 | 11 | Set getRoots(); 12 | } 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.consts; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | 6 | public class PooledConstant implements CodeConstants { 7 | public final int type; 8 | 9 | public PooledConstant(int type) { 10 | this.type = type; 11 | } 12 | 13 | public void resolveConstant(ConstantPool pool) { } 14 | } -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.gen.generics; 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class GenericClassDescriptor { 8 | 9 | public GenericType superclass; 10 | 11 | public final List superinterfaces = new ArrayList<>(); 12 | 13 | public final List fparameters = new ArrayList<>(); 14 | 15 | public final List> fbounds = new ArrayList<>(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/ExceptionTable.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class ExceptionTable { 8 | public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList()); 9 | 10 | private final List handlers; 11 | 12 | public ExceptionTable(List handlers) { 13 | this.handlers = handlers; 14 | } 15 | 16 | public List getHandlers() { 17 | return handlers; 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | public interface IIdentifierRenamer { 5 | 6 | enum Type {ELEMENT_CLASS, ELEMENT_FIELD, ELEMENT_METHOD} 7 | 8 | boolean toBeRenamed(Type elementType, String className, String element, String descriptor); 9 | 10 | String getNextClassName(String fullName, String shortName); 11 | 12 | String getNextFieldName(String className, String field, String descriptor); 13 | 14 | String getNextMethodName(String className, String method, String descriptor); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/util/DataInputFullStream.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.util; 3 | 4 | import java.io.ByteArrayInputStream; 5 | import java.io.DataInputStream; 6 | import java.io.IOException; 7 | 8 | public class DataInputFullStream extends DataInputStream { 9 | public DataInputFullStream(byte[] bytes) { 10 | super(new ByteArrayInputStream(bytes)); 11 | } 12 | 13 | public byte[] read(int n) throws IOException { 14 | return InterpreterUtil.readBytes(this, n); 15 | } 16 | 17 | public void discard(int n) throws IOException { 18 | InterpreterUtil.discardBytes(this, n); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | 9 | public class StructConstantValueAttribute extends StructGeneralAttribute { 10 | 11 | private int index; 12 | 13 | @Override 14 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 15 | index = data.readUnsignedShort(); 16 | } 17 | 18 | public int getIndex() { 19 | return index; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.collectors; 3 | 4 | public class CounterContainer { 5 | public static final int STATEMENT_COUNTER = 0; 6 | public static final int EXPRESSION_COUNTER = 1; 7 | public static final int VAR_COUNTER = 2; 8 | 9 | private final int[] values = new int[]{1, 1, 1}; 10 | 11 | public void setCounter(int counter, int value) { 12 | values[counter] = value; 13 | } 14 | 15 | public int getCounter(int counter) { 16 | return values[counter]; 17 | } 18 | 19 | public int getCounterAndIncrement(int counter) { 20 | return values[counter]++; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/ExpressionStack.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | import org.jetbrains.java.decompiler.util.ListStack; 6 | 7 | public class ExpressionStack extends ListStack { 8 | public ExpressionStack() { } 9 | 10 | private ExpressionStack(int initialCapacity) { 11 | super(initialCapacity); 12 | } 13 | 14 | @Override 15 | public ExpressionStack copy() { 16 | ExpressionStack copy = new ExpressionStack(size()); 17 | for (Exprent expr : this) copy.push(expr.copy()); 18 | return copy; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 5 | 6 | public class SimpleInstructionSequence extends InstructionSequence { 7 | 8 | public SimpleInstructionSequence() { 9 | } 10 | 11 | public SimpleInstructionSequence(VBStyleCollection collinstr) { 12 | super(collinstr); 13 | } 14 | 15 | @Override 16 | public SimpleInstructionSequence clone() { 17 | SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); 18 | newseq.setPointer(this.getPointer()); 19 | 20 | return newseq; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | import org.jetbrains.java.decompiler.util.ListStack; 6 | 7 | public class ExprentStack extends ListStack { 8 | 9 | public ExprentStack() { 10 | } 11 | 12 | public ExprentStack(ListStack list) { 13 | super(list); 14 | pointer = list.getPointer(); 15 | } 16 | 17 | @Override 18 | public Exprent pop() { 19 | 20 | return this.remove(--pointer); 21 | } 22 | 23 | @Override 24 | public ExprentStack clone() { 25 | return new ExprentStack(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/JumpInstruction.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | public class JumpInstruction extends Instruction { 5 | public int destination; 6 | 7 | public JumpInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { 8 | super(opcode, group, wide, bytecodeVersion, operands); 9 | } 10 | 11 | @Override 12 | public void initInstruction(InstructionSequence seq) { 13 | destination = seq.getPointerByRelOffset(this.operand(0)); 14 | } 15 | 16 | @Override 17 | public JumpInstruction clone() { 18 | JumpInstruction copy = (JumpInstruction)super.clone(); 19 | copy.destination = destination; 20 | return copy; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; 5 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; 6 | 7 | import java.util.LinkedList; 8 | 9 | 10 | public final class ClearStructHelper { 11 | 12 | public static void clearStatements(RootStatement root) { 13 | 14 | LinkedList stack = new LinkedList<>(); 15 | stack.add(root); 16 | 17 | while (!stack.isEmpty()) { 18 | 19 | Statement stat = stack.removeFirst(); 20 | 21 | stat.clearTempInformation(); 22 | 23 | stack.addAll(stat.getStats()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | 9 | public class StructGenericSignatureAttribute extends StructGeneralAttribute { 10 | 11 | private String signature; 12 | 13 | @Override 14 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 15 | int index = data.readUnsignedShort(); 16 | signature = pool.getPrimitiveConstant(index).getString(); 17 | } 18 | 19 | public String getSignature() { 20 | return signature; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.renamer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class PoolInterceptor { 8 | private final Map mapOldToNewNames = new HashMap<>(); 9 | private final Map mapNewToOldNames = new HashMap<>(); 10 | 11 | public void addName(String oldName, String newName) { 12 | mapOldToNewNames.put(oldName, newName); 13 | mapNewToOldNames.put(newName, oldName); 14 | } 15 | 16 | public String getName(String oldName) { 17 | return mapOldToNewNames.get(oldName); 18 | } 19 | 20 | public String getOldName(String newName) { 21 | return mapNewToOldNames.get(newName); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.renamer; 3 | 4 | import org.jetbrains.java.decompiler.struct.StructClass; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ClassWrapperNode { 10 | private final StructClass classStruct; 11 | private final List subclasses = new ArrayList<>(); 12 | 13 | public ClassWrapperNode(StructClass cl) { 14 | this.classStruct = cl; 15 | } 16 | 17 | public void addSubclass(ClassWrapperNode node) { 18 | subclasses.add(node); 19 | } 20 | 21 | public StructClass getClassStruct() { 22 | return classStruct; 23 | } 24 | 25 | public List getSubclasses() { 26 | return subclasses; 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 7 | 8 | import java.io.IOException; 9 | 10 | public class StructAnnDefaultAttribute extends StructGeneralAttribute { 11 | 12 | private Exprent defaultValue; 13 | 14 | @Override 15 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 16 | defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool); 17 | } 18 | 19 | public Exprent getDefaultValue() { 20 | return defaultValue; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/match/IMatchable.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.match; 3 | 4 | public interface IMatchable { 5 | enum MatchProperties { 6 | STATEMENT_TYPE, 7 | STATEMENT_RET, 8 | STATEMENT_STATSIZE, 9 | STATEMENT_EXPRSIZE, 10 | STATEMENT_POSITION, 11 | STATEMENT_IFTYPE, 12 | 13 | EXPRENT_TYPE, 14 | EXPRENT_RET, 15 | EXPRENT_POSITION, 16 | EXPRENT_FUNCTYPE, 17 | EXPRENT_EXITTYPE, 18 | EXPRENT_CONSTTYPE, 19 | EXPRENT_CONSTVALUE, 20 | EXPRENT_INVOCATION_CLASS, 21 | EXPRENT_INVOCATION_SIGNATURE, 22 | EXPRENT_INVOCATION_PARAMETER, 23 | EXPRENT_VAR_INDEX, 24 | EXPRENT_FIELD_NAME, 25 | } 26 | 27 | IMatchable findObject(MatchNode matchNode, int index); 28 | 29 | boolean match(MatchNode matchNode, MatchEngine engine); 30 | } -------------------------------------------------------------------------------- /src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.*; 2 | 3 | public class Test { 4 | public static void main(String[] args) throws Exception { 5 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 6 | for(int a=0;a<10;a++){ 7 | functionTimeoutTest2(executorService); 8 | } 9 | executorService.shutdown(); 10 | 11 | } 12 | 13 | public static void functionTimeoutTest2(ExecutorService executorService) throws Exception { 14 | 15 | Future future = executorService.submit(() -> { 16 | Thread.sleep(1000); 17 | return "success"; 18 | }); 19 | try { 20 | String result = future.get(5, TimeUnit.SECONDS); 21 | System.out.println("result:" + result); 22 | return; 23 | } catch (TimeoutException e) { 24 | System.out.println("超时了!"); 25 | return; 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public class DummyExitStatement extends Statement { 9 | public Set bytecode = null; // offsets of bytecode instructions mapped to dummy exit 10 | 11 | public DummyExitStatement() { 12 | super(StatementType.DUMMY_EXIT); 13 | } 14 | 15 | public void addBytecodeOffsets(Collection bytecodeOffsets) { 16 | if (bytecodeOffsets != null && !bytecodeOffsets.isEmpty()) { 17 | if (bytecode == null) { 18 | bytecode = new HashSet<>(bytecodeOffsets); 19 | } 20 | else { 21 | bytecode.addAll(bytecodeOffsets); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/IResultSaver.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | import java.util.jar.Manifest; 5 | 6 | public interface IResultSaver { 7 | void saveFolder(String path); 8 | 9 | void copyFile(String source, String path, String entryName); 10 | 11 | void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping); 12 | 13 | void createArchive(String path, String archiveName, Manifest manifest); 14 | 15 | void saveDirEntry(String path, String archiveName, String entryName); 16 | 17 | void copyEntry(String source, String path, String archiveName, String entry); 18 | 19 | void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content); 20 | 21 | void closeArchive(String path, String archiveName); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | 6 | public class ExceptionHandler { 7 | public int from = 0; 8 | public int to = 0; 9 | public int handler = 0; 10 | 11 | public int from_instr = 0; 12 | public int to_instr = 0; 13 | public int handler_instr = 0; 14 | 15 | public String exceptionClass = null; 16 | 17 | public String toString() { 18 | String new_line_separator = DecompilerContext.getNewLineSeparator(); 19 | return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator + 20 | "from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator + 21 | "exceptionClass: " + exceptionClass + new_line_separator; 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.collectors; 3 | 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public class VarNamesCollector { 9 | 10 | private final Set usedNames = new HashSet<>(); 11 | 12 | public VarNamesCollector() { } 13 | 14 | public VarNamesCollector(Collection setNames) { 15 | usedNames.addAll(setNames); 16 | } 17 | 18 | public void addName(String value) { 19 | usedNames.add(value); 20 | } 21 | 22 | public String getFreeName(int index) { 23 | return getFreeName("var" + index); 24 | } 25 | 26 | public String getFreeName(String proposition) { 27 | while (usedNames.contains(proposition)) { 28 | proposition += "x"; 29 | } 30 | usedNames.add(proposition); 31 | return proposition; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExpressionList.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class PrimitiveExpressionList { 10 | private final List expressions = new ArrayList<>(); 11 | private final ExpressionStack stack; 12 | 13 | public PrimitiveExpressionList() { 14 | this(new ExpressionStack()); 15 | } 16 | 17 | private PrimitiveExpressionList(ExpressionStack stack) { 18 | this.stack = stack; 19 | } 20 | 21 | public PrimitiveExpressionList copy() { 22 | return new PrimitiveExpressionList(stack.copy()); 23 | } 24 | 25 | public List getExpressions() { 26 | return expressions; 27 | } 28 | 29 | public ExpressionStack getStack() { 30 | return stack; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class PrimitiveExprsList { 10 | 11 | private final List lstExprents = new ArrayList<>(); 12 | 13 | private ExprentStack stack = new ExprentStack(); 14 | 15 | public PrimitiveExprsList() { 16 | } 17 | 18 | public PrimitiveExprsList copyStack() { 19 | PrimitiveExprsList prlst = new PrimitiveExprsList(); 20 | prlst.setStack(stack.clone()); 21 | return prlst; 22 | } 23 | 24 | public List getLstExprents() { 25 | return lstExprents; 26 | } 27 | 28 | public ExprentStack getStack() { 29 | return stack; 30 | } 31 | 32 | public void setStack(ExprentStack stack) { 33 | this.stack = stack; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/decompiler/shxjia/CustomeBytecodeProvider.java: -------------------------------------------------------------------------------- 1 | package org.jetbrains.java.decompiler.main.decompiler.shxjia; 2 | 3 | import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; 4 | import org.jetbrains.java.decompiler.util.InterpreterUtil; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.zip.ZipEntry; 9 | import java.util.zip.ZipFile; 10 | 11 | public class CustomeBytecodeProvider implements IBytecodeProvider { 12 | @Override 13 | public byte[] getBytecode(String externalPath, String internalPath) throws IOException { 14 | File file = new File(externalPath); 15 | if (internalPath == null) { 16 | return InterpreterUtil.getBytes(file); 17 | } else { 18 | try (ZipFile archive = new ZipFile(file)) { 19 | ZipEntry entry = archive.getEntry(internalPath); 20 | if (entry == null) throw new IOException("Entry not found: " + internalPath); 21 | return InterpreterUtil.getBytes(archive, entry); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/FullInstructionSequence.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 5 | 6 | 7 | public class FullInstructionSequence extends InstructionSequence { 8 | 9 | // ***************************************************************************** 10 | // constructors 11 | // ***************************************************************************** 12 | 13 | public FullInstructionSequence(VBStyleCollection collinstr, ExceptionTable extable) { 14 | super(collinstr); 15 | this.exceptionTable = extable; 16 | 17 | // translate raw exception handlers to instr 18 | for (ExceptionHandler handler : extable.getHandlers()) { 19 | handler.from_instr = this.getPointerByAbsOffset(handler.from); 20 | handler.to_instr = this.getPointerByAbsOffset(handler.to); 21 | handler.handler_instr = this.getPointerByAbsOffset(handler.handler); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 5 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 6 | import org.jetbrains.java.decompiler.util.TextBuffer; 7 | 8 | public class RootStatement extends Statement { 9 | private final DummyExitStatement dummyExit; 10 | 11 | public RootStatement(Statement head, DummyExitStatement dummyExit) { 12 | super(StatementType.ROOT); 13 | 14 | first = head; 15 | this.dummyExit = dummyExit; 16 | 17 | stats.addWithKey(first, first.id); 18 | first.setParent(this); 19 | } 20 | 21 | @Override 22 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 23 | return ExprProcessor.listToJava(varDefinitions, indent, tracer).append(first.toJava(indent, tracer)); 24 | } 25 | 26 | public DummyExitStatement getDummyExit() { 27 | return dummyExit; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/Type.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct.gen; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public interface Type { 11 | int getType(); 12 | 13 | int getArrayDim(); 14 | 15 | String getValue(); 16 | 17 | /** 18 | * Checks whether this Type can be annotated. Nested types can't be annotated when the right side of the currently considered types 19 | * contains a reference to a static class. 20 | */ 21 | default boolean isAnnotatable() { 22 | List nestedTypes = Arrays.asList(DecompilerContext.getImportCollector().getNestedName(getValue()).split("\\.")); 23 | if (nestedTypes.isEmpty()) return true; 24 | String curPath = getValue().substring(0, getValue().lastIndexOf('/') + 1) + nestedTypes.get(0) + '$'; 25 | return ExprProcessor.canWriteNestedTypeAnnotation(curPath, nestedTypes.subList(1, nestedTypes.size())); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.main.Fernflower; 5 | import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; 6 | import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; 7 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 8 | 9 | import java.io.File; 10 | import java.util.Map; 11 | 12 | @SuppressWarnings("unused") 13 | public class BaseDecompiler { 14 | private final Fernflower engine; 15 | 16 | public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map options, IFernflowerLogger logger) { 17 | engine = new Fernflower(provider, saver, options, logger); 18 | } 19 | 20 | public void addSource(File source) { 21 | engine.addSource(source); 22 | } 23 | 24 | public void addLibrary(File library) { 25 | engine.addLibrary(library); 26 | } 27 | 28 | public void decompileContext() { 29 | try { 30 | engine.decompileContext(); 31 | } 32 | finally { 33 | engine.clearContext(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 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. 3 | */ 4 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 5 | 6 | import org.jetbrains.java.decompiler.util.TextBuffer; 7 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 8 | 9 | import java.util.List; 10 | 11 | public class AssertExprent extends Exprent { 12 | 13 | private final List parameters; 14 | 15 | public AssertExprent(List parameters) { 16 | super(EXPRENT_ASSERT); 17 | this.parameters = parameters; 18 | } 19 | 20 | @Override 21 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 22 | TextBuffer buffer = new TextBuffer(); 23 | 24 | buffer.append("assert "); 25 | 26 | tracer.addMapping(bytecode); 27 | 28 | if (parameters.get(0) == null) { 29 | buffer.append("false"); 30 | } 31 | else { 32 | buffer.append(parameters.get(0).toJava(indent, tracer)); 33 | } 34 | 35 | if (parameters.size() > 1) { 36 | buffer.append(" : "); 37 | buffer.append(parameters.get(1).toJava(indent, tracer)); 38 | } 39 | 40 | return buffer; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.vars; 3 | 4 | public class VarVersionEdge { // FIXME: can be removed? 5 | 6 | public static final int EDGE_GENERAL = 0; 7 | public static final int EDGE_PHANTOM = 1; 8 | 9 | public final int type; 10 | 11 | public final VarVersionNode source; 12 | 13 | public final VarVersionNode dest; 14 | 15 | private final int hashCode; 16 | 17 | public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) { 18 | this.type = type; 19 | this.source = source; 20 | this.dest = dest; 21 | this.hashCode = source.hashCode() ^ dest.hashCode() + type; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (o == this) return true; 27 | if (!(o instanceof VarVersionEdge edge)) return false; 28 | 29 | return type == edge.type && source == edge.source && dest == edge.dest; 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return hashCode; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return source.toString() + " ->" + type + "-> " + dest.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructPermittedSubclassesAttribute.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /* 12 | PermittedSubclasses_attribute { 13 | u2 attribute_name_index; 14 | u4 attribute_length; 15 | u2 number_of_classes; 16 | u2 classes[number_of_classes]; 17 | } 18 | */ 19 | public class StructPermittedSubclassesAttribute extends StructGeneralAttribute { 20 | List classes; 21 | 22 | @Override 23 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 24 | int numberOfClasses = data.readUnsignedShort(); 25 | String[] classes = new String[numberOfClasses]; 26 | for (int i = 0; i < numberOfClasses; i++) { 27 | classes[i] = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); 28 | } 29 | this.classes = Arrays.asList(classes); 30 | } 31 | 32 | public List getClasses() { 33 | return classes; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class StructExceptionsAttribute extends StructGeneralAttribute { 13 | 14 | private List throwsExceptions; 15 | 16 | @Override 17 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 18 | int len = data.readUnsignedShort(); 19 | if (len > 0) { 20 | throwsExceptions = new ArrayList<>(len); 21 | for (int i = 0; i < len; i++) { 22 | throwsExceptions.add(data.readUnsignedShort()); 23 | } 24 | } 25 | else { 26 | throwsExceptions = Collections.emptyList(); 27 | } 28 | } 29 | 30 | public String getExcClassname(int index, ConstantPool pool) { 31 | return pool.getPrimitiveConstant(throwsExceptions.get(index)).getString(); 32 | } 33 | 34 | public List getThrowsExceptions() { 35 | return throwsExceptions; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.vars; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; 5 | 6 | public class VarVersionPair { 7 | 8 | public final int var; 9 | public final int version; 10 | 11 | private int hashCode = -1; 12 | 13 | public VarVersionPair(int var, int version) { 14 | this.var = var; 15 | this.version = version; 16 | } 17 | 18 | public VarVersionPair(Integer var, Integer version) { 19 | this.var = var; 20 | this.version = version; 21 | } 22 | 23 | public VarVersionPair(VarExprent var) { 24 | this.var = var.getIndex(); 25 | this.version = var.getVersion(); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (o == this) return true; 31 | if (!(o instanceof VarVersionPair paar)) return false; 32 | 33 | return var == paar.var && version == paar.version; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | if (hashCode == -1) { 39 | hashCode = this.var * 3 + this.version; 40 | } 41 | return hashCode; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "(" + var + "," + version + ")"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | 9 | /* 10 | u2 local_variable_type_table_length; 11 | { u2 start_pc; 12 | u2 length; 13 | u2 name_index; 14 | u2 signature_index; 15 | u2 index; 16 | } local_variable_type_table[local_variable_type_table_length]; 17 | */ 18 | public class StructLocalVariableTypeTableAttribute extends StructGeneralAttribute { 19 | // store signature instead of descriptor 20 | private final StructLocalVariableTableAttribute backingAttribute = new StructLocalVariableTableAttribute(); 21 | 22 | @Override 23 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 24 | backingAttribute.initContent(data, pool); 25 | } 26 | 27 | public void add(StructLocalVariableTypeTableAttribute attr) { 28 | backingAttribute.add(attr.backingAttribute); 29 | } 30 | 31 | public String getSignature(int index, int visibleOffset) { 32 | return backingAttribute.getDescriptor(index, visibleOffset); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.gen.generics; 3 | 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class GenericMethodDescriptor { 8 | public final List typeParameters; 9 | public final List> typeParameterBounds; 10 | public final List parameterTypes; 11 | public final GenericType returnType; 12 | public final List exceptionTypes; 13 | 14 | public GenericMethodDescriptor(List typeParameters, 15 | List> typeParameterBounds, 16 | List parameterTypes, 17 | GenericType returnType, 18 | List exceptionTypes) { 19 | this.typeParameters = substitute(typeParameters); 20 | this.typeParameterBounds = substitute(typeParameterBounds); 21 | this.parameterTypes = substitute(parameterTypes); 22 | this.returnType = returnType; 23 | this.exceptionTypes = substitute(exceptionTypes); 24 | } 25 | 26 | private static List substitute(List list) { 27 | return list.isEmpty() ? Collections.emptyList() : list; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.vars; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | import org.jetbrains.java.decompiler.struct.gen.VarType; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class CheckTypesResult { 11 | private final List maxTypeExprents = new ArrayList<>(); 12 | private final List minTypeExprents = new ArrayList<>(); 13 | 14 | public void addMaxTypeExprent(Exprent exprent, VarType type) { 15 | maxTypeExprents.add(new ExprentTypePair(exprent, type)); 16 | } 17 | 18 | public void addMinTypeExprent(Exprent exprent, VarType type) { 19 | minTypeExprents.add(new ExprentTypePair(exprent, type)); 20 | } 21 | 22 | public List getMaxTypeExprents() { 23 | return maxTypeExprents; 24 | } 25 | 26 | public List getMinTypeExprents() { 27 | return minTypeExprents; 28 | } 29 | 30 | static class ExprentTypePair { 31 | public final Exprent exprent; 32 | public final VarType type; 33 | 34 | ExprentTypePair(Exprent exprent, VarType type) { 35 | this.exprent = exprent; 36 | this.type = type; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class StructAnnotationParameterAttribute extends StructGeneralAttribute { 14 | 15 | private List> paramAnnotations; 16 | 17 | @Override 18 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 19 | int len = data.readUnsignedByte(); 20 | if (len > 0) { 21 | paramAnnotations = new ArrayList<>(len); 22 | for (int i = 0; i < len; i++) { 23 | List annotations = StructAnnotationAttribute.parseAnnotations(pool, data); 24 | paramAnnotations.add(annotations); 25 | } 26 | } 27 | else { 28 | paramAnnotations = Collections.emptyList(); 29 | } 30 | } 31 | 32 | public List> getParamAnnotations() { 33 | return paramAnnotations; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.struct.consts.LinkConstant; 6 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 7 | 8 | import java.io.IOException; 9 | 10 | public class StructEnclosingMethodAttribute extends StructGeneralAttribute { 11 | 12 | private String className; 13 | private String methodName; 14 | private String methodDescriptor; 15 | 16 | @Override 17 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 18 | int classIndex = data.readUnsignedShort(); 19 | int methodIndex = data.readUnsignedShort(); 20 | 21 | className = pool.getPrimitiveConstant(classIndex).getString(); 22 | if (methodIndex != 0) { 23 | LinkConstant lk = pool.getLinkConstant(methodIndex); 24 | methodName = lk.elementname; 25 | methodDescriptor = lk.descriptor; 26 | } 27 | } 28 | 29 | public String getClassName() { 30 | return className; 31 | } 32 | 33 | public String getMethodDescriptor() { 34 | return methodDescriptor; 35 | } 36 | 37 | public String getMethodName() { 38 | return methodName; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.StructRecordComponent; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 7 | 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /* 14 | Record_attribute { 15 | u2 attribute_name_index; 16 | u4 attribute_length; 17 | u2 components_count; 18 | record_component_info components[components_count]; 19 | } 20 | */ 21 | public class StructRecordAttribute extends StructGeneralAttribute { 22 | List components; 23 | 24 | @Override 25 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 26 | int componentCount = data.readUnsignedShort(); 27 | StructRecordComponent[] components = new StructRecordComponent[componentCount]; 28 | for (int i = 0; i < componentCount; i++) { 29 | components[i] = StructRecordComponent.create(data, pool); 30 | } 31 | this.components = Arrays.asList(components); 32 | } 33 | 34 | public List getComponents() { 35 | return Collections.unmodifiableList(components); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | public abstract class IFernflowerLogger { 5 | 6 | public enum Severity { 7 | TRACE("TRACE: "), INFO("INFO: "), WARN("WARN: "), ERROR("ERROR: "); 8 | 9 | public final String prefix; 10 | 11 | Severity(String prefix) { 12 | this.prefix = prefix; 13 | } 14 | } 15 | 16 | private Severity severity = Severity.INFO; 17 | 18 | public boolean accepts(Severity severity) { 19 | return severity.ordinal() >= this.severity.ordinal(); 20 | } 21 | 22 | public void setSeverity(Severity severity) { 23 | this.severity = severity; 24 | } 25 | 26 | public abstract void writeMessage(String message, Severity severity); 27 | 28 | public abstract void writeMessage(String message, Severity severity, Throwable t); 29 | 30 | public void writeMessage(String message, Throwable t) { 31 | writeMessage(message, Severity.ERROR, t); 32 | } 33 | 34 | public void startReadingClass(String className) { } 35 | 36 | public void endReadingClass() { } 37 | 38 | public void startClass(String className) { } 39 | 40 | public void endClass() { } 41 | 42 | public void startMethod(String methodName) { } 43 | 44 | public void endMethod() { } 45 | 46 | public void startWriteClass(String className) { } 47 | 48 | public void endWriteClass() { } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.StructMember; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 7 | 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | /* 12 | u2 max_stack; 13 | u2 max_locals; 14 | u4 code_length; 15 | u1 code[]; 16 | u2 exception_table_length; 17 | exception_table[] { 18 | u2 start_pc; 19 | u2 end_pc; 20 | u2 handler_pc; 21 | u2 catch_type; 22 | }; 23 | u2 attributes_count; 24 | attribute_info attributes[]; 25 | */ 26 | public class StructCodeAttribute extends StructGeneralAttribute { 27 | public int localVariables = 0; 28 | public int codeLength = 0; 29 | public int codeFullLength = 0; 30 | public Map codeAttributes; 31 | 32 | @Override 33 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 34 | data.discard(2); 35 | localVariables = data.readUnsignedShort(); 36 | codeLength = data.readInt(); 37 | data.discard(codeLength); 38 | int excLength = data.readUnsignedShort(); 39 | data.discard(excLength * 8); 40 | codeFullLength = codeLength + excLength * 8 + 2; 41 | codeAttributes = StructMember.readAttributes(data, pool); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/StructRecordComponent.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct; 3 | 4 | import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; 7 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 8 | 9 | import java.io.IOException; 10 | import java.util.Map; 11 | 12 | /* 13 | record_component_info { 14 | u2 name_index; 15 | u2 descriptor_index; 16 | u2 attributes_count; 17 | attribute_info attributes[attributes_count]; 18 | } 19 | */ 20 | public class StructRecordComponent extends StructField { 21 | public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException { 22 | int nameIndex = in.readUnsignedShort(); 23 | int descriptorIndex = in.readUnsignedShort(); 24 | 25 | String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); 26 | String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); 27 | 28 | Map attributes = readAttributes(in, pool); 29 | 30 | return new StructRecordComponent(0, attributes, name, descriptor); 31 | } 32 | 33 | private StructRecordComponent(int flags, Map attributes, String name, String descriptor) { 34 | super(flags, attributes, name, descriptor); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.consts; 3 | 4 | public class PrimitiveConstant extends PooledConstant { 5 | public int index; 6 | public Object value; 7 | public boolean isArray; 8 | 9 | public PrimitiveConstant(int type, Object value) { 10 | super(type); 11 | this.value = value; 12 | 13 | initConstant(); 14 | } 15 | 16 | public PrimitiveConstant(int type, int index) { 17 | super(type); 18 | this.index = index; 19 | } 20 | 21 | private void initConstant() { 22 | if (type == CONSTANT_Class) { 23 | String className = getString(); 24 | isArray = (className.length() > 0 && className.charAt(0) == '['); // empty string for a class name seems to be possible in some android files 25 | } 26 | } 27 | 28 | public String getString() { 29 | return (String)value; 30 | } 31 | 32 | @Override 33 | public void resolveConstant(ConstantPool pool) { 34 | if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType || type == CONSTANT_Module || type == CONSTANT_Package) { 35 | value = pool.getPrimitiveConstant(index).getString(); 36 | initConstant(); 37 | } 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (o == this) return true; 43 | if (!(o instanceof PrimitiveConstant cn)) return false; 44 | 45 | return this.type == cn.type && 46 | this.isArray == cn.isArray && 47 | this.value.equals(cn.value); 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/util/ListStack.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.util; 3 | 4 | import java.util.ArrayList; 5 | 6 | public class ListStack extends ArrayList { 7 | private int pointer = 0; 8 | 9 | public ListStack() { 10 | super(); 11 | } 12 | 13 | public ListStack(int initialCapacity) { 14 | super(initialCapacity); 15 | } 16 | 17 | private ListStack(ArrayList list) { 18 | super(list); 19 | } 20 | 21 | public ListStack copy() { 22 | ListStack copy = new ListStack<>(this); 23 | copy.pointer = this.pointer; 24 | return copy; 25 | } 26 | 27 | public void push(T item) { 28 | this.add(item); 29 | pointer++; 30 | } 31 | 32 | public T pop() { 33 | pointer--; 34 | T o = this.get(pointer); 35 | this.remove(pointer); 36 | return o; 37 | } 38 | 39 | public T pop(int count) { 40 | T o = null; 41 | for (int i = count; i > 0; i--) { 42 | o = this.pop(); 43 | } 44 | return o; 45 | } 46 | 47 | public void removeMultiple(int count) { 48 | while (count > 0) { 49 | pointer--; 50 | this.remove(pointer); 51 | count--; 52 | } 53 | } 54 | 55 | public T getByOffset(int offset) { 56 | return this.get(pointer + offset); 57 | } 58 | 59 | public void insertByOffset(int offset, T item) { 60 | this.add(pointer + offset, item); 61 | pointer++; 62 | } 63 | 64 | @Override 65 | public void clear() { 66 | super.clear(); 67 | pointer = 0; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | import org.jetbrains.java.decompiler.util.InterpreterUtil; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * u2 line_number_table_length; 12 | * { u2 start_pc; 13 | * u2 line_number; 14 | * } line_number_table[line_number_table_length]; 15 | * 16 | * Created by Egor on 05.10.2014. 17 | */ 18 | public class StructLineNumberTableAttribute extends StructGeneralAttribute { 19 | private int[] myLineInfo = InterpreterUtil.EMPTY_INT_ARRAY; 20 | 21 | @Override 22 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 23 | int len = data.readUnsignedShort() * 2; 24 | if (len > 0) { 25 | myLineInfo = new int[len]; 26 | for (int i = 0; i < len; i += 2) { 27 | myLineInfo[i] = data.readUnsignedShort(); 28 | myLineInfo[i + 1] = data.readUnsignedShort(); 29 | } 30 | } 31 | else if (myLineInfo.length > 0) { 32 | myLineInfo = InterpreterUtil.EMPTY_INT_ARRAY; 33 | } 34 | } 35 | 36 | public int findLineNumber(int pc) { 37 | if (myLineInfo.length >= 2) { 38 | for (int i = myLineInfo.length - 2; i >= 0; i -= 2) { 39 | if (pc >= myLineInfo[i]) { 40 | return myLineInfo[i + 1]; 41 | } 42 | } 43 | } 44 | return -1; 45 | } 46 | 47 | public int[] getRawData() { 48 | return myLineInfo; 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.sforms; 3 | 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 7 | import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; 8 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | 14 | public class DirectNode { 15 | public final @NotNull DirectNodeType type; 16 | public final @NotNull String id; 17 | public final @NotNull Statement statement; 18 | public final @Nullable BasicBlockStatement block; 19 | public final List successors = new ArrayList<>(); 20 | public final List predecessors = new ArrayList<>(); 21 | public List exprents = new ArrayList<>(); 22 | 23 | public DirectNode(@NotNull DirectNodeType type, @NotNull Statement statement, @NotNull String id) { 24 | this.type = type; 25 | this.statement = statement; 26 | this.id = id; 27 | this.block = null; 28 | } 29 | 30 | public DirectNode(@NotNull DirectNodeType type, @NotNull Statement statement, @NotNull BasicBlockStatement block) { 31 | this.type = type; 32 | this.statement = statement; 33 | this.id = Integer.toString(block.id); 34 | this.block = block; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return id; 40 | } 41 | 42 | public enum DirectNodeType { 43 | DIRECT, 44 | TAIL, 45 | INIT, 46 | CONDITION, 47 | INCREMENT, 48 | TRY 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import org.jetbrains.java.decompiler.main.rels.ClassWrapper; 5 | import org.jetbrains.java.decompiler.main.rels.MethodWrapper; 6 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 7 | import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; 8 | import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; 9 | import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; 10 | 11 | public final class Statements { 12 | public static Statement findFirstData(Statement stat) { 13 | if (stat.getExprents() != null) { 14 | return stat; 15 | } 16 | else if (stat.isLabeled()) { // FIXME: Why?? 17 | return null; 18 | } 19 | 20 | return switch (stat.type) { 21 | case SEQUENCE, IF, ROOT, SWITCH, SYNCHRONIZED -> findFirstData(stat.getFirst()); 22 | default -> null; 23 | }; 24 | } 25 | 26 | public static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper method, ClassWrapper wrapper, boolean withThis) { 27 | if (inv.getFuncType() == InvocationExprent.TYPE_INIT && inv.getInstance().type == Exprent.EXPRENT_VAR) { 28 | VarExprent instVar = (VarExprent)inv.getInstance(); 29 | VarVersionPair varPair = new VarVersionPair(instVar); 30 | String className = method.varproc.getThisVars().get(varPair); 31 | if (className != null) { // any this instance. TODO: Restrict to current class? 32 | return withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassName()); 33 | } 34 | } 35 | 36 | return false; 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.rels; 3 | 4 | import org.jetbrains.java.decompiler.main.collectors.CounterContainer; 5 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; 6 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; 7 | import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; 8 | import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; 9 | import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; 10 | import org.jetbrains.java.decompiler.struct.StructMethod; 11 | 12 | import java.util.HashSet; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | public class MethodWrapper { 17 | public final RootStatement root; 18 | public final VarProcessor varproc; 19 | public final StructMethod methodStruct; 20 | public final CounterContainer counter; 21 | public final Set setOuterVarNames = new HashSet<>(); 22 | 23 | public DirectGraph graph; 24 | public List synthParameters; 25 | public boolean decompiledWithErrors; 26 | 27 | public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) { 28 | this.root = root; 29 | this.varproc = varproc; 30 | this.methodStruct = methodStruct; 31 | this.counter = counter; 32 | } 33 | 34 | public DirectGraph getOrBuildGraph() { 35 | if (graph == null && root != null) { 36 | graph = new FlattenStatementsHelper().buildDirectGraph(root); 37 | } 38 | return graph; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return methodStruct.getName(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 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. 3 | */ 4 | package org.jetbrains.java.decompiler.struct.attr; 5 | 6 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 7 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | /* 15 | u1 parameters_count; 16 | { u2 name_index; 17 | u2 access_flags; 18 | } parameters[parameters_count]; 19 | */ 20 | public class StructMethodParametersAttribute extends StructGeneralAttribute { 21 | private List myEntries; 22 | 23 | @Override 24 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 25 | int len = data.readUnsignedByte(); 26 | List entries; 27 | if (len > 0) { 28 | entries = new ArrayList<>(len); 29 | 30 | for (int i = 0; i < len; i++) { 31 | int nameIndex = data.readUnsignedShort(); 32 | String name = nameIndex != 0 ? pool.getPrimitiveConstant(nameIndex).getString() : null; 33 | int access_flags = data.readUnsignedShort(); 34 | entries.add(new Entry(name, access_flags)); 35 | } 36 | } 37 | else { 38 | entries = Collections.emptyList(); 39 | } 40 | myEntries = Collections.unmodifiableList(entries); 41 | } 42 | 43 | public List getEntries() { 44 | return myEntries; 45 | } 46 | 47 | public static class Entry { 48 | public final String myName; 49 | public final int myAccessFlags; 50 | 51 | public Entry(String name, int accessFlags) { 52 | myName = name; 53 | myAccessFlags = accessFlags; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler.vars; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; 5 | import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | public class VarVersionNode implements IGraphNode { 13 | public static final int FLAG_PHANTOM_FIN_EXIT = 2; 14 | 15 | public final int var; 16 | public final int version; 17 | public final Set predecessors = new HashSet<>(); 18 | public final Set successors = new HashSet<>(); 19 | 20 | public int flags; 21 | public SFormsFastMapDirect live = new SFormsFastMapDirect(); 22 | 23 | public VarVersionNode(int var, int version) { 24 | this.var = var; 25 | this.version = version; 26 | } 27 | 28 | public void addPredecessor(VarVersionEdge edge) { 29 | predecessors.add(edge); 30 | } 31 | 32 | public void removePredecessor(VarVersionEdge edge) { 33 | predecessors.remove(edge); 34 | } 35 | 36 | public void addSuccessor(VarVersionEdge edge) { 37 | successors.add(edge); 38 | } 39 | 40 | public void removeSuccessor(VarVersionEdge edge) { 41 | successors.remove(edge); 42 | } 43 | 44 | @Override 45 | public List getPredecessorNodes() { 46 | List lst = new ArrayList<>(predecessors.size()); 47 | for (VarVersionEdge edge : predecessors) { 48 | lst.add(edge.source); 49 | } 50 | return lst; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "(" + var + '_' + version + ')'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct.gen; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | 6 | public final class FieldDescriptor { 7 | public static final FieldDescriptor SHORT_DESCRIPTOR = parseDescriptor("Ljava/lang/Short;"); 8 | public static final FieldDescriptor INTEGER_DESCRIPTOR = parseDescriptor("Ljava/lang/Integer;"); 9 | public static final FieldDescriptor LONG_DESCRIPTOR = parseDescriptor("Ljava/lang/Long;"); 10 | public static final FieldDescriptor FLOAT_DESCRIPTOR = parseDescriptor("Ljava/lang/Float;"); 11 | public static final FieldDescriptor DOUBLE_DESCRIPTOR = parseDescriptor("Ljava/lang/Double;"); 12 | 13 | public final VarType type; 14 | public final String descriptorString; 15 | 16 | private FieldDescriptor(String descriptor) { 17 | type = new VarType(descriptor); 18 | descriptorString = descriptor; 19 | } 20 | 21 | public static FieldDescriptor parseDescriptor(String descriptor) { 22 | return new FieldDescriptor(descriptor); 23 | } 24 | 25 | public String buildNewDescriptor(NewClassNameBuilder builder) { 26 | if (type.getType() == CodeConstants.TYPE_OBJECT) { 27 | String newClassName = builder.buildNewClassname(type.getValue()); 28 | if (newClassName != null) { 29 | return new VarType(type.getType(), type.getArrayDim(), newClassName).toString(); 30 | } 31 | } 32 | 33 | return null; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (o == this) return true; 39 | if (!(o instanceof FieldDescriptor fd)) return false; 40 | 41 | return type.equals(fd.type); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return type.hashCode(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/SwitchInstruction.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | public class SwitchInstruction extends Instruction { 5 | private int[] destinations; 6 | private int[] values; 7 | private int defaultDestination; 8 | 9 | public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { 10 | super(opcode, group, wide, bytecodeVersion, operands); 11 | } 12 | 13 | @Override 14 | public void initInstruction(InstructionSequence seq) { 15 | defaultDestination = seq.getPointerByRelOffset(operands[0]); 16 | 17 | int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2; 18 | int len = operands.length - prefix; 19 | int low = 0; 20 | if (opcode == CodeConstants.opc_lookupswitch) { 21 | len /= 2; 22 | } 23 | else { 24 | low = operands[1]; 25 | } 26 | 27 | destinations = new int[len]; 28 | values = new int[len]; 29 | for (int i = 0, k = 0; i < len; i++, k++) { 30 | if (opcode == CodeConstants.opc_lookupswitch) { 31 | values[i] = operands[prefix + k]; 32 | k++; 33 | } 34 | else { 35 | values[i] = low + k; 36 | } 37 | destinations[i] = seq.getPointerByRelOffset(operands[prefix + k]); 38 | } 39 | } 40 | 41 | public int[] getDestinations() { 42 | return destinations; 43 | } 44 | 45 | public int[] getValues() { 46 | return values; 47 | } 48 | 49 | public int getDefaultDestination() { 50 | return defaultDestination; 51 | } 52 | 53 | @Override 54 | public SwitchInstruction clone() { 55 | SwitchInstruction copy = (SwitchInstruction)super.clone(); 56 | copy.defaultDestination = defaultDestination; 57 | copy.destinations = destinations.clone(); 58 | copy.values = values.clone(); 59 | return copy; 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.struct.consts.LinkConstant; 6 | import org.jetbrains.java.decompiler.struct.consts.PooledConstant; 7 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class StructBootstrapMethodsAttribute extends StructGeneralAttribute { 14 | 15 | private final List methodRefs = new ArrayList<>(); 16 | private final List> methodArguments = new ArrayList<>(); 17 | 18 | @Override 19 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 20 | int method_number = data.readUnsignedShort(); 21 | 22 | for (int i = 0; i < method_number; ++i) { 23 | int bootstrap_method_ref = data.readUnsignedShort(); 24 | int num_bootstrap_arguments = data.readUnsignedShort(); 25 | 26 | List list_arguments = new ArrayList<>(); 27 | 28 | for (int j = 0; j < num_bootstrap_arguments; ++j) { 29 | int bootstrap_argument_ref = data.readUnsignedShort(); 30 | 31 | list_arguments.add(pool.getConstant(bootstrap_argument_ref)); 32 | } 33 | 34 | methodRefs.add(pool.getLinkConstant(bootstrap_method_ref)); 35 | methodArguments.add(list_arguments); 36 | } 37 | } 38 | 39 | public int getMethodsNumber() { 40 | return methodRefs.size(); 41 | } 42 | 43 | public LinkConstant getMethodReference(int index) { 44 | return methodRefs.get(index); 45 | } 46 | 47 | public List getMethodArguments(int index) { 48 | return methodArguments.get(index); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/decompiler/shxjia/CustomeResultSaver.java: -------------------------------------------------------------------------------- 1 | package org.jetbrains.java.decompiler.main.decompiler.shxjia; 2 | 3 | import org.jetbrains.java.decompiler.main.DecompilerContext; 4 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 5 | import org.jetbrains.java.decompiler.util.InterpreterUtil; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.nio.charset.StandardCharsets; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | import java.util.jar.Manifest; 13 | 14 | public class CustomeResultSaver implements IResultSaver { 15 | @Override 16 | public void saveFolder(String path) { 17 | 18 | } 19 | 20 | @Override 21 | public void copyFile(String source, String path, String entryName) { 22 | try { 23 | InterpreterUtil.copyFile(new File(source), new File(path)); 24 | } catch (IOException ex) { 25 | DecompilerContext.getLogger().writeMessage("Cannot copy " + source + " to " + entryName, ex); 26 | } 27 | } 28 | 29 | @Override 30 | public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { 31 | 32 | try { 33 | Files.write(Paths.get(path), content.getBytes(StandardCharsets.UTF_8)); 34 | } catch (Exception e) { 35 | 36 | } 37 | } 38 | 39 | @Override 40 | public void createArchive(String path, String archiveName, Manifest manifest) { 41 | } 42 | 43 | @Override 44 | public void saveDirEntry(String path, String archiveName, String entryName) { 45 | } 46 | 47 | @Override 48 | public void copyEntry(String source, String path, String archiveName, String entry) { 49 | } 50 | 51 | @Override 52 | public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { 53 | } 54 | 55 | @Override 56 | public void closeArchive(String path, String archiveName) { 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/match/MatchNode.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.match; 3 | 4 | import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class MatchNode { 12 | public static class RuleValue { 13 | public final int parameter; 14 | public final Object value; 15 | 16 | public RuleValue(int parameter, Object value) { 17 | this.parameter = parameter; 18 | this.value = value; 19 | } 20 | 21 | public boolean isVariable() { 22 | String strValue = value.toString(); 23 | return (strValue.charAt(0) == '$' && strValue.charAt(strValue.length() - 1) == '$'); 24 | } 25 | 26 | public String toString() { 27 | return value.toString(); 28 | } 29 | } 30 | 31 | public static final int MATCHNODE_STATEMENT = 0; 32 | public static final int MATCHNODE_EXPRENT = 1; 33 | 34 | private final int type; 35 | private final Map rules = new HashMap<>(); 36 | private final List children = new ArrayList<>(); 37 | 38 | public MatchNode(int type) { 39 | this.type = type; 40 | } 41 | 42 | public void addChild(MatchNode child) { 43 | children.add(child); 44 | } 45 | 46 | public void addRule(MatchProperties property, RuleValue value) { 47 | rules.put(property, value); 48 | } 49 | 50 | public int getType() { 51 | return type; 52 | } 53 | 54 | public List getChildren() { 55 | return children; 56 | } 57 | 58 | public Map getRules() { 59 | return rules; 60 | } 61 | 62 | public Object getRuleValue(MatchProperties property) { 63 | RuleValue rule = rules.get(property); 64 | return rule == null ? null : rule.value; 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 5 | import org.jetbrains.java.decompiler.util.TextBuffer; 6 | 7 | import java.util.Collection; 8 | import java.util.HashSet; 9 | 10 | 11 | public class GeneralStatement extends Statement { 12 | 13 | // ***************************************************************************** 14 | // constructors 15 | // ***************************************************************************** 16 | 17 | private GeneralStatement() { 18 | super(StatementType.GENERAL); 19 | } 20 | 21 | public GeneralStatement(Statement head, Collection statements, Statement post) { 22 | 23 | this(); 24 | 25 | first = head; 26 | stats.addWithKey(head, head.id); 27 | 28 | HashSet set = new HashSet<>(statements); 29 | set.remove(head); 30 | 31 | for (Statement st : set) { 32 | stats.addWithKey(st, st.id); 33 | } 34 | 35 | this.post = post; 36 | } 37 | 38 | // ***************************************************************************** 39 | // public methods 40 | // ***************************************************************************** 41 | 42 | @Override 43 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 44 | TextBuffer buf = new TextBuffer(); 45 | 46 | if (isLabeled()) { 47 | buf.appendIndent(indent).append("label").append(Integer.toString(id)).append(":").appendLineSeparator(); 48 | } 49 | 50 | buf.appendIndent(indent).append("abstract statement {").appendLineSeparator(); 51 | for (Statement stat : stats) { 52 | buf.append(stat.toJava(indent + 1, tracer)); 53 | } 54 | buf.appendIndent(indent).append("}"); 55 | 56 | return buf; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/StructTypePathEntry.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct; 3 | 4 | /** 5 | * An entry in the type path which indicates the path of a type annotation. 6 | */ 7 | public class StructTypePathEntry { 8 | private final int typePathEntryKind; 9 | private final int typeArgumentIndex; 10 | 11 | public StructTypePathEntry(int typePathKind, int typeArgumentIndex) { 12 | this.typePathEntryKind = typePathKind; 13 | this.typeArgumentIndex = typeArgumentIndex; 14 | } 15 | 16 | /** 17 | * @return The type argument index, indicating which nested type argument is annotated. The index starts at 0. 18 | */ 19 | public int getTypeArgumentIndex() { 20 | return typeArgumentIndex; 21 | } 22 | 23 | /** 24 | * @return {@link Kind#id} of this type path entry. 25 | */ 26 | public int getTypePathEntryKind() { 27 | return typePathEntryKind; 28 | } 29 | 30 | /** 31 | * The type_path_kind. 32 | * 33 | * @see The JVM class File Format Spec Table 4.7.20.2 A 34 | */ 35 | public enum Kind { 36 | /** 37 | * Type path entry is an array type contained in e.g. @I String[] @G [] @H [] 38 | */ 39 | ARRAY(0), 40 | 41 | /** 42 | * Type path entry is a nested type contained in e.g. Outer . @B Middle . @C Inner 43 | */ 44 | NESTED(1), 45 | 46 | /** 47 | * Type path entry is a wildcard contained in e.g. Map<@B ? extends String, String> 48 | */ 49 | TYPE_WILDCARD(2), 50 | 51 | /** 52 | * Type path entry is a type argument of a parameterized type contained in e.g. List<@B Comparable<@F Object>> 53 | */ 54 | TYPE(3); 55 | 56 | private final int id; 57 | 58 | Kind(int id) { 59 | this.id = id; 60 | } 61 | 62 | public int getId() { 63 | return id; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/StructField.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct; 3 | 4 | import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; 5 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 6 | import org.jetbrains.java.decompiler.struct.gen.Type; 7 | import org.jetbrains.java.decompiler.struct.gen.VarType; 8 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 9 | 10 | import java.io.IOException; 11 | import java.util.Map; 12 | 13 | /* 14 | field_info { 15 | u2 access_flags; 16 | u2 name_index; 17 | u2 descriptor_index; 18 | u2 attributes_count; 19 | attribute_info attributes[attributes_count]; 20 | } 21 | */ 22 | public class StructField extends StructMember { 23 | public static StructField create(DataInputFullStream in, ConstantPool pool, String clQualifiedName) throws IOException { 24 | int accessFlags = in.readUnsignedShort(); 25 | int nameIndex = in.readUnsignedShort(); 26 | int descriptorIndex = in.readUnsignedShort(); 27 | 28 | String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex); 29 | 30 | Map attributes = readAttributes(in, pool); 31 | 32 | return new StructField(accessFlags, attributes, values[0], values[1]); 33 | } 34 | 35 | private final String name; 36 | private final String descriptor; 37 | 38 | protected StructField(int accessFlags, Map attributes, String name, String descriptor) { 39 | super(accessFlags, attributes); 40 | this.name = name; 41 | this.descriptor = descriptor; 42 | } 43 | 44 | public final String getName() { 45 | return name; 46 | } 47 | 48 | public final String getDescriptor() { 49 | return descriptor; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return name; 55 | } 56 | 57 | @Override 58 | protected Type getType() { 59 | return new VarType(descriptor); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 3 | 4 | public class TypeAnnotation { 5 | public static final int CLASS_TYPE_PARAMETER = 0x00; 6 | public static final int METHOD_TYPE_PARAMETER = 0x01; 7 | public static final int SUPER_TYPE_REFERENCE = 0x10; 8 | public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; 9 | public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; 10 | public static final int FIELD = 0x13; 11 | public static final int METHOD_RETURN_TYPE = 0x14; 12 | public static final int METHOD_RECEIVER = 0x15; 13 | public static final int METHOD_PARAMETER = 0x16; 14 | public static final int THROWS_REFERENCE = 0x17; 15 | public static final int LOCAL_VARIABLE = 0x40; 16 | public static final int RESOURCE_VARIABLE = 0x41; 17 | public static final int CATCH_CLAUSE = 0x42; 18 | public static final int EXPR_INSTANCEOF = 0x43; 19 | public static final int EXPR_NEW = 0x44; 20 | public static final int EXPR_CONSTRUCTOR_REF = 0x45; 21 | public static final int EXPR_METHOD_REF = 0x46; 22 | public static final int TYPE_ARG_CAST = 0x47; 23 | public static final int TYPE_ARG_CONSTRUCTOR_CALL = 0x48; 24 | public static final int TYPE_ARG_METHOD_CALL = 0x49; 25 | public static final int TYPE_ARG_CONSTRUCTOR_REF = 0x4A; 26 | public static final int TYPE_ARG_METHOD_REF = 0x4B; 27 | 28 | private final int target; 29 | private final byte[] path; 30 | private final AnnotationExprent annotation; 31 | 32 | public TypeAnnotation(int target, byte[] path, AnnotationExprent annotation) { 33 | this.target = target; 34 | this.path = path; 35 | this.annotation = annotation; 36 | } 37 | 38 | public int getTargetType() { 39 | return target >> 24; 40 | } 41 | 42 | public int getIndex() { 43 | return target & 0x0FFFF; 44 | } 45 | 46 | public boolean isTopLevel() { 47 | return path == null; 48 | } 49 | 50 | public AnnotationExprent getAnnotation() { 51 | return annotation; 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/typeann/TypeAnnotationWriteHelper.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.modules.decompiler.typeann; 3 | 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.java.decompiler.struct.StructTypePathEntry; 6 | import org.jetbrains.java.decompiler.util.TextBuffer; 7 | 8 | import java.util.ArrayDeque; 9 | import java.util.Deque; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * Wrapper around {@link TypeAnnotation} to maintain the state of the {@link StructTypePathEntry} list while writing. 15 | */ 16 | public class TypeAnnotationWriteHelper { 17 | private final @NotNull Deque paths; 18 | 19 | private final @NotNull TypeAnnotation annotation; 20 | 21 | public TypeAnnotationWriteHelper(@NotNull TypeAnnotation annotation) { 22 | this(annotation, new ArrayDeque<>(annotation.getPaths())); 23 | } 24 | 25 | public TypeAnnotationWriteHelper(@NotNull TypeAnnotation annotation, @NotNull Deque paths) { 26 | this.annotation = annotation; 27 | this.paths = paths; 28 | } 29 | 30 | /** 31 | * @return Active path relative to the current scope when writing. 32 | */ 33 | public @NotNull Deque getPaths() { 34 | return paths; 35 | } 36 | 37 | /** 38 | * @return The annotation to write 39 | */ 40 | public @NotNull TypeAnnotation getAnnotation() { 41 | return annotation; 42 | } 43 | 44 | public void writeTo(@NotNull StringBuilder sb) { 45 | annotation.writeTo(sb); 46 | } 47 | 48 | public void writeTo(@NotNull TextBuffer sb) { 49 | annotation.writeTo(sb); 50 | } 51 | 52 | public int arrayPathCount() { 53 | return (int) paths.stream().filter(entry -> entry.getTypePathEntryKind() == StructTypePathEntry.Kind.ARRAY.getId()).count(); 54 | } 55 | 56 | public static List create(List typeAnnotations) { 57 | return typeAnnotations.stream() 58 | .map(TypeAnnotationWriteHelper::new) 59 | .collect(Collectors.toList()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 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. 3 | */ 4 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 5 | 6 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 7 | import org.jetbrains.java.decompiler.util.TextBuffer; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Objects; 12 | import java.util.Set; 13 | 14 | public class MonitorExprent extends Exprent { 15 | 16 | public static final int MONITOR_ENTER = 0; 17 | public static final int MONITOR_EXIT = 1; 18 | 19 | private final int monType; 20 | private Exprent value; 21 | 22 | public MonitorExprent(int monType, Exprent value, Set bytecodeOffsets) { 23 | super(EXPRENT_MONITOR); 24 | this.monType = monType; 25 | this.value = value; 26 | 27 | addBytecodeOffsets(bytecodeOffsets); 28 | } 29 | 30 | @Override 31 | public Exprent copy() { 32 | return new MonitorExprent(monType, value.copy(), bytecode); 33 | } 34 | 35 | @Override 36 | public List getAllExprents() { 37 | List lst = new ArrayList<>(); 38 | lst.add(value); 39 | return lst; 40 | } 41 | 42 | @Override 43 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 44 | tracer.addMapping(bytecode); 45 | 46 | if (monType == MONITOR_ENTER) { 47 | return value.toJava(indent, tracer).enclose("synchronized(", ")"); 48 | } 49 | else { 50 | return new TextBuffer(); 51 | } 52 | } 53 | 54 | @Override 55 | public void replaceExprent(Exprent oldExpr, Exprent newExpr) { 56 | if (oldExpr == value) { 57 | value = newExpr; 58 | } 59 | } 60 | 61 | @Override 62 | public boolean equals(Object o) { 63 | if (o == this) return true; 64 | if (!(o instanceof MonitorExprent me)) return false; 65 | 66 | return monType == me.getMonType() && 67 | Objects.equals(value, me.getValue()); 68 | } 69 | 70 | public int getMonType() { 71 | return monType; 72 | } 73 | 74 | public Exprent getValue() { 75 | return value; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; 6 | import org.jetbrains.java.decompiler.main.DecompilerContext; 7 | import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; 8 | import org.jetbrains.java.decompiler.main.rels.ClassWrapper; 9 | import org.jetbrains.java.decompiler.main.rels.MethodWrapper; 10 | import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public final class ExprUtil { 17 | public static List getSyntheticParametersMask(String className, String descriptor, int parameters) { 18 | ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className); 19 | return node != null ? getSyntheticParametersMask(node, descriptor, parameters) : null; 20 | } 21 | 22 | public static List getSyntheticParametersMask(ClassNode node, String descriptor, int parameters) { 23 | List mask = null; 24 | 25 | ClassWrapper wrapper = node.getWrapper(); 26 | if (wrapper != null) { 27 | // own class 28 | MethodWrapper methodWrapper = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, descriptor); 29 | if (methodWrapper == null) { 30 | if (DecompilerContext.getOption(IFernflowerPreferences.IGNORE_INVALID_BYTECODE)) { 31 | return null; 32 | } 33 | throw new RuntimeException("Constructor " + node.classStruct.qualifiedName + "." + CodeConstants.INIT_NAME + descriptor + " not found"); 34 | } 35 | mask = methodWrapper.synthParameters; 36 | } 37 | else if (parameters > 0 && node.type == ClassNode.CLASS_MEMBER && (node.access & CodeConstants.ACC_STATIC) == 0) { 38 | // non-static member class 39 | mask = new ArrayList<>(Collections.nCopies(parameters, null)); 40 | mask.set(0, new VarVersionPair(-1, 0)); 41 | } 42 | 43 | return mask; 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/DataPoint.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct.gen; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | import org.jetbrains.java.decompiler.struct.StructMethod; 6 | import org.jetbrains.java.decompiler.util.ListStack; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class DataPoint { 12 | private final List localVariables; 13 | private final ListStack stack; 14 | 15 | public DataPoint() { 16 | this(new ArrayList<>(), new ListStack<>()); 17 | } 18 | 19 | private DataPoint(List localVariables, ListStack stack) { 20 | this.localVariables = localVariables; 21 | this.stack = stack; 22 | } 23 | 24 | public DataPoint copy() { 25 | return new DataPoint(new ArrayList<>(localVariables), stack.copy()); 26 | } 27 | 28 | public void setVariable(int index, VarType value) { 29 | if (index >= localVariables.size()) { 30 | for (int i = localVariables.size(); i <= index; i++) { 31 | localVariables.add(new VarType(CodeConstants.TYPE_NOTINITIALIZED)); 32 | } 33 | } 34 | 35 | localVariables.set(index, value); 36 | } 37 | 38 | public VarType getVariable(int index) { 39 | if (index < localVariables.size()) { 40 | return localVariables.get(index); 41 | } 42 | else { 43 | return new VarType(CodeConstants.TYPE_NOTINITIALIZED); 44 | } 45 | } 46 | 47 | public static DataPoint getInitialDataPoint(StructMethod mt) { 48 | DataPoint point = new DataPoint(); 49 | 50 | MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); 51 | 52 | int k = 0; 53 | if (!mt.hasModifier(CodeConstants.ACC_STATIC)) { 54 | point.setVariable(k++, new VarType(CodeConstants.TYPE_OBJECT, 0, null)); 55 | } 56 | 57 | for (int i = 0; i < md.params.length; i++) { 58 | VarType var = md.params[i]; 59 | 60 | point.setVariable(k++, var); 61 | if (var.getStackSize() == 2) { 62 | point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY)); 63 | } 64 | } 65 | 66 | return point; 67 | } 68 | 69 | public ListStack getStack() { 70 | return stack; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class StructInnerClassesAttribute extends StructGeneralAttribute { 13 | public static final class Entry { 14 | public final int outerNameIdx; 15 | public final int simpleNameIdx; 16 | public final int accessFlags; 17 | public final String innerName; 18 | public final String enclosingName; 19 | public final String simpleName; 20 | 21 | private Entry(int outerNameIdx, int simpleNameIdx, int accessFlags, String innerName, String enclosingName, String simpleName) { 22 | this.outerNameIdx = outerNameIdx; 23 | this.simpleNameIdx = simpleNameIdx; 24 | this.accessFlags = accessFlags; 25 | this.innerName = innerName; 26 | this.enclosingName = enclosingName; 27 | this.simpleName = simpleName; 28 | } 29 | } 30 | 31 | private List entries; 32 | 33 | @Override 34 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 35 | int len = data.readUnsignedShort(); 36 | if (len > 0) { 37 | entries = new ArrayList<>(len); 38 | 39 | for (int i = 0; i < len; i++) { 40 | int innerNameIdx = data.readUnsignedShort(); 41 | int outerNameIdx = data.readUnsignedShort(); 42 | int simpleNameIdx = data.readUnsignedShort(); 43 | int accessFlags = data.readUnsignedShort(); 44 | 45 | String innerName = pool.getPrimitiveConstant(innerNameIdx).getString(); 46 | String outerName = outerNameIdx != 0 ? pool.getPrimitiveConstant(outerNameIdx).getString() : null; 47 | String simpleName = simpleNameIdx != 0 ? pool.getPrimitiveConstant(simpleNameIdx).getString() : null; 48 | 49 | entries.add(new Entry(outerNameIdx, simpleNameIdx, accessFlags, innerName, outerName, simpleName)); 50 | } 51 | } 52 | else { 53 | entries = Collections.emptyList(); 54 | } 55 | } 56 | 57 | public List getEntries() { 58 | return entries; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; 5 | import org.jetbrains.java.decompiler.struct.gen.VarType; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public final class ClasspathHelper { 13 | 14 | private static final Map METHOD_CACHE = Collections.synchronizedMap(new HashMap<>()); 15 | 16 | public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) { 17 | String targetClass = classname.replace('/', '.'); 18 | String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor); 19 | 20 | Method method; 21 | if (METHOD_CACHE.containsKey(methodSignature)) { 22 | method = METHOD_CACHE.get(methodSignature); 23 | } 24 | else { 25 | method = findMethodOnClasspath(targetClass, methodSignature); 26 | METHOD_CACHE.put(methodSignature, method); 27 | } 28 | 29 | return method; 30 | } 31 | 32 | private static Method findMethodOnClasspath(String targetClass, String methodSignature) { 33 | try { 34 | // use bootstrap classloader to only provide access to JRE classes 35 | Class cls = new ClassLoader(null) {}.loadClass(targetClass); 36 | for (Method mtd : cls.getMethods()) { 37 | // use contains() to ignore access modifiers and thrown exceptions 38 | if (mtd.toString().contains(methodSignature)) { 39 | return mtd; 40 | } 41 | } 42 | } 43 | catch (Exception e) { 44 | // ignore 45 | } 46 | return null; 47 | } 48 | 49 | private static String buildMethodSignature(String name, MethodDescriptor md) { 50 | StringBuilder sb = new StringBuilder(); 51 | 52 | appendType(sb, md.ret); 53 | sb.append(' ').append(name).append('('); 54 | for (VarType param : md.params) { 55 | appendType(sb, param); 56 | sb.append(','); 57 | } 58 | if (sb.charAt(sb.length() - 1) == ',') { 59 | sb.setLength(sb.length() - 1); 60 | } 61 | sb.append(')'); 62 | 63 | return sb.toString(); 64 | } 65 | 66 | private static void appendType(StringBuilder sb, VarType type) { 67 | sb.append(type.getValue().replace('/', '.')); 68 | for (int i = 0; i < type.getArrayDim(); i++) { 69 | sb.append("[]"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | import org.jetbrains.java.decompiler.code.Instruction; 6 | import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; 7 | import org.jetbrains.java.decompiler.code.cfg.BasicBlock; 8 | import org.jetbrains.java.decompiler.main.DecompilerContext; 9 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 10 | import org.jetbrains.java.decompiler.main.collectors.CounterContainer; 11 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 12 | import org.jetbrains.java.decompiler.util.TextBuffer; 13 | 14 | public class BasicBlockStatement extends Statement { 15 | private final BasicBlock block; 16 | 17 | public BasicBlockStatement(BasicBlock block) { 18 | super(StatementType.BASIC_BLOCK, block.id); 19 | this.block = block; 20 | 21 | CounterContainer container = DecompilerContext.getCounterContainer(); 22 | if (id >= container.getCounter(CounterContainer.STATEMENT_COUNTER)) { 23 | container.setCounter(CounterContainer.STATEMENT_COUNTER, id + 1); 24 | } 25 | 26 | Instruction instr = block.getLastInstruction(); 27 | if (instr != null) { 28 | if (instr.group == CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) { 29 | lastBasicType = StatementType.IF; 30 | } 31 | else if (instr.group == CodeConstants.GROUP_SWITCH) { 32 | lastBasicType = StatementType.SWITCH; 33 | } 34 | } 35 | 36 | buildMonitorFlags(); 37 | } 38 | 39 | @Override 40 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 41 | TextBuffer tb = ExprProcessor.listToJava(varDefinitions, indent, tracer); 42 | tb.append(ExprProcessor.listToJava(exprents, indent, tracer)); 43 | return tb; 44 | } 45 | 46 | @Override 47 | public Statement getSimpleCopy() { 48 | int id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER); 49 | 50 | SimpleInstructionSequence seq = new SimpleInstructionSequence(); 51 | for (int i = 0; i < block.getSeq().length(); i++) { 52 | seq.addInstruction(block.getSeq().getInstr(i).clone(), -1); 53 | } 54 | 55 | return new BasicBlockStatement(new BasicBlock(id, seq)); 56 | } 57 | 58 | public BasicBlock getBlock() { 59 | return block; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code.cfg; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class ExceptionRangeCFG { 11 | private final List protectedRange; // FIXME: replace with set 12 | private BasicBlock handler; 13 | private List exceptionTypes; 14 | 15 | public ExceptionRangeCFG(List protectedRange, BasicBlock handler, List exceptionType) { 16 | this.protectedRange = protectedRange; 17 | this.handler = handler; 18 | 19 | if (exceptionType != null) { 20 | this.exceptionTypes = new ArrayList<>(exceptionType); 21 | } 22 | } 23 | 24 | public boolean isCircular() { 25 | return protectedRange.contains(handler); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | String new_line_separator = DecompilerContext.getNewLineSeparator(); 31 | StringBuilder buf = new StringBuilder(); 32 | 33 | buf.append("exceptionType:"); 34 | 35 | if (exceptionTypes == null) { 36 | buf.append(" null"); 37 | } 38 | else { 39 | for (String exception_type : exceptionTypes) { 40 | buf.append(" ").append(exception_type); 41 | } 42 | } 43 | 44 | buf.append(new_line_separator); 45 | 46 | buf.append("handler: ").append(handler.id).append(new_line_separator); 47 | buf.append("range: "); 48 | for (BasicBlock block : protectedRange) { 49 | buf.append(block.id).append(" "); 50 | } 51 | buf.append(new_line_separator); 52 | 53 | return buf.toString(); 54 | } 55 | 56 | public BasicBlock getHandler() { 57 | return handler; 58 | } 59 | 60 | public void setHandler(BasicBlock handler) { 61 | this.handler = handler; 62 | } 63 | 64 | public List getProtectedRange() { 65 | return protectedRange; 66 | } 67 | 68 | public List getExceptionTypes() { 69 | return this.exceptionTypes; 70 | } 71 | 72 | public void addExceptionType(String exceptionType) { 73 | if (this.exceptionTypes == null) { 74 | return; 75 | } 76 | 77 | if (exceptionType == null) { 78 | this.exceptionTypes = null; 79 | } 80 | else { 81 | this.exceptionTypes.add(exceptionType); 82 | } 83 | } 84 | 85 | public String getUniqueExceptionsString() { 86 | return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(":")) : null; 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; 5 | import org.jetbrains.java.decompiler.util.TextUtil; 6 | 7 | import java.io.PrintStream; 8 | 9 | public class PrintStreamLogger extends IFernflowerLogger { 10 | 11 | private final PrintStream stream; 12 | private int indent; 13 | 14 | public PrintStreamLogger(PrintStream printStream) { 15 | stream = printStream; 16 | indent = 0; 17 | } 18 | 19 | @Override 20 | public void writeMessage(String message, Severity severity) { 21 | if (accepts(severity)) { 22 | stream.println(severity.prefix + TextUtil.getIndentString(indent) + message); 23 | } 24 | } 25 | 26 | @Override 27 | public void writeMessage(String message, Severity severity, Throwable t) { 28 | if (accepts(severity)) { 29 | writeMessage(message, severity); 30 | t.printStackTrace(stream); 31 | } 32 | } 33 | 34 | @Override 35 | public void startReadingClass(String className) { 36 | if (accepts(Severity.INFO)) { 37 | writeMessage("Decompiling class " + className, Severity.INFO); 38 | ++indent; 39 | } 40 | } 41 | 42 | @Override 43 | public void endReadingClass() { 44 | if (accepts(Severity.INFO)) { 45 | --indent; 46 | writeMessage("... done", Severity.INFO); 47 | } 48 | } 49 | 50 | @Override 51 | public void startClass(String className) { 52 | if (accepts(Severity.INFO)) { 53 | writeMessage("Processing class " + className, Severity.TRACE); 54 | ++indent; 55 | } 56 | } 57 | 58 | @Override 59 | public void endClass() { 60 | if (accepts(Severity.INFO)) { 61 | --indent; 62 | writeMessage("... proceeded", Severity.TRACE); 63 | } 64 | } 65 | 66 | @Override 67 | public void startMethod(String methodName) { 68 | if (accepts(Severity.INFO)) { 69 | writeMessage("Processing method " + methodName, Severity.TRACE); 70 | ++indent; 71 | } 72 | } 73 | 74 | @Override 75 | public void endMethod() { 76 | if (accepts(Severity.INFO)) { 77 | --indent; 78 | writeMessage("... proceeded", Severity.TRACE); 79 | } 80 | } 81 | 82 | @Override 83 | public void startWriteClass(String className) { 84 | if (accepts(Severity.INFO)) { 85 | writeMessage("Writing class " + className, Severity.TRACE); 86 | ++indent; 87 | } 88 | } 89 | 90 | @Override 91 | public void endWriteClass() { 92 | if (accepts(Severity.INFO)) { 93 | --indent; 94 | writeMessage("... written", Severity.TRACE); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/EnumProcessor.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | import org.jetbrains.java.decompiler.main.rels.ClassWrapper; 6 | import org.jetbrains.java.decompiler.main.rels.MethodWrapper; 7 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 8 | import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; 9 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; 10 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements; 11 | import org.jetbrains.java.decompiler.struct.StructClass; 12 | import org.jetbrains.java.decompiler.struct.StructField; 13 | import org.jetbrains.java.decompiler.struct.StructMethod; 14 | import org.jetbrains.java.decompiler.util.InterpreterUtil; 15 | 16 | public final class EnumProcessor { 17 | public static void clearEnum(ClassWrapper wrapper) { 18 | StructClass cl = wrapper.getClassStruct(); 19 | 20 | // hide values/valueOf methods and super() invocations 21 | for (MethodWrapper method : wrapper.getMethods()) { 22 | StructMethod mt = method.methodStruct; 23 | String name = mt.getName(); 24 | String descriptor = mt.getDescriptor(); 25 | 26 | if ("values".equals(name)) { 27 | if (descriptor.equals("()[L" + cl.qualifiedName + ";")) { 28 | wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor)); 29 | } 30 | } 31 | else if ("valueOf".equals(name)) { 32 | if (descriptor.equals("(Ljava/lang/String;)L" + cl.qualifiedName + ";")) { 33 | wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor)); 34 | } 35 | } 36 | else if (CodeConstants.INIT_NAME.equals(name)) { 37 | Statement firstData = Statements.findFirstData(method.root); 38 | if (firstData != null && !firstData.getExprents().isEmpty()) { 39 | Exprent exprent = firstData.getExprents().get(0); 40 | if (exprent.type == Exprent.EXPRENT_INVOCATION) { 41 | InvocationExprent invExpr = (InvocationExprent)exprent; 42 | if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) { 43 | firstData.getExprents().remove(0); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | // hide synthetic fields of enum and it's constants 51 | for (StructField fd : cl.getFields()) { 52 | String descriptor = fd.getDescriptor(); 53 | if (fd.isSynthetic() && descriptor.equals("[L" + cl.qualifiedName + ";")) { 54 | wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), descriptor)); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.struct.consts; 3 | 4 | public class LinkConstant extends PooledConstant { 5 | public int index1, index2; 6 | public String classname; 7 | public String elementname; 8 | public String descriptor; 9 | 10 | public LinkConstant(int type, String classname, String elementname, String descriptor) { 11 | super(type); 12 | this.classname = classname; 13 | this.elementname = elementname; 14 | this.descriptor = descriptor; 15 | 16 | initConstant(); 17 | } 18 | 19 | public LinkConstant(int type, int index1, int index2) { 20 | super(type); 21 | this.index1 = index1; 22 | this.index2 = index2; 23 | } 24 | 25 | private void initConstant() { 26 | if (type == CONSTANT_Methodref || 27 | type == CONSTANT_InterfaceMethodref || 28 | type == CONSTANT_InvokeDynamic || 29 | (type == CONSTANT_MethodHandle && index1 != CONSTANT_MethodHandle_REF_getField && index1 != CONSTANT_MethodHandle_REF_putField)) { 30 | int parenth = descriptor.indexOf(')'); 31 | if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') { 32 | throw new IllegalArgumentException("Invalid descriptor: " + descriptor + 33 | "; type = " + type + "; classname = " + classname + "; elementname = " + elementname); 34 | } 35 | } 36 | } 37 | 38 | @Override 39 | public void resolveConstant(ConstantPool pool) { 40 | if (type == CONSTANT_NameAndType) { 41 | elementname = pool.getPrimitiveConstant(index1).getString(); 42 | descriptor = pool.getPrimitiveConstant(index2).getString(); 43 | } 44 | else if (type == CONSTANT_MethodHandle) { 45 | LinkConstant ref_info = pool.getLinkConstant(index2); 46 | 47 | classname = ref_info.classname; 48 | elementname = ref_info.elementname; 49 | descriptor = ref_info.descriptor; 50 | } 51 | else { 52 | if (type != CONSTANT_InvokeDynamic && type != CONSTANT_Dynamic) { 53 | classname = pool.getPrimitiveConstant(index1).getString(); 54 | } 55 | 56 | LinkConstant nametype = pool.getLinkConstant(index2); 57 | elementname = nametype.elementname; 58 | descriptor = nametype.descriptor; 59 | } 60 | 61 | initConstant(); 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) { 66 | if (o == this) return true; 67 | if (!(o instanceof LinkConstant cn)) return false; 68 | 69 | return this.type == cn.type && 70 | this.elementname.equals(cn.elementname) && 71 | this.descriptor.equals(cn.descriptor) && 72 | (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/util/InterpreterUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.util; 3 | 4 | import java.io.*; 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.zip.ZipEntry; 8 | import java.util.zip.ZipFile; 9 | 10 | public final class InterpreterUtil { 11 | public static final boolean IS_WINDOWS = System.getProperty("os.name", "").startsWith("Windows"); 12 | 13 | public static final int[] EMPTY_INT_ARRAY = new int[0]; 14 | 15 | private static final int BUFFER_SIZE = 16 * 1024; 16 | 17 | public static void copyFile(File source, File target) throws IOException { 18 | try (FileInputStream in = new FileInputStream(source); FileOutputStream out = new FileOutputStream(target)) { 19 | copyStream(in, out); 20 | } 21 | } 22 | 23 | public static void copyStream(InputStream in, OutputStream out) throws IOException { 24 | byte[] buffer = new byte[BUFFER_SIZE]; 25 | int len; 26 | while ((len = in.read(buffer)) >= 0) { 27 | out.write(buffer, 0, len); 28 | } 29 | } 30 | 31 | public static byte[] getBytes(ZipFile archive, ZipEntry entry) throws IOException { 32 | try (InputStream stream = archive.getInputStream(entry)) { 33 | return readBytes(stream, (int)entry.getSize()); 34 | } 35 | } 36 | 37 | public static byte[] getBytes(File file) throws IOException { 38 | try (FileInputStream stream = new FileInputStream(file)) { 39 | return readBytes(stream, (int)file.length()); 40 | } 41 | } 42 | 43 | public static byte[] readBytes(InputStream stream, int length) throws IOException { 44 | byte[] bytes = new byte[length]; 45 | 46 | int n = 0, off = 0; 47 | while (n < length) { 48 | int count = stream.read(bytes, off + n, length - n); 49 | if (count < 0) { 50 | throw new IOException("premature end of stream"); 51 | } 52 | n += count; 53 | } 54 | 55 | return bytes; 56 | } 57 | 58 | public static void discardBytes(InputStream stream, int length) throws IOException { 59 | if (stream.skip(length) != length) { 60 | throw new IOException("premature end of stream"); 61 | } 62 | } 63 | 64 | public static boolean equalSets(Collection c1, Collection c2) { 65 | if (c1 == null) { 66 | return c2 == null; 67 | } 68 | else if (c2 == null) { 69 | return false; 70 | } 71 | 72 | if (c1.size() != c2.size()) { 73 | return false; 74 | } 75 | 76 | HashSet set = new HashSet<>(c1); 77 | set.removeAll(c2); 78 | return (set.size() == 0); 79 | } 80 | 81 | public static String makeUniqueKey(String name, String descriptor) { 82 | return name + ' ' + descriptor; 83 | } 84 | 85 | public static String makeUniqueKey(String name, String descriptor1, String descriptor2) { 86 | return name + ' ' + descriptor1 + ' ' + descriptor2; 87 | } 88 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | jsx.ink 8 | fernflower 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | 19 | org.assertj 20 | assertj-core 21 | 3.12.2 22 | test 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-compiler-plugin 33 | 34 | 17 35 | 8 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-shade-plugin 42 | 3.2.4 43 | 44 | 45 | package 46 | 47 | shade 48 | 49 | 50 | 51 | 52 | *:* 53 | 54 | META-INF/*.SF 55 | META-INF/*.DSA 56 | META-INF/*.RSA 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.jetbrains.java.decompiler.main.decompiler.shxjia.CustomeDecompiler 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/util/FilterClass.java: -------------------------------------------------------------------------------- 1 | //package org.jetbrains.java.decompiler.util; 2 | // 3 | //import java.io.BufferedReader; 4 | //import java.io.File; 5 | //import java.io.FileReader; 6 | //import java.io.IOException; 7 | //import java.util.ArrayList; 8 | //import java.util.List; 9 | // 10 | // 11 | ////modify新增的筛选功能,创建源码文件、写源码内容时调用 12 | //public class FilterClass { 13 | // 14 | // public static boolean FILTER_FLAG = true; 15 | // private static boolean USE_WHITE = false; 16 | // private static List whiteList; 17 | // private static List blackList; 18 | // 19 | // static { 20 | // try { 21 | // System.out.println("start!"); 22 | // whiteList = getFileList("white"); 23 | // System.out.println(whiteList.size()); 24 | // blackList = getFileList("black"); 25 | // if (whiteList != null&&whiteList.size()>0) { 26 | // USE_WHITE = true; 27 | // } 28 | // if (blackList == null||blackList.size()<1) { 29 | // FILTER_FLAG = false; 30 | // } 31 | // System.out.println(FILTER_FLAG); 32 | // System.out.println(USE_WHITE); 33 | // } catch (Exception e) { 34 | // e.printStackTrace(); 35 | // FILTER_FLAG = false; 36 | // } 37 | // } 38 | // 39 | // public static void main(String[] args) { 40 | // String jarPath = getJarPath(); 41 | // System.out.println(whiteList.size()); 42 | // System.out.println(blackList.size()); 43 | // 44 | // System.out.println(new File(jarPath).exists()); 45 | // } 46 | // 47 | // public static boolean judge(String className) { 48 | // //System.out.println(className); 49 | // if (FILTER_FLAG) { 50 | // //判断是否使用白名单,白名单启用的话,黑名单失效 51 | // if (USE_WHITE) { 52 | // return findArray(className, whiteList); 53 | // } else { 54 | // return !findArray(className, blackList); 55 | // } 56 | // } 57 | // return false; 58 | // } 59 | // 60 | // private static boolean findArray(String str, List list) { 61 | // //System.out.println("查找class:"+str); 62 | // for (String temp : list) { 63 | // if (str.contains(temp)) { 64 | // return true; 65 | // } 66 | // } 67 | // return false; 68 | // } 69 | // 70 | // 71 | // private static List getFileList(String type) throws IOException { 72 | // String jarPath = getJarPath(); 73 | // String filePath = jarPath + "." + type + ".txt"; 74 | // File file = new File(filePath); 75 | // if (file.exists()) { 76 | // ArrayList list = new ArrayList(); 77 | // BufferedReader br = new BufferedReader(new FileReader(file)); 78 | // String temp; 79 | // while ((temp = br.readLine()) != null) { 80 | // list.add(temp); 81 | // } 82 | // br.close(); 83 | // return list; 84 | // } 85 | // return null; 86 | // } 87 | // 88 | // private static String getJarPath() { 89 | // System.out.println("getJarPath"); 90 | // String path = FilterClass.class.getProtectionDomain().getCodeSource().getLocation().getFile(); 91 | // System.out.println("path is:"+path); 92 | // return path; 93 | // } 94 | //} 95 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/Instruction.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import org.jetbrains.java.decompiler.util.TextUtil; 5 | 6 | public class Instruction implements CodeConstants { 7 | public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) { 8 | if (opcode >= opc_ifeq && opcode <= opc_if_acmpne || 9 | opcode == opc_ifnull || opcode == opc_ifnonnull || 10 | opcode == opc_jsr || opcode == opc_jsr_w || 11 | opcode == opc_goto || opcode == opc_goto_w) { 12 | return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands); 13 | } 14 | else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) { 15 | return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands); 16 | } 17 | else { 18 | return new Instruction(opcode, group, wide, bytecodeVersion, operands); 19 | } 20 | } 21 | 22 | public static boolean equals(Instruction i1, Instruction i2) { 23 | return i1 != null && i2 != null && 24 | (i1 == i2 || 25 | i1.opcode == i2.opcode && 26 | i1.wide == i2.wide && 27 | i1.operandsCount() == i2.operandsCount()); 28 | } 29 | 30 | public final int opcode; 31 | public final int group; 32 | public final boolean wide; 33 | public final int bytecodeVersion; 34 | 35 | protected final int[] operands; 36 | 37 | public Instruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { 38 | this.opcode = opcode; 39 | this.group = group; 40 | this.wide = wide; 41 | this.bytecodeVersion = bytecodeVersion; 42 | this.operands = operands; 43 | } 44 | 45 | public void initInstruction(InstructionSequence seq) { } 46 | 47 | public int operandsCount() { 48 | return operands == null ? 0 : operands.length; 49 | } 50 | 51 | public int operand(int index) { 52 | return operands[index]; 53 | } 54 | 55 | public boolean canFallThrough() { 56 | return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret && 57 | !(opcode >= opc_ireturn && opcode <= opc_return) && 58 | opcode != opc_athrow && 59 | opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | StringBuilder res = new StringBuilder(); 65 | if (wide) res.append("@wide "); 66 | res.append("@").append(TextUtil.getInstructionName(opcode)); 67 | 68 | int len = operandsCount(); 69 | for (int i = 0; i < len; i++) { 70 | int op = operands[i]; 71 | if (op < 0) { 72 | res.append(" -").append(Integer.toHexString(-op)); 73 | } 74 | else { 75 | res.append(" ").append(Integer.toHexString(op)); 76 | } 77 | } 78 | 79 | return res.toString(); 80 | } 81 | 82 | @Override 83 | @SuppressWarnings("MethodDoesntCallSuperMethod") 84 | public Instruction clone() { 85 | return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone()); 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 6 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 7 | import org.jetbrains.java.decompiler.util.TextBuffer; 8 | 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | public class AnnotationExprent extends Exprent { 13 | public static final int ANNOTATION_NORMAL = 1; 14 | public static final int ANNOTATION_MARKER = 2; 15 | public static final int ANNOTATION_SINGLE_ELEMENT = 3; 16 | 17 | private final String className; 18 | private final List parNames; 19 | private final List parValues; 20 | 21 | public AnnotationExprent(String className, List parNames, List parValues) { 22 | super(EXPRENT_ANNOTATION); 23 | this.className = className; 24 | this.parNames = parNames; 25 | this.parValues = parValues; 26 | } 27 | 28 | @Override 29 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 30 | TextBuffer buffer = new TextBuffer(); 31 | 32 | buffer.appendIndent(indent); 33 | buffer.append('@'); 34 | buffer.append(DecompilerContext.getImportCollector().getNestedName(ExprProcessor.buildJavaClassName(className))); 35 | 36 | int type = getAnnotationType(); 37 | 38 | if (type != ANNOTATION_MARKER) { 39 | buffer.append('('); 40 | 41 | boolean oneLiner = type == ANNOTATION_SINGLE_ELEMENT || indent < 0; 42 | 43 | for (int i = 0; i < parNames.size(); i++) { 44 | if (!oneLiner) { 45 | buffer.appendLineSeparator().appendIndent(indent + 1); 46 | } 47 | 48 | if (type != ANNOTATION_SINGLE_ELEMENT) { 49 | buffer.append(parNames.get(i)); 50 | buffer.append(" = "); 51 | } 52 | 53 | buffer.append(parValues.get(i).toJava(0, tracer)); 54 | 55 | if (i < parNames.size() - 1) { 56 | buffer.append(','); 57 | } 58 | } 59 | 60 | if (!oneLiner) { 61 | buffer.appendLineSeparator().appendIndent(indent); 62 | } 63 | 64 | buffer.append(')'); 65 | } 66 | 67 | return buffer; 68 | } 69 | 70 | public String getClassName() { 71 | return className; 72 | } 73 | 74 | public int getAnnotationType() { 75 | if (parNames.isEmpty()) { 76 | return ANNOTATION_MARKER; 77 | } 78 | else if (parNames.size() == 1 && "value".equals(parNames.get(0))) { 79 | return ANNOTATION_SINGLE_ELEMENT; 80 | } 81 | else { 82 | return ANNOTATION_NORMAL; 83 | } 84 | } 85 | 86 | @Override 87 | public int hashCode() { 88 | return Objects.hash(className, parNames, parValues); 89 | } 90 | 91 | @Override 92 | public boolean equals(Object o) { 93 | if (o == this) return true; 94 | if (!(o instanceof AnnotationExprent ann)) return false; 95 | 96 | return className.equals(ann.className) && 97 | parNames.equals(ann.parNames) && 98 | parValues.equals(ann.parValues); 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 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. 3 | */ 4 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 5 | 6 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 7 | import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; 8 | import org.jetbrains.java.decompiler.struct.gen.VarType; 9 | import org.jetbrains.java.decompiler.util.TextBuffer; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Objects; 14 | import java.util.Set; 15 | 16 | public class SwitchExprent extends Exprent { 17 | 18 | private Exprent value; 19 | private List> caseValues = new ArrayList<>(); 20 | 21 | public SwitchExprent(Exprent value, Set bytecodeOffsets) { 22 | super(EXPRENT_SWITCH); 23 | this.value = value; 24 | 25 | addBytecodeOffsets(bytecodeOffsets); 26 | } 27 | 28 | @Override 29 | public Exprent copy() { 30 | SwitchExprent swExpr = new SwitchExprent(value.copy(), bytecode); 31 | 32 | List> lstCaseValues = new ArrayList<>(); 33 | for (List lst : caseValues) { 34 | lstCaseValues.add(new ArrayList<>(lst)); 35 | } 36 | swExpr.setCaseValues(lstCaseValues); 37 | 38 | return swExpr; 39 | } 40 | 41 | @Override 42 | public VarType getExprType() { 43 | return value.getExprType(); 44 | } 45 | 46 | @Override 47 | public CheckTypesResult checkExprTypeBounds() { 48 | CheckTypesResult result = new CheckTypesResult(); 49 | 50 | result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR); 51 | result.addMaxTypeExprent(value, VarType.VARTYPE_INT); 52 | 53 | VarType valType = value.getExprType(); 54 | for (List lst : caseValues) { 55 | for (Exprent expr : lst) { 56 | if (expr != null) { 57 | VarType caseType = expr.getExprType(); 58 | if (!caseType.equals(valType)) { 59 | valType = VarType.getCommonSupertype(caseType, valType); 60 | result.addMinTypeExprent(value, valType); 61 | } 62 | } 63 | } 64 | } 65 | 66 | return result; 67 | } 68 | 69 | @Override 70 | public List getAllExprents() { 71 | List lst = new ArrayList<>(); 72 | lst.add(value); 73 | return lst; 74 | } 75 | 76 | @Override 77 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 78 | tracer.addMapping(bytecode); 79 | return value.toJava(indent, tracer).enclose("switch (", ")"); 80 | } 81 | 82 | @Override 83 | public void replaceExprent(Exprent oldExpr, Exprent newExpr) { 84 | if (oldExpr == value) { 85 | value = newExpr; 86 | } 87 | } 88 | 89 | @Override 90 | public boolean equals(Object o) { 91 | if (o == this) { 92 | return true; 93 | } 94 | 95 | if (!(o instanceof SwitchExprent sw)) { 96 | return false; 97 | } 98 | 99 | return Objects.equals(value, sw.getValue()); 100 | } 101 | 102 | public Exprent getValue() { 103 | return value; 104 | } 105 | 106 | public void setCaseValues(List> caseValues) { 107 | this.caseValues = caseValues; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.decompose; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; 5 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; 6 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 7 | 8 | import java.util.List; 9 | 10 | public class DominatorEngine { 11 | 12 | private final Statement statement; 13 | 14 | private final VBStyleCollection colOrderedIDoms = new VBStyleCollection<>(); 15 | 16 | 17 | public DominatorEngine(Statement statement) { 18 | this.statement = statement; 19 | } 20 | 21 | public void initialize() { 22 | calcIDoms(); 23 | } 24 | 25 | private void orderStatements() { 26 | 27 | for (Statement stat : statement.getReversePostOrderList()) { 28 | colOrderedIDoms.addWithKey(null, stat.id); 29 | } 30 | } 31 | 32 | private static Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection orderedIDoms) { 33 | 34 | if (key1 == null) { 35 | return key2; 36 | } 37 | else if (key2 == null) { 38 | return key1; 39 | } 40 | 41 | int index1 = orderedIDoms.getIndexByKey(key1); 42 | int index2 = orderedIDoms.getIndexByKey(key2); 43 | 44 | while (index1 != index2) { 45 | if (index1 > index2) { 46 | key1 = orderedIDoms.getWithKey(key1); 47 | index1 = orderedIDoms.getIndexByKey(key1); 48 | } 49 | else { 50 | key2 = orderedIDoms.getWithKey(key2); 51 | index2 = orderedIDoms.getIndexByKey(key2); 52 | } 53 | } 54 | 55 | return key1; 56 | } 57 | 58 | private void calcIDoms() { 59 | 60 | orderStatements(); 61 | 62 | colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id); 63 | 64 | // exclude first statement 65 | List lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size()); 66 | 67 | while (true) { 68 | 69 | boolean changed = false; 70 | 71 | for (Integer id : lstIds) { 72 | 73 | Statement stat = statement.getStats().getWithKey(id); 74 | Integer idom = null; 75 | 76 | for (StatEdge edge : stat.getAllPredecessorEdges()) { 77 | if (colOrderedIDoms.getWithKey(edge.getSource().id) != null) { 78 | idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms); 79 | } 80 | } 81 | 82 | Integer oldidom = colOrderedIDoms.putWithKey(idom, id); 83 | if (!idom.equals(oldidom)) { 84 | changed = true; 85 | } 86 | } 87 | 88 | if (!changed) { 89 | break; 90 | } 91 | } 92 | } 93 | 94 | public VBStyleCollection getOrderedIDoms() { 95 | return colOrderedIDoms; 96 | } 97 | 98 | public boolean isDominator(Integer node, Integer dom) { 99 | 100 | while (!node.equals(dom)) { 101 | 102 | Integer idom = colOrderedIDoms.getWithKey(node); 103 | 104 | if (idom.equals(node)) { 105 | return false; // root node 106 | } 107 | else { 108 | node = idom; 109 | } 110 | } 111 | 112 | return true; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 5 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 6 | 7 | import java.io.IOException; 8 | import java.util.*; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | /* 13 | u2 local_variable_table_length; 14 | local_variable { 15 | u2 start_pc; 16 | u2 length; 17 | u2 name_index; 18 | u2 descriptor_index; 19 | u2 index; 20 | } 21 | */ 22 | public class StructLocalVariableTableAttribute extends StructGeneralAttribute { 23 | private List localVariables = Collections.emptyList(); 24 | 25 | @Override 26 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 27 | int len = data.readUnsignedShort(); 28 | if (len > 0) { 29 | localVariables = new ArrayList<>(len); 30 | 31 | for (int i = 0; i < len; i++) { 32 | int start_pc = data.readUnsignedShort(); 33 | int length = data.readUnsignedShort(); 34 | int nameIndex = data.readUnsignedShort(); 35 | int descriptorIndex = data.readUnsignedShort(); 36 | int varIndex = data.readUnsignedShort(); 37 | localVariables.add(new LocalVariable(start_pc, 38 | length, 39 | pool.getPrimitiveConstant(nameIndex).getString(), 40 | pool.getPrimitiveConstant(descriptorIndex).getString(), 41 | varIndex)); 42 | } 43 | } 44 | else { 45 | localVariables = Collections.emptyList(); 46 | } 47 | } 48 | 49 | public void add(StructLocalVariableTableAttribute attr) { 50 | localVariables.addAll(attr.localVariables); 51 | } 52 | 53 | public String getName(int index, int visibleOffset) { 54 | return matchingVars(index, visibleOffset).map(v -> v.name).findFirst().orElse(null); 55 | } 56 | 57 | public String getDescriptor(int index, int visibleOffset) { 58 | return matchingVars(index, visibleOffset).map(v -> v.descriptor).findFirst().orElse(null); 59 | } 60 | 61 | private Stream matchingVars(int index, int visibleOffset) { 62 | return localVariables.stream().filter(v -> v.index == index && (visibleOffset >= v.start_pc && visibleOffset < v.start_pc + v.length)); 63 | } 64 | 65 | public boolean containsName(String name) { 66 | return localVariables.stream().anyMatch(v -> Objects.equals(v.name, name)); 67 | } 68 | 69 | public Map getMapParamNames() { 70 | return localVariables.stream().filter(v -> v.start_pc == 0).collect(Collectors.toMap(v -> v.index, v -> v.name, (n1, n2) -> n2)); 71 | } 72 | 73 | private static final class LocalVariable { 74 | final int start_pc; 75 | final int length; 76 | final String name; 77 | final String descriptor; 78 | final int index; 79 | 80 | private LocalVariable(int start_pc, int length, String name, String descriptor, int index) { 81 | this.start_pc = start_pc; 82 | this.length = length; 83 | this.name = name; 84 | this.descriptor = descriptor; 85 | this.index = index; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.collectors; 3 | 4 | import org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute; 5 | 6 | import java.util.*; 7 | import java.util.Map.Entry; 8 | 9 | public class BytecodeMappingTracer { 10 | public static final BytecodeMappingTracer DUMMY = new BytecodeMappingTracer(); 11 | 12 | private int currentSourceLine; 13 | private StructLineNumberTableAttribute lineNumberTable = null; 14 | private final Map mapping = new HashMap<>(); // bytecode offset, source line 15 | 16 | public BytecodeMappingTracer() { } 17 | 18 | public BytecodeMappingTracer(int initial_source_line) { 19 | currentSourceLine = initial_source_line; 20 | } 21 | 22 | public void incrementCurrentSourceLine() { 23 | currentSourceLine++; 24 | } 25 | 26 | public void incrementCurrentSourceLine(int number_lines) { 27 | currentSourceLine += number_lines; 28 | } 29 | 30 | public void addMapping(int bytecode_offset) { 31 | mapping.putIfAbsent(bytecode_offset, currentSourceLine); 32 | } 33 | 34 | public void addMapping(Set bytecode_offsets) { 35 | if (bytecode_offsets != null) { 36 | for (Integer bytecode_offset : bytecode_offsets) { 37 | addMapping(bytecode_offset); 38 | } 39 | } 40 | } 41 | 42 | public void addTracer(BytecodeMappingTracer tracer) { 43 | if (tracer != null) { 44 | for (Entry entry : tracer.mapping.entrySet()) { 45 | mapping.putIfAbsent(entry.getKey(), entry.getValue()); 46 | } 47 | } 48 | } 49 | 50 | public Map getMapping() { 51 | return mapping; 52 | } 53 | 54 | public int getCurrentSourceLine() { 55 | return currentSourceLine; 56 | } 57 | 58 | public void setCurrentSourceLine(int currentSourceLine) { 59 | this.currentSourceLine = currentSourceLine; 60 | } 61 | 62 | public void setLineNumberTable(StructLineNumberTableAttribute lineNumberTable) { 63 | this.lineNumberTable = lineNumberTable; 64 | } 65 | 66 | private final Set unmappedLines = new HashSet<>(); 67 | 68 | public Set getUnmappedLines() { 69 | return unmappedLines; 70 | } 71 | 72 | public Map getOriginalLinesMapping() { 73 | if (lineNumberTable == null) { 74 | return Collections.emptyMap(); 75 | } 76 | 77 | Map res = new HashMap<>(); 78 | 79 | // first match offsets from line number table 80 | int[] data = lineNumberTable.getRawData(); 81 | for (int i = 0; i < data.length; i += 2) { 82 | int originalOffset = data[i]; 83 | int originalLine = data[i + 1]; 84 | Integer newLine = mapping.get(originalOffset); 85 | if (newLine != null) { 86 | res.put(originalLine, newLine); 87 | } 88 | else { 89 | unmappedLines.add(originalLine); 90 | } 91 | } 92 | 93 | // now match offsets from decompiler mapping 94 | for (Entry entry : mapping.entrySet()) { 95 | int originalLine = lineNumberTable.findLineNumber(entry.getKey()); 96 | if (originalLine > -1 && !res.containsKey(originalLine)) { 97 | res.put(originalLine, entry.getValue()); 98 | unmappedLines.remove(originalLine); 99 | } 100 | } 101 | return res; 102 | } 103 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 3 | 4 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 5 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 6 | import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; 7 | import org.jetbrains.java.decompiler.struct.gen.VarType; 8 | import org.jetbrains.java.decompiler.util.TextBuffer; 9 | 10 | import java.util.*; 11 | 12 | public class ArrayExprent extends Exprent { 13 | private Exprent array; 14 | private Exprent index; 15 | private final VarType hardType; 16 | 17 | public ArrayExprent(Exprent array, Exprent index, VarType hardType, Set bytecodeOffsets) { 18 | super(EXPRENT_ARRAY); 19 | this.array = array; 20 | this.index = index; 21 | this.hardType = hardType; 22 | 23 | addBytecodeOffsets(bytecodeOffsets); 24 | } 25 | 26 | @Override 27 | public Exprent copy() { 28 | return new ArrayExprent(array.copy(), index.copy(), hardType, bytecode); 29 | } 30 | 31 | @Override 32 | public VarType getExprType() { 33 | VarType exprType = array.getExprType(); 34 | if (exprType.equals(VarType.VARTYPE_NULL)) { 35 | return hardType.copy(); 36 | } 37 | else { 38 | return exprType.decreaseArrayDim(); 39 | } 40 | } 41 | 42 | @Override 43 | public int getExprentUse() { 44 | return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES; 45 | } 46 | 47 | @Override 48 | public CheckTypesResult checkExprTypeBounds() { 49 | CheckTypesResult result = new CheckTypesResult(); 50 | result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR); 51 | result.addMaxTypeExprent(index, VarType.VARTYPE_INT); 52 | return result; 53 | } 54 | 55 | @Override 56 | public List getAllExprents() { 57 | List lst = new ArrayList<>(); 58 | lst.add(array); 59 | lst.add(index); 60 | return lst; 61 | } 62 | 63 | @Override 64 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 65 | TextBuffer res = array.toJava(indent, tracer); 66 | 67 | if (array.getPrecedence() > getPrecedence()) { // array precedence equals 0 68 | res.enclose("(", ")"); 69 | } 70 | 71 | VarType arrType = array.getExprType(); 72 | if (arrType.getArrayDim() == 0) { 73 | VarType objArr = VarType.VARTYPE_OBJECT.resizeArrayDim(1); // type family does not change 74 | res.enclose("((" + ExprProcessor.getCastTypeName(objArr, Collections.emptyList()) + ")", ")"); 75 | } 76 | 77 | tracer.addMapping(bytecode); 78 | 79 | return res.append('[').append(index.toJava(indent, tracer)).append(']'); 80 | } 81 | 82 | @Override 83 | public void replaceExprent(Exprent oldExpr, Exprent newExpr) { 84 | if (oldExpr == array) { 85 | array = newExpr; 86 | } 87 | if (oldExpr == index) { 88 | index = newExpr; 89 | } 90 | } 91 | 92 | @Override 93 | public boolean equals(Object o) { 94 | if (o == this) return true; 95 | if (!(o instanceof ArrayExprent arr)) return false; 96 | 97 | return Objects.equals(array, arr.getArray()) && 98 | Objects.equals(index, arr.getIndex()); 99 | } 100 | 101 | public Exprent getArray() { 102 | return array; 103 | } 104 | 105 | public Exprent getIndex() { 106 | return index; 107 | } 108 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler.decompose; 3 | 4 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 5 | 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | public class GenericDominatorEngine { 10 | 11 | private final IGraph graph; 12 | 13 | private final VBStyleCollection colOrderedIDoms = new VBStyleCollection<>(); 14 | 15 | private Set setRoots; 16 | 17 | public GenericDominatorEngine(IGraph graph) { 18 | this.graph = graph; 19 | } 20 | 21 | public void initialize() { 22 | calcIDoms(); 23 | } 24 | 25 | private void orderNodes() { 26 | 27 | setRoots = graph.getRoots(); 28 | 29 | for (IGraphNode node : graph.getReversePostOrderList()) { 30 | colOrderedIDoms.addWithKey(null, node); 31 | } 32 | } 33 | 34 | private static IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection orderedIDoms) { 35 | 36 | IGraphNode nodeOld; 37 | 38 | if (node1 == null) { 39 | return node2; 40 | } 41 | else if (node2 == null) { 42 | return node1; 43 | } 44 | 45 | int index1 = orderedIDoms.getIndexByKey(node1); 46 | int index2 = orderedIDoms.getIndexByKey(node2); 47 | 48 | while (index1 != index2) { 49 | if (index1 > index2) { 50 | nodeOld = node1; 51 | node1 = orderedIDoms.getWithKey(node1); 52 | 53 | if (nodeOld == node1) { // no idom - root or merging point 54 | return null; 55 | } 56 | 57 | index1 = orderedIDoms.getIndexByKey(node1); 58 | } 59 | else { 60 | nodeOld = node2; 61 | node2 = orderedIDoms.getWithKey(node2); 62 | 63 | if (nodeOld == node2) { // no idom - root or merging point 64 | return null; 65 | } 66 | 67 | index2 = orderedIDoms.getIndexByKey(node2); 68 | } 69 | } 70 | 71 | return node1; 72 | } 73 | 74 | private void calcIDoms() { 75 | 76 | orderNodes(); 77 | 78 | List lstNodes = colOrderedIDoms.getLstKeys(); 79 | 80 | while (true) { 81 | 82 | boolean changed = false; 83 | 84 | for (IGraphNode node : lstNodes) { 85 | 86 | IGraphNode idom = null; 87 | 88 | if (!setRoots.contains(node)) { 89 | for (IGraphNode pred : node.getPredecessorNodes()) { 90 | if (colOrderedIDoms.getWithKey(pred) != null) { 91 | idom = getCommonIDom(idom, pred, colOrderedIDoms); 92 | if (idom == null) { 93 | break; // no idom found: merging point of two trees 94 | } 95 | } 96 | } 97 | } 98 | 99 | if (idom == null) { 100 | idom = node; 101 | } 102 | 103 | IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node); 104 | if (!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time 105 | changed = true; 106 | } 107 | } 108 | 109 | if (!changed) { 110 | break; 111 | } 112 | } 113 | } 114 | 115 | public boolean isDominator(IGraphNode node, IGraphNode dom) { 116 | 117 | while (!node.equals(dom)) { 118 | 119 | IGraphNode idom = colOrderedIDoms.getWithKey(node); 120 | 121 | if (idom == node) { 122 | return false; // root node or merging point 123 | } 124 | else if (idom == null) { 125 | throw new RuntimeException("Inconsistent idom sequence discovered!"); 126 | } 127 | else { 128 | node = idom; 129 | } 130 | } 131 | 132 | return true; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.sforms; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 5 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; 6 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 7 | 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | 14 | public class DirectGraph { 15 | 16 | public final VBStyleCollection nodes = new VBStyleCollection<>(); 17 | 18 | public DirectNode first; 19 | 20 | // exit, [source, destination] 21 | public final HashMap> mapShortRangeFinallyPaths = new HashMap<>(); 22 | 23 | // exit, [source, destination] 24 | public final HashMap> mapLongRangeFinallyPaths = new HashMap<>(); 25 | 26 | // negative if branches (recorded for handling of && and ||) 27 | public final HashMap mapNegIfBranch = new HashMap<>(); 28 | 29 | // nodes, that are exception exits of a finally block with monitor variable 30 | public final HashMap mapFinallyMonitorExceptionPathExits = new HashMap<>(); 31 | 32 | public void sortReversePostOrder() { 33 | LinkedList res = new LinkedList<>(); 34 | addToReversePostOrderListIterative(first, res); 35 | 36 | nodes.clear(); 37 | for (DirectNode node : res) { 38 | nodes.addWithKey(node, node.id); 39 | } 40 | } 41 | 42 | private static void addToReversePostOrderListIterative(DirectNode root, List lst) { 43 | 44 | LinkedList stackNode = new LinkedList<>(); 45 | LinkedList stackIndex = new LinkedList<>(); 46 | 47 | HashSet setVisited = new HashSet<>(); 48 | 49 | stackNode.add(root); 50 | stackIndex.add(0); 51 | 52 | while (!stackNode.isEmpty()) { 53 | 54 | DirectNode node = stackNode.getLast(); 55 | int index = stackIndex.removeLast(); 56 | 57 | setVisited.add(node); 58 | 59 | for (; index < node.successors.size(); index++) { 60 | DirectNode succ = node.successors.get(index); 61 | 62 | if (!setVisited.contains(succ)) { 63 | stackIndex.add(index + 1); 64 | 65 | stackNode.add(succ); 66 | stackIndex.add(0); 67 | 68 | break; 69 | } 70 | } 71 | 72 | if (index == node.successors.size()) { 73 | lst.add(0, node); 74 | 75 | stackNode.removeLast(); 76 | } 77 | } 78 | } 79 | 80 | 81 | public boolean iterateExprents(ExprentIterator iter) { 82 | 83 | LinkedList stack = new LinkedList<>(); 84 | stack.add(first); 85 | 86 | HashSet setVisited = new HashSet<>(); 87 | 88 | while (!stack.isEmpty()) { 89 | 90 | DirectNode node = stack.removeFirst(); 91 | 92 | if (setVisited.contains(node)) { 93 | continue; 94 | } 95 | setVisited.add(node); 96 | 97 | for (int i = 0; i < node.exprents.size(); i++) { 98 | int res = iter.processExprent(node.exprents.get(i)); 99 | 100 | if (res == 1) { 101 | return false; 102 | } 103 | 104 | if (res == 2) { 105 | node.exprents.remove(i); 106 | i--; 107 | } 108 | } 109 | 110 | stack.addAll(node.successors); 111 | } 112 | 113 | return true; 114 | } 115 | 116 | public interface ExprentIterator { 117 | // 0 - success, do nothing 118 | // 1 - cancel iteration 119 | // 2 - success, delete exprent 120 | int processExprent(Exprent exprent); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.extern; 3 | 4 | import org.jetbrains.java.decompiler.util.InterpreterUtil; 5 | 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public interface IFernflowerPreferences { 11 | String REMOVE_BRIDGE = "rbr"; 12 | String REMOVE_SYNTHETIC = "rsy"; 13 | String DECOMPILE_INNER = "din"; 14 | String DECOMPILE_CLASS_1_4 = "dc4"; 15 | String DECOMPILE_ASSERTIONS = "das"; 16 | String HIDE_EMPTY_SUPER = "hes"; 17 | String HIDE_DEFAULT_CONSTRUCTOR = "hdc"; 18 | String DECOMPILE_GENERIC_SIGNATURES = "dgs"; 19 | String NO_EXCEPTIONS_RETURN = "ner"; 20 | String ENSURE_SYNCHRONIZED_MONITOR = "esm"; 21 | String DECOMPILE_ENUM = "den"; 22 | String REMOVE_GET_CLASS_NEW = "rgn"; 23 | String LITERALS_AS_IS = "lit"; 24 | String BOOLEAN_TRUE_ONE = "bto"; 25 | String ASCII_STRING_CHARACTERS = "asc"; 26 | String SYNTHETIC_NOT_SET = "nns"; 27 | String UNDEFINED_PARAM_TYPE_OBJECT = "uto"; 28 | String USE_DEBUG_VAR_NAMES = "udv"; 29 | String USE_METHOD_PARAMETERS = "ump"; 30 | String REMOVE_EMPTY_RANGES = "rer"; 31 | String FINALLY_DEINLINE = "fdi"; 32 | String IDEA_NOT_NULL_ANNOTATION = "inn"; 33 | String LAMBDA_TO_ANONYMOUS_CLASS = "lac"; 34 | String BYTECODE_SOURCE_MAPPING = "bsm"; 35 | String IGNORE_INVALID_BYTECODE = "iib"; 36 | String VERIFY_ANONYMOUS_CLASSES = "vac"; 37 | 38 | String LOG_LEVEL = "log"; 39 | String MAX_PROCESSING_METHOD = "mpm"; 40 | String RENAME_ENTITIES = "ren"; 41 | String USER_RENAMER_CLASS = "urc"; 42 | String NEW_LINE_SEPARATOR = "nls"; 43 | String INDENT_STRING = "ind"; 44 | String BANNER = "ban"; 45 | 46 | String DUMP_ORIGINAL_LINES = "__dump_original_lines__"; 47 | String UNIT_TEST_MODE = "__unit_test_mode__"; 48 | 49 | String LINE_SEPARATOR_WIN = "\r\n"; 50 | String LINE_SEPARATOR_UNX = "\n"; 51 | 52 | Map DEFAULTS = getDefaults(); 53 | 54 | static Map getDefaults() { 55 | Map defaults = new HashMap<>(); 56 | 57 | defaults.put(REMOVE_BRIDGE, "1"); 58 | defaults.put(REMOVE_SYNTHETIC, "0"); 59 | defaults.put(DECOMPILE_INNER, "1"); 60 | defaults.put(DECOMPILE_CLASS_1_4, "1"); 61 | defaults.put(DECOMPILE_ASSERTIONS, "1"); 62 | defaults.put(HIDE_EMPTY_SUPER, "1"); 63 | defaults.put(HIDE_DEFAULT_CONSTRUCTOR, "1"); 64 | defaults.put(DECOMPILE_GENERIC_SIGNATURES, "0"); 65 | defaults.put(NO_EXCEPTIONS_RETURN, "1"); 66 | defaults.put(ENSURE_SYNCHRONIZED_MONITOR, "1"); 67 | defaults.put(DECOMPILE_ENUM, "1"); 68 | defaults.put(REMOVE_GET_CLASS_NEW, "1"); 69 | defaults.put(LITERALS_AS_IS, "0"); 70 | defaults.put(BOOLEAN_TRUE_ONE, "1"); 71 | defaults.put(ASCII_STRING_CHARACTERS, "0"); 72 | defaults.put(SYNTHETIC_NOT_SET, "0"); 73 | defaults.put(UNDEFINED_PARAM_TYPE_OBJECT, "1"); 74 | defaults.put(USE_DEBUG_VAR_NAMES, "1"); 75 | defaults.put(USE_METHOD_PARAMETERS, "1"); 76 | defaults.put(REMOVE_EMPTY_RANGES, "1"); 77 | defaults.put(FINALLY_DEINLINE, "1"); 78 | defaults.put(IDEA_NOT_NULL_ANNOTATION, "1"); 79 | defaults.put(LAMBDA_TO_ANONYMOUS_CLASS, "0"); 80 | defaults.put(BYTECODE_SOURCE_MAPPING, "0"); 81 | defaults.put(IGNORE_INVALID_BYTECODE, "0"); 82 | defaults.put(VERIFY_ANONYMOUS_CLASSES, "0"); 83 | 84 | defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name()); 85 | defaults.put(MAX_PROCESSING_METHOD, "0"); 86 | defaults.put(RENAME_ENTITIES, "0"); 87 | defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1")); 88 | defaults.put(INDENT_STRING, " "); 89 | defaults.put(BANNER, ""); 90 | defaults.put(UNIT_TEST_MODE, "0"); 91 | defaults.put(DUMP_ORIGINAL_LINES, "0"); 92 | 93 | return Collections.unmodifiableMap(defaults); 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct.gen; 3 | 4 | import org.jetbrains.java.decompiler.code.CodeConstants; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public final class MethodDescriptor { 11 | public final VarType[] params; 12 | public final VarType ret; 13 | 14 | private MethodDescriptor(VarType[] params, VarType ret) { 15 | this.params = params; 16 | this.ret = ret; 17 | } 18 | 19 | public static MethodDescriptor parseDescriptor(String descriptor) { 20 | int parenth = descriptor.lastIndexOf(')'); 21 | if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') { 22 | throw new IllegalArgumentException("Invalid descriptor: " + descriptor); 23 | } 24 | 25 | VarType[] params; 26 | 27 | if (parenth > 1) { 28 | String parameters = descriptor.substring(1, parenth); 29 | List lst = new ArrayList<>(); 30 | 31 | int indexFrom = -1, ind, len = parameters.length(), index = 0; 32 | while (index < len) { 33 | switch (parameters.charAt(index)) { 34 | case '[' -> { 35 | if (indexFrom < 0) { 36 | indexFrom = index; 37 | } 38 | } 39 | case 'L' -> { 40 | ind = parameters.indexOf(";", index); 41 | lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, ind + 1)); 42 | index = ind; 43 | indexFrom = -1; 44 | } 45 | default -> { 46 | lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, index + 1)); 47 | indexFrom = -1; 48 | } 49 | } 50 | index++; 51 | } 52 | 53 | params = new VarType[lst.size()]; 54 | for (int i = 0; i < lst.size(); i++) { 55 | params[i] = new VarType(lst.get(i)); 56 | } 57 | } 58 | else { 59 | params = VarType.EMPTY_ARRAY; 60 | } 61 | 62 | VarType ret = new VarType(descriptor.substring(parenth + 1)); 63 | 64 | return new MethodDescriptor(params, ret); 65 | } 66 | 67 | public String buildNewDescriptor(NewClassNameBuilder builder) { 68 | boolean updated = false; 69 | 70 | VarType[] newParams; 71 | if (params.length > 0) { 72 | newParams = params.clone(); 73 | for (int i = 0; i < params.length; i++) { 74 | VarType substitute = buildNewType(params[i], builder); 75 | if (substitute != null) { 76 | newParams[i] = substitute; 77 | updated = true; 78 | } 79 | } 80 | } 81 | else { 82 | newParams = VarType.EMPTY_ARRAY; 83 | } 84 | 85 | VarType newRet = ret; 86 | VarType substitute = buildNewType(ret, builder); 87 | if (substitute != null) { 88 | newRet = substitute; 89 | updated = true; 90 | } 91 | 92 | if (updated) { 93 | StringBuilder res = new StringBuilder("("); 94 | for (VarType param : newParams) { 95 | res.append(param); 96 | } 97 | res.append(")").append(newRet.toString()); 98 | return res.toString(); 99 | } 100 | 101 | return null; 102 | } 103 | 104 | private static VarType buildNewType(VarType type, NewClassNameBuilder builder) { 105 | if (type.getType() == CodeConstants.TYPE_OBJECT) { 106 | String newClassName = builder.buildNewClassname(type.getValue()); 107 | if (newClassName != null) { 108 | return new VarType(type.getType(), type.getArrayDim(), newClassName); 109 | } 110 | } 111 | return null; 112 | } 113 | 114 | @Override 115 | public boolean equals(Object o) { 116 | if (o == this) return true; 117 | if (!(o instanceof MethodDescriptor md)) return false; 118 | 119 | return ret.equals(md.ret) && Arrays.equals(params, md.params); 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | int result = ret.hashCode(); 125 | result = 31 * result + params.length; 126 | return result; 127 | } 128 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.struct.attr; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; 5 | import org.jetbrains.java.decompiler.modules.decompiler.typeann.TargetInfo; 6 | import org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotation; 7 | import org.jetbrains.java.decompiler.struct.StructTypePathEntry; 8 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 9 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 10 | 11 | import java.io.DataInputStream; 12 | import java.io.IOException; 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | public class StructTypeAnnotationAttribute extends StructGeneralAttribute { 18 | private List annotations = Collections.emptyList(); 19 | 20 | @Override 21 | public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { 22 | int len = data.readUnsignedShort(); 23 | if (len > 0) { 24 | annotations = new ArrayList<>(len); 25 | for (int i = 0; i < len; i++) { 26 | annotations.add(parse(data, pool)); 27 | } 28 | } 29 | else { 30 | annotations = Collections.emptyList(); 31 | } 32 | } 33 | 34 | private static TypeAnnotation parse(DataInputStream data, ConstantPool pool) throws IOException { 35 | int targetType = data.readUnsignedByte(); 36 | 37 | TargetInfo targetInfo = switch (targetType) { 38 | case TypeAnnotation.CLASS_TYPE_PARAMETER, TypeAnnotation.METHOD_TYPE_PARAMETER -> 39 | new TargetInfo.TypeParameterTarget(data.readUnsignedByte()); 40 | case TypeAnnotation.SUPER_TYPE_REFERENCE -> 41 | new TargetInfo.SupertypeTarget(data.readUnsignedShort()); 42 | case TypeAnnotation.CLASS_TYPE_PARAMETER_BOUND, TypeAnnotation.METHOD_TYPE_PARAMETER_BOUND -> 43 | new TargetInfo.TypeParameterBoundTarget(data.readUnsignedByte(), data.readUnsignedByte()); 44 | case TypeAnnotation.FIELD, TypeAnnotation.METHOD_RETURN_TYPE, TypeAnnotation.METHOD_RECEIVER -> 45 | new TargetInfo.EmptyTarget(); 46 | case TypeAnnotation.METHOD_PARAMETER -> 47 | new TargetInfo.FormalParameterTarget(data.readUnsignedByte()); 48 | case TypeAnnotation.THROWS_REFERENCE -> 49 | new TargetInfo.ThrowsTarget(data.readUnsignedShort()); 50 | case TypeAnnotation.LOCAL_VARIABLE, TypeAnnotation.RESOURCE_VARIABLE -> { 51 | int tableLength = data.readUnsignedShort(); 52 | TargetInfo.LocalvarTarget.Offsets[] offsets = new TargetInfo.LocalvarTarget.Offsets[tableLength]; 53 | for (int i = 0; i < tableLength; i++) { 54 | offsets[i] = new TargetInfo.LocalvarTarget.Offsets(data.readUnsignedShort(), data.readUnsignedShort(), data.readUnsignedShort()); 55 | } 56 | yield new TargetInfo.LocalvarTarget(offsets); 57 | } 58 | case TypeAnnotation.CATCH_CLAUSE -> 59 | new TargetInfo.CatchTarget(data.readUnsignedShort()); 60 | case TypeAnnotation.EXPR_INSTANCEOF, TypeAnnotation.EXPR_NEW, TypeAnnotation.EXPR_CONSTRUCTOR_REF, TypeAnnotation.EXPR_METHOD_REF -> 61 | new TargetInfo.OffsetTarget(data.readUnsignedShort()); 62 | case TypeAnnotation.TYPE_ARG_CAST, TypeAnnotation.TYPE_ARG_CONSTRUCTOR_CALL, TypeAnnotation.TYPE_ARG_METHOD_CALL, 63 | TypeAnnotation.TYPE_ARG_CONSTRUCTOR_REF, TypeAnnotation.TYPE_ARG_METHOD_REF -> 64 | new TargetInfo.TypeArgumentTarget(data.readUnsignedShort(), data.readUnsignedByte()); 65 | default -> throw new RuntimeException("unknown target type: " + targetType); 66 | }; 67 | 68 | int pathLength = data.readUnsignedByte(); 69 | List paths = new ArrayList<>(pathLength); 70 | for (int i = 0; i < pathLength; i++) { 71 | paths.add(i, new StructTypePathEntry(data.readUnsignedByte(), data.readUnsignedByte())); 72 | } 73 | 74 | AnnotationExprent annotation = StructAnnotationAttribute.parseAnnotation(data, pool); 75 | 76 | return new TypeAnnotation(targetType, targetInfo, paths, annotation); 77 | } 78 | 79 | public List getAnnotations() { 80 | return annotations; 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler.stats; 3 | 4 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 5 | import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; 6 | import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 7 | import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; 8 | import org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType; 9 | import org.jetbrains.java.decompiler.util.TextBuffer; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | 15 | public class SequenceStatement extends Statement { 16 | 17 | 18 | // ***************************************************************************** 19 | // constructors 20 | // ***************************************************************************** 21 | 22 | private SequenceStatement() { 23 | super(StatementType.SEQUENCE); 24 | } 25 | 26 | public SequenceStatement(List lst) { 27 | 28 | this(); 29 | 30 | lastBasicType = lst.get(lst.size() - 1).getLastBasicType(); 31 | 32 | for (Statement st : lst) { 33 | stats.addWithKey(st, st.id); 34 | } 35 | 36 | first = stats.get(0); 37 | } 38 | 39 | private SequenceStatement(Statement head, Statement tail) { 40 | 41 | this(Arrays.asList(head, tail)); 42 | 43 | List lstSuccs = tail.getSuccessorEdges(EdgeType.DIRECT_ALL); 44 | if (!lstSuccs.isEmpty()) { 45 | StatEdge edge = lstSuccs.get(0); 46 | 47 | if (edge.getType() == EdgeType.REGULAR && edge.getDestination() != head) { 48 | post = edge.getDestination(); 49 | } 50 | } 51 | } 52 | 53 | 54 | // ***************************************************************************** 55 | // public methods 56 | // ***************************************************************************** 57 | 58 | public static Statement isHead2Block(Statement head) { 59 | 60 | if (head.getLastBasicType() != StatementType.GENERAL) { 61 | return null; 62 | } 63 | 64 | // at most one outgoing edge 65 | StatEdge edge = null; 66 | List lstSuccs = head.getSuccessorEdges(EdgeType.DIRECT_ALL); 67 | if (!lstSuccs.isEmpty()) { 68 | edge = lstSuccs.get(0); 69 | } 70 | 71 | if (edge != null && edge.getType() == EdgeType.REGULAR) { 72 | Statement stat = edge.getDestination(); 73 | 74 | if (stat != head && stat.getPredecessorEdges(EdgeType.REGULAR).size() == 1 75 | && !stat.isMonitorEnter()) { 76 | 77 | if (stat.getLastBasicType() == StatementType.GENERAL) { 78 | if (DecHelper.checkStatementExceptions(Arrays.asList(head, stat))) { 79 | return new SequenceStatement(head, stat); 80 | } 81 | } 82 | } 83 | } 84 | 85 | return null; 86 | } 87 | 88 | @Override 89 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 90 | TextBuffer buf = new TextBuffer(); 91 | boolean isLabeled = isLabeled(); 92 | 93 | buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); 94 | 95 | if (isLabeled) { 96 | buf.appendIndent(indent++).append("label").append(Integer.toString(id)).append(": {").appendLineSeparator(); 97 | tracer.incrementCurrentSourceLine(); 98 | } 99 | 100 | boolean notEmpty = false; 101 | 102 | for (int i = 0; i < stats.size(); i++) { 103 | 104 | Statement stat = stats.get(i); 105 | 106 | if (i > 0 && notEmpty) { 107 | buf.appendLineSeparator(); 108 | tracer.incrementCurrentSourceLine(); 109 | } 110 | 111 | TextBuffer str = ExprProcessor.jmpWrapper(stat, indent, false, tracer); 112 | buf.append(str); 113 | 114 | notEmpty = !str.containsOnlyWhitespaces(); 115 | } 116 | 117 | if (isLabeled) { 118 | buf.appendIndent(indent - 1).append("}").appendLineSeparator(); 119 | tracer.incrementCurrentSourceLine(); 120 | } 121 | 122 | return buf; 123 | } 124 | 125 | @Override 126 | public Statement getSimpleCopy() { 127 | return new SequenceStatement(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection; 6 | import org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType; 7 | import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; 8 | import org.jetbrains.java.decompiler.util.ListStack; 9 | 10 | import java.util.*; 11 | 12 | /** 13 | * The class finds the strongly connected components (SCCs) of a directed graph, 14 | * implementing "Tarjan's strongly connected components" algorithm. 15 | * Running time is linear. 16 | */ 17 | // todo should be replaced or reuse InferenceGraphNode.strongConnect or DFSTBuilder.Tarjan? 18 | public class StrongConnectivityHelper { 19 | private final List> components = new ArrayList<>(); 20 | private final Set setProcessed = new HashSet<>(); 21 | private final ListStack component = new ListStack<>(); 22 | private final Set visited = new HashSet<>(); 23 | private final Map indices = new HashMap<>(); 24 | private final Map lowIndices = new HashMap<>(); 25 | 26 | private int nextIndex; 27 | 28 | public StrongConnectivityHelper(@NotNull Statement startStatement) { 29 | visitTree(startStatement.getFirst()); 30 | for (Statement statement : startStatement.getStats()) { 31 | if (!setProcessed.contains(statement) && statement.getPredecessorEdges(EdgeType.DIRECT_ALL).isEmpty()) { 32 | visitTree(statement); 33 | } 34 | } 35 | // should not find any more nodes! FIXME: ?? 36 | for (Statement statement : startStatement.getStats()) { 37 | if (!setProcessed.contains(statement)) { 38 | visitTree(statement); 39 | } 40 | } 41 | } 42 | 43 | private void visitTree(@NotNull Statement statement) { 44 | component.clear(); 45 | visited.clear(); 46 | indices.clear(); 47 | lowIndices.clear(); 48 | nextIndex = 0; 49 | 50 | visit(statement); 51 | 52 | setProcessed.addAll(visited); 53 | setProcessed.add(statement); 54 | } 55 | 56 | private void visit(@NotNull Statement statement) { 57 | component.push(statement); 58 | indices.put(statement, nextIndex); 59 | lowIndices.put(statement, nextIndex); 60 | nextIndex++; 61 | List successors = statement.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD); // TODO: set? 62 | successors.removeAll(setProcessed); 63 | for (Statement successor : successors) { 64 | int successorIndex; 65 | if (visited.contains(successor)) { 66 | successorIndex = indices.get(successor); 67 | } 68 | else { 69 | visited.add(successor); 70 | visit(successor); 71 | successorIndex = lowIndices.get(successor); 72 | } 73 | lowIndices.put(statement, Math.min(lowIndices.get(statement), successorIndex)); 74 | } 75 | if (lowIndices.get(statement).intValue() == indices.get(statement).intValue()) { 76 | List component = new ArrayList<>(); 77 | Statement statementInComponent; 78 | do { 79 | statementInComponent = this.component.pop(); 80 | component.add(statementInComponent); 81 | } 82 | while (statementInComponent != statement); 83 | components.add(component); 84 | } 85 | } 86 | 87 | public static boolean isExitComponent(@NotNull List component) { 88 | Set statements = new HashSet<>(); 89 | for (Statement statement : component) { 90 | statements.addAll(statement.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD)); 91 | } 92 | for (Statement statement : component) { 93 | statements.remove(statement); 94 | } 95 | return statements.size() == 0; 96 | } 97 | 98 | public @NotNull List getExitReps() { 99 | List result = new ArrayList<>(); 100 | for (List component : components) { 101 | if (isExitComponent(component)) { 102 | result.add(component.get(0)); 103 | } 104 | } 105 | return result; 106 | } 107 | 108 | public @NotNull List<@NotNull List> getComponents() { 109 | return components; 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/util/VBStyleCollection.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.util; 3 | 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | 8 | public class VBStyleCollection extends ArrayList { 9 | 10 | private HashMap map = new HashMap<>(); 11 | 12 | private ArrayList lstKeys = new ArrayList<>(); 13 | 14 | public VBStyleCollection() { 15 | super(); 16 | } 17 | 18 | public VBStyleCollection(int initialCapacity) { 19 | super(initialCapacity); 20 | lstKeys = new ArrayList<>(initialCapacity); 21 | map = new HashMap<>(initialCapacity); 22 | } 23 | 24 | @Override 25 | public boolean add(E element) { 26 | lstKeys.add(null); 27 | super.add(element); 28 | return true; 29 | } 30 | 31 | @Override 32 | public boolean remove(Object element) { // TODO: error on void remove(E element) 33 | throw new RuntimeException("not implemented!"); 34 | } 35 | 36 | @Override 37 | public boolean addAll(Collection c) { 38 | for (int i = c.size() - 1; i >= 0; i--) { 39 | lstKeys.add(null); 40 | } 41 | return super.addAll(c); 42 | } 43 | 44 | public void addAllWithKey(Collection elements, Collection keys) { 45 | int index = super.size(); 46 | 47 | for (K key : keys) { 48 | map.put(key, index++); 49 | } 50 | 51 | super.addAll(elements); 52 | lstKeys.addAll(keys); 53 | } 54 | 55 | public void addWithKey(E element, K key) { 56 | map.put(key, super.size()); 57 | super.add(element); 58 | lstKeys.add(key); 59 | } 60 | 61 | // TODO: speed up the method 62 | public E putWithKey(E element, K key) { 63 | Integer index = map.get(key); 64 | if (index == null) { 65 | addWithKey(element, key); 66 | } 67 | else { 68 | return super.set(index, element); 69 | } 70 | return null; 71 | } 72 | 73 | @Override 74 | public void add(int index, E element) { 75 | addToListIndex(index, 1); 76 | lstKeys.add(index, null); 77 | super.add(index, element); 78 | } 79 | 80 | public void addWithKeyAndIndex(int index, E element, K key) { 81 | addToListIndex(index, 1); 82 | map.put(key, index); 83 | super.add(index, element); 84 | lstKeys.add(index, key); 85 | } 86 | 87 | public void removeWithKey(K key) { 88 | int index = map.get(key); 89 | addToListIndex(index + 1, -1); 90 | super.remove(index); 91 | lstKeys.remove(index); 92 | map.remove(key); 93 | } 94 | 95 | @Override 96 | public E remove(int index) { 97 | addToListIndex(index + 1, -1); 98 | K obj = lstKeys.get(index); 99 | if (obj != null) { 100 | map.remove(obj); 101 | } 102 | lstKeys.remove(index); 103 | return super.remove(index); 104 | } 105 | 106 | public E getWithKey(K key) { 107 | Integer index = map.get(key); 108 | if (index == null) { 109 | return null; 110 | } 111 | return super.get(index); 112 | } 113 | 114 | public int getIndexByKey(K key) { 115 | return map.get(key); 116 | } 117 | 118 | public E getLast() { 119 | return super.get(super.size() - 1); 120 | } 121 | 122 | public boolean containsKey(K key) { 123 | return map.containsKey(key); 124 | } 125 | 126 | @Override 127 | public void clear() { 128 | map.clear(); 129 | lstKeys.clear(); 130 | super.clear(); 131 | } 132 | 133 | @Override 134 | public VBStyleCollection clone() { 135 | VBStyleCollection c = new VBStyleCollection<>(); 136 | c.addAll(new ArrayList<>(this)); 137 | c.setMap(new HashMap<>(map)); 138 | c.setLstKeys(new ArrayList<>(lstKeys)); 139 | return c; 140 | } 141 | 142 | public void setMap(HashMap map) { 143 | this.map = map; 144 | } 145 | 146 | public K getKey(int index) { 147 | return lstKeys.get(index); 148 | } 149 | 150 | public ArrayList getLstKeys() { 151 | return lstKeys; 152 | } 153 | 154 | public void setLstKeys(ArrayList lstKeys) { 155 | this.lstKeys = lstKeys; 156 | } 157 | 158 | private void addToListIndex(int index, int diff) { 159 | for (int i = lstKeys.size() - 1; i >= index; i--) { 160 | K obj = lstKeys.get(i); 161 | if (obj != null) { 162 | map.put(obj, i + diff); 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/code/InstructionSequence.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.code; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | import org.jetbrains.java.decompiler.util.TextUtil; 6 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 7 | 8 | public abstract class InstructionSequence { 9 | 10 | // ***************************************************************************** 11 | // private fields 12 | // ***************************************************************************** 13 | 14 | protected final VBStyleCollection collinstr; 15 | 16 | protected int pointer = 0; 17 | 18 | protected ExceptionTable exceptionTable = ExceptionTable.EMPTY; 19 | 20 | protected InstructionSequence() { 21 | this(new VBStyleCollection<>()); 22 | } 23 | 24 | protected InstructionSequence(VBStyleCollection collinstr) { 25 | this.collinstr = collinstr; 26 | } 27 | 28 | // ***************************************************************************** 29 | // public methods 30 | // ***************************************************************************** 31 | 32 | // to nbe overwritten 33 | @Override 34 | public InstructionSequence clone() { 35 | return null; 36 | } 37 | 38 | public void clear() { 39 | collinstr.clear(); 40 | pointer = 0; 41 | exceptionTable = ExceptionTable.EMPTY; 42 | } 43 | 44 | public void addInstruction(Instruction inst, int offset) { 45 | collinstr.addWithKey(inst, offset); 46 | } 47 | 48 | public void addInstruction(int index, Instruction inst, int offset) { 49 | collinstr.addWithKeyAndIndex(index, inst, offset); 50 | } 51 | 52 | public void addSequence(InstructionSequence seq) { 53 | for (int i = 0; i < seq.length(); i++) { 54 | addInstruction(seq.getInstr(i), -1); // TODO: any sensible value possible? 55 | } 56 | } 57 | 58 | public void removeInstruction(int index) { 59 | collinstr.remove(index); 60 | } 61 | 62 | public void removeLast() { 63 | if (!collinstr.isEmpty()) { 64 | collinstr.remove(collinstr.size() - 1); 65 | } 66 | } 67 | 68 | public Instruction getInstr(int index) { 69 | return collinstr.get(index); 70 | } 71 | 72 | public Instruction getLastInstr() { 73 | return collinstr.getLast(); 74 | } 75 | 76 | public int getOffset(int index) { 77 | return collinstr.getKey(index); 78 | } 79 | 80 | public int getPointerByAbsOffset(int offset) { 81 | Integer absoffset = offset; 82 | if (collinstr.containsKey(absoffset)) { 83 | return collinstr.getIndexByKey(absoffset); 84 | } 85 | else { 86 | return -1; 87 | } 88 | } 89 | 90 | public int getPointerByRelOffset(int offset) { 91 | Integer absoffset = collinstr.getKey(pointer) + offset; 92 | if (collinstr.containsKey(absoffset)) { 93 | return collinstr.getIndexByKey(absoffset); 94 | } 95 | else { 96 | return -1; 97 | } 98 | } 99 | 100 | public int length() { 101 | return collinstr.size(); 102 | } 103 | 104 | public boolean isEmpty() { 105 | return collinstr.isEmpty(); 106 | } 107 | 108 | public void addToPointer(int diff) { 109 | this.pointer += diff; 110 | } 111 | 112 | public String toString() { 113 | return toString(0); 114 | } 115 | 116 | public String toString(int indent) { 117 | 118 | String new_line_separator = DecompilerContext.getNewLineSeparator(); 119 | 120 | StringBuilder buf = new StringBuilder(); 121 | 122 | for (int i = 0; i < collinstr.size(); i++) { 123 | buf.append(TextUtil.getIndentString(indent)); 124 | buf.append(collinstr.getKey(i).intValue()); 125 | buf.append(": "); 126 | buf.append(collinstr.get(i).toString()); 127 | buf.append(new_line_separator); 128 | } 129 | 130 | return buf.toString(); 131 | } 132 | 133 | // ***************************************************************************** 134 | // getter and setter methods 135 | // ***************************************************************************** 136 | 137 | public int getPointer() { 138 | return pointer; 139 | } 140 | 141 | public void setPointer(int pointer) { 142 | this.pointer = pointer; 143 | } 144 | 145 | public ExceptionTable getExceptionTable() { 146 | return exceptionTable; 147 | } 148 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 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. 3 | */ 4 | package org.jetbrains.java.decompiler.modules.decompiler.exps; 5 | 6 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 7 | import org.jetbrains.java.decompiler.struct.gen.VarType; 8 | import org.jetbrains.java.decompiler.util.ListStack; 9 | import org.jetbrains.java.decompiler.util.TextBuffer; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Objects; 14 | import java.util.Set; 15 | 16 | public class IfExprent extends Exprent { 17 | 18 | public static final int IF_EQ = 0; 19 | public static final int IF_NE = 1; 20 | public static final int IF_LT = 2; 21 | public static final int IF_GE = 3; 22 | public static final int IF_GT = 4; 23 | public static final int IF_LE = 5; 24 | 25 | public static final int IF_NULL = 6; 26 | public static final int IF_NONNULL = 7; 27 | 28 | public static final int IF_ICMPEQ = 8; 29 | public static final int IF_ICMPNE = 9; 30 | public static final int IF_ICMPLT = 10; 31 | public static final int IF_ICMPGE = 11; 32 | public static final int IF_ICMPGT = 12; 33 | public static final int IF_ICMPLE = 13; 34 | public static final int IF_ACMPEQ = 14; 35 | public static final int IF_ACMPNE = 15; 36 | 37 | //public static final int IF_CAND = 16; 38 | //public static final int IF_COR = 17; 39 | //public static final int IF_NOT = 18; 40 | public static final int IF_VALUE = 19; 41 | 42 | private static final int[] FUNC_TYPES = { 43 | FunctionExprent.FUNCTION_EQ, 44 | FunctionExprent.FUNCTION_NE, 45 | FunctionExprent.FUNCTION_LT, 46 | FunctionExprent.FUNCTION_GE, 47 | FunctionExprent.FUNCTION_GT, 48 | FunctionExprent.FUNCTION_LE, 49 | FunctionExprent.FUNCTION_EQ, 50 | FunctionExprent.FUNCTION_NE, 51 | FunctionExprent.FUNCTION_EQ, 52 | FunctionExprent.FUNCTION_NE, 53 | FunctionExprent.FUNCTION_LT, 54 | FunctionExprent.FUNCTION_GE, 55 | FunctionExprent.FUNCTION_GT, 56 | FunctionExprent.FUNCTION_LE, 57 | FunctionExprent.FUNCTION_EQ, 58 | FunctionExprent.FUNCTION_NE, 59 | FunctionExprent.FUNCTION_CADD, 60 | FunctionExprent.FUNCTION_COR, 61 | FunctionExprent.FUNCTION_BOOL_NOT, 62 | -1 63 | }; 64 | 65 | private Exprent condition; 66 | 67 | public IfExprent(int ifType, ListStack stack, Set bytecodeOffsets) { 68 | this(null, bytecodeOffsets); 69 | 70 | if (ifType <= IF_LE) { 71 | stack.push(new ConstExprent(0, true, null)); 72 | } 73 | else if (ifType <= IF_NONNULL) { 74 | stack.push(new ConstExprent(VarType.VARTYPE_NULL, null, null)); 75 | } 76 | 77 | if (ifType == IF_VALUE) { 78 | condition = stack.pop(); 79 | } 80 | else { 81 | condition = new FunctionExprent(FUNC_TYPES[ifType], stack, bytecodeOffsets); 82 | } 83 | } 84 | 85 | private IfExprent(Exprent condition, Set bytecodeOffsets) { 86 | super(EXPRENT_IF); 87 | this.condition = condition; 88 | 89 | addBytecodeOffsets(bytecodeOffsets); 90 | } 91 | 92 | @Override 93 | public Exprent copy() { 94 | return new IfExprent(condition.copy(), bytecode); 95 | } 96 | 97 | @Override 98 | public List getAllExprents() { 99 | List lst = new ArrayList<>(); 100 | lst.add(condition); 101 | return lst; 102 | } 103 | 104 | @Override 105 | public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { 106 | tracer.addMapping(bytecode); 107 | return condition.toJava(indent, tracer).enclose("if (", ")"); 108 | } 109 | 110 | @Override 111 | public void replaceExprent(Exprent oldExpr, Exprent newExpr) { 112 | if (oldExpr == condition) { 113 | condition = newExpr; 114 | } 115 | } 116 | 117 | @Override 118 | public boolean equals(Object o) { 119 | if (o == this) return true; 120 | if (!(o instanceof IfExprent ie)) return false; 121 | 122 | return Objects.equals(condition, ie.getCondition()); 123 | } 124 | 125 | public IfExprent negateIf() { 126 | condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, condition, condition.bytecode); 127 | return this; 128 | } 129 | 130 | public Exprent getCondition() { 131 | return condition; 132 | } 133 | 134 | public void setCondition(Exprent condition) { 135 | this.condition = condition; 136 | } 137 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main.collectors; 3 | 4 | import org.jetbrains.java.decompiler.main.DecompilerContext; 5 | import org.jetbrains.java.decompiler.util.TextBuffer; 6 | 7 | import java.util.*; 8 | import java.util.Map.Entry; 9 | 10 | public class BytecodeSourceMapper { 11 | private int offset_total; 12 | 13 | // class, method, bytecode offset, source line 14 | private final Map>> mapping = new LinkedHashMap<>(); 15 | 16 | // original line to decompiled line 17 | private final Map linesMapping = new HashMap<>(); 18 | private final Set unmappedLines = new TreeSet<>(); 19 | 20 | public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) { 21 | Map> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order 22 | Map method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>()); 23 | 24 | // don't overwrite 25 | method_mapping.putIfAbsent(bytecodeOffset, sourceLine); 26 | } 27 | 28 | public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) { 29 | for (Entry entry : tracer.getMapping().entrySet()) { 30 | addMapping(className, methodName, entry.getKey(), entry.getValue()); 31 | } 32 | linesMapping.putAll(tracer.getOriginalLinesMapping()); 33 | unmappedLines.addAll(tracer.getUnmappedLines()); 34 | } 35 | 36 | public void dumpMapping(TextBuffer buffer, boolean offsetsToHex) { 37 | if (mapping.isEmpty() && linesMapping.isEmpty()) { 38 | return; 39 | } 40 | 41 | String lineSeparator = DecompilerContext.getNewLineSeparator(); 42 | 43 | for (Entry>> class_entry : mapping.entrySet()) { 44 | Map> class_mapping = class_entry.getValue(); 45 | buffer.append("class '" + class_entry.getKey() + "' {" + lineSeparator); 46 | 47 | boolean is_first_method = true; 48 | for (Entry> method_entry : class_mapping.entrySet()) { 49 | Map method_mapping = method_entry.getValue(); 50 | 51 | if (!is_first_method) { 52 | buffer.appendLineSeparator(); 53 | } 54 | 55 | buffer.appendIndent(1).append("method '" + method_entry.getKey() + "' {" + lineSeparator); 56 | 57 | List lstBytecodeOffsets = new ArrayList<>(method_mapping.keySet()); 58 | Collections.sort(lstBytecodeOffsets); 59 | 60 | for (Integer offset : lstBytecodeOffsets) { 61 | Integer line = method_mapping.get(offset); 62 | 63 | String strOffset = offsetsToHex ? Integer.toHexString(offset) : line.toString(); 64 | buffer.appendIndent(2).append(strOffset).appendIndent(2).append((line + offset_total) + lineSeparator); 65 | } 66 | buffer.appendIndent(1).append("}").appendLineSeparator(); 67 | 68 | is_first_method = false; 69 | } 70 | 71 | buffer.append("}").appendLineSeparator().appendLineSeparator(); 72 | } 73 | 74 | // lines mapping 75 | buffer.append("Lines mapping:").appendLineSeparator(); 76 | Map sorted = new TreeMap<>(linesMapping); 77 | for (Entry entry : sorted.entrySet()) { 78 | buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue() + offset_total + 1).appendLineSeparator(); 79 | } 80 | 81 | if (!unmappedLines.isEmpty()) { 82 | buffer.append("Not mapped:").appendLineSeparator(); 83 | for (Integer line : unmappedLines) { 84 | if (!linesMapping.containsKey(line)) { 85 | buffer.append(line).appendLineSeparator(); 86 | } 87 | } 88 | } 89 | } 90 | 91 | public void addTotalOffset(int offset_total) { 92 | this.offset_total += offset_total; 93 | } 94 | 95 | /** 96 | * Original to decompiled line mapping. 97 | */ 98 | public int[] getOriginalLinesMapping() { 99 | int[] res = new int[linesMapping.size() * 2]; 100 | int i = 0; 101 | for (Entry entry : linesMapping.entrySet()) { 102 | res[i] = entry.getKey(); 103 | unmappedLines.remove(entry.getKey()); 104 | res[i + 1] = entry.getValue() + offset_total + 1; // make it 1 based 105 | i += 2; 106 | } 107 | return res; 108 | } 109 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package org.jetbrains.java.decompiler.modules.decompiler.vars; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine; 5 | import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph; 6 | import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; 7 | import org.jetbrains.java.decompiler.util.VBStyleCollection; 8 | 9 | import java.util.*; 10 | 11 | public class VarVersionsGraph { 12 | public final VBStyleCollection nodes = new VBStyleCollection<>(); 13 | 14 | private GenericDominatorEngine engine; 15 | 16 | public VarVersionNode createNode(VarVersionPair ver) { 17 | VarVersionNode node; 18 | nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver); 19 | return node; 20 | } 21 | 22 | public void addNodes(Collection colnodes, Collection colpaars) { 23 | nodes.addAllWithKey(colnodes, colpaars); 24 | } 25 | 26 | public boolean isDominatorSet(VarVersionNode node, Set domnodes) { 27 | if (domnodes.size() == 1) { 28 | return engine.isDominator(node, domnodes.iterator().next()); 29 | } 30 | else { 31 | Set marked = new HashSet<>(); 32 | 33 | if (domnodes.contains(node)) { 34 | return true; 35 | } 36 | 37 | List lstNodes = new LinkedList<>(); 38 | lstNodes.add(node); 39 | 40 | while (!lstNodes.isEmpty()) { 41 | VarVersionNode nd = lstNodes.remove(0); 42 | if (marked.contains(nd)) { 43 | continue; 44 | } 45 | else { 46 | marked.add(nd); 47 | } 48 | 49 | if (nd.predecessors.isEmpty()) { 50 | return false; 51 | } 52 | 53 | for (VarVersionEdge edge : nd.predecessors) { 54 | VarVersionNode pred = edge.source; 55 | if (!marked.contains(pred) && !domnodes.contains(pred)) { 56 | lstNodes.add(pred); 57 | } 58 | } 59 | } 60 | } 61 | 62 | return true; 63 | } 64 | 65 | public void initDominators() { 66 | Set roots = new HashSet<>(); 67 | 68 | for (VarVersionNode node : nodes) { 69 | if (node.predecessors.isEmpty()) { 70 | roots.add(node); 71 | } 72 | } 73 | 74 | engine = new GenericDominatorEngine(new IGraph() { 75 | @Override 76 | public List getReversePostOrderList() { 77 | return getReversedPostOrder(roots); 78 | } 79 | 80 | @Override 81 | public Set getRoots() { 82 | return new HashSet(roots); 83 | } 84 | }); 85 | 86 | engine.initialize(); 87 | } 88 | 89 | private static List getReversedPostOrder(Collection roots) { 90 | List lst = new LinkedList<>(); 91 | Set setVisited = new HashSet<>(); 92 | 93 | for (VarVersionNode root : roots) { 94 | List lstTemp = new LinkedList<>(); 95 | addToReversePostOrderListIterative(root, lstTemp, setVisited); 96 | lst.addAll(lstTemp); 97 | } 98 | 99 | return lst; 100 | } 101 | 102 | private static void addToReversePostOrderListIterative(VarVersionNode root, List lst, Set setVisited) { 103 | Map> mapNodeSuccs = new HashMap<>(); 104 | LinkedList stackNode = new LinkedList<>(); 105 | LinkedList stackIndex = new LinkedList<>(); 106 | 107 | stackNode.add(root); 108 | stackIndex.add(0); 109 | 110 | while (!stackNode.isEmpty()) { 111 | VarVersionNode node = stackNode.getLast(); 112 | int index = stackIndex.removeLast(); 113 | 114 | setVisited.add(node); 115 | 116 | List lstSuccs = mapNodeSuccs.computeIfAbsent(node, n -> new ArrayList<>(n.successors)); 117 | for (; index < lstSuccs.size(); index++) { 118 | VarVersionNode succ = lstSuccs.get(index).dest; 119 | 120 | if (!setVisited.contains(succ)) { 121 | stackIndex.add(index + 1); 122 | stackNode.add(succ); 123 | stackIndex.add(0); 124 | break; 125 | } 126 | } 127 | 128 | if (index == lstSuccs.size()) { 129 | lst.add(0, node); 130 | stackNode.removeLast(); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/typeann/TypeAnnotation.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | package org.jetbrains.java.decompiler.modules.decompiler.typeann; 3 | 4 | import org.intellij.lang.annotations.MagicConstant; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; 7 | import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; 8 | import org.jetbrains.java.decompiler.struct.StructMember; 9 | import org.jetbrains.java.decompiler.struct.StructTypePathEntry; 10 | import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; 11 | import org.jetbrains.java.decompiler.struct.attr.StructTypeAnnotationAttribute; 12 | import org.jetbrains.java.decompiler.struct.gen.Type; 13 | import org.jetbrains.java.decompiler.util.TextBuffer; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | import java.util.stream.Stream; 19 | 20 | public class TypeAnnotation { 21 | public static final int CLASS_TYPE_PARAMETER = 0x00; 22 | public static final int METHOD_TYPE_PARAMETER = 0x01; 23 | public static final int SUPER_TYPE_REFERENCE = 0x10; 24 | public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; 25 | public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; 26 | public static final int FIELD = 0x13; 27 | public static final int METHOD_RETURN_TYPE = 0x14; 28 | public static final int METHOD_RECEIVER = 0x15; 29 | public static final int METHOD_PARAMETER = 0x16; 30 | public static final int THROWS_REFERENCE = 0x17; 31 | public static final int LOCAL_VARIABLE = 0x40; 32 | public static final int RESOURCE_VARIABLE = 0x41; 33 | public static final int CATCH_CLAUSE = 0x42; 34 | public static final int EXPR_INSTANCEOF = 0x43; 35 | public static final int EXPR_NEW = 0x44; 36 | public static final int EXPR_CONSTRUCTOR_REF = 0x45; 37 | public static final int EXPR_METHOD_REF = 0x46; 38 | public static final int TYPE_ARG_CAST = 0x47; 39 | public static final int TYPE_ARG_CONSTRUCTOR_CALL = 0x48; 40 | public static final int TYPE_ARG_METHOD_CALL = 0x49; 41 | public static final int TYPE_ARG_CONSTRUCTOR_REF = 0x4A; 42 | public static final int TYPE_ARG_METHOD_REF = 0x4B; 43 | 44 | private final int targetType; 45 | private final TargetInfo targetInfo; 46 | private final @NotNull List paths; 47 | private final @NotNull AnnotationExprent annotation; 48 | 49 | public TypeAnnotation( 50 | int targetType, 51 | TargetInfo targetInfo, 52 | @NotNull List paths, 53 | @NotNull AnnotationExprent annotation 54 | ) { 55 | this.targetType = targetType; 56 | this.targetInfo = targetInfo; 57 | this.paths = paths; 58 | this.annotation = annotation; 59 | } 60 | 61 | @MagicConstant(valuesFromClass = TypeAnnotation.class) 62 | public int getTargetType() { 63 | return targetType; 64 | } 65 | 66 | public TargetInfo getTargetInfo() { 67 | return targetInfo; 68 | } 69 | 70 | public @NotNull AnnotationExprent getAnnotationExpr() { 71 | return annotation; 72 | } 73 | 74 | public @NotNull List getPaths() { 75 | return paths; 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return getAnnotationExpr().toJava(0, BytecodeMappingTracer.DUMMY).toString(); 81 | } 82 | 83 | public void writeTo(@NotNull StringBuilder sb) { 84 | sb.append(this); 85 | sb.append(' '); 86 | } 87 | 88 | public void writeTo(@NotNull TextBuffer sb) { 89 | sb.append(toString()); 90 | sb.append(' '); 91 | } 92 | 93 | public static List listFrom(StructMember md) { 94 | return Arrays.stream(StructGeneralAttribute.TYPE_ANNOTATION_ATTRIBUTES) 95 | .flatMap(attrKey -> { 96 | StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)md.getAttribute(attrKey); 97 | if (attribute == null) { 98 | return Stream.empty(); 99 | } else { 100 | return attribute.getAnnotations().stream(); 101 | } 102 | }) 103 | .collect(Collectors.toList()); 104 | } 105 | 106 | public boolean isWrittenBeforeType(Type type) { 107 | StructTypePathEntry pathEntry = getPaths().stream().findFirst().orElse(null); 108 | if (pathEntry == null && type.getArrayDim() == 0) { 109 | return type.isAnnotatable(); 110 | } 111 | if (pathEntry != null 112 | && pathEntry.getTypePathEntryKind() == StructTypePathEntry.Kind.ARRAY.getId() 113 | && getPaths().size() == type.getArrayDim() 114 | ) return true; 115 | return false; 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.struct.lazy; 3 | 4 | import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; 5 | import org.jetbrains.java.decompiler.struct.StructClass; 6 | import org.jetbrains.java.decompiler.struct.StructMethod; 7 | import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; 8 | import org.jetbrains.java.decompiler.struct.consts.ConstantPool; 9 | import org.jetbrains.java.decompiler.util.DataInputFullStream; 10 | 11 | import java.io.IOException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class LazyLoader { 16 | private final Map mapClassLinks = new HashMap<>(); 17 | private final IBytecodeProvider provider; 18 | 19 | public LazyLoader(IBytecodeProvider provider) { 20 | this.provider = provider; 21 | } 22 | 23 | public void addClassLink(String className, Link link) { 24 | mapClassLinks.put(className, link); 25 | } 26 | 27 | public void removeClassLink(String className) { 28 | mapClassLinks.remove(className); 29 | } 30 | 31 | public Link getClassLink(String className) { 32 | return mapClassLinks.get(className); 33 | } 34 | 35 | public ConstantPool loadPool(String className) { 36 | try (DataInputFullStream in = getClassStream(className)) { 37 | if (in != null) { 38 | in.discard(8); 39 | return new ConstantPool(in); 40 | } 41 | 42 | return null; 43 | } 44 | catch (IOException ex) { 45 | throw new RuntimeException(ex); 46 | } 47 | } 48 | 49 | public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) { 50 | String className = classStruct.qualifiedName; 51 | 52 | try (DataInputFullStream in = getClassStream(className)) { 53 | if (in != null) { 54 | in.discard(8); 55 | 56 | ConstantPool pool = classStruct.getPool(); 57 | if (pool == null) { 58 | pool = new ConstantPool(in); 59 | } 60 | else { 61 | ConstantPool.skipPool(in); 62 | } 63 | 64 | in.discard(6); 65 | 66 | // interfaces 67 | in.discard(in.readUnsignedShort() * 2); 68 | 69 | // fields 70 | int size = in.readUnsignedShort(); 71 | for (int i = 0; i < size; i++) { 72 | in.discard(6); 73 | skipAttributes(in); 74 | } 75 | 76 | // methods 77 | size = in.readUnsignedShort(); 78 | for (int i = 0; i < size; i++) { 79 | in.discard(2); 80 | 81 | int nameIndex = in.readUnsignedShort(); 82 | int descriptorIndex = in.readUnsignedShort(); 83 | 84 | String[] values = pool.getClassElement(ConstantPool.METHOD, className, nameIndex, descriptorIndex); 85 | if (!mt.getName().equals(values[0]) || !mt.getDescriptor().equals(values[1])) { 86 | skipAttributes(in); 87 | continue; 88 | } 89 | 90 | int attrSize = in.readUnsignedShort(); 91 | for (int j = 0; j < attrSize; j++) { 92 | int attrNameIndex = in.readUnsignedShort(); 93 | String attrName = pool.getPrimitiveConstant(attrNameIndex).getString(); 94 | if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) { 95 | in.discard(in.readInt()); 96 | continue; 97 | } 98 | 99 | in.discard(12); 100 | 101 | return in.read(codeFullLength); 102 | } 103 | 104 | break; 105 | } 106 | } 107 | 108 | return null; 109 | } 110 | catch (IOException ex) { 111 | throw new RuntimeException(ex); 112 | } 113 | } 114 | 115 | public DataInputFullStream getClassStream(String externalPath, String internalPath) throws IOException { 116 | byte[] bytes = provider.getBytecode(externalPath, internalPath); 117 | return new DataInputFullStream(bytes); 118 | } 119 | 120 | public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException { 121 | Link link = mapClassLinks.get(qualifiedClassName); 122 | return link == null ? null : getClassStream(link.externalPath, link.internalPath); 123 | } 124 | 125 | public static void skipAttributes(DataInputFullStream in) throws IOException { 126 | int length = in.readUnsignedShort(); 127 | for (int i = 0; i < length; i++) { 128 | in.discard(2); 129 | in.discard(in.readInt()); 130 | } 131 | } 132 | 133 | public static class Link { 134 | public final String externalPath; 135 | public final String internalPath; 136 | 137 | public Link(String externalPath, String internalPath) { 138 | this.externalPath = externalPath; 139 | this.internalPath = internalPath; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/main/Fernflower.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.main; 3 | 4 | import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; 5 | import org.jetbrains.java.decompiler.main.extern.*; 6 | import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper; 7 | import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; 8 | import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; 9 | import org.jetbrains.java.decompiler.struct.IDecompiledData; 10 | import org.jetbrains.java.decompiler.struct.StructClass; 11 | import org.jetbrains.java.decompiler.struct.StructContext; 12 | import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; 13 | import org.jetbrains.java.decompiler.util.TextBuffer; 14 | 15 | import java.io.File; 16 | import java.util.HashMap; 17 | import java.util.Locale; 18 | import java.util.Map; 19 | 20 | public class Fernflower implements IDecompiledData { 21 | private final StructContext structContext; 22 | private final ClassesProcessor classProcessor; 23 | private final IIdentifierRenamer helper; 24 | private final IdentifierConverter converter; 25 | 26 | public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map customProperties, IFernflowerLogger logger) { 27 | Map properties = new HashMap<>(IFernflowerPreferences.DEFAULTS); 28 | if (customProperties != null) { 29 | properties.putAll(customProperties); 30 | } 31 | 32 | String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL); 33 | if (level != null) { 34 | try { 35 | logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.ENGLISH))); 36 | } 37 | catch (IllegalArgumentException ignore) { } 38 | } 39 | 40 | structContext = new StructContext(saver, this, new LazyLoader(provider)); 41 | classProcessor = new ClassesProcessor(structContext); 42 | 43 | PoolInterceptor interceptor = null; 44 | if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) { 45 | helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger); 46 | interceptor = new PoolInterceptor(); 47 | converter = new IdentifierConverter(structContext, helper, interceptor); 48 | } 49 | else { 50 | helper = null; 51 | converter = null; 52 | } 53 | 54 | DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor); 55 | DecompilerContext.setCurrentContext(context); 56 | } 57 | 58 | private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) { 59 | if (className != null) { 60 | try { 61 | Class renamerClass = Fernflower.class.getClassLoader().loadClass(className); 62 | return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance(); 63 | } 64 | catch (Exception e) { 65 | logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e); 66 | } 67 | } 68 | 69 | return new ConverterHelper(); 70 | } 71 | 72 | public void addSource(File source) { 73 | structContext.addSpace(source, true); 74 | } 75 | 76 | public void addLibrary(File library) { 77 | structContext.addSpace(library, false); 78 | } 79 | 80 | public void decompileContext() { 81 | if (converter != null) { 82 | converter.rename(); 83 | } 84 | 85 | classProcessor.loadClasses(helper); 86 | 87 | structContext.saveContext(); 88 | } 89 | 90 | public void clearContext() { 91 | DecompilerContext.setCurrentContext(null); 92 | } 93 | 94 | @Override 95 | public String getClassEntryName(StructClass cl, String entryName) { 96 | ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName); 97 | if (node.type != ClassNode.CLASS_ROOT) { 98 | return null; 99 | } 100 | else if (converter != null) { 101 | String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1); 102 | return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simpleClassName + ".java"; 103 | } 104 | else { 105 | return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java"; 106 | } 107 | } 108 | 109 | @Override 110 | public String getClassContent(StructClass cl) { 111 | try { 112 | TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE); 113 | buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString()); 114 | classProcessor.writeClass(cl, buffer); 115 | return buffer.toString(); 116 | } 117 | catch (Throwable t) { 118 | DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t); 119 | return null; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/main/java/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java: -------------------------------------------------------------------------------- 1 | // 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. 2 | package org.jetbrains.java.decompiler.modules.decompiler; 3 | 4 | import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; 5 | import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; 6 | import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; 7 | import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; 8 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; 9 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; 10 | import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; 11 | import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; 12 | import org.jetbrains.java.decompiler.struct.gen.VarType; 13 | 14 | import java.util.HashSet; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | 18 | public class PPandMMHelper { 19 | 20 | private boolean exprentReplaced; 21 | 22 | public boolean findPPandMM(RootStatement root) { 23 | 24 | FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); 25 | DirectGraph dgraph = flatthelper.buildDirectGraph(root); 26 | 27 | LinkedList stack = new LinkedList<>(); 28 | stack.add(dgraph.first); 29 | 30 | HashSet setVisited = new HashSet<>(); 31 | 32 | boolean res = false; 33 | 34 | while (!stack.isEmpty()) { 35 | 36 | DirectNode node = stack.removeFirst(); 37 | 38 | if (setVisited.contains(node)) { 39 | continue; 40 | } 41 | setVisited.add(node); 42 | 43 | res |= processExprentList(node.exprents); 44 | 45 | stack.addAll(node.successors); 46 | } 47 | 48 | return res; 49 | } 50 | 51 | private boolean processExprentList(List lst) { 52 | 53 | boolean result = false; 54 | 55 | for (int i = 0; i < lst.size(); i++) { 56 | Exprent exprent = lst.get(i); 57 | exprentReplaced = false; 58 | 59 | Exprent retexpr = processExprentRecursive(exprent); 60 | if (retexpr != null) { 61 | lst.set(i, retexpr); 62 | 63 | result = true; 64 | i--; // process the same exprent again 65 | } 66 | 67 | result |= exprentReplaced; 68 | } 69 | 70 | return result; 71 | } 72 | 73 | private Exprent processExprentRecursive(Exprent exprent) { 74 | 75 | boolean replaced = true; 76 | while (replaced) { 77 | replaced = false; 78 | 79 | for (Exprent expr : exprent.getAllExprents()) { 80 | Exprent retexpr = processExprentRecursive(expr); 81 | if (retexpr != null) { 82 | exprent.replaceExprent(expr, retexpr); 83 | replaced = true; 84 | exprentReplaced = true; 85 | break; 86 | } 87 | } 88 | } 89 | 90 | if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { 91 | AssignmentExprent as = (AssignmentExprent)exprent; 92 | 93 | if (as.getRight().type == Exprent.EXPRENT_FUNCTION) { 94 | FunctionExprent func = (FunctionExprent)as.getRight(); 95 | 96 | VarType midlayer = null; 97 | if (func.getFuncType() >= FunctionExprent.FUNCTION_I2L && 98 | func.getFuncType() <= FunctionExprent.FUNCTION_I2S) { 99 | midlayer = func.getSimpleCastType(); 100 | if (func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { 101 | func = (FunctionExprent)func.getLstOperands().get(0); 102 | } 103 | else { 104 | return null; 105 | } 106 | } 107 | 108 | if (func.getFuncType() == FunctionExprent.FUNCTION_ADD || 109 | func.getFuncType() == FunctionExprent.FUNCTION_SUB) { 110 | Exprent econd = func.getLstOperands().get(0); 111 | Exprent econst = func.getLstOperands().get(1); 112 | 113 | if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && 114 | func.getFuncType() == FunctionExprent.FUNCTION_ADD) { 115 | econd = econst; 116 | econst = func.getLstOperands().get(0); 117 | } 118 | 119 | if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { 120 | Exprent left = as.getLeft(); 121 | 122 | VarType condtype = econd.getExprType(); 123 | if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { 124 | FunctionExprent ret = new FunctionExprent( 125 | func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, 126 | econd, func.bytecode); 127 | ret.setImplicitType(condtype); 128 | 129 | exprentReplaced = true; 130 | return ret; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | return null; 138 | } 139 | } 140 | --------------------------------------------------------------------------------