Fixed 'IDEA-127301: NPE in decompiler' - a couple of issues with lambda processing

master
Stiver 11 years ago
parent 7f116b6eb5
commit 55beef6b7e
  1. 11
      src/de/fernflower/main/ClassWriter.java
  2. 25
      src/de/fernflower/main/ClassesProcessor.java
  3. 3
      src/de/fernflower/main/rels/LambdaProcessor.java
  4. 2
      src/de/fernflower/main/rels/MethodProcessorThread.java
  5. 4
      src/de/fernflower/main/rels/NestedClassProcessor.java
  6. 54
      src/de/fernflower/modules/decompiler/ExprProcessor.java
  7. 2
      src/de/fernflower/modules/decompiler/FinallyProcessor.java
  8. 3
      src/de/fernflower/modules/decompiler/SimplifyExprentsHelper.java
  9. 11
      src/de/fernflower/modules/decompiler/exps/InvocationExprent.java
  10. 14
      src/de/fernflower/modules/decompiler/exps/NewExprent.java

@ -112,7 +112,7 @@ public class ClassWriter {
} }
public void classLambdaToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException {
// get the class node with the content method // get the class node with the content method
ClassNode node_content = node; ClassNode node_content = node;
@ -136,7 +136,12 @@ public class ClassWriter {
if(node.lambda_information.is_method_reference) { if(node.lambda_information.is_method_reference) {
if(!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method
writer.write(method_object.toJava(indent));
} else { // reference to a static method
writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false)));
}
writer.write("::"); writer.write("::");
writer.write(node.lambda_information.content_method_name); writer.write(node.lambda_information.content_method_name);
@ -156,7 +161,7 @@ public class ClassWriter {
StringBuilder buff = new StringBuilder("("); StringBuilder buff = new StringBuilder("(");
boolean firstpar = true; boolean firstpar = true;
int index = 1; int index = node.lambda_information.is_content_method_static ? 0 : 1;;
int start_index = md_content.params.length - md_lambda.params.length; int start_index = md_content.params.length - md_lambda.params.length;
@ -604,7 +609,7 @@ public class ClassWriter {
bufstrwriter.write("("); bufstrwriter.write("(");
boolean firstpar = true; boolean firstpar = true;
int index = 1; int index = node_lambda.lambda_information.is_content_method_static ? 0 : 1;;
int start_index = md_content.params.length - md_lambda.params.length; int start_index = md_content.params.length - md_lambda.params.length;

@ -379,8 +379,8 @@ public class ClassesProcessor {
public LambdaInformation lambda_information; public LambdaInformation lambda_information;
public ClassNode(String content_class_name, 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, int content_method_invokation_type,
String lambda_method_descriptor, StructClass classStruct) { // lambda class constructor String lambda_class_name, String lambda_method_name, String lambda_method_descriptor, StructClass classStruct) { // lambda class constructor
this.type = CLASS_LAMBDA; this.type = CLASS_LAMBDA;
this.classStruct = classStruct; // 'parent' class containing the static function this.classStruct = classStruct; // 'parent' class containing the static function
@ -393,19 +393,22 @@ public class ClassesProcessor {
lambda_information.content_class_name = content_class_name; lambda_information.content_class_name = content_class_name;
lambda_information.content_method_name = content_method_name; lambda_information.content_method_name = content_method_name;
lambda_information.content_method_descriptor = content_method_descriptor; lambda_information.content_method_descriptor = content_method_descriptor;
lambda_information.content_method_invokation_type = content_method_invokation_type;
lambda_information.content_method_key = InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.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); anonimousClassType = new VarType(lambda_class_name, true);
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. boolean is_method_reference = (content_class_name != classStruct.qualifiedName);
lambda_information.is_method_reference = true; StructMethod mt = null;
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; if(!is_method_reference) { // content method in the same class, check synthetic flag
lambda_information.is_content_method_static = ((mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0); mt = classStruct.getMethod(content_method_name, content_method_descriptor);
is_method_reference = !((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic")); // if not synthetic -> method reference
} }
lambda_information.is_method_reference = is_method_reference;
lambda_information.is_content_method_static = (lambda_information.content_method_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?
} }
public ClassNode(int type, StructClass classStruct) { public ClassNode(int type, StructClass classStruct) {
@ -425,6 +428,9 @@ public class ClassesProcessor {
} }
public class LambdaInformation { public class LambdaInformation {
public String class_name; public String class_name;
public String method_name; public String method_name;
public String method_descriptor; public String method_descriptor;
@ -432,6 +438,7 @@ public class ClassesProcessor {
public String content_class_name; public String content_class_name;
public String content_method_name; public String content_method_name;
public String content_method_descriptor; public String content_method_descriptor;
public int content_method_invokation_type; // values from CONSTANT_MethodHandle_REF_*
public String content_method_key; public String content_method_key;

@ -98,7 +98,8 @@ public class LambdaProcessor {
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname, content_method_handle.descriptor, ClassNode node_lambda = clprocessor.new ClassNode(content_method_handle.classname, content_method_handle.elementname,
content_method_handle.descriptor, content_method_handle.index1,
lambda_class_name, lambda_method_name, lambda_method_descriptor, cl); lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);
node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2; node_lambda.simpleName = cl.qualifiedName + "##Lambda_" + invoke_dynamic.index1 + "_" + invoke_dynamic.index2;
node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()); node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());

@ -175,7 +175,7 @@ public class MethodProcessorThread implements Runnable {
ClearStructHelper.clearStatements(root); ClearStructHelper.clearStatements(root);
ExprProcessor proc = new ExprProcessor(); ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, cl.getPool()); proc.processStatement(root, cl);
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true);
// System.out.println(graph.toString()); // System.out.println(graph.toString());

@ -56,8 +56,8 @@ public class NestedClassProcessor {
public void processClass(ClassNode root, ClassNode node) { public void processClass(ClassNode root, ClassNode node) {
// hide lambda content methods // hide synthetic lambda content methods
if(node.type == ClassNode.CLASS_LAMBDA) { if(node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.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.wrapper != null) {
node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key); node_content.wrapper.getHideMembers().add(node.lambda_information.content_method_key);

@ -14,22 +14,48 @@
package de.fernflower.modules.decompiler; package de.fernflower.modules.decompiler;
import java.util.*; import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.fernflower.code.CodeConstants; import de.fernflower.code.CodeConstants;
import de.fernflower.code.Instruction; import de.fernflower.code.Instruction;
import de.fernflower.code.InstructionSequence; import de.fernflower.code.InstructionSequence;
import de.fernflower.code.cfg.BasicBlock; import de.fernflower.code.cfg.BasicBlock;
import de.fernflower.main.DecompilerContext; import de.fernflower.main.DecompilerContext;
import de.fernflower.modules.decompiler.exps.*; import de.fernflower.modules.decompiler.exps.ArrayExprent;
import de.fernflower.modules.decompiler.exps.AssignmentExprent;
import de.fernflower.modules.decompiler.exps.ConstExprent;
import de.fernflower.modules.decompiler.exps.ExitExprent;
import de.fernflower.modules.decompiler.exps.Exprent;
import de.fernflower.modules.decompiler.exps.FieldExprent;
import de.fernflower.modules.decompiler.exps.FunctionExprent;
import de.fernflower.modules.decompiler.exps.IfExprent;
import de.fernflower.modules.decompiler.exps.InvocationExprent;
import de.fernflower.modules.decompiler.exps.MonitorExprent;
import de.fernflower.modules.decompiler.exps.NewExprent;
import de.fernflower.modules.decompiler.exps.SwitchExprent;
import de.fernflower.modules.decompiler.exps.VarExprent;
import de.fernflower.modules.decompiler.sforms.DirectGraph; import de.fernflower.modules.decompiler.sforms.DirectGraph;
import de.fernflower.modules.decompiler.sforms.DirectNode; import de.fernflower.modules.decompiler.sforms.DirectNode;
import de.fernflower.modules.decompiler.sforms.FlattenStatementsHelper; import de.fernflower.modules.decompiler.sforms.FlattenStatementsHelper;
import de.fernflower.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; import de.fernflower.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;
import de.fernflower.modules.decompiler.stats.*; import de.fernflower.modules.decompiler.stats.BasicBlockStatement;
import de.fernflower.modules.decompiler.stats.CatchAllStatement;
import de.fernflower.modules.decompiler.stats.CatchStatement;
import de.fernflower.modules.decompiler.stats.RootStatement;
import de.fernflower.modules.decompiler.stats.Statement;
import de.fernflower.modules.decompiler.vars.VarProcessor; import de.fernflower.modules.decompiler.vars.VarProcessor;
import de.fernflower.struct.StructClass; import de.fernflower.struct.StructClass;
import de.fernflower.struct.attr.StructBootstrapMethodsAttribute;
import de.fernflower.struct.attr.StructGeneralAttribute;
import de.fernflower.struct.consts.ConstantPool; import de.fernflower.struct.consts.ConstantPool;
import de.fernflower.struct.consts.LinkConstant;
import de.fernflower.struct.consts.PooledConstant;
import de.fernflower.struct.consts.PrimitiveConstant; import de.fernflower.struct.consts.PrimitiveConstant;
import de.fernflower.struct.gen.MethodDescriptor; import de.fernflower.struct.gen.MethodDescriptor;
import de.fernflower.struct.gen.VarType; import de.fernflower.struct.gen.VarType;
@ -124,7 +150,7 @@ public class ExprProcessor implements CodeConstants {
private VarProcessor varProcessor = (VarProcessor) DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); private VarProcessor varProcessor = (VarProcessor) DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR);
public void processStatement(RootStatement root, ConstantPool pool) { public void processStatement(RootStatement root, StructClass cl) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root); DirectGraph dgraph = flatthelper.buildDirectGraph(root);
@ -179,7 +205,7 @@ public class ExprProcessor implements CodeConstants {
BasicBlockStatement block = node.block; BasicBlockStatement block = node.block;
if (block != null) { if (block != null) {
processBlock(block, data, pool); processBlock(block, data, cl);
block.setExprents(data.getLstExprents()); block.setExprents(data.getLstExprents());
} }
@ -291,7 +317,10 @@ public class ExprProcessor implements CodeConstants {
} }
} }
public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, ConstantPool pool) { public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) {
ConstantPool pool = cl.getPool();
StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
BasicBlock block = stat.getBlock(); BasicBlock block = stat.getBlock();
@ -516,7 +545,18 @@ public class ExprProcessor implements CodeConstants {
case opc_invokeinterface: case opc_invokeinterface:
case opc_invokedynamic: case opc_invokedynamic:
if(instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { if(instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
InvocationExprent exprinv = new InvocationExprent(instr.opcode, pool.getLinkConstant(instr.getOperand(0)), stack);
LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
int dynamic_invokation_type = -1;
if(instr.opcode == opc_invokedynamic && bootstrap != null) {
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
dynamic_invokation_type = content_method_handle.index1;
}
InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type);
if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) {
exprlist.add(exprinv); exprlist.add(exprinv);
} else { } else {

@ -206,7 +206,7 @@ public class FinallyProcessor {
} }
ExprProcessor proc = new ExprProcessor(); ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, mt.getClassStruct().getPool()); proc.processStatement(root, mt.getClassStruct());
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt); ssa.splitVariables(root, mt);

@ -721,6 +721,9 @@ public class SimplifyExprentsHelper {
NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0); NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0);
newexp.setConstructor(in); newexp.setConstructor(in);
// note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation
// lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);)
// in this case instance will hold the corresponding object
return newexp; return newexp;
} }

@ -76,7 +76,7 @@ public class InvocationExprent extends Exprent {
public InvocationExprent() {} public InvocationExprent() {}
public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack) { public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamic_invokation_type) {
name = cn.elementname; name = cn.elementname;
classname = cn.classname; classname = cn.classname;
@ -99,6 +99,7 @@ public class InvocationExprent extends Exprent {
classname = "java/lang/Class"; // dummy class name classname = "java/lang/Class"; // dummy class name
invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
} }
if("<init>".equals(name)) { if("<init>".equals(name)) {
@ -114,7 +115,13 @@ public class InvocationExprent extends Exprent {
lstParameters.add(0, stack.pop()); lstParameters.add(0, stack.pop());
} }
if(opcode == CodeConstants.opc_invokestatic || opcode == CodeConstants.opc_invokedynamic) { if(opcode == CodeConstants.opc_invokedynamic) {
if(dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {
isStatic = true;
} else {
instance = lstParameters.get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method.
}
} else if(opcode == CodeConstants.opc_invokestatic) {
isStatic = true; isStatic = true;
} else { } else {
instance = stack.pop(); instance = stack.pop();

@ -133,6 +133,12 @@ public class NewExprent extends Exprent {
List<Exprent> lst = new ArrayList<Exprent>(); List<Exprent> lst = new ArrayList<Exprent>();
if(newtype.arraydim == 0) { if(newtype.arraydim == 0) {
if(constructor != null) { if(constructor != null) {
Exprent constructor_instance = constructor.getInstance();
if(constructor_instance != null) { // should be true only for a lambda expression with a virtual content method
lst.add(constructor_instance);
}
lst.addAll(constructor.getLstParameters()); lst.addAll(constructor.getLstParameters());
} }
} else { } else {
@ -193,7 +199,8 @@ public class NewExprent extends Exprent {
boolean firstpar = true; boolean firstpar = true;
int start = 0, end = invsuper.getLstParameters().size(); int start = 0, end = invsuper.getLstParameters().size();
if (enumconst) { if (enumconst) {
start += 2; end -= 1; start += 2;
end -= 1;
} }
for(int i = start; i < end; i++) { for(int i = start; i < end; i++) {
if (sigFields == null || sigFields.get(i) == null) { if (sigFields == null || sigFields.get(i) == null) {
@ -254,7 +261,7 @@ public class NewExprent extends Exprent {
ClassWriter clwriter = new ClassWriter(); ClassWriter clwriter = new ClassWriter();
try { try {
if (lambda) { if (lambda) {
clwriter.classLambdaToJava(child, bufstrwriter, indent); clwriter.classLambdaToJava(child, bufstrwriter, (constructor == null ? null : constructor.getInstance()), indent);
} else { } else {
clwriter.classToJava(child, bufstrwriter, indent); clwriter.classToJava(child, bufstrwriter, indent);
} }
@ -264,7 +271,8 @@ public class NewExprent extends Exprent {
} }
if (lambda && !DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { 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.setLength(0); // remove the usual 'new <class>()', it will
// be replaced with lambda style '() ->'
} }
buf.append(strwriter.toString()); buf.append(strwriter.toString());

Loading…
Cancel
Save