diff options
author | Stiver <stiver.mail@gmail.com> | 2014-07-23 22:49:29 +0200 |
---|---|---|
committer | Stiver <stiver.mail@gmail.com> | 2014-07-23 22:49:29 +0200 |
commit | 7f116b6eb506080d552c71445737e444a065e7e1 (patch) | |
tree | 2af492b23a0bfb9a82d6ab33635a01d57e108ad1 | |
parent | 6606a474bf8a634e6ee5a525f338c06a40590712 (diff) | |
download | fernflower-7f116b6eb506080d552c71445737e444a065e7e1.tar fernflower-7f116b6eb506080d552c71445737e444a065e7e1.tar.gz fernflower-7f116b6eb506080d552c71445737e444a065e7e1.tar.lz fernflower-7f116b6eb506080d552c71445737e444a065e7e1.tar.xz fernflower-7f116b6eb506080d552c71445737e444a065e7e1.zip |
IDEA-127301: handling of static method references
-rw-r--r-- | src/de/fernflower/main/ClassWriter.java | 231 | ||||
-rw-r--r-- | src/de/fernflower/main/ClassesProcessor.java | 17 | ||||
-rw-r--r-- | src/de/fernflower/main/rels/LambdaProcessor.java | 122 | ||||
-rw-r--r-- | src/de/fernflower/main/rels/NestedClassProcessor.java | 30 |
4 files changed, 215 insertions, 185 deletions
diff --git a/src/de/fernflower/main/ClassWriter.java b/src/de/fernflower/main/ClassWriter.java index 6a12cc5..8e569d7 100644 --- a/src/de/fernflower/main/ClassWriter.java +++ b/src/de/fernflower/main/ClassWriter.java @@ -134,63 +134,74 @@ public class ClassWriter { 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_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); - - if(!lambda_to_anonymous) { // lambda parameters '() ->' - - StringBuilder buff = new StringBuilder("("); - - boolean firstpar = true; - int index = 1; - - int start_index = md_content.params.length - md_lambda.params.length; - - for(int i=0;i<md_content.params.length;i++) { - - if(i >= start_index) { + if(node.lambda_information.is_method_reference) { - 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_content.params[i].stack_size; - } - buff.append(") ->"); + writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); + writer.write("::"); + writer.write(node.lambda_information.content_method_name); + + writer.flush(); - 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.write(DecompilerContext.getNewLineSeparator()); - - writer.write(strwriter.toString()); - - writer.write(InterpreterUtil.getIndentString(indent)); - writer.write("}"); - writer.flush(); + // lambda method + StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); + MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); + + if(!lambda_to_anonymous) { // lambda parameters '() ->' + + StringBuilder buff = new StringBuilder("("); + + boolean firstpar = true; + int index = 1; + + int start_index = md_content.params.length - md_lambda.params.length; + + for(int i=0;i<md_content.params.length;i++) { + + if(i >= start_index) { + + 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_content.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.write(DecompilerContext.getNewLineSeparator()); + + writer.write(strwriter.toString()); + + writer.write(InterpreterUtil.getIndentString(indent)); + writer.write("}"); + writer.flush(); + } DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); @@ -345,18 +356,18 @@ public class ClassWriter { } } - if(isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } // class annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } + List<AnnotationExprent> lstAnn = getAllAnnotations(cl.getAttributes()); + for(AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); @@ -481,18 +492,18 @@ public class ClassWriter { boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); - if(isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } // field annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - writer.write(annexpr.toJava(indent)); - writer.write(DecompilerContext.getNewLineSeparator()); - } + List<AnnotationExprent> lstAnn = getAllAnnotations(fd.getAttributes()); + for(AnnotationExprent annexpr : lstAnn) { + writer.write(annexpr.toJava(indent)); + writer.write(DecompilerContext.getNewLineSeparator()); + } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; @@ -674,7 +685,7 @@ public class ClassWriter { boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); String indstr = InterpreterUtil.getIndentString(indent); @@ -705,18 +716,18 @@ public class ClassWriter { } } - if(isDeprecated) { - writer.write(indstr); - writer.write("/** @deprecated */"); - writer.write(DecompilerContext.getNewLineSeparator()); - } + if (isDeprecated) { + writer.write(indstr); + writer.write("/** @deprecated */"); + writer.write(DecompilerContext.getNewLineSeparator()); + } - // method annotations - List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); - for(AnnotationExprent annexpr : lstAnn) { - bufstrwriter.write(annexpr.toJava(indent)); - bufstrwriter.write(DecompilerContext.getNewLineSeparator()); - } + // method annotations + List<AnnotationExprent> lstAnn = getAllAnnotations(mt.getAttributes()); + for(AnnotationExprent annexpr : lstAnn) { + bufstrwriter.write(annexpr.toJava(indent)); + bufstrwriter.write(DecompilerContext.getNewLineSeparator()); + } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; @@ -747,19 +758,19 @@ public class ClassWriter { bufstrwriter.write("default "); } - String name = mt.getName(); - if("<init>".equals(name)) { - if(node.type == ClassNode.CLASS_ANONYMOUS) { - name = ""; - dinit = true; - } else { - name = node.simpleName; - init = true; - } - } else if("<clinit>".equals(name)) { - name = ""; - clinit = true; - } + String name = mt.getName(); + if ("<init>".equals(name)) { + if (node.type == ClassNode.CLASS_ANONYMOUS) { + name = ""; + dinit = true; + } else { + name = node.simpleName; + init = true; + } + } else if ("<clinit>".equals(name)) { + name = ""; + clinit = true; + } GenericMethodDescriptor descriptor = null; if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { @@ -793,14 +804,14 @@ public class ClassWriter { bufstrwriter.write(descriptor.fparameters.get(i)); List<GenericType> lstBounds = descriptor.fbounds.get(i); - if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { - bufstrwriter.write(" extends "); - bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); + if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { + bufstrwriter.write(" extends "); + bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); - for(int j=1;j<lstBounds.size();j++) { - bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); - } - } + for(int j = 1; j < lstBounds.size(); j++) { + bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); + } + } } bufstrwriter.write("> "); } @@ -831,11 +842,11 @@ public class ClassWriter { } boolean firstpar = true; - int index = isEnum && init ? 3 : thisvar ? 1 : 0; - int start = isEnum && init && descriptor == null ? 2 : 0; - int params = descriptor == null ? md.params.length : descriptor.params.size(); - for(int i=start;i<params;i++) { - if(signFields == null || signFields.get(i) == null) { + int index = isEnum && init ? 3 : thisvar ? 1 : 0; + int start = isEnum && init && descriptor == null ? 2 : 0; + int params = descriptor == null ? md.params.length : descriptor.params.size(); + for(int i = start; i < params; i++) { + if (signFields == null || signFields.get(i) == null) { if(!firstpar) { bufstrwriter.write(", "); diff --git a/src/de/fernflower/main/ClassesProcessor.java b/src/de/fernflower/main/ClassesProcessor.java index f2a25c9..7f85696 100644 --- a/src/de/fernflower/main/ClassesProcessor.java +++ b/src/de/fernflower/main/ClassesProcessor.java @@ -41,6 +41,7 @@ import de.fernflower.modules.decompiler.exps.InvocationExprent; import de.fernflower.modules.decompiler.vars.VarVersionPaar; import de.fernflower.struct.StructClass; import de.fernflower.struct.StructContext; +import de.fernflower.struct.StructMethod; import de.fernflower.struct.attr.StructInnerClassesAttribute; import de.fernflower.struct.gen.VarType; import de.fernflower.util.InterpreterUtil; @@ -378,7 +379,7 @@ public class ClassesProcessor { public LambdaInformation lambda_information; - public ClassNode(String content_method_name, String content_method_descriptor, String lambda_class_name, String lambda_method_name, + public ClassNode(String content_class_name, 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 @@ -389,13 +390,22 @@ public class ClassesProcessor { lambda_information.method_name = lambda_method_name; lambda_information.method_descriptor = lambda_method_descriptor; + lambda_information.content_class_name = content_class_name; 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); + if(content_class_name != classStruct.qualifiedName) { // method reference. FIXME: class name alone doesn't cover it. Synthetic flag seems to be the only 'reliable' difference. + lambda_information.is_method_reference = true; + lambda_information.is_content_method_static = true; // FIXME: consider argument flag + } else { + StructMethod mt = classStruct.getMethod(content_method_name, content_method_descriptor); + + lambda_information.is_method_reference = false; + lambda_information.is_content_method_static = ((mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0); + } } public ClassNode(int type, StructClass classStruct) { @@ -419,10 +429,13 @@ public class ClassesProcessor { public String method_name; public String method_descriptor; + public String content_class_name; public String content_method_name; public String content_method_descriptor; + public String content_method_key; + public boolean is_method_reference; public boolean is_content_method_static; } } diff --git a/src/de/fernflower/main/rels/LambdaProcessor.java b/src/de/fernflower/main/rels/LambdaProcessor.java index d2fe790..6e404ae 100644 --- a/src/de/fernflower/main/rels/LambdaProcessor.java +++ b/src/de/fernflower/main/rels/LambdaProcessor.java @@ -50,85 +50,87 @@ public class LambdaProcessor { } 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(bootstrap == null || bootstrap.getMethodsNumber() == 0) { + return false; // no bootstrap constants in pool + } + + 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(lambda_methods.isEmpty()) { - return false; + 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; // no lambda bootstrap constant found + } + + 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(); - 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(); - 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); - 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(instr.opcode == CodeConstants.opc_invokedynamic) { - LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0)); + if(lambda_methods.contains(invoke_dynamic.index1)) { // lambda invocation found - 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); + 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); + 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()); + ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, 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); - } + 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; - } + mt.releaseResources(); + } + + // 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? - } + // FIXME: mixed hierarchy? + return false; } diff --git a/src/de/fernflower/main/rels/NestedClassProcessor.java b/src/de/fernflower/main/rels/NestedClassProcessor.java index 6667f02..32b0161 100644 --- a/src/de/fernflower/main/rels/NestedClassProcessor.java +++ b/src/de/fernflower/main/rels/NestedClassProcessor.java @@ -76,19 +76,19 @@ public class NestedClassProcessor { checkNotFoundClasses(root, node); } - int nameless = 0, synthetics = 0; - for(ClassNode child : node.nested) { - // ensure not-empty class name - if((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { - StructClass cl = child.classStruct; - if(((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { - child.simpleName = "SyntheticClass_" + (++synthetics); - } else { - DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); - child.simpleName = "NamelessClass_" + (++nameless); - } - } - } + int nameless = 0, synthetics = 0; + for(ClassNode child : node.nested) { + // ensure not-empty class name + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { + StructClass cl = child.classStruct; + if (((child.access | cl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic")) { + child.simpleName = "SyntheticClass_" + (++synthetics); + } else { + DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); + child.simpleName = "NamelessClass_" + (++nameless); + } + } + } for(ClassNode child : node.nested) { if(child.type == ClassNode.CLASS_LAMBDA) { @@ -112,6 +112,10 @@ public class NestedClassProcessor { private void setLambdaVars(ClassNode parent, ClassNode child) { + if(child.lambda_information.is_method_reference) { // method reference, no code and no parameters + return; + } + final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); |