java-decompiler: post-import cleanup (formatting and copyright)

This commit is contained in:
Roman Shevchenko
2014-08-28 21:34:14 +04:00
parent 663631f045
commit 076e4393f2
355 changed files with 38995 additions and 36094 deletions

View File

@@ -1,22 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main.rels;
import java.io.IOException;
import java.util.HashSet;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
@@ -36,181 +34,191 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException;
import java.util.HashSet;
public class ClassWrapper {
private StructClass classStruct;
private HashSet<String> hideMembers = new HashSet<String>();
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
private StructClass classStruct;
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
@SuppressWarnings("deprecation")
public void init() throws IOException {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
private HashSet<String> hideMembers = new HashSet<String>();
// collect field names
HashSet<String> setFieldNames = new HashSet<String>();
for(StructField fd: classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
for(StructMethod mt: classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName()+" "+mt.getDescriptor());
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarncollector(vc);
CounterContainer counter = new CounterContainer();
DecompilerContext.setCountercontainer(counter);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
VarProcessor varproc = new VarProcessor();
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc);
Thread mtthread = null;
RootStatement root = null;
boolean isError = false;
try {
if(mt.containsCode()) {
int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
if(maxsec == 0) { // blocking wait
root = MethodProcessorThread.codeToJava(mt, varproc);
} else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext());
mtthread = new Thread(mtproc);
private VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
mtthread.start();
private VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
int sec = 0;
while(mtthread.isAlive()) {
private VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
synchronized(mtproc) {
mtproc.wait(100);
}
if(maxsec > 0 && ++sec > maxsec) {
DecompilerContext.getLogger().writeMessage("Processing time limit ("+maxsec+" sec.) for method " +
mt.getName()+" "+mt.getDescriptor()+ " exceeded, execution interrupted.", IFernflowerLogger.ERROR);
mtthread.stop();
isError = true;
break;
}
}
public ClassWrapper(StructClass classStruct) {
this.classStruct = classStruct;
}
if(!isError) {
if(mtproc.getError() != null) {
throw mtproc.getError();
} else {
root = mtproc.getRoot();
}
}
}
} else {
boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0;
if(thisvar) {
varproc.getThisvars().put(new VarVersionPaar(0,0), classStruct.qualifiedName);
paramcount = 1;
}
paramcount += md.params.length;
int varindex = 0;
for(int i=0;i<paramcount;i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
if(thisvar) {
if(i==0) {
varindex++;
} else {
varindex+=md.params[i-1].stack_size;
}
} else {
varindex+=md.params[i].stack_size;
}
}
}
@SuppressWarnings("deprecation")
public void init() throws IOException {
} catch(ThreadDeath ex) {
try {
if(mtthread != null) {
mtthread.stop();
}
} catch(Throwable ignored) { }
throw ex;
} catch(Throwable ex) {
DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be decompiled.", ex);
isError = true;
}
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter);
meth.decompiledWithErrors = isError;
methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
// rename vars so that no one has the same name as a field
varproc.refreshVarNames(new VarNamesCollector(setFieldNames));
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
// if debug information present and should be used
if(DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
if(attr != null) {
varproc.setDebugVarNames(attr.getMapVarNames());
}
}
DecompilerContext.getLogger().endMethod();
}
// collect field names
HashSet<String> setFieldNames = new HashSet<String>();
for (StructField fd : classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
DecompilerContext.getLogger().endClass();
}
public MethodWrapper getMethodWrapper(String name, String descriptor) {
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
}
public StructClass getClassStruct() {
return classStruct;
}
for (StructMethod mt : classStruct.getMethods()) {
public VBStyleCollection<MethodWrapper, String> getMethods() {
return methods;
}
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
public HashSet<String> getHideMembers() {
return hideMembers;
}
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarncollector(vc);
public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {
return staticFieldInitializers;
}
CounterContainer counter = new CounterContainer();
DecompilerContext.setCountercontainer(counter);
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers;
}
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
VarProcessor varproc = new VarProcessor();
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc);
Thread mtthread = null;
RootStatement root = null;
boolean isError = false;
try {
if (mt.containsCode()) {
int maxsec = 10 * Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
if (maxsec == 0) { // blocking wait
root = MethodProcessorThread.codeToJava(mt, varproc);
}
else {
MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext());
mtthread = new Thread(mtproc);
mtthread.start();
int sec = 0;
while (mtthread.isAlive()) {
synchronized (mtproc) {
mtproc.wait(100);
}
if (maxsec > 0 && ++sec > maxsec) {
DecompilerContext.getLogger().writeMessage("Processing time limit (" + maxsec + " sec.) for method " +
mt.getName() + " " + mt.getDescriptor() + " exceeded, execution interrupted.",
IFernflowerLogger.ERROR);
mtthread.stop();
isError = true;
break;
}
}
if (!isError) {
if (mtproc.getError() != null) {
throw mtproc.getError();
}
else {
root = mtproc.getRoot();
}
}
}
}
else {
boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0;
if (thisvar) {
varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName);
paramcount = 1;
}
paramcount += md.params.length;
int varindex = 0;
for (int i = 0; i < paramcount; i++) {
varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
if (thisvar) {
if (i == 0) {
varindex++;
}
else {
varindex += md.params[i - 1].stack_size;
}
}
else {
varindex += md.params[i].stack_size;
}
}
}
}
catch (ThreadDeath ex) {
try {
if (mtthread != null) {
mtthread.stop();
}
}
catch (Throwable ignored) {
}
throw ex;
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex);
isError = true;
}
MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter);
meth.decompiledWithErrors = isError;
methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
// rename vars so that no one has the same name as a field
varproc.refreshVarNames(new VarNamesCollector(setFieldNames));
// if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VARNAMES)) {
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
if (attr != null) {
varproc.setDebugVarNames(attr.getMapVarNames());
}
}
DecompilerContext.getLogger().endMethod();
}
DecompilerContext.getLogger().endClass();
}
public MethodWrapper getMethodWrapper(String name, String descriptor) {
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
}
public StructClass getClassStruct() {
return classStruct;
}
public VBStyleCollection<MethodWrapper, String> getMethods() {
return methods;
}
public HashSet<String> getHideMembers() {
return hideMembers;
}
public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {
return staticFieldInitializers;
}
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
return dynamicFieldInitializers;
}
}

View File

@@ -1,12 +1,20 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main.rels;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.code.InstructionSequence;
@@ -23,116 +31,121 @@ import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.io.IOException;
import java.util.*;
public class LambdaProcessor {
private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
private static final String JAVAC_LAMBDA_METHOD = "metafactory";
private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
public void processClass(ClassNode node) throws IOException {
private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
private static final String JAVAC_LAMBDA_METHOD = "metafactory";
private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR =
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
for(ClassNode child : node.nested) {
processClass(child);
}
if(node.nested.isEmpty()) {
hasLambda(node);
}
}
public void processClass(ClassNode node) throws IOException {
public boolean hasLambda(ClassNode node) throws IOException {
for (ClassNode child : node.nested) {
processClass(child);
}
ClassesProcessor clprocessor = DecompilerContext.getClassprocessor();
StructClass cl = node.classStruct;
if(cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
return false;
}
StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if(bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return false; // no bootstrap constants in pool
}
if (node.nested.isEmpty()) {
hasLambda(node);
}
}
Set<Integer> lambda_methods = new HashSet<Integer>();
// find lambda bootstrap constants
for(int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle
if(JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) &&
JAVAC_LAMBDA_METHOD_DESCRIPTOR.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point
lambda_methods.add(i);
}
}
if(lambda_methods.isEmpty()) {
return false; // no lambda bootstrap constant found
}
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for(StructMethod mt: cl.getMethods()) {
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
if(seq != null && seq.length() > 0) {
int len = seq.length();
for(int i = 0; i < len; ++i) {
Instruction instr = seq.getInstr(i);
if(instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
if(lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
public boolean hasLambda(ClassNode node) throws IOException {
String lambda_class_name = md.ret.value;
String lambda_method_name = invoke_dynamic.elementname;
String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
ClassesProcessor clprocessor = DecompilerContext.getClassprocessor();
StructClass cl = node.classStruct;
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname,
content_method_handle.descriptor, content_method_handle.index1,
lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
return false;
}
node.nested.add(node_lambda);
node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
}
}
}
}
mt.releaseResources();
}
// build class hierarchy on lambda
for(ClassNode nd : node.nested) {
if(nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if(parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd);
nd.parent = parent_class;
}
}
}
// FIXME: mixed hierarchy?
return false;
}
StructBootstrapMethodsAttribute bootstrap =
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return false; // no bootstrap constants in pool
}
Set<Integer> lambda_methods = new HashSet<Integer>();
// find lambda bootstrap constants
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) &&
JAVAC_LAMBDA_METHOD_DESCRIPTOR
.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point
lambda_methods.add(i);
}
}
if (lambda_methods.isEmpty()) {
return false; // no lambda bootstrap constant found
}
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for (StructMethod mt : cl.getMethods()) {
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) {
int len = seq.length();
for (int i = 0; i < len; ++i) {
Instruction instr = seq.getInstr(i);
if (instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
if (lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
String lambda_class_name = md.ret.value;
String lambda_method_name = invoke_dynamic.elementname;
String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname,
content_method_handle.descriptor, content_method_handle.index1,
lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());
node.nested.add(node_lambda);
node_lambda.parent = node;
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
}
}
}
}
mt.releaseResources();
}
// build class hierarchy on lambda
for (ClassNode nd : node.nested) {
if (nd.type == ClassNode.CLASS_LAMBDA) {
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
if (parent_class_name != null) {
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
parent_class.nested.add(nd);
nd.parent = parent_class;
}
}
}
// FIXME: mixed hierarchy?
return false;
}
}

View File

@@ -1,21 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main.rels;
import java.io.IOException;
import org.jetbrains.java.decompiler.code.InstructionSequence;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@@ -23,252 +22,239 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ClearStructHelper;
import org.jetbrains.java.decompiler.modules.decompiler.DomHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ExitHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.FinallyProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.IdeaNotNullHelper;
import org.jetbrains.java.decompiler.modules.decompiler.IfHelper;
import org.jetbrains.java.decompiler.modules.decompiler.InlineSingleBlockHelper;
import org.jetbrains.java.decompiler.modules.decompiler.LabelHelper;
import org.jetbrains.java.decompiler.modules.decompiler.LoopExtractHelper;
import org.jetbrains.java.decompiler.modules.decompiler.MergeHelper;
import org.jetbrains.java.decompiler.modules.decompiler.PPandMMHelper;
import org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper;
import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper;
import org.jetbrains.java.decompiler.modules.decompiler.StackVarsProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.*;
import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import java.io.IOException;
public class MethodProcessorThread implements Runnable {
private StructMethod method;
private VarProcessor varproc;
private DecompilerContext parentContext;
private RootStatement root;
private Throwable error;
public MethodProcessorThread(StructMethod method, VarProcessor varproc,
DecompilerContext parentContext) {
this.method = method;
this.varproc = varproc;
this.parentContext = parentContext;
}
public void run() {
private StructMethod method;
private VarProcessor varproc;
private DecompilerContext parentContext;
DecompilerContext.setCurrentContext(parentContext);
error = null;
root = null;
try {
root = codeToJava(method, varproc);
private RootStatement root;
synchronized(this) {
this.notify();
}
} catch(ThreadDeath ex) {
;
} catch(Throwable ex) {
error = ex;
}
}
public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException {
private Throwable error;
StructClass cl = mt.getClassStruct();
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
public MethodProcessorThread(StructMethod method, VarProcessor varproc,
DecompilerContext parentContext) {
this.method = method;
this.varproc = varproc;
this.parentContext = parentContext;
}
// System.out.println(graph.toString());
// if(mt.getName().endsWith("_getActiveServers")) {
// System.out.println();
// }
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
public void run() {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
DecompilerContext.setCurrentContext(parentContext);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
error = null;
root = null;
try {
root = codeToJava(method, varproc);
synchronized (this) {
this.notify();
}
}
catch (ThreadDeath ex) {
;
}
catch (Throwable ex) {
error = ex;
}
}
public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException {
StructClass cl = mt.getClassStruct();
boolean isInitializer = "<clinit>".equals(mt.getName()); // for now static initializer only
mt.expandData();
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
// System.out.println(graph.toString());
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// if(mt.getName().endsWith("_getActiveServers")) {
// System.out.println();
// }
ExceptionDeobfuscator.restorePopRanges(graph);
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true);
if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
if(DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true);
DeadCodeHelper.mergeBasicBlocks(graph);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
//System.out.println(graph.toString());
if(ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING);
}
RootStatement root = DomHelper.parseGraph(graph);
if(!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) {
FinallyProcessor fproc = new FinallyProcessor(varproc);
while(fproc.iterateGraph(mt, root, graph)) {
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true);
//System.out.println(graph.toString());
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
//DeadCodeHelper.removeCircularRanges(graph);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
root = DomHelper.parseGraph(graph);
}
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
ExceptionDeobfuscator.restorePopRanges(graph);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
for(;;) {
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if(!new PPandMMHelper().findPPandMM(root)) {
break;
}
}
for(;;) {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
LabelHelper.cleanUpEdges(root);
for(;;) {
MergeHelper.enhanceLoops(root);
if(LoopExtractHelper.extractLoops(root)) {
continue;
}
if(!IfHelper.mergeAllIfs(root)) {
break;
}
}
if(DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if(IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if(InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if(isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
}
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root);
varproc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root;
}
DeadCodeHelper.mergeBasicBlocks(graph);
public RootStatement getRoot() {
return root;
}
DecompilerContext.getCountercontainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
public Throwable getError() {
return error;
}
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
//System.out.println(graph.toString());
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING);
}
RootStatement root = DomHelper.parseGraph(graph);
if (!DecompilerContext.getOption(IFernflowerPreferences.FINALLY_CATCHALL)) {
FinallyProcessor fproc = new FinallyProcessor(varproc);
while (fproc.iterateGraph(mt, root, graph)) {
//DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true);
//System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
root = DomHelper.parseGraph(graph);
}
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString());
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
for (; ; ) {
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
//System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
varproc.setVarVersions(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (!new PPandMMHelper().findPPandMM(root)) {
break;
}
}
for (; ; ) {
LabelHelper.cleanUpEdges(root);
for (; ; ) {
MergeHelper.enhanceLoops(root);
if (LoopExtractHelper.extractLoops(root)) {
continue;
}
if (!IfHelper.mergeAllIfs(root)) {
break;
}
}
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackproc = new StackVarsProcessor();
stackproc.simplifyStackVars(root, mt, cl);
varproc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if (isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
}
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root);
varproc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
// System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava());
return root;
}
public RootStatement getRoot() {
return root;
}
public Throwable getError() {
return error;
}
}

