java-decompiler: post-import cleanup (formatting and copyright)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user