lambda expressions (Java 8)

This commit is contained in:
Stiver 2014-03-26 20:53:06 +01:00
parent 96379678e6
commit bd99d3eb2f
14 changed files with 622 additions and 61 deletions

View File

@ -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();

View File

@ -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;
}
}
}

View File

@ -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";

View 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;
}
}

View File

@ -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);
}

View File

@ -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>>>();

View File

@ -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) {

View File

@ -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) {

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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);
}
}