parent
96379678e6
commit
bd99d3eb2f
@ -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; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue