mirror of
https://github.com/moparisthebest/fernflower
synced 2024-11-17 14:55:14 -05:00
lambda expressions (Java 8)
This commit is contained in:
parent
96379678e6
commit
bd99d3eb2f
@ -112,6 +112,84 @@ public class ClassWriter {
|
||||
|
||||
}
|
||||
|
||||
public void classLambdaToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException {
|
||||
|
||||
// get the class node with the content method
|
||||
ClassNode node_content = node;
|
||||
while(node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) {
|
||||
node_content = node_content.parent;
|
||||
}
|
||||
|
||||
if(node_content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS);
|
||||
|
||||
ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node);
|
||||
|
||||
ClassWrapper wrapper = node_content.wrapper;
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
DecompilerContext.getLogger().startWriteClass(node.simpleName);
|
||||
|
||||
// lambda method
|
||||
StructMethod mt = cl.getMethod(node.lambda_information.content_method_key);
|
||||
MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor);
|
||||
|
||||
if(!lambda_to_anonymous) { // lambda parameters '() ->'
|
||||
|
||||
StringBuilder buff = new StringBuilder("(");
|
||||
|
||||
boolean firstpar = true;
|
||||
int index = 1;
|
||||
|
||||
for(int i=0;i<md.params.length;i++) {
|
||||
|
||||
if(!firstpar) {
|
||||
buff.append(", ");
|
||||
}
|
||||
|
||||
String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0));
|
||||
buff.append(parname==null ? "param"+index : parname); // null iff decompiled with errors
|
||||
|
||||
firstpar = false;
|
||||
|
||||
index+=md.params[i].stack_size;
|
||||
}
|
||||
buff.append(") ->");
|
||||
|
||||
writer.write(buff.toString());
|
||||
}
|
||||
|
||||
StringWriter strwriter = new StringWriter();
|
||||
BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
|
||||
|
||||
if(lambda_to_anonymous) {
|
||||
methodLambdaToJava(node, node_content, mt, bufstrwriter, indent+1, false);
|
||||
} else {
|
||||
methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true);
|
||||
}
|
||||
|
||||
bufstrwriter.flush();
|
||||
|
||||
// closing up class definition
|
||||
writer.write(" {");
|
||||
writer.newLine();
|
||||
|
||||
writer.write(strwriter.toString());
|
||||
|
||||
writer.write(InterpreterUtil.getIndentString(indent));
|
||||
writer.write("}");
|
||||
writer.flush();
|
||||
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold);
|
||||
|
||||
DecompilerContext.getLogger().endWriteClass();
|
||||
}
|
||||
|
||||
public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException {
|
||||
|
||||
ClassWrapper wrapper = node.wrapper;
|
||||
@ -498,7 +576,99 @@ public class ClassWriter {
|
||||
|
||||
}
|
||||
|
||||
private boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException {
|
||||
public boolean methodLambdaToJava(ClassNode node_lambda, ClassNode node_content, StructMethod mt, BufferedWriter writer, int indent, boolean code_only) throws IOException {
|
||||
|
||||
ClassWrapper wrapper = node_content.wrapper;
|
||||
|
||||
MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
|
||||
|
||||
MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth);
|
||||
|
||||
String indstr = InterpreterUtil.getIndentString(indent);
|
||||
|
||||
String method_name = node_lambda.lambda_information.method_name;
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(node_lambda.lambda_information.method_descriptor);
|
||||
|
||||
StringWriter strwriter = new StringWriter();
|
||||
BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
|
||||
|
||||
if(!code_only) {
|
||||
bufstrwriter.write(indstr);
|
||||
bufstrwriter.write("public ");
|
||||
bufstrwriter.write(method_name);
|
||||
bufstrwriter.write("(");
|
||||
|
||||
boolean firstpar = true;
|
||||
int index = 1;
|
||||
|
||||
for(int i=0;i<md.params.length;i++) {
|
||||
|
||||
if(!firstpar) {
|
||||
bufstrwriter.write(", ");
|
||||
}
|
||||
|
||||
VarType partype = md.params[i].copy();
|
||||
|
||||
String strpartype = ExprProcessor.getCastTypeName(partype);
|
||||
if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
|
||||
strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
|
||||
}
|
||||
|
||||
bufstrwriter.write(strpartype);
|
||||
bufstrwriter.write(" ");
|
||||
|
||||
String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0));
|
||||
bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors
|
||||
|
||||
firstpar = false;
|
||||
|
||||
index+=md.params[i].stack_size;
|
||||
}
|
||||
|
||||
bufstrwriter.write(")");
|
||||
bufstrwriter.write(" ");
|
||||
bufstrwriter.write("{");
|
||||
bufstrwriter.newLine();
|
||||
}
|
||||
|
||||
RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
|
||||
|
||||
if(root != null && !meth.decompiledWithErrors) { // check for existence
|
||||
try {
|
||||
String code = root.toJava(indent+1);
|
||||
bufstrwriter.write(code);
|
||||
} catch(Throwable ex) {
|
||||
if(DecompilerContext.getLogger().getShowStacktrace()) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", IFernflowerLogger.ERROR);
|
||||
meth.decompiledWithErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(meth.decompiledWithErrors) {
|
||||
bufstrwriter.write(InterpreterUtil.getIndentString(indent+1));
|
||||
bufstrwriter.write("// $FF: Couldn't be decompiled");
|
||||
bufstrwriter.newLine();
|
||||
}
|
||||
|
||||
if(!code_only) {
|
||||
bufstrwriter.write(indstr+"}");
|
||||
bufstrwriter.newLine();
|
||||
}
|
||||
|
||||
bufstrwriter.flush();
|
||||
|
||||
writer.write(strwriter.toString());
|
||||
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException {
|
||||
|
||||
ClassWrapper wrapper = node.wrapper;
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
@ -24,8 +24,8 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import de.fernflower.code.CodeConstants;
|
||||
import de.fernflower.main.collectors.CounterContainer;
|
||||
@ -34,6 +34,7 @@ import de.fernflower.main.extern.IFernflowerLogger;
|
||||
import de.fernflower.main.extern.IFernflowerPreferences;
|
||||
import de.fernflower.main.extern.IIdentifierRenamer;
|
||||
import de.fernflower.main.rels.ClassWrapper;
|
||||
import de.fernflower.main.rels.LambdaProcessor;
|
||||
import de.fernflower.main.rels.NestedClassProcessor;
|
||||
import de.fernflower.main.rels.NestedMemberAccess;
|
||||
import de.fernflower.modules.decompiler.exps.InvocationExprent;
|
||||
@ -238,6 +239,10 @@ public class ClassesProcessor {
|
||||
DecompilerContext.setImpcollector(new ImportCollector(root));
|
||||
DecompilerContext.setCountercontainer(new CounterContainer());
|
||||
|
||||
// lambda processing
|
||||
LambdaProcessor lambda_proc = new LambdaProcessor();
|
||||
lambda_proc.processClass(root);
|
||||
|
||||
// add simple class names to implicit import
|
||||
addClassnameToImport(root, DecompilerContext.getImpcollector());
|
||||
// build wrappers for all nested classes
|
||||
@ -291,6 +296,10 @@ public class ClassesProcessor {
|
||||
|
||||
private void initWrappers(ClassNode node) throws IOException {
|
||||
|
||||
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
|
||||
wrapper.init();
|
||||
|
||||
@ -333,6 +342,7 @@ public class ClassesProcessor {
|
||||
public static final int CLASS_MEMBER = 1;
|
||||
public static final int CLASS_ANONYMOUS = 2;
|
||||
public static final int CLASS_LOCAL = 4;
|
||||
public static final int CLASS_LAMBDA = 8;
|
||||
|
||||
public int type;
|
||||
|
||||
@ -358,6 +368,28 @@ public class ClassesProcessor {
|
||||
|
||||
public ClassNode parent;
|
||||
|
||||
public LambdaInformation lambda_information;
|
||||
|
||||
public ClassNode(String content_method_name, String content_method_descriptor, String lambda_class_name, String lambda_method_name,
|
||||
String lambda_method_descriptor, StructClass classStruct) { // lambda class constructor
|
||||
this.type = CLASS_LAMBDA;
|
||||
this.classStruct = classStruct; // 'parent' class containing the static function
|
||||
|
||||
lambda_information = new LambdaInformation();
|
||||
|
||||
lambda_information.class_name = lambda_class_name;
|
||||
lambda_information.method_name = lambda_method_name;
|
||||
lambda_information.method_descriptor = lambda_method_descriptor;
|
||||
|
||||
lambda_information.content_method_name = content_method_name;
|
||||
lambda_information.content_method_descriptor = content_method_descriptor;
|
||||
lambda_information.content_method_key = InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor);
|
||||
|
||||
anonimousClassType = new VarType(lambda_class_name, true);
|
||||
|
||||
lambda_information.is_content_method_static = ((classStruct.getMethod(content_method_name, content_method_descriptor).getAccessFlags() & CodeConstants.ACC_STATIC) != 0);
|
||||
}
|
||||
|
||||
public ClassNode(int type, StructClass classStruct) {
|
||||
this.type = type;
|
||||
this.classStruct = classStruct;
|
||||
@ -374,5 +406,16 @@ public class ClassesProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
public class LambdaInformation {
|
||||
public String class_name;
|
||||
public String method_name;
|
||||
public String method_descriptor;
|
||||
|
||||
public String content_method_name;
|
||||
public String content_method_descriptor;
|
||||
public String content_method_key;
|
||||
|
||||
public boolean is_content_method_static;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ public interface IFernflowerPreferences {
|
||||
public static final String DEPRECATED_COMMENT = "dpc";
|
||||
public static final String NEW_LINE_SEPARATOR = "nls";
|
||||
public static final String IDEA_NOT_NULL_ANNOTATION = "inn";
|
||||
public static final String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
|
||||
|
||||
public static final String LINE_SEPARATOR_WIN = "\r\n";
|
||||
public static final String LINE_SEPARATOR_LIN = "\n";
|
||||
|
135
src/de/fernflower/main/rels/LambdaProcessor.java
Normal file
135
src/de/fernflower/main/rels/LambdaProcessor.java
Normal file
@ -0,0 +1,135 @@
|
||||
package de.fernflower.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 de.fernflower.code.CodeConstants;
|
||||
import de.fernflower.code.Instruction;
|
||||
import de.fernflower.code.InstructionSequence;
|
||||
import de.fernflower.main.ClassesProcessor;
|
||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
import de.fernflower.struct.StructClass;
|
||||
import de.fernflower.struct.StructMethod;
|
||||
import de.fernflower.struct.attr.StructBootstrapMethodsAttribute;
|
||||
import de.fernflower.struct.attr.StructGeneralAttribute;
|
||||
import de.fernflower.struct.consts.LinkConstant;
|
||||
import de.fernflower.struct.consts.PooledConstant;
|
||||
import de.fernflower.struct.consts.PrimitiveConstant;
|
||||
import de.fernflower.struct.gen.MethodDescriptor;
|
||||
import de.fernflower.util.InterpreterUtil;
|
||||
|
||||
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 {
|
||||
|
||||
for(ClassNode child : node.nested) {
|
||||
processClass(child);
|
||||
}
|
||||
|
||||
if(node.nested.isEmpty()) {
|
||||
hasLambda(node);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasLambda(ClassNode node) throws IOException {
|
||||
|
||||
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) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.elementname, content_method_handle.descriptor, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
@ -14,10 +14,8 @@
|
||||
|
||||
package de.fernflower.main.rels;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import test.util.DotExporter;
|
||||
import de.fernflower.code.InstructionSequence;
|
||||
import de.fernflower.code.cfg.ControlFlowGraph;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
@ -186,7 +184,7 @@ public class MethodProcessorThread implements Runnable {
|
||||
|
||||
for(;;) {
|
||||
StackVarsProcessor stackproc = new StackVarsProcessor();
|
||||
stackproc.simplifyStackVars(root, mt);
|
||||
stackproc.simplifyStackVars(root, mt, cl);
|
||||
|
||||
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
|
||||
|
||||
@ -223,7 +221,7 @@ public class MethodProcessorThread implements Runnable {
|
||||
SequenceHelper.condenseSequences(root);
|
||||
|
||||
StackVarsProcessor stackproc = new StackVarsProcessor();
|
||||
stackproc.simplifyStackVars(root, mt);
|
||||
stackproc.simplifyStackVars(root, mt, cl);
|
||||
|
||||
varproc.setVarVersions(root);
|
||||
}
|
||||
|
@ -56,14 +56,25 @@ public class NestedClassProcessor {
|
||||
|
||||
public void processClass(ClassNode root, ClassNode node) {
|
||||
|
||||
// hide lambda content methods
|
||||
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||
ClassNode node_content = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName);
|
||||
if(node_content != null && node_content.wrapper != null) {
|
||||
node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key);
|
||||
}
|
||||
}
|
||||
|
||||
if(node.nested.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(node.type != ClassNode.CLASS_LAMBDA) {
|
||||
|
||||
computeLocalVarsAndDefinitions(node);
|
||||
|
||||
// for each local or anonymous class ensure not empty enclosing method
|
||||
checkNotFoundClasses(root, node);
|
||||
}
|
||||
|
||||
for(ClassNode child : node.nested) {
|
||||
// ensure not-empty class name
|
||||
@ -75,6 +86,9 @@ public class NestedClassProcessor {
|
||||
|
||||
|
||||
for(ClassNode child : node.nested) {
|
||||
if(child.type == ClassNode.CLASS_LAMBDA) {
|
||||
setLambdaVars(node, child);
|
||||
} else {
|
||||
if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) {
|
||||
insertLocalVars(node, child);
|
||||
|
||||
@ -83,6 +97,7 @@ public class NestedClassProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(ClassNode child : node.nested) {
|
||||
processClass(root, child);
|
||||
@ -90,6 +105,74 @@ public class NestedClassProcessor {
|
||||
|
||||
}
|
||||
|
||||
private void setLambdaVars(ClassNode parent, ClassNode child) {
|
||||
|
||||
final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key);
|
||||
final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod);
|
||||
|
||||
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor);
|
||||
final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor);
|
||||
|
||||
final int vars_count = md_content.params.length - md_lambda.params.length;
|
||||
// if(vars_count < 0) { // should not happen, but just in case...
|
||||
// vars_count = 0;
|
||||
// }
|
||||
|
||||
final boolean is_static_lambda_content = child.lambda_information.is_content_method_static;
|
||||
|
||||
if(!is_static_lambda_content) { // this pointer
|
||||
if(DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {
|
||||
//meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName;
|
||||
final String lambda_class_name = child.simpleName;
|
||||
|
||||
final VarType lambda_class_type = new VarType(lambda_class_name, true);
|
||||
|
||||
DirectGraph graph = encmeth.getOrBuildGraph();
|
||||
|
||||
graph.iterateExprents(new DirectGraph.ExprentIterator() {
|
||||
public int processExprent(Exprent exprent) {
|
||||
|
||||
List<Exprent> lst = exprent.getAllExprents(true);
|
||||
lst.add(exprent);
|
||||
|
||||
for(Exprent expr: lst) {
|
||||
|
||||
if(expr.type == Exprent.EXPRENT_NEW) {
|
||||
NewExprent new_expr = (NewExprent)expr;
|
||||
if(new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) {
|
||||
|
||||
InvocationExprent inv_dynamic = new_expr.getConstructor();
|
||||
|
||||
int param_index = is_static_lambda_content ? 0 : 1;;
|
||||
int varindex = is_static_lambda_content ? 0 : 1;
|
||||
|
||||
for(int i = 0; i < vars_count; ++i) {
|
||||
|
||||
Exprent param = inv_dynamic.getLstParameters().get(param_index + i);
|
||||
|
||||
if(param.type == Exprent.EXPRENT_VAR) {
|
||||
VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param);
|
||||
String enc_varname = encmeth.varproc.getVarName(enc_varpaar);
|
||||
|
||||
meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname);
|
||||
}
|
||||
|
||||
varindex+=md_content.params[i].stack_size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkNotFoundClasses(ClassNode root, ClassNode node) {
|
||||
|
||||
List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested);
|
||||
@ -166,8 +249,8 @@ public class NestedClassProcessor {
|
||||
int cltypes = 0;
|
||||
|
||||
for(ClassNode nd: node.nested) {
|
||||
if((nd.access & CodeConstants.ACC_STATIC) == 0 &&
|
||||
(nd.access & CodeConstants.ACC_INTERFACE) == 0) {
|
||||
if(nd.type != ClassNode.CLASS_LAMBDA) {
|
||||
if((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) {
|
||||
|
||||
cltypes |= nd.type;
|
||||
|
||||
@ -179,6 +262,7 @@ public class NestedClassProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// local var masks
|
||||
final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = new HashMap<String, HashMap<String, List<VarFieldPair>>>();
|
||||
|
@ -65,6 +65,10 @@ public class NestedMemberAccess {
|
||||
|
||||
private void computeMethodTypes(ClassNode node) {
|
||||
|
||||
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(ClassNode nd : node.nested) {
|
||||
computeMethodTypes(nd);
|
||||
}
|
||||
@ -219,6 +223,10 @@ public class NestedMemberAccess {
|
||||
|
||||
private void eliminateStaticAccess(ClassNode node) {
|
||||
|
||||
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(MethodWrapper meth : node.wrapper.getMethods()) {
|
||||
|
||||
if(meth.root != null) {
|
||||
|
@ -26,23 +26,23 @@ import de.fernflower.modules.decompiler.stats.Statement;
|
||||
public class EliminateLoopsHelper {
|
||||
|
||||
|
||||
public static boolean eliminateLoops(Statement root) {
|
||||
|
||||
boolean ret = eliminateLoopsRec(root);
|
||||
|
||||
if(ret) {
|
||||
SequenceHelper.condenseSequences(root);
|
||||
|
||||
HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||
|
||||
SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
|
||||
while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
|
||||
SequenceHelper.condenseSequences(root);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// public static boolean eliminateLoops(Statement root) {
|
||||
//
|
||||
// boolean ret = eliminateLoopsRec(root);
|
||||
//
|
||||
// if(ret) {
|
||||
// SequenceHelper.condenseSequences(root);
|
||||
//
|
||||
// HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||
//
|
||||
// SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
|
||||
// while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
|
||||
// SequenceHelper.condenseSequences(root);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
private static boolean eliminateLoopsRec(Statement stat) {
|
||||
|
||||
|
@ -19,12 +19,12 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import de.fernflower.code.CodeConstants;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
import de.fernflower.main.extern.IFernflowerPreferences;
|
||||
import de.fernflower.modules.decompiler.exps.ArrayExprent;
|
||||
import de.fernflower.modules.decompiler.exps.AssignmentExprent;
|
||||
@ -41,6 +41,7 @@ import de.fernflower.modules.decompiler.sforms.SSAConstructorSparseEx;
|
||||
import de.fernflower.modules.decompiler.stats.IfStatement;
|
||||
import de.fernflower.modules.decompiler.stats.Statement;
|
||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||
import de.fernflower.struct.StructClass;
|
||||
import de.fernflower.struct.gen.VarType;
|
||||
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
||||
|
||||
@ -52,7 +53,7 @@ public class SimplifyExprentsHelper {
|
||||
this.firstInvocation = firstInvocation;
|
||||
}
|
||||
|
||||
public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa) {
|
||||
public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) {
|
||||
|
||||
boolean res = false;
|
||||
|
||||
@ -63,7 +64,7 @@ public class SimplifyExprentsHelper {
|
||||
boolean changed = false;
|
||||
|
||||
for(Statement st: stat.getStats()) {
|
||||
res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa);
|
||||
res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);
|
||||
|
||||
// collapse composed if's
|
||||
if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) {
|
||||
@ -84,13 +85,13 @@ public class SimplifyExprentsHelper {
|
||||
}
|
||||
|
||||
} else {
|
||||
res |= simplifyStackVarsExprents(stat.getExprents());
|
||||
res |= simplifyStackVarsExprents(stat.getExprents(), cl);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean simplifyStackVarsExprents(List<Exprent> list) {
|
||||
private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {
|
||||
|
||||
boolean res = false;
|
||||
|
||||
@ -108,6 +109,15 @@ public class SimplifyExprentsHelper {
|
||||
continue;
|
||||
}
|
||||
|
||||
// lambda expression (Java 8)
|
||||
ret = isLambda(current, cl);
|
||||
if(ret != null) {
|
||||
list.set(index, ret);
|
||||
res = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove monitor exit
|
||||
if(isMonitorExit(current)) {
|
||||
list.remove(index);
|
||||
@ -689,6 +699,38 @@ public class SimplifyExprentsHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Exprent isLambda(Exprent exprent, StructClass cl) {
|
||||
|
||||
List<Exprent> lst = exprent.getAllExprents();
|
||||
for(Exprent expr: lst) {
|
||||
Exprent ret = isLambda(expr, cl);
|
||||
if(ret != null) {
|
||||
exprent.replaceExprent(expr, ret);
|
||||
}
|
||||
}
|
||||
|
||||
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
|
||||
InvocationExprent in = (InvocationExprent)exprent;
|
||||
|
||||
if(in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) {
|
||||
|
||||
String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix();
|
||||
ClassNode lambda_class = DecompilerContext.getClassprocessor().getMapRootClasses().get(lambda_class_name);
|
||||
|
||||
if(lambda_class != null) { // real lambda class found, replace invocation with an anonymous class
|
||||
|
||||
NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0);
|
||||
newexp.setConstructor(in);
|
||||
|
||||
return newexp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static Exprent isSimpleConstructorInvocation(Exprent exprent) {
|
||||
|
||||
List<Exprent> lst = exprent.getAllExprents();
|
||||
|
@ -39,15 +39,16 @@ import de.fernflower.modules.decompiler.vars.VarVersionEdge;
|
||||
import de.fernflower.modules.decompiler.vars.VarVersionNode;
|
||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||
import de.fernflower.modules.decompiler.vars.VarVersionsGraph;
|
||||
import de.fernflower.struct.StructClass;
|
||||
import de.fernflower.struct.StructMethod;
|
||||
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
||||
import de.fernflower.util.InterpreterUtil;
|
||||
import de.fernflower.util.SFormsFastMapDirect;
|
||||
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
||||
|
||||
|
||||
public class StackVarsProcessor {
|
||||
|
||||
public void simplifyStackVars(RootStatement root, StructMethod mt) {
|
||||
public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) {
|
||||
|
||||
HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||
|
||||
@ -66,7 +67,7 @@ public class StackVarsProcessor {
|
||||
|
||||
|
||||
SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null);
|
||||
while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa)) {
|
||||
while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) {
|
||||
// System.out.println("--------------- \r\n"+root.toJava());
|
||||
found = true;
|
||||
}
|
||||
|
@ -14,6 +14,9 @@
|
||||
|
||||
package de.fernflower.modules.decompiler.exps;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -21,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import de.fernflower.code.CodeConstants;
|
||||
import de.fernflower.main.ClassWriter;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||
import de.fernflower.main.rels.MethodWrapper;
|
||||
@ -67,6 +71,8 @@ public class InvocationExprent extends Exprent {
|
||||
|
||||
private String stringDescriptor;
|
||||
|
||||
private String invoke_dynamic_classsuffix;
|
||||
|
||||
private int invocationTyp = INVOKE_VIRTUAL;
|
||||
|
||||
private List<Exprent> lstParameters = new ArrayList<Exprent>();
|
||||
@ -97,7 +103,9 @@ public class InvocationExprent extends Exprent {
|
||||
break;
|
||||
case CodeConstants.opc_invokedynamic:
|
||||
invocationTyp = INVOKE_DYNAMIC;
|
||||
|
||||
classname = "java/lang/Class"; // dummy class name
|
||||
invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
|
||||
}
|
||||
|
||||
if("<init>".equals(name)) {
|
||||
@ -178,13 +186,43 @@ public class InvocationExprent extends Exprent {
|
||||
String super_qualifier = null;
|
||||
boolean isInstanceThis = false;
|
||||
|
||||
if(isStatic) {
|
||||
if(invocationTyp != INVOKE_DYNAMIC) {
|
||||
if(invocationTyp == INVOKE_DYNAMIC) {
|
||||
// ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
||||
//
|
||||
// if(node != null) {
|
||||
// ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix);
|
||||
// if(lambda_node != null) {
|
||||
//
|
||||
// String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType);
|
||||
//
|
||||
// StringWriter strwriter = new StringWriter();
|
||||
// BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
|
||||
//
|
||||
// ClassWriter clwriter = new ClassWriter();
|
||||
//
|
||||
// try {
|
||||
// bufstrwriter.write("new " + typename + "() {");
|
||||
// bufstrwriter.newLine();
|
||||
//
|
||||
//
|
||||
//
|
||||
// bufstrwriter.flush();
|
||||
// } catch(IOException ex) {
|
||||
// throw new RuntimeException(ex);
|
||||
// }
|
||||
//
|
||||
// buf.append(strwriter.toString());
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
} else if(isStatic) {
|
||||
|
||||
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
||||
if(node == null || !classname.equals(node.classStruct.qualifiedName)) {
|
||||
buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if(instance != null && instance.type == Exprent.EXPRENT_VAR) {
|
||||
@ -457,4 +495,8 @@ public class InvocationExprent extends Exprent {
|
||||
this.invocationTyp = invocationTyp;
|
||||
}
|
||||
|
||||
public String getInvokeDynamicClassSuffix() {
|
||||
return invoke_dynamic_classsuffix;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import de.fernflower.code.CodeConstants;
|
||||
import de.fernflower.main.ClassWriter;
|
||||
import de.fernflower.main.DecompilerContext;
|
||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||
import de.fernflower.main.extern.IFernflowerPreferences;
|
||||
import de.fernflower.modules.decompiler.ExprProcessor;
|
||||
import de.fernflower.modules.decompiler.vars.CheckTypesResult;
|
||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||
@ -47,6 +48,8 @@ public class NewExprent extends Exprent {
|
||||
|
||||
private boolean anonymous;
|
||||
|
||||
private boolean lambda;
|
||||
|
||||
private boolean enumconst;
|
||||
|
||||
{
|
||||
@ -70,13 +73,19 @@ public class NewExprent extends Exprent {
|
||||
}
|
||||
|
||||
private void setAnonymous() {
|
||||
|
||||
anonymous = false;
|
||||
lambda = false;
|
||||
|
||||
if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) {
|
||||
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value);
|
||||
|
||||
if(node != null && node.type == ClassNode.CLASS_ANONYMOUS) {
|
||||
if(node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) {
|
||||
anonymous = true;
|
||||
|
||||
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||
lambda = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +171,7 @@ public class NewExprent extends Exprent {
|
||||
|
||||
buf.append("(");
|
||||
|
||||
if(constructor != null) {
|
||||
if(!lambda && constructor != null) {
|
||||
|
||||
InvocationExprent invsuper = child.superInvocation;
|
||||
|
||||
@ -208,7 +217,7 @@ public class NewExprent extends Exprent {
|
||||
|
||||
if(!enumconst) {
|
||||
String enclosing = null;
|
||||
if(constructor != null) {
|
||||
if(!lambda && constructor != null) {
|
||||
enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent);
|
||||
}
|
||||
|
||||
@ -236,12 +245,20 @@ public class NewExprent extends Exprent {
|
||||
|
||||
ClassWriter clwriter = new ClassWriter();
|
||||
try {
|
||||
if(lambda) {
|
||||
clwriter.classLambdaToJava(child, bufstrwriter, indent);
|
||||
} else {
|
||||
clwriter.classToJava(child, bufstrwriter, indent);
|
||||
}
|
||||
bufstrwriter.flush();
|
||||
} catch(IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if(lambda && !DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {
|
||||
buf.setLength(0); // remove the usual 'new <class>()', it will be replaced with lambda style '() ->'
|
||||
}
|
||||
|
||||
buf.append(strwriter.toString());
|
||||
|
||||
} else if(directArrayInit) {
|
||||
@ -455,6 +472,10 @@ public class NewExprent extends Exprent {
|
||||
this.directArrayInit = directArrayInit;
|
||||
}
|
||||
|
||||
public boolean isLambda() {
|
||||
return lambda;
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
return anonymous;
|
||||
}
|
||||
|
@ -122,6 +122,10 @@ public class StructClass {
|
||||
return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
||||
}
|
||||
|
||||
public StructMethod getMethod(String key) {
|
||||
return methods.getWithKey(key);
|
||||
}
|
||||
|
||||
public StructMethod getMethod(String name, String descriptor) {
|
||||
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
||||
}
|
||||
|
@ -47,4 +47,16 @@ public class StructBootstrapMethodsAttribute extends StructGeneralAttribute {
|
||||
|
||||
}
|
||||
|
||||
public int getMethodsNumber() {
|
||||
return method_refs.size();
|
||||
}
|
||||
|
||||
public LinkConstant getMethodReference(int index) {
|
||||
return method_refs.get(index);
|
||||
}
|
||||
|
||||
public List<PooledConstant> getMethodArguments(int index) {
|
||||
return method_arguments.get(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user