Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java

275 lines
10 KiB

// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructMethodParametersAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructOriginalPcTableAttribute;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
public class ClassWrapper {
private final StructClass classStruct;
private final Set<String> hiddenMembers = new HashSet<>();
private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<>();
private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<>();
private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<>();
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
public void init() {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);
for (StructMethod mt : classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
VarProcessor varProc = new VarProcessor(mt, md);
DecompilerContext.startMethod(varProc);
VarNamesCollector vc = varProc.getVarNamesCollector();
CounterContainer counter = DecompilerContext.getCounterContainer();
RootStatement root = null;
boolean isError = false;
try {
if (mt.containsCode()) {
if (maxSec == 0 || testMode) {
root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);
}
else {
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000L;
mtThread.start();
while (!mtProc.isFinished()) {
try {
synchronized (mtProc.lock) {
mtProc.lock.wait(200);
}
}
catch (InterruptedException e) {
killThread(mtThread);
throw e;
}
if (System.currentTimeMillis() >= stopAt) {
String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR);
killThread(mtThread);
isError = true;
break;
}
}
if (!isError) {
root = mtProc.getResult();
}
}
}
else {
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
int paramCount = 0;
if (thisVar) {
varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName);
paramCount = 1;
}
paramCount += md.params.length;
int varIndex = 0;
for (int i = 0; i < paramCount; i++) {
varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getFreeName(varIndex));
if (thisVar) {
if (i == 0) {
varIndex++;
}
else {
varIndex += md.params[i - 1].stackSize;
}
}
else {
varIndex += md.params[i].stackSize;
}
}
}
}
catch (Throwable t) {
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
isError = true;
}
MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter);
methodWrapper.decompiledWithErrors = isError;
methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
if (!isError) {
// rename vars so that no one has the same name as a field
VarNamesCollector namesCollector = new VarNamesCollector();
classStruct.getFields().forEach(f -> namesCollector.addName(f.getName()));
int index = mt.hasModifier(CodeConstants.ACC_STATIC) ? 0 : 1;
for (int i = 0; i < md.params.length; i++) {
varProc.setVarName(new VarVersionPair(index, 0), "arg" + i);
index += md.params[i].stackSize;
}
Map<Integer, String> paramNames = new HashMap<>();
if (DecompilerContext.getOption(IFernflowerPreferences.USE_METHOD_PARAMETERS)) {
StructMethodParametersAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS);
if (attr != null) {
List<StructMethodParametersAttribute.Entry> entries = attr.getEntries();
index = mt.hasModifier(CodeConstants.ACC_STATIC) ? 0 : 1;
for (int i = 0; i < md.params.length && i < entries.size(); i++) {
String myName = entries.get(i).myName;
if (myName != null) {
paramNames.put(index, myName);
}
index += md.params[i].stackSize;
}
}
}
// if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
if (attr != null) {
// only param names here
paramNames.putAll(attr.getMapParamNames());
}
}
varProc.setDebugVarNames(paramNames);
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
if (attr != null) {
// the rest is here
methodWrapper.getOrBuildGraph().iterateExprents(exprent -> {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
lst.stream()
.filter(e -> e.type == Exprent.EXPRENT_VAR)
.forEach(e -> {
VarExprent varExprent = (VarExprent)e;
String name = varExprent.getDebugName(mt);
if (name != null) {
varProc.setVarName(varExprent.getVarVersionPair(), name);
}
});
return 0;
}, true);
}
}
StructOriginalPcTableAttribute originalPcTable = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_ORIGINAL_PC_TABLE);
if (originalPcTable != null) {
methodWrapper.getOrBuildGraph().iterateExprents(rootExpr -> {
List<Exprent> exprs = rootExpr.getAllExprents(true);
exprs.add(rootExpr);
for (Exprent expr : exprs) {
if (expr.type != Exprent.EXPRENT_VAR) {
continue;
}
VarExprent varExpr = (VarExprent) expr;
if (!varExpr.isDefinition()) {
continue;
}
int bytecodeOffset = varExpr.getBytecodeOffset();
if (bytecodeOffset == -1 || !originalPcTable.hasOriginalPc(bytecodeOffset)) {
continue;
}
int pc = originalPcTable.getOriginalPc(bytecodeOffset);
String name;
if (originalPcTable.hasName(pc)) {
name = originalPcTable.getName(pc);
} else {
name = "local" + pc;
}
varProc.setVarName(varExpr.getVarVersionPair(), name);
}
return 0;
}, true);
}
varProc.refreshVarNames(namesCollector);
}
DecompilerContext.getLogger().endMethod();
}
DecompilerContext.getLogger().endClass();
}
@SuppressWarnings("deprecation")
private static void killThread(Thread thread) {
thread.stop();
}
public MethodWrapper getMethodWrapper(String name, String descriptor) {
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
}
public StructClass getClassStruct() {
return classStruct;
}
public VBStyleCollection<MethodWrapper, String> getMethods() {
return methods;
}
public Set<String> getHiddenMembers() {
return hiddenMembers;
}
public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {
return staticFieldInitializers;
}
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers;
}
@Override
public String toString() {
return classStruct.qualifiedName;
}
}