proguard.backport.LambdaExpressionConverter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-base Show documentation
Show all versions of proguard-base Show documentation
ProGuard is a free shrinker, optimizer, obfuscator, and preverifier for Java bytecode
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.backport;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.AccessConstants;
import proguard.classfile.ClassConstants;
import proguard.classfile.ClassPool;
import proguard.classfile.Clazz;
import proguard.classfile.Member;
import proguard.classfile.Method;
import proguard.classfile.ProgramClass;
import proguard.classfile.ProgramMethod;
import proguard.classfile.TypeConstants;
import proguard.classfile.VersionConstants;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.visitor.AllAttributeVisitor;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.InvokeDynamicConstant;
import proguard.classfile.constant.MethodHandleConstant;
import proguard.classfile.editor.ClassBuilder;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.editor.CompactCodeAttributeComposer;
import proguard.classfile.editor.ConstantPoolEditor;
import proguard.classfile.editor.InstructionSequenceBuilder;
import proguard.classfile.editor.MemberRemover;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.ClassReferenceInitializer;
import proguard.classfile.util.ClassSubHierarchyInitializer;
import proguard.classfile.util.ClassSuperHierarchyInitializer;
import proguard.classfile.util.ClassUtil;
import proguard.classfile.util.InternalTypeEnumeration;
import proguard.classfile.visitor.AllMethodVisitor;
import proguard.classfile.visitor.ClassVisitor;
import proguard.classfile.visitor.MemberAccessFlagSetter;
import proguard.classfile.visitor.MemberAccessSetter;
import proguard.classfile.visitor.MemberVisitor;
import proguard.classfile.visitor.MultiClassVisitor;
import proguard.io.ExtraDataEntryNameMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
* This ClassVisitor converts all lambda expressions in the visited
* classes to anonymous inner classes.
*
* @author Thomas Neidhart
*/
public class LambdaExpressionConverter
implements ClassVisitor,
// Implementation interfaces.
MemberVisitor,
AttributeVisitor,
InstructionVisitor
{
private static final Logger logger = LogManager.getLogger(LambdaExpressionConverter.class);
private static final String LAMBDA_SINGLETON_FIELD_NAME = "INSTANCE";
private final ClassPool programClassPool;
private final ClassPool libraryClassPool;
private final ExtraDataEntryNameMap extraDataEntryNameMap;
private final ClassVisitor extraClassVisitor;
private final Map lambdaExpressionMap;
private final CodeAttributeEditor codeAttributeEditor;
private final MemberRemover memberRemover;
public LambdaExpressionConverter(ClassPool programClassPool,
ClassPool libraryClassPool,
ExtraDataEntryNameMap extraDataEntryNameMap,
ClassVisitor extraClassVisitor)
{
this.programClassPool = programClassPool;
this.libraryClassPool = libraryClassPool;
this.extraDataEntryNameMap = extraDataEntryNameMap;
this.extraClassVisitor = extraClassVisitor;
this.lambdaExpressionMap = new HashMap<>();
this.codeAttributeEditor = new CodeAttributeEditor(true, true);
this.memberRemover = new MemberRemover();
}
// Implementations for ClassVisitor.
@Override
public void visitAnyClass(Clazz clazz) { }
@Override
public void visitProgramClass(ProgramClass programClass)
{
lambdaExpressionMap.clear();
programClass.accept(new LambdaExpressionCollector(lambdaExpressionMap));
if (!lambdaExpressionMap.isEmpty())
{
logger.debug("LambdaExpressionConverter: converting lambda expressions in [{}]", programClass.getName());
for (LambdaExpression lambdaExpression : lambdaExpressionMap.values())
{
ProgramClass lambdaClass = createLambdaClass(lambdaExpression);
// Add the converted lambda class to the program class pool
// and the injected class name map.
programClassPool.addClass(lambdaClass);
extraDataEntryNameMap.addExtraClassToClass(programClass, lambdaClass);
if (extraClassVisitor != null)
{
extraClassVisitor.visitProgramClass(lambdaClass);
}
}
// Replace all InvokeDynamic instructions.
programClass.accept(
new AllMethodVisitor(
new AllAttributeVisitor(
this)));
// Initialize the hierarchy and references of all lambda classes.
for (LambdaExpression lambdaExpression : lambdaExpressionMap.values())
{
lambdaExpression.lambdaClass.accept(
new MultiClassVisitor(
new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool),
new ClassSubHierarchyInitializer(),
new ClassReferenceInitializer(programClassPool, libraryClassPool)
));
}
// Remove deserialization hooks as they are no longer needed.
programClass.methodsAccept(this);
memberRemover.visitProgramClass(programClass);
}
}
// Implementations for AttributeVisitor.
@Override
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
@Override
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
codeAttributeEditor.reset(codeAttribute.u4codeLength);
codeAttribute.instructionsAccept(clazz, method, this);
if (codeAttributeEditor.isModified())
{
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
}
}
// Implementations for InstructionVisitor.
@Override
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
@Override
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
if (constantInstruction.opcode == Instruction.OP_INVOKEDYNAMIC)
{
ProgramClass programClass = (ProgramClass) clazz;
InvokeDynamicConstant invokeDynamicConstant =
(InvokeDynamicConstant) programClass.getConstant(constantInstruction.constantIndex);
int bootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex();
if (lambdaExpressionMap.containsKey(bootstrapMethodIndex))
{
LambdaExpression lambdaExpression = lambdaExpressionMap.get(bootstrapMethodIndex);
String lambdaClassName = lambdaExpression.getLambdaClassName();
InstructionSequenceBuilder builder =
new InstructionSequenceBuilder(programClass);
if (lambdaExpression.isStateless())
{
logger.debug("LambdaExpressionConverter: {} -> getting static {}.{}", constantInstruction.toString(offset), lambdaClassName, LAMBDA_SINGLETON_FIELD_NAME);
builder.getstatic(lambdaClassName,
LAMBDA_SINGLETON_FIELD_NAME,
ClassUtil.internalTypeFromClassName(lambdaClassName));
}
else
{
logger.debug("LambdaExpressionConverter: {} -> new instance of {}", constantInstruction.toString(offset), lambdaClassName);
int maxLocals = codeAttribute.u2maxLocals;
String methodDescriptor =
lambdaExpression.getConstructorDescriptor();
int parameterSize =
ClassUtil.internalMethodParameterSize(methodDescriptor);
// TODO: the special optimization in case there is only 1
// parameter has been disabled as the used stack
// manipulation instructions might confuse the class
// converter (testcase 1988).
if (parameterSize == 1 && false)
{
// If only 1 parameter is captured by the lambda expression,
// and it is a Category 1 value, we can avoid storing the
// current stack to variables.
builder.new_(lambdaClassName)
.dup_x1()
.swap()
.invokespecial(lambdaClassName,
ClassConstants.METHOD_NAME_INIT,
methodDescriptor);
}
else
{
// More than 1 (or a Category 2) parameter is captured
// by the lambda expression. We need to store the current
// call stack to variables, create the lambda instance and
// load the call stack again from the temporary variables.
// Collect the argument types.
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(methodDescriptor);
List types = new ArrayList();
while(typeEnumeration.hasMoreTypes())
{
types.add(typeEnumeration.nextType());
}
// Store the current call stack in reverse order
// into temporary variables.
int variableIndex = maxLocals;
ListIterator typeIterator =
types.listIterator(types.size());
while (typeIterator.hasPrevious())
{
String type = typeIterator.previous();
builder.store(variableIndex, type);
variableIndex += ClassUtil.internalTypeSize(type);
}
// Create the lambda instance.
builder.new_(lambdaClassName);
builder.dup();
// Reconstruct the call stack by loading it from
// the temporary variables.
typeIterator = types.listIterator();
while (typeIterator.hasNext())
{
String type = typeIterator.next();
int variableSize = ClassUtil.internalTypeSize(type);
variableIndex -= variableSize;
builder.load(variableIndex, type);
}
builder.invokespecial(lambdaClassName,
ClassConstants.METHOD_NAME_INIT,
methodDescriptor);
}
}
codeAttributeEditor.replaceInstruction(offset,
builder.instructions());
}
}
}
// Implementations for MemberVisitor.
@Override
public void visitAnyMember(Clazz clazz, Member member) {}
@Override
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
{
if (isDeserializationHook(programClass, programMethod))
{
memberRemover.visitProgramMethod(programClass, programMethod);
}
}
// Small utility methods.
private static final String METHOD_NAME_DESERIALIZE_LAMBDA = "$deserializeLambda$";
private static final String METHOD_TYPE_DESERIALIZE_LAMBDA = "(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;";
private static boolean isDeserializationHook(Clazz clazz, Method method)
{
return method.getName(clazz) .equals(METHOD_NAME_DESERIALIZE_LAMBDA) &&
method.getDescriptor(clazz).equals(METHOD_TYPE_DESERIALIZE_LAMBDA) &&
hasFlag(method, AccessConstants.PRIVATE |
AccessConstants.STATIC |
AccessConstants.SYNTHETIC);
}
private static boolean hasFlag(Member member, int flag)
{
return (member.getAccessFlags() & flag) == flag;
}
private ProgramClass createLambdaClass(LambdaExpression lambdaExpression)
{
String lambdaClassName = lambdaExpression.getLambdaClassName();
logger.debug("LambdaExpressionConverter: creating lambda class [{}]", lambdaClassName);
// Start creating the lambda class.
ClassBuilder classBuilder =
new ClassBuilder(VersionConstants.CLASS_VERSION_1_2,
0,
lambdaClassName,
ClassConstants.NAME_JAVA_LANG_OBJECT);
// Add its interfaces.
String[] interfaces = lambdaExpression.interfaces;
for (String interfaceName : interfaces)
{
classBuilder.addInterface(interfaceName);
}
ProgramClass lambdaClass =
classBuilder.getProgramClass();
// Store the created lambda class in the LambdaExpression
// data structure for later use.
lambdaExpression.lambdaClass = lambdaClass;
// [DGD-968] When a lambda expression is called from a `default`
// interface method, ensure that it is stateless and visible to the
// lambda class instead of generating an accessor method. The method
// will be properly backported by the {@link StaticInterfaceMethodConverter}.
if (lambdaExpression.referencesPrivateSyntheticInterfaceMethod())
{
fixInterfaceLambdaMethod(lambdaExpression.referencedClass,
(ProgramMethod) lambdaExpression.referencedInvokedMethod,
lambdaExpression);
}
else if (lambdaExpression.referencesPrivateConstructor() ||
lambdaExpression.needsAccessorMethod())
{
// In case the invoked method can not be accessed directly
// by the lambda class, add a synthetic accessor method.
addAccessorMethod(lambdaExpression.referencedClass,
lambdaExpression);
}
if (lambdaExpression.isStateless())
{
completeStatelessLambdaClass(lambdaClass, lambdaExpression);
}
else
{
completeCapturingLambdaClass(lambdaClass, lambdaExpression);
}
if (lambdaExpression.bridgeMethodDescriptors.length > 0)
{
addBridgeMethods(lambdaClass, lambdaExpression);
}
return lambdaClass;
}
private void fixInterfaceLambdaMethod(ProgramClass programClass,
ProgramMethod programMethod,
LambdaExpression lambdaExpression)
{
// Change the access flags to package private to make the method
// accessible from the lambda class.
programMethod.accept(programClass, new MemberAccessSetter(0));
// If the method is not yet static, make it static
// by updating its access flags / descriptor.
if ((programMethod.getAccessFlags() & (AccessConstants.STATIC)) == 0)
{
programMethod.accept(programClass,
new MemberAccessFlagSetter(AccessConstants.STATIC));
String newDescriptor =
prependParameterToMethodDescriptor(lambdaExpression.invokedMethodDesc,
ClassUtil.internalTypeFromClassType(programClass.getName()));
programMethod.u2descriptorIndex =
new ConstantPoolEditor(programClass).addUtf8Constant(newDescriptor);
// Update the lambda expression accordingly.
lambdaExpression.invokedMethodDesc = newDescriptor;
lambdaExpression.invokedReferenceKind = MethodHandleConstant.REF_INVOKE_STATIC;
}
}
private void addAccessorMethod(ProgramClass programClass,
LambdaExpression lambdaExpression)
{
ClassBuilder classBuilder = new ClassBuilder(programClass,
programClassPool,
libraryClassPool);
String className = programClass.getName();
// Create accessor method.
String shortClassName =
ClassUtil.externalShortClassName(
ClassUtil.externalClassName(className));
String accessorMethodName =
String.format("accessor$%s$lambda%d",
shortClassName,
lambdaExpression.bootstrapMethodIndex);
String accessorMethodDescriptor =
lambdaExpression.invokedMethodDesc;
int accessFlags =
lambdaExpression.referencedInvokedMethod.getAccessFlags();
logger.debug("LambdaExpressionConverter: creating accessor method [{}.{}{}]",
className,
accessorMethodName,
accessorMethodDescriptor
);
// Method reference to a constructor.
if (lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL)
{
// Replace the return type of the accessor method -> change to created type.
// Collect first all parameters.
List invokedParameterTypes = new ArrayList();
int methodParameterSize =
ClassUtil.internalMethodParameterSize(accessorMethodDescriptor);
for (int i = 0; i < methodParameterSize; i++)
{
String invokedParameterType =
ClassUtil.internalMethodParameterType(accessorMethodDescriptor, i);
invokedParameterTypes.add(invokedParameterType);
}
String invokedClassType =
ClassUtil.internalTypeFromClassName(lambdaExpression.invokedClassName);
// Build new method descriptor with the updated return type.
accessorMethodDescriptor =
ClassUtil.internalMethodDescriptorFromInternalTypes(invokedClassType,
invokedParameterTypes);
}
else if ((accessFlags & AccessConstants.STATIC) == 0)
{
accessorMethodDescriptor =
prependParameterToMethodDescriptor(accessorMethodDescriptor,
ClassUtil.internalTypeFromClassType(className));
}
final String methodDescriptor = accessorMethodDescriptor;
classBuilder.addMethod(
AccessConstants.STATIC |
AccessConstants.SYNTHETIC,
accessorMethodName,
accessorMethodDescriptor,
50,
____ ->
{
// If the lambda expression is a method reference to a constructor,
// we need to create the object first.
if (lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL)
{
____.new_(lambdaExpression.invokedClassName)
.dup();
}
// Load the parameters next.
completeInterfaceMethod(lambdaExpression,
null,
methodDescriptor,
null,
false,
0,
____);
});
// Update the lambda expression to point to the created
// accessor method instead.
lambdaExpression.invokedClassName = programClass.getName();
lambdaExpression.invokedMethodName = accessorMethodName;
lambdaExpression.invokedMethodDesc = accessorMethodDescriptor;
lambdaExpression.invokedReferenceKind = MethodHandleConstant.REF_INVOKE_STATIC;
lambdaExpression.referencedInvokedClass = programClass;
lambdaExpression.referencedInvokedMethod = programClass.findMethod(accessorMethodName,
accessorMethodDescriptor);
}
private void completeStatelessLambdaClass(ProgramClass lambdaClass,
LambdaExpression lambdaExpression)
{
String lambdaClassType = ClassUtil.internalTypeFromClassName(lambdaClass.getName());
ClassBuilder classBuilder = new ClassBuilder(lambdaClass,
programClassPool,
libraryClassPool);
// Add singleton field
classBuilder.addField(
AccessConstants.PUBLIC |
AccessConstants.STATIC |
AccessConstants.FINAL,
LAMBDA_SINGLETON_FIELD_NAME,
lambdaClassType);
// Add the constructor.
classBuilder.addMethod(
AccessConstants.PUBLIC,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT,
10,
code -> code
.aload_0()
.invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT)
.return_());
// Add static initializer.
classBuilder.addMethod(
AccessConstants.STATIC,
ClassConstants.METHOD_NAME_CLINIT,
ClassConstants.METHOD_TYPE_CLINIT,
30,
code -> code
.new_(lambdaClass)
.dup()
.invokespecial(lambdaClass.getName(),
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT)
.putstatic(lambdaClass.getName(),
LAMBDA_SINGLETON_FIELD_NAME,
lambdaClassType)
.return_());
// If the lambda expression is serializable, create a readResolve method
// to return the singleton field.
if (lambdaExpression.isSerializable())
{
classBuilder.addMethod(
AccessConstants.PRIVATE,
ClassConstants.METHOD_NAME_READ_RESOLVE,
ClassConstants.METHOD_TYPE_READ_RESOLVE,
10,
code -> code
.getstatic(lambdaClass.getName(),
LAMBDA_SINGLETON_FIELD_NAME,
lambdaClassType)
.areturn());
}
logger.debug("LambdaExpressionConverter: creating interface method [{}.{}{}]",
lambdaClass.getName(),
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor
);
// Add the interface method.
classBuilder.addMethod(
AccessConstants.PUBLIC,
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor,
50,
____ ->
{
if (lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL)
{
____.new_(lambdaExpression.invokedClassName)
.dup();
// Convert the remaining parameters if they are present.
completeInterfaceMethod(lambdaExpression,
null,
lambdaExpression.interfaceMethodDescriptor,
lambdaExpression.invokedMethodDesc,
false,
1,
____);
}
else
{
boolean isInvokeVirtualOrInterface =
lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_INVOKE_VIRTUAL ||
lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_INVOKE_INTERFACE;
// Convert the remaining parameters if they are present.
completeInterfaceMethod(lambdaExpression,
null,
lambdaExpression.interfaceMethodDescriptor,
lambdaExpression.invokedMethodDesc,
isInvokeVirtualOrInterface,
1,
____);
}
});
}
private void completeCapturingLambdaClass(ProgramClass lambdaClass,
LambdaExpression lambdaExpression)
{
ClassBuilder classBuilder = new ClassBuilder(lambdaClass,
programClassPool,
libraryClassPool);
String lambdaClassName = lambdaClass.getName();
// Add the constructor.
String ctorDescriptor = lambdaExpression.getConstructorDescriptor();
logger.debug("LambdaExpressionConverter: creating constructor [{}.{}{}]",
lambdaClass,
ClassConstants.METHOD_NAME_INIT,
ctorDescriptor
);
classBuilder.addMethod(
AccessConstants.PUBLIC,
ClassConstants.METHOD_NAME_INIT,
ctorDescriptor,
50,
____ ->
{
____.aload_0()
.invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT);
InternalTypeEnumeration ctorTypeEnumeration =
new InternalTypeEnumeration(ctorDescriptor);
int ctorArgIndex = 0;
int ctorVariableIndex = 1;
while (ctorTypeEnumeration.hasMoreTypes())
{
String fieldName = "arg$" + ctorArgIndex++;
String fieldType = ctorTypeEnumeration.nextType();
____.aload_0();
____.load(ctorVariableIndex, fieldType);
____.putfield(lambdaClassName, fieldName, fieldType);
ctorVariableIndex += ClassUtil.internalTypeSize(fieldType);
}
____.return_();
});
// Add the fields.
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(ctorDescriptor);
int argIndex = 0;
while (typeEnumeration.hasMoreTypes())
{
String type = typeEnumeration.nextType();
String fieldName = "arg$" + argIndex++;
logger.debug("LambdaExpressionConverter: creating field [{}.{} {}]",
lambdaClass,
fieldName,
type
);
classBuilder.addField(AccessConstants.PRIVATE |
AccessConstants.FINAL,
fieldName,
type);
}
logger.debug("LambdaExpressionConverter: creating interface method [{}.{}{}]",
lambdaClassName,
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor
);
// Add the interface method implementation.
classBuilder.addMethod(
AccessConstants.PUBLIC,
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor,
50,
____ ->
{
boolean isInvokeVirtualOrInterface =
lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_INVOKE_VIRTUAL ||
lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_INVOKE_INTERFACE;
// Load the instance fields and the remaining parameters.
completeInterfaceMethod(lambdaExpression,
ctorDescriptor,
lambdaExpression.interfaceMethodDescriptor,
lambdaExpression.invokedMethodDesc,
isInvokeVirtualOrInterface,
1,
____);
});
}
private void completeInterfaceMethod(LambdaExpression lambdaExpression,
String fieldTypes,
String methodDescriptor,
String invokedMethodDescriptor,
boolean isInvokeVirtualOrInterface,
int parameterIndex,
CompactCodeAttributeComposer ____)
{
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(methodDescriptor);
InternalTypeEnumeration invokedTypeEnumeration = invokedMethodDescriptor == null ? null :
new InternalTypeEnumeration(invokedMethodDescriptor);
// Get the instance fields.
if (fieldTypes != null)
{
String lambdaClassName = ____.getTargetClass().getName();
InternalTypeEnumeration fieldTypeEnumeration =
new InternalTypeEnumeration(fieldTypes);
int fieldIndex = 0;
while (fieldTypeEnumeration.hasMoreTypes())
{
String fieldName = "arg$" + fieldIndex;
String fieldType = fieldTypeEnumeration.nextType();
____.aload_0()
.getfield(lambdaClassName, fieldName, fieldType);
if (!isInvokeVirtualOrInterface || fieldIndex > 0)
{
convertToTargetType(fieldType, invokedTypeEnumeration.nextType(), ____);
}
fieldIndex++;
}
}
// If we invoke a method on an object, we need to cast it to the invoked type.
else if (isInvokeVirtualOrInterface)
{
String type = typeEnumeration.nextType();
String invokedType =
ClassUtil.internalTypeFromClassName(lambdaExpression.invokedClassName);
____.load(parameterIndex, type);
parameterIndex += ClassUtil.internalTypeSize(type);
convertToTargetType(type, invokedType, ____);
}
// Load the remaining arguments.
while (typeEnumeration.hasMoreTypes())
{
String type = typeEnumeration.nextType();
String invokedType =
invokedTypeEnumeration != null ?
invokedTypeEnumeration.nextType() :
null;
____.load(parameterIndex, type);
parameterIndex += ClassUtil.internalTypeSize(type);
if (invokedType != null)
{
convertToTargetType(type, invokedType, ____);
}
}
// Invoke the method.
switch (lambdaExpression.invokedReferenceKind)
{
case MethodHandleConstant.REF_INVOKE_STATIC:
if (lambdaExpression.invokesStaticInterfaceMethod())
{
____.invokestatic_interface(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
}
else
{
____.invokestatic(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
}
break;
case MethodHandleConstant.REF_INVOKE_VIRTUAL:
____.invokevirtual(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
case MethodHandleConstant.REF_INVOKE_INTERFACE:
____.invokeinterface(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
case MethodHandleConstant.REF_NEW_INVOKE_SPECIAL:
case MethodHandleConstant.REF_INVOKE_SPECIAL:
____.invokespecial(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
}
// Cast the return type.
String methodReturnType = typeEnumeration.returnType();
if (invokedTypeEnumeration != null)
{
convertToTargetType(invokedTypeEnumeration.returnType(),
methodReturnType,
____);
}
____.return_(methodReturnType);
}
private void addBridgeMethods(ProgramClass lambdaClass, LambdaExpression lambdaExpression)
{
ClassBuilder classBuilder = new ClassBuilder(lambdaClass,
programClassPool,
libraryClassPool);
String methodName = lambdaExpression.interfaceMethod;
for (String bridgeMethodDescriptor : lambdaExpression.bridgeMethodDescriptors)
{
Method method = lambdaClass.findMethod(methodName, bridgeMethodDescriptor);
if (method == null)
{
logger.debug("LambdaExpressionConverter: adding bridge method [{}.{}{}]",
lambdaClass.getName(),
methodName,
bridgeMethodDescriptor
);
classBuilder.addMethod(
AccessConstants.PUBLIC |
AccessConstants.SYNTHETIC |
AccessConstants.BRIDGE,
methodName,
bridgeMethodDescriptor,
50,
____ ->
{
____.aload_0();
InternalTypeEnumeration interfaceTypeEnumeration =
new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor);
InternalTypeEnumeration bridgeTypeEnumeration =
new InternalTypeEnumeration(bridgeMethodDescriptor);
int variableIndex = 1;
while (bridgeTypeEnumeration.hasMoreTypes())
{
String type = bridgeTypeEnumeration.nextType();
String interfaceType = interfaceTypeEnumeration.nextType();
____.load(variableIndex, type);
variableIndex += ClassUtil.internalTypeSize(type);
convertToTargetType(type, interfaceType, ____);
}
____.invokevirtual(lambdaClass.getName(),
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor);
String methodReturnType = bridgeTypeEnumeration.returnType();
convertToTargetType(interfaceTypeEnumeration.returnType(),
methodReturnType,
____);
____.return_(methodReturnType);
});
}
}
}
private static String prependParameterToMethodDescriptor(String methodDescriptor,
String type)
{
StringBuilder methodDescBuilder = new StringBuilder();
methodDescBuilder.append('(');
methodDescBuilder.append(type);
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(methodDescriptor);
while (typeEnumeration.hasMoreTypes())
{
methodDescBuilder.append(typeEnumeration.nextType());
}
methodDescBuilder.append(')');
methodDescBuilder.append(typeEnumeration.returnType());
return methodDescBuilder.toString();
}
/**
* Adds the required instructions to the provided CodeAttributeComposer
* to convert the current value on the stack to the given targetType.
*/
private static void convertToTargetType(String sourceType,
String targetType,
CompactCodeAttributeComposer composer)
{
if (ClassUtil.isInternalPrimitiveType(sourceType) &&
!ClassUtil.isInternalPrimitiveType(targetType))
{
// Perform auto-boxing.
switch (sourceType.charAt(0))
{
case TypeConstants.INT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_INTEGER,
"valueOf",
"(I)Ljava/lang/Integer;");
break;
case TypeConstants.BYTE:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BYTE,
"valueOf",
"(B)Ljava/lang/Byte;");
break;
case TypeConstants.CHAR:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_CHARACTER,
"valueOf",
"(C)Ljava/lang/Character;");
break;
case TypeConstants.SHORT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_SHORT,
"valueOf",
"(S)Ljava/lang/Short;");
break;
case TypeConstants.BOOLEAN:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BOOLEAN,
"valueOf",
"(Z)Ljava/lang/Boolean;");
break;
case TypeConstants.LONG:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_LONG,
"valueOf",
"(J)Ljava/lang/Long;");
break;
case TypeConstants.FLOAT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_FLOAT,
"valueOf",
"(F)Ljava/lang/Float;");
break;
case TypeConstants.DOUBLE:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_DOUBLE,
"valueOf",
"(D)Ljava/lang/Double;");
break;
}
}
else if (!ClassUtil.isInternalPrimitiveType(sourceType) &&
ClassUtil.isInternalPrimitiveType(targetType))
{
boolean castRequired = sourceType.equals(ClassConstants.TYPE_JAVA_LANG_OBJECT);
// Perform auto-unboxing.
switch (targetType.charAt(0))
{
case TypeConstants.INT:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"intValue",
"()I");
break;
case TypeConstants.BYTE:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_BYTE);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BYTE,
"byteValue",
"()B");
break;
case TypeConstants.CHAR:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_CHARACTER);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_CHARACTER,
"charValue",
"()C");
break;
case TypeConstants.SHORT:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_SHORT);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_SHORT,
"shortValue",
"()S");
break;
case TypeConstants.BOOLEAN:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_BOOLEAN);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BOOLEAN,
"booleanValue",
"()Z");
break;
case TypeConstants.LONG:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"longValue",
"()J");
break;
case TypeConstants.FLOAT:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"floatValue",
"()F");
break;
case TypeConstants.DOUBLE:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"doubleValue",
"()D");
break;
}
}
else if (ClassUtil.isInternalClassType(sourceType) &&
(ClassUtil.isInternalClassType(targetType) ||
ClassUtil.isInternalArrayType(targetType)) &&
!sourceType.equals(targetType) &&
// No need to cast to java/lang/Object.
!ClassConstants.TYPE_JAVA_LANG_OBJECT.equals(targetType))
{
// Cast to target type.
composer.checkcast(ClassUtil.internalClassTypeFromType(targetType));
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy