proguard.backport.LambdaExpressionConverter Maven / Gradle / Ivy
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2018 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 proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.*;
import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.*;
import proguard.classfile.util.*;
import proguard.classfile.visitor.*;
import proguard.util.*;
import java.util.*;
/**
* This ClassVisitor converts all lambda expressions in the visited
* classes to anonymous inner classes.
*
* @author Thomas Neidhart
*/
public class LambdaExpressionConverter
extends SimplifiedVisitor
implements ClassVisitor,
// Implementation interfaces.
MemberVisitor,
AttributeVisitor,
InstructionVisitor
{
private static final String LAMBDA_SINGLETON_FIELD_NAME = "INSTANCE";
private final ClassPool programClassPool;
private final ClassPool libraryClassPool;
private final MultiValueMap injectedClassNameMap;
private final ClassVisitor extraClassVisitor;
private final Map lambdaExpressionMap;
private final CodeAttributeEditor codeAttributeEditor;
private final MemberRemover memberRemover;
public LambdaExpressionConverter(ClassPool programClassPool,
ClassPool libraryClassPool,
MultiValueMap injectedClassNameMap,
ClassVisitor extraClassVisitor)
{
this.programClassPool = programClassPool;
this.libraryClassPool = libraryClassPool;
this.injectedClassNameMap = injectedClassNameMap;
this.extraClassVisitor = extraClassVisitor;
this.lambdaExpressionMap = new HashMap();
this.codeAttributeEditor = new CodeAttributeEditor(true, true);
this.memberRemover = new MemberRemover();
}
// Implementations for ClassVisitor.
@Override
public void visitLibraryClass(LibraryClass libraryClass) {}
@Override
public void visitProgramClass(ProgramClass programClass)
{
lambdaExpressionMap.clear();
programClass.accept(new LambdaExpressionCollector(lambdaExpressionMap));
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);
injectedClassNameMap.put(programClass.getName(), lambdaClass.getName());
if (extraClassVisitor != null)
{
extraClassVisitor.visitProgramClass(lambdaClass);
}
}
if (!lambdaExpressionMap.isEmpty())
{
// 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 == InstructionConstants.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())
{
builder.getstatic(lambdaClassName,
LAMBDA_SINGLETON_FIELD_NAME,
ClassUtil.internalTypeFromClassName(lambdaClassName));
}
else
{
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.
public void visitAnyMember(Clazz clazz, Member member) {}
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, ClassConstants.ACC_PRIVATE |
ClassConstants.ACC_STATIC |
ClassConstants.ACC_SYNTHETIC);
}
private static boolean hasFlag(Member member, int flag)
{
return (member.getAccessFlags() & flag) == flag;
}
private ProgramClass createLambdaClass(LambdaExpression lambdaExpression)
{
String lambdaClassName = lambdaExpression.getLambdaClassName();
ProgramClass lambdaClass =
new ProgramClass(ClassConstants.CLASS_VERSION_1_5,
1,
new Constant[10],
0,
0,
0);
ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(lambdaClass);
lambdaClass.u2thisClass =
constantPoolEditor.addClassConstant(lambdaClassName, lambdaClass);
lambdaClass.u2superClass =
constantPoolEditor.addClassConstant(ClassConstants.NAME_JAVA_LANG_OBJECT,
null);
ClassEditor classEditor = new ClassEditor(lambdaClass);
String[] interfaces = lambdaExpression.interfaces;
for (String interfaceName : interfaces)
{
classEditor.addInterface(constantPoolEditor.addClassConstant(interfaceName,
null));
}
// Store the created lambda class in the LambdaExpression
// data structure for later use.
lambdaExpression.lambdaClass = lambdaClass;
// Apply some optimizations to the lambda expression to
// avoid creating accessor methods all the time.
//fixAccessFlags(lambdaExpression);
// In case the invoked method can not be accessed directly
// by the lambda class, add a synthetic accessor method.
if (lambdaExpression.needsAccessorMethod())
{
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 fixAccessFlags(LambdaExpression lambdaExpression)
{
// If the invoked method is private, we can make it package-private
// to be able to access it from the lambda class. Otherwise we would
// need to add an additional accessor method.
if (lambdaExpression.referencedInvokedMethod instanceof ProgramMethod)
{
ProgramMethod invokedProgramMethod =
(ProgramMethod) lambdaExpression.referencedInvokedMethod;
int currentAccessFlags = invokedProgramMethod.getAccessFlags();
if ((currentAccessFlags & ClassConstants.ACC_PRIVATE) != 0)
{
invokedProgramMethod.u2accessFlags =
AccessUtil.replaceAccessFlags(currentAccessFlags,
AccessUtil.accessFlags(AccessUtil.PACKAGE_VISIBLE));
// In case of instance-capturing lambdas or method references to private
// methods the reference kind is invokeSpecial. After fixing the
// access flags we need to update the reference kind as well.
if (lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeSpecial)
{
lambdaExpression.invokedReferenceKind = ClassConstants.REF_invokeVirtual;
}
}
}
}
private void addAccessorMethod(ProgramClass programClass,
LambdaExpression lambdaExpression)
{
SimplifiedClassEditor classEditor = new SimplifiedClassEditor(programClass);
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();
if ((accessFlags & ClassConstants.ACC_STATIC) == 0)
{
accessorMethodDescriptor =
prependParameterToMethodDescriptor(accessorMethodDescriptor,
ClassUtil.internalTypeFromClassType(className));
}
CompactCodeAttributeComposer composer =
classEditor.addMethod(ClassConstants.ACC_STATIC |
ClassConstants.ACC_SYNTHETIC,
accessorMethodName,
accessorMethodDescriptor,
50);
// Load the parameters first.
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(accessorMethodDescriptor);
completeInterfaceMethod(lambdaExpression,
composer,
0,
typeEnumeration,
null);
classEditor.finishEditing();
// Update the lambda expression to point to the created
// accessor method instead.
lambdaExpression.invokedClassName = programClass.getName();
lambdaExpression.invokedMethodName = accessorMethodName;
lambdaExpression.invokedMethodDesc = accessorMethodDescriptor;
lambdaExpression.invokedReferenceKind = ClassConstants.REF_invokeStatic;
lambdaExpression.referencedInvokedClass = programClass;
lambdaExpression.referencedInvokedMethod = programClass.findMethod(accessorMethodName,
accessorMethodDescriptor);
}
private void completeStatelessLambdaClass(ProgramClass lambdaClass,
LambdaExpression lambdaExpression)
{
String lambdaClassType = ClassUtil.internalTypeFromClassName(lambdaClass.getName());
SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass);
// Add singleton field
classEditor.addField(ClassConstants.ACC_PUBLIC |
ClassConstants.ACC_STATIC |
ClassConstants.ACC_FINAL,
LAMBDA_SINGLETON_FIELD_NAME,
lambdaClassType);
// Add the constructor.
CompactCodeAttributeComposer composer =
classEditor.addMethod(ClassConstants.ACC_PUBLIC,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT,
10);
composer.aload_0()
.invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT)
.return_();
// Add static initializer.
composer =
classEditor.addMethod(ClassConstants.ACC_STATIC,
ClassConstants.METHOD_NAME_CLINIT,
ClassConstants.METHOD_TYPE_CLINIT,
30);
composer.new_(lambdaClass.getName())
.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())
{
composer =
classEditor.addMethod(ClassConstants.ACC_PRIVATE,
ClassConstants.METHOD_NAME_READ_RESOLVE,
ClassConstants.METHOD_TYPE_READ_RESOLVE,
10);
composer.getstatic(lambdaClass.getName(),
LAMBDA_SINGLETON_FIELD_NAME,
lambdaClassType)
.areturn();
}
// Add the interface method.
composer =
classEditor.addMethod(ClassConstants.ACC_PUBLIC,
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor,
50);
if (lambdaExpression.invokedReferenceKind == ClassConstants.REF_newInvokeSpecial)
{
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor);
InternalTypeEnumeration invokedTypeEnumeration =
new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc);
composer.new_(lambdaExpression.invokedClassName)
.dup();
// Convert the remaining parameters if they are present.
completeInterfaceMethod(lambdaExpression,
composer,
1,
typeEnumeration,
invokedTypeEnumeration);
}
else
{
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor);
InternalTypeEnumeration invokedTypeEnumeration =
new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc);
boolean isInvokeVirtualOrInterface =
lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeVirtual ||
lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeInterface;
int paramIndex = 1;
// If we invoke a method on an object, we need to cast it to the invoked type.
if (isInvokeVirtualOrInterface)
{
String type = typeEnumeration.nextType();
String invokedType =
ClassUtil.internalTypeFromClassName(lambdaExpression.invokedClassName);
composer.load(paramIndex, type);
paramIndex += ClassUtil.internalTypeSize(type);
convertToTargetType(type, invokedType, composer);
}
// Convert the remaining parameters if they are present.
completeInterfaceMethod(lambdaExpression,
composer,
paramIndex,
typeEnumeration,
invokedTypeEnumeration);
}
classEditor.finishEditing();
}
private void completeCapturingLambdaClass(ProgramClass lambdaClass,
LambdaExpression lambdaExpression)
{
SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass);
// Create constructor.
String ctorDescriptor = lambdaExpression.getConstructorDescriptor();
CompactCodeAttributeComposer composer =
classEditor.addMethod(ClassConstants.ACC_PUBLIC,
ClassConstants.METHOD_NAME_INIT,
ctorDescriptor,
50);
composer.aload_0()
.invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT,
ClassConstants.METHOD_NAME_INIT,
ClassConstants.METHOD_TYPE_INIT);
InternalTypeEnumeration typeEnumeration =
new InternalTypeEnumeration(ctorDescriptor);
int argIndex = 0;
int variableIndex = 1;
while (typeEnumeration.hasMoreTypes())
{
String type = typeEnumeration.nextType();
String fieldName = "arg$" + argIndex++;
classEditor.addField(ClassConstants.ACC_PRIVATE | ClassConstants.ACC_FINAL,
fieldName,
type);
composer.aload_0();
composer.load(variableIndex, type);
composer.putfield(lambdaClass.getName(), fieldName, type);
variableIndex += ClassUtil.internalTypeSize(type);
}
composer.return_();
// Create interface method.
composer =
classEditor.addMethod(ClassConstants.ACC_PUBLIC,
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor,
50);
// Load the instance fields first.
typeEnumeration =
new InternalTypeEnumeration(ctorDescriptor);
InternalTypeEnumeration invokedTypeEnumeration =
new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc);
boolean isInvokeVirtualOrInterface =
lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeVirtual ||
lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeInterface;
argIndex = 0;
while (typeEnumeration.hasMoreTypes())
{
String type = typeEnumeration.nextType();
String invokedType = isInvokeVirtualOrInterface && argIndex == 0 ?
null : invokedTypeEnumeration.nextType();
String fieldName = "arg$" + argIndex++;
composer.aload_0()
.getfield(lambdaClass.getName(), fieldName, type);
if (invokedType != null)
{
convertToTargetType(type, invokedType, composer);
}
}
// And then the method parameters.
typeEnumeration =
new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor);
completeInterfaceMethod(lambdaExpression,
composer,
1,
typeEnumeration,
invokedTypeEnumeration);
classEditor.finishEditing();
}
private void completeInterfaceMethod(LambdaExpression lambdaExpression,
CompactCodeAttributeComposer composer,
int parameterIndex,
InternalTypeEnumeration typeEnumeration,
InternalTypeEnumeration invokedTypeEnumeration)
{
while (typeEnumeration.hasMoreTypes())
{
String type = typeEnumeration.nextType();
String invokedType =
invokedTypeEnumeration != null ?
invokedTypeEnumeration.nextType() :
null;
composer.load(parameterIndex, type);
parameterIndex += ClassUtil.internalTypeSize(type);
if (invokedType != null)
{
convertToTargetType(type, invokedType, composer);
}
}
switch (lambdaExpression.invokedReferenceKind)
{
case ClassConstants.REF_invokeStatic:
if (lambdaExpression.invokesStaticInterfaceMethod())
{
composer.invokestaticinterface(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
}
else
{
composer.invokestatic(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
}
break;
case ClassConstants.REF_invokeVirtual:
composer.invokevirtual(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
case ClassConstants.REF_invokeInterface:
composer.invokeinterface(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
case ClassConstants.REF_newInvokeSpecial:
case ClassConstants.REF_invokeSpecial:
composer.invokespecial(lambdaExpression.invokedClassName,
lambdaExpression.invokedMethodName,
lambdaExpression.invokedMethodDesc,
lambdaExpression.referencedInvokedClass,
lambdaExpression.referencedInvokedMethod);
break;
}
String methodReturnType = typeEnumeration.returnType();
if (invokedTypeEnumeration != null)
{
convertToTargetType(invokedTypeEnumeration.returnType(),
methodReturnType,
composer);
}
composer.return_(methodReturnType);
}
private void addBridgeMethods(ProgramClass lambdaClass, LambdaExpression lambdaExpression)
{
SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass);
String methodName = lambdaExpression.interfaceMethod;
for (String bridgeMethodDescriptor : lambdaExpression.bridgeMethodDescriptors)
{
Method method = lambdaClass.findMethod(methodName, bridgeMethodDescriptor);
if (method != null)
{
continue;
}
CompactCodeAttributeComposer composer =
classEditor.addMethod(ClassConstants.ACC_PUBLIC |
ClassConstants.ACC_SYNTHETIC |
ClassConstants.ACC_BRIDGE,
methodName,
bridgeMethodDescriptor,
50);
composer.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();
composer.load(variableIndex, type);
variableIndex += ClassUtil.internalTypeSize(type);
convertToTargetType(type, interfaceType, composer);
}
composer.invokevirtual(lambdaClass.getName(),
lambdaExpression.interfaceMethod,
lambdaExpression.interfaceMethodDescriptor);
String methodReturnType = bridgeTypeEnumeration.returnType();
convertToTargetType(interfaceTypeEnumeration.returnType(),
methodReturnType,
composer);
composer.return_(methodReturnType);
}
classEditor.finishEditing();
}
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 ClassConstants.TYPE_INT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_INTEGER,
"valueOf",
"(I)Ljava/lang/Integer;");
break;
case ClassConstants.TYPE_BYTE:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BYTE,
"valueOf",
"(B)Ljava/lang/Byte;");
break;
case ClassConstants.TYPE_CHAR:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_CHARACTER,
"valueOf",
"(C)Ljava/lang/Character;");
break;
case ClassConstants.TYPE_SHORT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_SHORT,
"valueOf",
"(S)Ljava/lang/Short;");
break;
case ClassConstants.TYPE_BOOLEAN:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BOOLEAN,
"valueOf",
"(Z)Ljava/lang/Boolean;");
break;
case ClassConstants.TYPE_LONG:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_LONG,
"valueOf",
"(J)Ljava/lang/Long;");
break;
case ClassConstants.TYPE_FLOAT:
composer.invokestatic(ClassConstants.NAME_JAVA_LANG_FLOAT,
"valueOf",
"(F)Ljava/lang/Float;");
break;
case ClassConstants.TYPE_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 ClassConstants.TYPE_INT:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"intValue",
"()I");
break;
case ClassConstants.TYPE_BYTE:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_BYTE);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BYTE,
"byteValue",
"()B");
break;
case ClassConstants.TYPE_CHAR:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_CHARACTER);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_CHARACTER,
"charValue",
"()C");
break;
case ClassConstants.TYPE_SHORT:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_SHORT);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_SHORT,
"shortValue",
"()S");
break;
case ClassConstants.TYPE_BOOLEAN:
if (castRequired)
{
composer.checkcast(ClassConstants.NAME_JAVA_LANG_BOOLEAN);
}
composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BOOLEAN,
"booleanValue",
"()Z");
break;
case ClassConstants.TYPE_LONG:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"longValue",
"()J");
break;
case ClassConstants.TYPE_FLOAT:
if (castRequired)
{
composer.checkcast("java/lang/Number");
}
composer.invokevirtual("java/lang/Number",
"floatValue",
"()F");
break;
case ClassConstants.TYPE_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