summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStiver <stiver.mail@gmail.com>2014-03-26 20:53:06 +0100
committerStiver <stiver.mail@gmail.com>2014-03-26 20:53:06 +0100
commitbd99d3eb2f6ba60d7d2f9c81199708ca68db837c (patch)
treed14675c7c9bb0ec55544a8b67cb7340534615a12
parent96379678e6290428a7a0d840e8a62d8a9026beef (diff)
downloadfernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar
fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.gz
fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.lz
fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.xz
fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.zip
lambda expressions (Java 8)
-rw-r--r--src/de/fernflower/main/ClassWriter.java172
-rw-r--r--src/de/fernflower/main/ClassesProcessor.java45
-rw-r--r--src/de/fernflower/main/extern/IFernflowerPreferences.java1
-rw-r--r--src/de/fernflower/main/rels/LambdaProcessor.java135
-rw-r--r--src/de/fernflower/main/rels/MethodProcessorThread.java6
-rw-r--r--src/de/fernflower/main/rels/NestedClassProcessor.java122
-rw-r--r--src/de/fernflower/main/rels/NestedMemberAccess.java8
-rw-r--r--src/de/fernflower/modules/decompiler/EliminateLoopsHelper.java34
-rw-r--r--src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java54
-rw-r--r--src/de/fernflower/modules/decompiler/StackVarsProcessor.java7
-rw-r--r--src/de/fernflower/modules/decompiler/exps/InvocationExprent.java54
-rw-r--r--src/de/fernflower/modules/decompiler/exps/NewExprent.java29
-rw-r--r--src/de/fernflower/struct/StructClass.java4
-rw-r--r--src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java12
14 files changed, 622 insertions, 61 deletions
diff --git a/src/de/fernflower/main/ClassWriter.java b/src/de/fernflower/main/ClassWriter.java
index 4e49f20..7e1266a 100644
--- a/src/de/fernflower/main/ClassWriter.java
+++ b/src/de/fernflower/main/ClassWriter.java
@@ -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();
diff --git a/src/de/fernflower/main/ClassesProcessor.java b/src/de/fernflower/main/ClassesProcessor.java
index c19d746..5155798 100644
--- a/src/de/fernflower/main/ClassesProcessor.java
+++ b/src/de/fernflower/main/ClassesProcessor.java
@@ -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;
+ }
}
}
diff --git a/src/de/fernflower/main/extern/IFernflowerPreferences.java b/src/de/fernflower/main/extern/IFernflowerPreferences.java
index 2fd285a..0d3ea52 100644
--- a/src/de/fernflower/main/extern/IFernflowerPreferences.java
+++ b/src/de/fernflower/main/extern/IFernflowerPreferences.java
@@ -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";
diff --git a/src/de/fernflower/main/rels/LambdaProcessor.java b/src/de/fernflower/main/rels/LambdaProcessor.java
new file mode 100644
index 0000000..d2fe790
--- /dev/null
+++ b/src/de/fernflower/main/rels/LambdaProcessor.java
@@ -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;
+ }
+
+}
diff --git a/src/de/fernflower/main/rels/MethodProcessorThread.java b/src/de/fernflower/main/rels/MethodProcessorThread.java
index a651e71..3ec6db9 100644
--- a/src/de/fernflower/main/rels/MethodProcessorThread.java
+++ b/src/de/fernflower/main/rels/MethodProcessorThread.java
@@ -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);
}
diff --git a/src/de/fernflower/main/rels/NestedClassProcessor.java b/src/de/fernflower/main/rels/NestedClassProcessor.java
index db71b1d..8358f63 100644
--- a/src/de/fernflower/main/rels/NestedClassProcessor.java
+++ b/src/de/fernflower/main/rels/NestedClassProcessor.java
@@ -55,15 +55,26 @@ 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);
+ 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,11 +86,15 @@ public class NestedClassProcessor {
for(ClassNode child : node.nested) {
- if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) {
- insertLocalVars(node, child);
-
- if(child.type == ClassNode.CLASS_LOCAL) {
- setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child);
+ 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);
+
+ if(child.type == ClassNode.CLASS_LOCAL) {
+ setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), 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,16 +249,17 @@ public class NestedClassProcessor {
int cltypes = 0;
for(ClassNode nd: node.nested) {
- if((nd.access & CodeConstants.ACC_STATIC) == 0 &&
- (nd.access & CodeConstants.ACC_INTERFACE) == 0) {
-
- cltypes |= nd.type;
-
- HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper);
- if(mask.isEmpty()) {
- DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING);
- } else {
- mapVarMasks.put(nd.classStruct.qualifiedName, mask);
+ if(nd.type != ClassNode.CLASS_LAMBDA) {
+ if((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) {
+
+ cltypes |= nd.type;
+
+ HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper);
+ if(mask.isEmpty()) {
+ DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING);
+ } else {
+ mapVarMasks.put(nd.classStruct.qualifiedName, mask);
+ }
}
}
}
diff --git a/src/de/fernflower/main/rels/NestedMemberAccess.java b/src/de/fernflower/main/rels/NestedMemberAccess.java
index a922d36..236b611 100644
--- a/src/de/fernflower/main/rels/NestedMemberAccess.java
+++ b/src/de/fernflower/main/rels/NestedMemberAccess.java
@@ -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) {
diff --git a/src/de/fernflower/modules/decompiler/EliminateLoopsHelper.java b/src/de/fernflower/modules/decompiler/EliminateLoopsHelper.java
index df69892..9b6f91b 100644
--- a/src/de/fernflower/modules/decompiler/EliminateLoopsHelper.java
+++ b/src/de/fernflower/modules/decompiler/EliminateLoopsHelper.java
@@ -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) {
diff --git a/src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java b/src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java
index 6ea4b16..49db596 100644
--- a/src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java
+++ b/src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java
@@ -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);
@@ -688,6 +698,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) {
diff --git a/src/de/fernflower/modules/decompiler/StackVarsProcessor.java b/src/de/fernflower/modules/decompiler/StackVarsProcessor.java
index 9af7f5a..1632355 100644
--- a/src/de/fernflower/modules/decompiler/StackVarsProcessor.java
+++ b/src/de/fernflower/modules/decompiler/StackVarsProcessor.java
@@ -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;
}
diff --git a/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java b/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java
index 5f0f5c1..8427cbf 100644
--- a/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java
+++ b/src/de/fernflower/modules/decompiler/exps/InvocationExprent.java
@@ -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) {
- ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
- if(node == null || !classname.equals(node.classStruct.qualifiedName)) {
- buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
- }
+ 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) {
@@ -455,6 +493,10 @@ public class InvocationExprent extends Exprent {
public void setInvocationTyp(int invocationTyp) {
this.invocationTyp = invocationTyp;
+ }
+
+ public String getInvokeDynamicClassSuffix() {
+ return invoke_dynamic_classsuffix;
}
}
diff --git a/src/de/fernflower/modules/decompiler/exps/NewExprent.java b/src/de/fernflower/modules/decompiler/exps/NewExprent.java
index a9ff7f2..3e636d7 100644
--- a/src/de/fernflower/modules/decompiler/exps/NewExprent.java
+++ b/src/de/fernflower/modules/decompiler/exps/NewExprent.java
@@ -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 {
- clwriter.classToJava(child, bufstrwriter, indent);
+ 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;
}
diff --git a/src/de/fernflower/struct/StructClass.java b/src/de/fernflower/struct/StructClass.java
index de2e856..3f1d687 100644
--- a/src/de/fernflower/struct/StructClass.java
+++ b/src/de/fernflower/struct/StructClass.java
@@ -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));
}
diff --git a/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java b/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java
index e9e8837..37b10a6 100644
--- a/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java
+++ b/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java
@@ -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);
+ }
+
}