diff options
author | Stiver <stiver.mail@gmail.com> | 2014-03-26 20:53:06 +0100 |
---|---|---|
committer | Stiver <stiver.mail@gmail.com> | 2014-03-26 20:53:06 +0100 |
commit | bd99d3eb2f6ba60d7d2f9c81199708ca68db837c (patch) | |
tree | d14675c7c9bb0ec55544a8b67cb7340534615a12 /src/de | |
parent | 96379678e6290428a7a0d840e8a62d8a9026beef (diff) | |
download | fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.gz fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.lz fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.tar.xz fernflower-bd99d3eb2f6ba60d7d2f9c81199708ca68db837c.zip |
lambda expressions (Java 8)
Diffstat (limited to 'src/de')
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); + } + } |