View File

@@ -1,22 +1,20 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main.rels;
import java.util.HashSet;
import java.util.List;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
@@ -25,38 +23,40 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
import org.jetbrains.java.decompiler.struct.StructMethod;
import java.util.HashSet;
import java.util.List;
public class MethodWrapper {
public RootStatement root;
public VarProcessor varproc;
public StructMethod methodStruct;
public CounterContainer counter;
public DirectGraph graph;
public List<VarVersionPaar> signatureFields;
public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root;
this.varproc = varproc;
this.methodStruct = methodStruct;
this.counter = counter;
}
public DirectGraph getOrBuildGraph() {
if(graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
graph = flatthelper.buildDirectGraph(root);
}
return graph;
}
public RootStatement root;
public VarProcessor varproc;
public StructMethod methodStruct;
public CounterContainer counter;
public DirectGraph graph;
public List<VarVersionPaar> signatureFields;
public boolean decompiledWithErrors;
public HashSet<String> setOuterVarNames = new HashSet<String>();
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
this.root = root;
this.varproc = varproc;
this.methodStruct = methodStruct;
this.counter = counter;
}
public DirectGraph getOrBuildGraph() {
if (graph == null && root != null) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
graph = flatthelper.buildDirectGraph(root);
}
return graph;
}
}

View File

@@ -1,35 +1,27 @@
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
* Copyright 2000-2014 JetBrains s.r.o.
*
* (C) 2008 - 2010, Stiver
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.main.rels;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
@@ -37,421 +29,426 @@ import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
public class NestedMemberAccess {
private static final int METHOD_ACCESS_NORMAL = 1;
private static final int METHOD_ACCESS_FIELDGET = 2;
private static final int METHOD_ACCESS_FIELDSET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
private boolean notSetSync;
private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
public void propagateMemberAccess(ClassNode root) {
if(root.nested.isEmpty()) {
return;
}
private static final int METHOD_ACCESS_NORMAL = 1;
private static final int METHOD_ACCESS_FIELDGET = 2;
private static final int METHOD_ACCESS_FIELDSET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
computeMethodTypes(root);
eliminateStaticAccess(root);
}
private void computeMethodTypes(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for(ClassNode nd : node.nested) {
computeMethodTypes(nd);
}
for(MethodWrapper meth : node.wrapper.getMethods()) {
computeMethodType(node, meth);
}
}
private void computeMethodType(ClassNode node, MethodWrapper meth) {
private boolean notSetSync;
int type = METHOD_ACCESS_NORMAL;
if(meth.root != null) {
DirectGraph graph = meth.getOrBuildGraph();
int flags = meth.methodStruct.getAccessFlags();
if(((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) &&
(flags & CodeConstants.ACC_STATIC) != 0) {
if(graph.nodes.size() == 2) { // incl. dummy exit node
if(graph.first.exprents.size() == 1) {
Exprent exprent = graph.first.exprents.get(0);
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
Exprent exprCore = exprent;
if(exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue();
}
}
switch(exprCore.type) {
case Exprent.EXPRENT_FIELD:
FieldExprent fexpr = (FieldExprent)exprCore;
if((parcount == 1 && !fexpr.isStatic()) ||
(parcount == 0 && fexpr.isStatic())) {
if(fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpr.isStatic() || (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELDGET;
}
}
}
break;
case Exprent.EXPRENT_VAR: // qualified this
if(parcount == 1) {
// this or final variable
if(((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELDGET;
}
}
break;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
if(type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
public void propagateMemberAccess(ClassNode root) {
type = METHOD_ACCESS_NORMAL;
if (root.nested.isEmpty()) {
return;
}
InvocationExprent invexpr = (InvocationExprent)exprCore;
notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
if((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount-1)) {
computeMethodTypes(root);
boolean equalpars = true;
eliminateStaticAccess(root);
}
for(int i=0;i<invexpr.getLstParameters().size();i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if(parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic()?0:1)) {
equalpars = false;
break;
}
}
if(equalpars) {
type = METHOD_ACCESS_METHOD;
}
}
}
} else if(graph.first.exprents.size() == 2) {
Exprent exprentFirst = graph.first.exprents.get(0);
Exprent exprentSecond = graph.first.exprents.get(1);
private void computeMethodTypes(ClassNode node) {
if(exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&
exprentSecond.type == Exprent.EXPRENT_EXIT) {
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if(exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
}
}
}
}
}
}
for (ClassNode nd : node.nested) {
computeMethodTypes(nd);
}
if(type != METHOD_ACCESS_NORMAL) {
mapMethodType.put(meth, type);
} else {
mapMethodType.remove(meth);
}
}
private void eliminateStaticAccess(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for(MethodWrapper meth : node.wrapper.getMethods()) {
if(meth.root != null) {
boolean replaced = false;
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(graph.first);
while(!stack.isEmpty()) { // TODO: replace with interface iterator?
DirectNode nd = stack.removeFirst();
if(setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
for(int i=0;i<nd.exprents.size();i++) {
Exprent exprent = nd.exprents.get(i);
replaced |= replaceInvocations(node, meth, exprent);
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);
if(ret != null) {
nd.exprents.set(i, ret);
replaced = true;
}
}
}
for(DirectNode ndx: nd.succs) {
stack.add(ndx);
}
}
if(replaced) {
computeMethodType(node, meth);
}
}
}
for(ClassNode child : node.nested) {
eliminateStaticAccess(child);
}
}
private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {
boolean res = false;
for(Exprent expr : exprent.getAllExprents()) {
res |= replaceInvocations(caller, meth, expr);
}
for(;;) {
boolean found = false;
for(Exprent expr : exprent.getAllExprents()) {
if(expr.type == Exprent.EXPRENT_INVOCATION) {
Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);
if(newexpr != null) {
exprent.replaceExprent(expr, newexpr);
found = true;
res = true;
break;
}
}
}
if(!found) {
break;
}
}
return res;
}
private boolean sameTree(ClassNode caller, ClassNode callee) {
if(caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {
return false;
}
while(caller.parent != null) {
caller = caller.parent;
}
while(callee.parent != null) {
callee = callee.parent;
}
return caller == callee;
}
for (MethodWrapper meth : node.wrapper.getMethods()) {
computeMethodType(node, meth);
}
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if(node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if(methsource == null || !mapMethodType.containsKey(methsource)) {
return null;
}
// if same method, return
if(node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&
methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&
methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {
// no recursive invocations permitted!
return null;
}
int type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// return null;
// }
if(!sameTree(caller, node)) {
return null;
}
DirectGraph graph = methsource.getOrBuildGraph();
Exprent source = graph.first.exprents.get(0);
Exprent retexprent = null;
switch(type) {
case METHOD_ACCESS_FIELDGET:
ExitExprent exsource = (ExitExprent)source;
if(exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
if(!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
vnc.addName(varname);
methdest.varproc.refreshVarNames(vnc);
methdest.setOuterVarNames.add(varname);
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
retexprent = ret;
} else { // field
FieldExprent ret = (FieldExprent)exsource.getValue().copy();
if(!ret.isStatic()) {
ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELDSET:
AssignmentExprent ret;
if(source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy();
} else {
ret = (AssignmentExprent)((AssignmentExprent)source).copy();
}
FieldExprent fexpr = (FieldExprent)ret.getLeft();
if(fexpr.isStatic()) {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0));
} else {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
if(source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
InvocationExprent invret = (InvocationExprent)source.copy();
int index = 0;
if(!invret.isStatic()) {
invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0));
index = 1;
}
for(int i=0;i<invret.getLstParameters().size();i++) {
invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index));
}
retexprent = invret;
}
if(retexprent != null) {
// hide synthetic access method
boolean hide = true;
if(node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {
StructMethod mt = methsource.methodStruct;
if((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) {
hide = false;
}
}
if(hide) {
node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
return retexprent;
}
private void computeMethodType(ClassNode node, MethodWrapper meth) {
int type = METHOD_ACCESS_NORMAL;
if (meth.root != null) {
DirectGraph graph = meth.getOrBuildGraph();
int flags = meth.methodStruct.getAccessFlags();
if (((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) &&
(flags & CodeConstants.ACC_STATIC) != 0) {
if (graph.nodes.size() == 2) { // incl. dummy exit node
if (graph.first.exprents.size() == 1) {
Exprent exprent = graph.first.exprents.get(0);
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
Exprent exprCore = exprent;
if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue();
}
}
switch (exprCore.type) {
case Exprent.EXPRENT_FIELD:
FieldExprent fexpr = (FieldExprent)exprCore;
if ((parcount == 1 && !fexpr.isStatic()) ||
(parcount == 0 && fexpr.isStatic())) {
if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpr.isStatic() ||
(fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELDGET;
}
}
}
break;
case Exprent.EXPRENT_VAR: // qualified this
if (parcount == 1) {
// this or final variable
if (((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELDGET;
}
}
break;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if ((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
type = METHOD_ACCESS_NORMAL;
InvocationExprent invexpr = (InvocationExprent)exprCore;
if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) ||
(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) {
boolean equalpars = true;
for (int i = 0; i < invexpr.getLstParameters().size(); i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if (parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) {
equalpars = false;
break;
}
}
if (equalpars) {
type = METHOD_ACCESS_METHOD;
}
}
}
}
else if (graph.first.exprents.size() == 2) {
Exprent exprentFirst = graph.first.exprents.get(0);
Exprent exprentSecond = graph.first.exprents.get(1);
if (exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&
exprentSecond.type == Exprent.EXPRENT_EXIT) {
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if ((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if (fexpras.isStatic() ||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond;
if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if (exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
}
}
}
}
}
}
if (type != METHOD_ACCESS_NORMAL) {
mapMethodType.put(meth, type);
}
else {
mapMethodType.remove(meth);
}
}
private void eliminateStaticAccess(ClassNode node) {
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
for (MethodWrapper meth : node.wrapper.getMethods()) {
if (meth.root != null) {
boolean replaced = false;
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(graph.first);
while (!stack.isEmpty()) { // TODO: replace with interface iterator?
DirectNode nd = stack.removeFirst();
if (setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
for (int i = 0; i < nd.exprents.size(); i++) {
Exprent exprent = nd.exprents.get(i);
replaced |= replaceInvocations(node, meth, exprent);
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);
if (ret != null) {
nd.exprents.set(i, ret);
replaced = true;
}
}
}
for (DirectNode ndx : nd.succs) {
stack.add(ndx);
}
}
if (replaced) {
computeMethodType(node, meth);
}
}
}
for (ClassNode child : node.nested) {
eliminateStaticAccess(child);
}
}
private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {
boolean res = false;
for (Exprent expr : exprent.getAllExprents()) {
res |= replaceInvocations(caller, meth, expr);
}
for (; ; ) {
boolean found = false;
for (Exprent expr : exprent.getAllExprents()) {
if (expr.type == Exprent.EXPRENT_INVOCATION) {
Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);
if (newexpr != null) {
exprent.replaceExprent(expr, newexpr);
found = true;
res = true;
break;
}
}
}
if (!found) {
break;
}
}
return res;
}
private boolean sameTree(ClassNode caller, ClassNode callee) {
if (caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {
return false;
}
while (caller.parent != null) {
caller = caller.parent;
}
while (callee.parent != null) {
callee = callee.parent;
}
return caller == callee;
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if (node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if (methsource == null || !mapMethodType.containsKey(methsource)) {
return null;
}
// if same method, return
if (node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&
methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&
methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {
// no recursive invocations permitted!
return null;
}
int type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// return null;
// }
if (!sameTree(caller, node)) {
return null;
}
DirectGraph graph = methsource.getOrBuildGraph();
Exprent source = graph.first.exprents.get(0);
Exprent retexprent = null;
switch (type) {
case METHOD_ACCESS_FIELDGET:
ExitExprent exsource = (ExitExprent)source;
if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
if (!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
vnc.addName(varname);
methdest.varproc.refreshVarNames(vnc);
methdest.setOuterVarNames.add(varname);
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
retexprent = ret;
}
else { // field
FieldExprent ret = (FieldExprent)exsource.getValue().copy();
if (!ret.isStatic()) {
ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELDSET:
AssignmentExprent ret;
if (source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy();
}
else {
ret = (AssignmentExprent)((AssignmentExprent)source).copy();
}
FieldExprent fexpr = (FieldExprent)ret.getLeft();
if (fexpr.isStatic()) {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0));
}
else {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
if (source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
InvocationExprent invret = (InvocationExprent)source.copy();
int index = 0;
if (!invret.isStatic()) {
invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0));
index = 1;
}
for (int i = 0; i < invret.getLstParameters().size(); i++) {
invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index));
}
retexprent = invret;
}
if (retexprent != null) {
// hide synthetic access method
boolean hide = true;
if (node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {
StructMethod mt = methsource.methodStruct;
if ((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) {
hide = false;
}
}
if (hide) {
node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
return retexprent;
}
}