decompiler: do not fail on nested lambdas

This commit is contained in:
Egor.Ushakov 2014-12-31 15:09:51 +03:00
parent f4e6c8d56f
commit 5a96486853
12 changed files with 74 additions and 46 deletions

View File

@ -41,7 +41,7 @@ public class AssertProcessor {
public static void buildAssertions(ClassNode node) { public static void buildAssertions(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructField field = findAssertionField(node); StructField field = findAssertionField(node);
@ -67,7 +67,7 @@ public class AssertProcessor {
private static StructField findAssertionField(ClassNode node) { private static StructField findAssertionField(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
@ -105,7 +105,7 @@ public class AssertProcessor {
ClassNode nd = node; ClassNode nd = node;
while (nd != null) { while (nd != null) {
if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) { if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) {
break; break;
} }
nd = nd.parent; nd = nd.parent;

View File

@ -85,7 +85,7 @@ public class ClassReference14Processor {
public void processClassReferences(ClassNode node) { public void processClassReferences(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
// int major_version = wrapper.getClassStruct().major_version; // int major_version = wrapper.getClassStruct().major_version;
// int minor_version = wrapper.getClassStruct().minor_version; // int minor_version = wrapper.getClassStruct().minor_version;
@ -123,7 +123,7 @@ public class ClassReference14Processor {
final HashMap<ClassWrapper, MethodWrapper> mapClassMeths, final HashMap<ClassWrapper, MethodWrapper> mapClassMeths,
final HashSet<ClassWrapper> setFound) { final HashSet<ClassWrapper> setFound) {
final ClassWrapper wrapper = node.wrapper; final ClassWrapper wrapper = node.getWrapper();
// search code // search code
for (MethodWrapper meth : wrapper.getMethods()) { for (MethodWrapper meth : wrapper.getMethods()) {
@ -176,7 +176,7 @@ public class ClassReference14Processor {
private void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) { private void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
for (MethodWrapper method : wrapper.getMethods()) { for (MethodWrapper method : wrapper.getMethods()) {
StructMethod mt = method.methodStruct; StructMethod mt = method.methodStruct;

View File

@ -56,7 +56,7 @@ public class ClassWriter {
} }
private void invokeProcessors(ClassNode node) { private void invokeProcessors(ClassNode node) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
InitializerProcessor.extractInitializers(wrapper); InitializerProcessor.extractInitializers(wrapper);
@ -75,12 +75,8 @@ public class ClassWriter {
} }
public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent) { public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent) {
// get the class node with the content method ClassWrapper wrapper = node.getWrapper();
ClassNode classNode = node; if (wrapper == null) {
while (classNode != null && classNode.type == ClassNode.CLASS_LAMBDA) {
classNode = classNode.parent;
}
if (classNode == null) {
return; return;
} }
@ -92,7 +88,6 @@ public class ClassWriter {
BytecodeMappingTracer tracer = new BytecodeMappingTracer(); BytecodeMappingTracer tracer = new BytecodeMappingTracer();
try { try {
ClassWrapper wrapper = classNode.wrapper;
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
DecompilerContext.getLogger().startWriteClass(node.simpleName); DecompilerContext.getLogger().startWriteClass(node.simpleName);
@ -144,7 +139,7 @@ public class ClassWriter {
buffer.append(" {").appendLineSeparator(); buffer.append(" {").appendLineSeparator();
methodLambdaToJava(node, classNode, mt, buffer, indent + 1, !lambdaToAnonymous, tracer); methodLambdaToJava(node, wrapper, mt, buffer, indent + 1, !lambdaToAnonymous, tracer);
buffer.appendIndent(indent).append("}"); buffer.appendIndent(indent).append("}");
} }
@ -167,7 +162,7 @@ public class ClassWriter {
// last minute processing // last minute processing
invokeProcessors(node); invokeProcessors(node);
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
@ -287,7 +282,7 @@ public class ClassWriter {
return; return;
} }
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
@ -474,12 +469,11 @@ public class ClassWriter {
} }
private static void methodLambdaToJava(ClassNode lambdaNode, private static void methodLambdaToJava(ClassNode lambdaNode,
ClassNode classNode, ClassWrapper classWrapper,
StructMethod mt, StructMethod mt,
TextBuffer buffer, TextBuffer buffer,
int indent, int indent,
boolean codeOnly, BytecodeMappingTracer tracer) { boolean codeOnly, BytecodeMappingTracer tracer) {
ClassWrapper classWrapper = classNode.wrapper;
MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
@ -562,7 +556,7 @@ public class ClassWriter {
} }
private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
ClassWrapper wrapper = node.wrapper; ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct(); StructClass cl = wrapper.getClassStruct();
MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());

View File

@ -354,7 +354,7 @@ public class ClassesProcessor {
public int access; public int access;
public String simpleName; public String simpleName;
public StructClass classStruct; public StructClass classStruct;
public ClassWrapper wrapper; private ClassWrapper wrapper;
public String enclosingMethod; public String enclosingMethod;
public InvocationExprent superInvocation; public InvocationExprent superInvocation;
public Map<String, VarVersionPair> mapFieldsToVars = new HashMap<String, VarVersionPair>(); public Map<String, VarVersionPair> mapFieldsToVars = new HashMap<String, VarVersionPair>();
@ -419,6 +419,14 @@ public class ClassesProcessor {
return null; return null;
} }
public ClassWrapper getWrapper() {
ClassNode node = this;
while (node.type == CLASS_LAMBDA) {
node = node.parent;
}
return node.wrapper;
}
public static class LambdaInformation { public static class LambdaInformation {
public String class_name; public String class_name;
public String method_name; public String method_name;

View File

@ -49,8 +49,8 @@ public class NestedClassProcessor {
// hide synthetic lambda content methods // hide synthetic lambda content methods
if (node.type == ClassNode.CLASS_LAMBDA && !node.lambdaInformation.is_method_reference) { if (node.type == ClassNode.CLASS_LAMBDA && !node.lambdaInformation.is_method_reference) {
ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName); ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName);
if (node_content != null && node_content.wrapper != null) { if (node_content != null && node_content.getWrapper() != null) {
node_content.wrapper.getHiddenMembers().add(node.lambdaInformation.content_method_key); node_content.getWrapper().getHiddenMembers().add(node.lambdaInformation.content_method_key);
} }
} }
@ -91,7 +91,7 @@ public class NestedClassProcessor {
insertLocalVars(node, child); insertLocalVars(node, child);
if (child.type == ClassNode.CLASS_LOCAL) { if (child.type == ClassNode.CLASS_LOCAL) {
setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); setLocalClassDefinition(node.getWrapper().getMethods().getWithKey(child.enclosingMethod), child);
} }
} }
} }
@ -107,8 +107,8 @@ public class NestedClassProcessor {
return; return;
} }
final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambdaInformation.content_method_key); final MethodWrapper meth = parent.getWrapper().getMethods().getWithKey(child.lambdaInformation.content_method_key);
final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); final MethodWrapper encmeth = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor); MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor);
final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.content_method_descriptor); final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.content_method_descriptor);
@ -120,7 +120,7 @@ public class NestedClassProcessor {
final boolean is_static_lambda_content = child.lambdaInformation.is_content_method_static; final boolean is_static_lambda_content = child.lambdaInformation.is_content_method_static;
final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; final String parent_class_name = parent.getWrapper().getClassStruct().qualifiedName;
final String lambda_class_name = child.simpleName; final String lambda_class_name = child.simpleName;
final VarType lambda_class_type = new VarType(lambda_class_name, true); final VarType lambda_class_type = new VarType(lambda_class_name, true);
@ -272,7 +272,7 @@ public class NestedClassProcessor {
cltypes |= nd.type; cltypes |= nd.type;
HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.getWrapper());
if (mask.isEmpty()) { if (mask.isEmpty()) {
String message = "Nested class " + nd.classStruct.qualifiedName + " has no constructor!"; String message = "Nested class " + nd.classStruct.qualifiedName + " has no constructor!";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
@ -291,7 +291,7 @@ public class NestedClassProcessor {
if (cltypes != ClassNode.CLASS_MEMBER) { if (cltypes != ClassNode.CLASS_MEMBER) {
// iterate enclosing class // iterate enclosing class
for (final MethodWrapper meth : node.wrapper.getMethods()) { for (final MethodWrapper meth : node.getWrapper().getMethods()) {
if (meth.root != null) { // neither abstract, nor native if (meth.root != null) { // neither abstract, nor native
DirectGraph graph = meth.getOrBuildGraph(); DirectGraph graph = meth.getOrBuildGraph();
@ -423,7 +423,7 @@ public class NestedClassProcessor {
for (Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { for (Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) {
mergeListSignatures(entmt.getValue(), intrPairMask, false); mergeListSignatures(entmt.getValue(), intrPairMask, false);
MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); MethodWrapper meth = nestedNode.getWrapper().getMethodWrapper("<init>", entmt.getKey());
meth.signatureFields = new ArrayList<VarVersionPair>(); meth.signatureFields = new ArrayList<VarVersionPair>();
for (VarFieldPair pair : entmt.getValue()) { for (VarFieldPair pair : entmt.getValue()) {
@ -436,10 +436,10 @@ public class NestedClassProcessor {
private static void insertLocalVars(final ClassNode parent, final ClassNode child) { private static void insertLocalVars(final ClassNode parent, final ClassNode child) {
// enclosing method, is null iff member class // enclosing method, is null iff member class
MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); MethodWrapper encmeth = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);
// iterate all child methods // iterate all child methods
for (final MethodWrapper meth : child.wrapper.getMethods()) { for (final MethodWrapper meth : child.getWrapper().getMethods()) {
if (meth.root != null) { // neither abstract nor native if (meth.root != null) { // neither abstract nor native
@ -503,7 +503,7 @@ public class NestedClassProcessor {
if (clnode.type != ClassNode.CLASS_MEMBER) { if (clnode.type != ClassNode.CLASS_MEMBER) {
MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); MethodWrapper enclosing_method = clnode.parent.getWrapper().getMethods().getWithKey(clnode.enclosingMethod);
varname = enclosing_method.varproc.getVarName(entr.getValue()); varname = enclosing_method.varproc.getVarName(entr.getValue());
vartype = enclosing_method.varproc.getVarType(entr.getValue()); vartype = enclosing_method.varproc.getVarType(entr.getValue());
@ -528,7 +528,7 @@ public class NestedClassProcessor {
// hide synthetic field // hide synthetic field
if (clnode == child) { // fields higher up the chain were already handled with their classes if (clnode == child) { // fields higher up the chain were already handled with their classes
StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); StructField fd = child.classStruct.getFields().getWithKey(entr.getKey());
child.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
} }
} }
} }

View File

@ -67,7 +67,7 @@ public class NestedMemberAccess {
computeMethodTypes(nd); computeMethodTypes(nd);
} }
for (MethodWrapper method : node.wrapper.getMethods()) { for (MethodWrapper method : node.getWrapper().getMethods()) {
computeMethodType(node, method); computeMethodType(node, method);
} }
} }
@ -220,7 +220,7 @@ public class NestedMemberAccess {
return; return;
} }
for (MethodWrapper meth : node.wrapper.getMethods()) { for (MethodWrapper meth : node.getWrapper().getMethods()) {
if (meth.root != null) { if (meth.root != null) {
@ -327,8 +327,8 @@ public class NestedMemberAccess {
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname()); ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null; MethodWrapper methsource = null;
if (node != null && node.wrapper != null) { if (node != null && node.getWrapper() != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); methsource = node.getWrapper().getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
} }
if (methsource == null || !mapMethodType.containsKey(methsource)) { if (methsource == null || !mapMethodType.containsKey(methsource)) {
@ -440,7 +440,7 @@ public class NestedMemberAccess {
} }
} }
if (hide) { if (hide) {
node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
} }
} }

View File

@ -118,7 +118,7 @@ public class AssignmentExprent extends Exprent {
if (field.isStatic() && fd.hasModifier(CodeConstants.ACC_FINAL)) { if (field.isStatic() && fd.hasModifier(CodeConstants.ACC_FINAL)) {
fieldInClassInit = true; fieldInClassInit = true;
} }
if (node.wrapper != null && node.wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { if (node.getWrapper() != null && node.getWrapper().getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) {
hiddenField = true; hiddenField = true;
} }
} }

View File

@ -324,8 +324,8 @@ public class InvocationExprent extends Exprent {
ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname); ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
if (newNode != null) { // own class if (newNode != null) { // own class
if (newNode.wrapper != null) { if (newNode.getWrapper() != null) {
sigFields = newNode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields; sigFields = newNode.getWrapper().getMethodWrapper("<init>", stringDescriptor).signatureFields;
} }
else { else {
if (newNode.type == ClassNode.CLASS_MEMBER && (newNode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class if (newNode.type == ClassNode.CLASS_MEMBER && (newNode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class

View File

@ -176,8 +176,8 @@ public class NewExprent extends Exprent {
List<VarVersionPair> sigFields = null; List<VarVersionPair> sigFields = null;
if (newnode != null) { // own class if (newnode != null) { // own class
if (newnode.wrapper != null) { if (newnode.getWrapper() != null) {
sigFields = newnode.wrapper.getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields; sigFields = newnode.getWrapper().getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields;
} }
else { else {
if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 &&
@ -282,8 +282,8 @@ public class NewExprent extends Exprent {
List<VarVersionPair> sigFields = null; List<VarVersionPair> sigFields = null;
if (newnode != null) { // own class if (newnode != null) { // own class
if (newnode.wrapper != null) { if (newnode.getWrapper() != null) {
sigFields = newnode.wrapper.getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields; sigFields = newnode.getWrapper().getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields;
} }
else { else {
if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 &&

View File

@ -70,6 +70,16 @@ public class TestClassLambda {
public static int localMax(int var0, int var1) { public static int localMax(int var0, int var1) {
return 0;// 75 return 0;// 75
} }
public void nestedLambdas() {
byte var1 = 5;// 79
Runnable var2 = () -> {
Runnable var1x = () -> {
System.out.println("hello2" + var1);
};
System.out.println("hello1" + var1);
};// 80
}
} }
class 'pkg/TestClassLambda' { class 'pkg/TestClassLambda' {
@ -150,6 +160,12 @@ class 'pkg/TestClassLambda' {
0 70 0 70
1 70 1 70
} }
method 'nestedLambdas ()V' {
0 74
1 74
8 80
}
} }
Lines mapping: Lines mapping:
@ -171,3 +187,5 @@ Lines mapping:
67 <-> 63 67 <-> 63
71 <-> 67 71 <-> 67
75 <-> 71 75 <-> 71
79 <-> 75
80 <-> 81

View File

@ -74,4 +74,12 @@ public class TestClassLambda {
public static int localMax(int first, int second) { public static int localMax(int first, int second) {
return 0; return 0;
} }
public void nestedLambdas() {
int a =5;
Runnable r1 = () -> {
Runnable r2 = () -> { System.out.println("hello2" + a); };
System.out.println("hello1" + a);
};
}
} }