mirror of
https://github.com/moparisthebest/fernflower
synced 2024-11-17 14:55:14 -05:00
lambda expressions (Java 8)
This commit is contained in:
parent
96379678e6
commit
bd99d3eb2f
@ -112,6 +112,84 @@ public class ClassWriter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void classLambdaToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException {
|
||||||
|
|
||||||
|
// get the class node with the content method
|
||||||
|
ClassNode node_content = node;
|
||||||
|
while(node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
node_content = node_content.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node_content == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS);
|
||||||
|
|
||||||
|
ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
||||||
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node);
|
||||||
|
|
||||||
|
ClassWrapper wrapper = node_content.wrapper;
|
||||||
|
StructClass cl = wrapper.getClassStruct();
|
||||||
|
|
||||||
|
DecompilerContext.getLogger().startWriteClass(node.simpleName);
|
||||||
|
|
||||||
|
// lambda method
|
||||||
|
StructMethod mt = cl.getMethod(node.lambda_information.content_method_key);
|
||||||
|
MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
|
||||||
|
MethodDescriptor md = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor);
|
||||||
|
|
||||||
|
if(!lambda_to_anonymous) { // lambda parameters '() ->'
|
||||||
|
|
||||||
|
StringBuilder buff = new StringBuilder("(");
|
||||||
|
|
||||||
|
boolean firstpar = true;
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
for(int i=0;i<md.params.length;i++) {
|
||||||
|
|
||||||
|
if(!firstpar) {
|
||||||
|
buff.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0));
|
||||||
|
buff.append(parname==null ? "param"+index : parname); // null iff decompiled with errors
|
||||||
|
|
||||||
|
firstpar = false;
|
||||||
|
|
||||||
|
index+=md.params[i].stack_size;
|
||||||
|
}
|
||||||
|
buff.append(") ->");
|
||||||
|
|
||||||
|
writer.write(buff.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringWriter strwriter = new StringWriter();
|
||||||
|
BufferedWriter bufstrwriter = new BufferedWriter(strwriter);
|
||||||
|
|
||||||
|
if(lambda_to_anonymous) {
|
||||||
|
methodLambdaToJava(node, node_content, mt, bufstrwriter, indent+1, false);
|
||||||
|
} else {
|
||||||
|
methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufstrwriter.flush();
|
||||||
|
|
||||||
|
// closing up class definition
|
||||||
|
writer.write(" {");
|
||||||
|
writer.newLine();
|
||||||
|
|
||||||
|
writer.write(strwriter.toString());
|
||||||
|
|
||||||
|
writer.write(InterpreterUtil.getIndentString(indent));
|
||||||
|
writer.write("}");
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold);
|
||||||
|
|
||||||
|
DecompilerContext.getLogger().endWriteClass();
|
||||||
|
}
|
||||||
|
|
||||||
public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException {
|
public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException {
|
||||||
|
|
||||||
ClassWrapper wrapper = node.wrapper;
|
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;
|
ClassWrapper wrapper = node.wrapper;
|
||||||
StructClass cl = wrapper.getClassStruct();
|
StructClass cl = wrapper.getClassStruct();
|
||||||
|
@ -24,8 +24,8 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import de.fernflower.code.CodeConstants;
|
import de.fernflower.code.CodeConstants;
|
||||||
import de.fernflower.main.collectors.CounterContainer;
|
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.IFernflowerPreferences;
|
||||||
import de.fernflower.main.extern.IIdentifierRenamer;
|
import de.fernflower.main.extern.IIdentifierRenamer;
|
||||||
import de.fernflower.main.rels.ClassWrapper;
|
import de.fernflower.main.rels.ClassWrapper;
|
||||||
|
import de.fernflower.main.rels.LambdaProcessor;
|
||||||
import de.fernflower.main.rels.NestedClassProcessor;
|
import de.fernflower.main.rels.NestedClassProcessor;
|
||||||
import de.fernflower.main.rels.NestedMemberAccess;
|
import de.fernflower.main.rels.NestedMemberAccess;
|
||||||
import de.fernflower.modules.decompiler.exps.InvocationExprent;
|
import de.fernflower.modules.decompiler.exps.InvocationExprent;
|
||||||
@ -238,6 +239,10 @@ public class ClassesProcessor {
|
|||||||
DecompilerContext.setImpcollector(new ImportCollector(root));
|
DecompilerContext.setImpcollector(new ImportCollector(root));
|
||||||
DecompilerContext.setCountercontainer(new CounterContainer());
|
DecompilerContext.setCountercontainer(new CounterContainer());
|
||||||
|
|
||||||
|
// lambda processing
|
||||||
|
LambdaProcessor lambda_proc = new LambdaProcessor();
|
||||||
|
lambda_proc.processClass(root);
|
||||||
|
|
||||||
// add simple class names to implicit import
|
// add simple class names to implicit import
|
||||||
addClassnameToImport(root, DecompilerContext.getImpcollector());
|
addClassnameToImport(root, DecompilerContext.getImpcollector());
|
||||||
// build wrappers for all nested classes
|
// build wrappers for all nested classes
|
||||||
@ -291,6 +296,10 @@ public class ClassesProcessor {
|
|||||||
|
|
||||||
private void initWrappers(ClassNode node) throws IOException {
|
private void initWrappers(ClassNode node) throws IOException {
|
||||||
|
|
||||||
|
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
|
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
|
||||||
wrapper.init();
|
wrapper.init();
|
||||||
|
|
||||||
@ -333,6 +342,7 @@ public class ClassesProcessor {
|
|||||||
public static final int CLASS_MEMBER = 1;
|
public static final int CLASS_MEMBER = 1;
|
||||||
public static final int CLASS_ANONYMOUS = 2;
|
public static final int CLASS_ANONYMOUS = 2;
|
||||||
public static final int CLASS_LOCAL = 4;
|
public static final int CLASS_LOCAL = 4;
|
||||||
|
public static final int CLASS_LAMBDA = 8;
|
||||||
|
|
||||||
public int type;
|
public int type;
|
||||||
|
|
||||||
@ -358,6 +368,28 @@ public class ClassesProcessor {
|
|||||||
|
|
||||||
public ClassNode parent;
|
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) {
|
public ClassNode(int type, StructClass classStruct) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.classStruct = classStruct;
|
this.classStruct = classStruct;
|
||||||
@ -374,5 +406,16 @@ public class ClassesProcessor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LambdaInformation {
|
||||||
|
public String class_name;
|
||||||
|
public String method_name;
|
||||||
|
public String method_descriptor;
|
||||||
|
|
||||||
|
public String content_method_name;
|
||||||
|
public String content_method_descriptor;
|
||||||
|
public String content_method_key;
|
||||||
|
|
||||||
|
public boolean is_content_method_static;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ public interface IFernflowerPreferences {
|
|||||||
public static final String DEPRECATED_COMMENT = "dpc";
|
public static final String DEPRECATED_COMMENT = "dpc";
|
||||||
public static final String NEW_LINE_SEPARATOR = "nls";
|
public static final String NEW_LINE_SEPARATOR = "nls";
|
||||||
public static final String IDEA_NOT_NULL_ANNOTATION = "inn";
|
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_WIN = "\r\n";
|
||||||
public static final String LINE_SEPARATOR_LIN = "\n";
|
public static final String LINE_SEPARATOR_LIN = "\n";
|
||||||
|
135
src/de/fernflower/main/rels/LambdaProcessor.java
Normal file
135
src/de/fernflower/main/rels/LambdaProcessor.java
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package de.fernflower.main.rels;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.fernflower.code.CodeConstants;
|
||||||
|
import de.fernflower.code.Instruction;
|
||||||
|
import de.fernflower.code.InstructionSequence;
|
||||||
|
import de.fernflower.main.ClassesProcessor;
|
||||||
|
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||||
|
import de.fernflower.main.DecompilerContext;
|
||||||
|
import de.fernflower.struct.StructClass;
|
||||||
|
import de.fernflower.struct.StructMethod;
|
||||||
|
import de.fernflower.struct.attr.StructBootstrapMethodsAttribute;
|
||||||
|
import de.fernflower.struct.attr.StructGeneralAttribute;
|
||||||
|
import de.fernflower.struct.consts.LinkConstant;
|
||||||
|
import de.fernflower.struct.consts.PooledConstant;
|
||||||
|
import de.fernflower.struct.consts.PrimitiveConstant;
|
||||||
|
import de.fernflower.struct.gen.MethodDescriptor;
|
||||||
|
import de.fernflower.util.InterpreterUtil;
|
||||||
|
|
||||||
|
public class LambdaProcessor {
|
||||||
|
|
||||||
|
private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory";
|
||||||
|
private static final String JAVAC_LAMBDA_METHOD = "metafactory";
|
||||||
|
private static final String JAVAC_LAMBDA_METHOD_DESCRIPTOR = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
|
||||||
|
|
||||||
|
public void processClass(ClassNode node) throws IOException {
|
||||||
|
|
||||||
|
for(ClassNode child : node.nested) {
|
||||||
|
processClass(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.nested.isEmpty()) {
|
||||||
|
hasLambda(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasLambda(ClassNode node) throws IOException {
|
||||||
|
|
||||||
|
ClassesProcessor clprocessor = DecompilerContext.getClassprocessor();
|
||||||
|
StructClass cl = node.classStruct;
|
||||||
|
|
||||||
|
if(cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
|
||||||
|
if(bootstrap != null && bootstrap.getMethodsNumber() > 0) {
|
||||||
|
|
||||||
|
Set<Integer> lambda_methods = new HashSet<Integer>();
|
||||||
|
|
||||||
|
// find lambda bootstrap constants
|
||||||
|
for(int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
|
||||||
|
LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle
|
||||||
|
|
||||||
|
if(JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
|
||||||
|
JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) &&
|
||||||
|
JAVAC_LAMBDA_METHOD_DESCRIPTOR.equals(method_ref.descriptor)) { // check for javac lambda structure. FIXME: extend for Eclipse etc. at some point
|
||||||
|
lambda_methods.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lambda_methods.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
|
||||||
|
|
||||||
|
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
|
||||||
|
for(StructMethod mt: cl.getMethods()) {
|
||||||
|
mt.expandData();
|
||||||
|
|
||||||
|
InstructionSequence seq = mt.getInstructionSequence();
|
||||||
|
if(seq != null && seq.length() > 0) {
|
||||||
|
int len = seq.length();
|
||||||
|
|
||||||
|
for(int i = 0; i < len; ++i) {
|
||||||
|
Instruction instr = seq.getInstr(i);
|
||||||
|
|
||||||
|
if(instr.opcode == CodeConstants.opc_invokedynamic) {
|
||||||
|
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
|
||||||
|
|
||||||
|
if(lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found
|
||||||
|
|
||||||
|
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
|
||||||
|
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);
|
||||||
|
|
||||||
|
String lambda_class_name = md.ret.value;
|
||||||
|
String lambda_method_name = invoke_dynamic.elementname;
|
||||||
|
String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type
|
||||||
|
|
||||||
|
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
|
||||||
|
|
||||||
|
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.elementname, content_method_handle.descriptor, lambda_class_name,
|
||||||
|
lambda_method_name, lambda_method_descriptor, cl);
|
||||||
|
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
|
||||||
|
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());
|
||||||
|
|
||||||
|
node.nested.add(node_lambda);
|
||||||
|
node_lambda.parent = node;
|
||||||
|
|
||||||
|
clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
|
||||||
|
mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build class hierarchy on lambda
|
||||||
|
for(ClassNode nd : node.nested) {
|
||||||
|
if(nd.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);
|
||||||
|
if(parent_class_name != null) {
|
||||||
|
ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name);
|
||||||
|
|
||||||
|
parent_class.nested.add(nd);
|
||||||
|
nd.parent = parent_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: mixed hierarchy?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,10 +14,8 @@
|
|||||||
|
|
||||||
package de.fernflower.main.rels;
|
package de.fernflower.main.rels;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import test.util.DotExporter;
|
|
||||||
import de.fernflower.code.InstructionSequence;
|
import de.fernflower.code.InstructionSequence;
|
||||||
import de.fernflower.code.cfg.ControlFlowGraph;
|
import de.fernflower.code.cfg.ControlFlowGraph;
|
||||||
import de.fernflower.main.DecompilerContext;
|
import de.fernflower.main.DecompilerContext;
|
||||||
@ -186,7 +184,7 @@ public class MethodProcessorThread implements Runnable {
|
|||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
StackVarsProcessor stackproc = new StackVarsProcessor();
|
StackVarsProcessor stackproc = new StackVarsProcessor();
|
||||||
stackproc.simplifyStackVars(root, mt);
|
stackproc.simplifyStackVars(root, mt, cl);
|
||||||
|
|
||||||
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
|
// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
|
||||||
|
|
||||||
@ -223,7 +221,7 @@ public class MethodProcessorThread implements Runnable {
|
|||||||
SequenceHelper.condenseSequences(root);
|
SequenceHelper.condenseSequences(root);
|
||||||
|
|
||||||
StackVarsProcessor stackproc = new StackVarsProcessor();
|
StackVarsProcessor stackproc = new StackVarsProcessor();
|
||||||
stackproc.simplifyStackVars(root, mt);
|
stackproc.simplifyStackVars(root, mt, cl);
|
||||||
|
|
||||||
varproc.setVarVersions(root);
|
varproc.setVarVersions(root);
|
||||||
}
|
}
|
||||||
|
@ -55,15 +55,26 @@ public class NestedClassProcessor {
|
|||||||
|
|
||||||
|
|
||||||
public void processClass(ClassNode root, ClassNode node) {
|
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()) {
|
if(node.nested.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(node.type != ClassNode.CLASS_LAMBDA) {
|
||||||
|
|
||||||
computeLocalVarsAndDefinitions(node);
|
computeLocalVarsAndDefinitions(node);
|
||||||
|
|
||||||
// for each local or anonymous class ensure not empty enclosing method
|
// for each local or anonymous class ensure not empty enclosing method
|
||||||
checkNotFoundClasses(root, node);
|
checkNotFoundClasses(root, node);
|
||||||
|
}
|
||||||
|
|
||||||
for(ClassNode child : node.nested) {
|
for(ClassNode child : node.nested) {
|
||||||
// ensure not-empty class name
|
// ensure not-empty class name
|
||||||
@ -75,11 +86,15 @@ public class NestedClassProcessor {
|
|||||||
|
|
||||||
|
|
||||||
for(ClassNode child : node.nested) {
|
for(ClassNode child : node.nested) {
|
||||||
if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) {
|
if(child.type == ClassNode.CLASS_LAMBDA) {
|
||||||
insertLocalVars(node, child);
|
setLambdaVars(node, child);
|
||||||
|
} else {
|
||||||
if(child.type == ClassNode.CLASS_LOCAL) {
|
if(child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) {
|
||||||
setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child);
|
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) {
|
private void checkNotFoundClasses(ClassNode root, ClassNode node) {
|
||||||
|
|
||||||
List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested);
|
List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested);
|
||||||
@ -166,16 +249,17 @@ public class NestedClassProcessor {
|
|||||||
int cltypes = 0;
|
int cltypes = 0;
|
||||||
|
|
||||||
for(ClassNode nd: node.nested) {
|
for(ClassNode nd: node.nested) {
|
||||||
if((nd.access & CodeConstants.ACC_STATIC) == 0 &&
|
if(nd.type != ClassNode.CLASS_LAMBDA) {
|
||||||
(nd.access & CodeConstants.ACC_INTERFACE) == 0) {
|
if((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) {
|
||||||
|
|
||||||
cltypes |= nd.type;
|
cltypes |= nd.type;
|
||||||
|
|
||||||
HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper);
|
HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper);
|
||||||
if(mask.isEmpty()) {
|
if(mask.isEmpty()) {
|
||||||
DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING);
|
DecompilerContext.getLogger().writeMessage("Nested class "+nd.classStruct.qualifiedName+" has no constructor!", IFernflowerLogger.WARNING);
|
||||||
} else {
|
} else {
|
||||||
mapVarMasks.put(nd.classStruct.qualifiedName, mask);
|
mapVarMasks.put(nd.classStruct.qualifiedName, mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,10 @@ public class NestedMemberAccess {
|
|||||||
|
|
||||||
private void computeMethodTypes(ClassNode node) {
|
private void computeMethodTypes(ClassNode node) {
|
||||||
|
|
||||||
|
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for(ClassNode nd : node.nested) {
|
for(ClassNode nd : node.nested) {
|
||||||
computeMethodTypes(nd);
|
computeMethodTypes(nd);
|
||||||
}
|
}
|
||||||
@ -219,6 +223,10 @@ public class NestedMemberAccess {
|
|||||||
|
|
||||||
private void eliminateStaticAccess(ClassNode node) {
|
private void eliminateStaticAccess(ClassNode node) {
|
||||||
|
|
||||||
|
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for(MethodWrapper meth : node.wrapper.getMethods()) {
|
for(MethodWrapper meth : node.wrapper.getMethods()) {
|
||||||
|
|
||||||
if(meth.root != null) {
|
if(meth.root != null) {
|
||||||
|
@ -26,23 +26,23 @@ import de.fernflower.modules.decompiler.stats.Statement;
|
|||||||
public class EliminateLoopsHelper {
|
public class EliminateLoopsHelper {
|
||||||
|
|
||||||
|
|
||||||
public static boolean eliminateLoops(Statement root) {
|
// public static boolean eliminateLoops(Statement root) {
|
||||||
|
//
|
||||||
boolean ret = eliminateLoopsRec(root);
|
// boolean ret = eliminateLoopsRec(root);
|
||||||
|
//
|
||||||
if(ret) {
|
// if(ret) {
|
||||||
SequenceHelper.condenseSequences(root);
|
// SequenceHelper.condenseSequences(root);
|
||||||
|
//
|
||||||
HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
// HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||||
|
//
|
||||||
SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
|
// SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
|
||||||
while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
|
// while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
|
||||||
SequenceHelper.condenseSequences(root);
|
// SequenceHelper.condenseSequences(root);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
// }
|
||||||
|
|
||||||
private static boolean eliminateLoopsRec(Statement stat) {
|
private static boolean eliminateLoopsRec(Statement stat) {
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import de.fernflower.code.CodeConstants;
|
import de.fernflower.code.CodeConstants;
|
||||||
import de.fernflower.main.DecompilerContext;
|
|
||||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||||
|
import de.fernflower.main.DecompilerContext;
|
||||||
import de.fernflower.main.extern.IFernflowerPreferences;
|
import de.fernflower.main.extern.IFernflowerPreferences;
|
||||||
import de.fernflower.modules.decompiler.exps.ArrayExprent;
|
import de.fernflower.modules.decompiler.exps.ArrayExprent;
|
||||||
import de.fernflower.modules.decompiler.exps.AssignmentExprent;
|
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.IfStatement;
|
||||||
import de.fernflower.modules.decompiler.stats.Statement;
|
import de.fernflower.modules.decompiler.stats.Statement;
|
||||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||||
|
import de.fernflower.struct.StructClass;
|
||||||
import de.fernflower.struct.gen.VarType;
|
import de.fernflower.struct.gen.VarType;
|
||||||
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ public class SimplifyExprentsHelper {
|
|||||||
this.firstInvocation = firstInvocation;
|
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;
|
boolean res = false;
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ public class SimplifyExprentsHelper {
|
|||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
for(Statement st: stat.getStats()) {
|
for(Statement st: stat.getStats()) {
|
||||||
res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa);
|
res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);
|
||||||
|
|
||||||
// collapse composed if's
|
// collapse composed if's
|
||||||
if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) {
|
if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) {
|
||||||
@ -84,13 +85,13 @@ public class SimplifyExprentsHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
res |= simplifyStackVarsExprents(stat.getExprents());
|
res |= simplifyStackVarsExprents(stat.getExprents(), cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean simplifyStackVarsExprents(List<Exprent> list) {
|
private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {
|
||||||
|
|
||||||
boolean res = false;
|
boolean res = false;
|
||||||
|
|
||||||
@ -108,6 +109,15 @@ public class SimplifyExprentsHelper {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lambda expression (Java 8)
|
||||||
|
ret = isLambda(current, cl);
|
||||||
|
if(ret != null) {
|
||||||
|
list.set(index, ret);
|
||||||
|
res = true;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// remove monitor exit
|
// remove monitor exit
|
||||||
if(isMonitorExit(current)) {
|
if(isMonitorExit(current)) {
|
||||||
list.remove(index);
|
list.remove(index);
|
||||||
@ -688,6 +698,38 @@ public class SimplifyExprentsHelper {
|
|||||||
|
|
||||||
return false;
|
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) {
|
private static Exprent isSimpleConstructorInvocation(Exprent exprent) {
|
||||||
|
|
||||||
|
@ -39,15 +39,16 @@ import de.fernflower.modules.decompiler.vars.VarVersionEdge;
|
|||||||
import de.fernflower.modules.decompiler.vars.VarVersionNode;
|
import de.fernflower.modules.decompiler.vars.VarVersionNode;
|
||||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||||
import de.fernflower.modules.decompiler.vars.VarVersionsGraph;
|
import de.fernflower.modules.decompiler.vars.VarVersionsGraph;
|
||||||
|
import de.fernflower.struct.StructClass;
|
||||||
import de.fernflower.struct.StructMethod;
|
import de.fernflower.struct.StructMethod;
|
||||||
|
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
||||||
import de.fernflower.util.InterpreterUtil;
|
import de.fernflower.util.InterpreterUtil;
|
||||||
import de.fernflower.util.SFormsFastMapDirect;
|
import de.fernflower.util.SFormsFastMapDirect;
|
||||||
import de.fernflower.util.FastSparseSetFactory.FastSparseSet;
|
|
||||||
|
|
||||||
|
|
||||||
public class StackVarsProcessor {
|
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>();
|
HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ public class StackVarsProcessor {
|
|||||||
|
|
||||||
|
|
||||||
SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null);
|
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());
|
// System.out.println("--------------- \r\n"+root.toJava());
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
package de.fernflower.modules.decompiler.exps;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -21,6 +24,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.fernflower.code.CodeConstants;
|
import de.fernflower.code.CodeConstants;
|
||||||
|
import de.fernflower.main.ClassWriter;
|
||||||
import de.fernflower.main.DecompilerContext;
|
import de.fernflower.main.DecompilerContext;
|
||||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||||
import de.fernflower.main.rels.MethodWrapper;
|
import de.fernflower.main.rels.MethodWrapper;
|
||||||
@ -67,6 +71,8 @@ public class InvocationExprent extends Exprent {
|
|||||||
|
|
||||||
private String stringDescriptor;
|
private String stringDescriptor;
|
||||||
|
|
||||||
|
private String invoke_dynamic_classsuffix;
|
||||||
|
|
||||||
private int invocationTyp = INVOKE_VIRTUAL;
|
private int invocationTyp = INVOKE_VIRTUAL;
|
||||||
|
|
||||||
private List<Exprent> lstParameters = new ArrayList<Exprent>();
|
private List<Exprent> lstParameters = new ArrayList<Exprent>();
|
||||||
@ -97,7 +103,9 @@ public class InvocationExprent extends Exprent {
|
|||||||
break;
|
break;
|
||||||
case CodeConstants.opc_invokedynamic:
|
case CodeConstants.opc_invokedynamic:
|
||||||
invocationTyp = INVOKE_DYNAMIC;
|
invocationTyp = INVOKE_DYNAMIC;
|
||||||
|
|
||||||
classname = "java/lang/Class"; // dummy class name
|
classname = "java/lang/Class"; // dummy class name
|
||||||
|
invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if("<init>".equals(name)) {
|
if("<init>".equals(name)) {
|
||||||
@ -178,13 +186,43 @@ public class InvocationExprent extends Exprent {
|
|||||||
String super_qualifier = null;
|
String super_qualifier = null;
|
||||||
boolean isInstanceThis = false;
|
boolean isInstanceThis = false;
|
||||||
|
|
||||||
if(isStatic) {
|
if(invocationTyp == INVOKE_DYNAMIC) {
|
||||||
if(invocationTyp != INVOKE_DYNAMIC) {
|
// ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
||||||
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE);
|
//
|
||||||
if(node == null || !classname.equals(node.classStruct.qualifiedName)) {
|
// if(node != null) {
|
||||||
buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
|
// 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 {
|
} else {
|
||||||
|
|
||||||
if(instance != null && instance.type == Exprent.EXPRENT_VAR) {
|
if(instance != null && instance.type == Exprent.EXPRENT_VAR) {
|
||||||
@ -455,6 +493,10 @@ public class InvocationExprent extends Exprent {
|
|||||||
|
|
||||||
public void setInvocationTyp(int invocationTyp) {
|
public void setInvocationTyp(int invocationTyp) {
|
||||||
this.invocationTyp = invocationTyp;
|
this.invocationTyp = invocationTyp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInvokeDynamicClassSuffix() {
|
||||||
|
return invoke_dynamic_classsuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import de.fernflower.code.CodeConstants;
|
|||||||
import de.fernflower.main.ClassWriter;
|
import de.fernflower.main.ClassWriter;
|
||||||
import de.fernflower.main.DecompilerContext;
|
import de.fernflower.main.DecompilerContext;
|
||||||
import de.fernflower.main.ClassesProcessor.ClassNode;
|
import de.fernflower.main.ClassesProcessor.ClassNode;
|
||||||
|
import de.fernflower.main.extern.IFernflowerPreferences;
|
||||||
import de.fernflower.modules.decompiler.ExprProcessor;
|
import de.fernflower.modules.decompiler.ExprProcessor;
|
||||||
import de.fernflower.modules.decompiler.vars.CheckTypesResult;
|
import de.fernflower.modules.decompiler.vars.CheckTypesResult;
|
||||||
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
|
||||||
@ -47,6 +48,8 @@ public class NewExprent extends Exprent {
|
|||||||
|
|
||||||
private boolean anonymous;
|
private boolean anonymous;
|
||||||
|
|
||||||
|
private boolean lambda;
|
||||||
|
|
||||||
private boolean enumconst;
|
private boolean enumconst;
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -70,13 +73,19 @@ public class NewExprent extends Exprent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setAnonymous() {
|
private void setAnonymous() {
|
||||||
|
|
||||||
anonymous = false;
|
anonymous = false;
|
||||||
|
lambda = false;
|
||||||
|
|
||||||
if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) {
|
if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) {
|
||||||
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value);
|
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;
|
anonymous = true;
|
||||||
|
|
||||||
|
if(node.type == ClassNode.CLASS_LAMBDA) {
|
||||||
|
lambda = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +171,7 @@ public class NewExprent extends Exprent {
|
|||||||
|
|
||||||
buf.append("(");
|
buf.append("(");
|
||||||
|
|
||||||
if(constructor != null) {
|
if(!lambda && constructor != null) {
|
||||||
|
|
||||||
InvocationExprent invsuper = child.superInvocation;
|
InvocationExprent invsuper = child.superInvocation;
|
||||||
|
|
||||||
@ -208,7 +217,7 @@ public class NewExprent extends Exprent {
|
|||||||
|
|
||||||
if(!enumconst) {
|
if(!enumconst) {
|
||||||
String enclosing = null;
|
String enclosing = null;
|
||||||
if(constructor != null) {
|
if(!lambda && constructor != null) {
|
||||||
enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent);
|
enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,12 +245,20 @@ public class NewExprent extends Exprent {
|
|||||||
|
|
||||||
ClassWriter clwriter = new ClassWriter();
|
ClassWriter clwriter = new ClassWriter();
|
||||||
try {
|
try {
|
||||||
clwriter.classToJava(child, bufstrwriter, indent);
|
if(lambda) {
|
||||||
|
clwriter.classLambdaToJava(child, bufstrwriter, indent);
|
||||||
|
} else {
|
||||||
|
clwriter.classToJava(child, bufstrwriter, indent);
|
||||||
|
}
|
||||||
bufstrwriter.flush();
|
bufstrwriter.flush();
|
||||||
} catch(IOException ex) {
|
} catch(IOException ex) {
|
||||||
throw new RuntimeException(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());
|
buf.append(strwriter.toString());
|
||||||
|
|
||||||
} else if(directArrayInit) {
|
} else if(directArrayInit) {
|
||||||
@ -455,6 +472,10 @@ public class NewExprent extends Exprent {
|
|||||||
this.directArrayInit = directArrayInit;
|
this.directArrayInit = directArrayInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLambda() {
|
||||||
|
return lambda;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAnonymous() {
|
public boolean isAnonymous() {
|
||||||
return anonymous;
|
return anonymous;
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,10 @@ public class StructClass {
|
|||||||
return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StructMethod getMethod(String key) {
|
||||||
|
return methods.getWithKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
public StructMethod getMethod(String name, String descriptor) {
|
public StructMethod getMethod(String name, String descriptor) {
|
||||||
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,16 @@ public class StructBootstrapMethodsAttribute extends StructGeneralAttribute {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMethodsNumber() {
|
||||||
|
return method_refs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkConstant getMethodReference(int index) {
|
||||||
|
return method_refs.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PooledConstant> getMethodArguments(int index) {
|
||||||
|
return method_arguments.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user