All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proguard.backport.LambdaExpression Maven / Gradle / Ivy

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2021 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package proguard.backport;

import proguard.classfile.*;
import proguard.classfile.attribute.BootstrapMethodInfo;
import proguard.classfile.constant.MethodHandleConstant;
import proguard.classfile.util.*;

/**
 * A small helper class that captures useful information
 * about a lambda expression as encountered in a class file.
 *
 * @author Thomas Neidhart
 */
public class LambdaExpression
{
    // The referenced class of the lambda expression.
    public ProgramClass referencedClass;

    // The referenced bootstrap method index.
    public int                 bootstrapMethodIndex;
    // The referenced bootstrap method info.
    public BootstrapMethodInfo bootstrapMethodInfo;

    // The lambda factory method type.
    public String factoryMethodDescriptor;

    // The implemented interfaces of the Lambda expression.
    public String[] interfaces;

    // The additional bridge method descriptors to be added.
    public String[] bridgeMethodDescriptors;

    // The name and descriptor of the implemented interface method.
    public String interfaceMethod;
    public String interfaceMethodDescriptor;

    // Information regarding the invoked method.
    public int    invokedReferenceKind;
    public String invokedClassName;
    public String invokedMethodName;
    public String invokedMethodDesc;

    public Clazz  referencedInvokedClass;
    public Method referencedInvokedMethod;

    // The created lambda class.
    public ProgramClass lambdaClass;

    //The index of the lambda expression, used for naming purposes.
    private final int   lambdaExpressionIndex;


    /**
     * Creates a new initialized LambdaExpression (except for the lambdaClass).
     */
    public LambdaExpression(ProgramClass        referencedClass,
                            int                 bootstrapMethodIndex,
                            BootstrapMethodInfo bootstrapMethodInfo,
                            String              factoryMethodDescriptor,
                            String[]            interfaces,
                            String[]            bridgeMethodDescriptors,
                            String              interfaceMethod,
                            String              interfaceMethodDescriptor,
                            int                 invokedReferenceKind,
                            String              invokedClassName,
                            String              invokedMethodName,
                            String              invokedMethodDesc,
                            Clazz               referencedInvokedClass,
                            Method              referencedInvokedMethod,
                            int                 lambdaExpressionIndex)
    {
        this.referencedClass           = referencedClass;
        this.bootstrapMethodIndex      = bootstrapMethodIndex;
        this.bootstrapMethodInfo       = bootstrapMethodInfo;
        this.factoryMethodDescriptor   = factoryMethodDescriptor;
        this.interfaces                = interfaces;
        this.bridgeMethodDescriptors   = bridgeMethodDescriptors;
        this.interfaceMethod           = interfaceMethod;
        this.interfaceMethodDescriptor = interfaceMethodDescriptor;
        this.invokedReferenceKind      = invokedReferenceKind;
        this.invokedClassName          = invokedClassName;
        this.invokedMethodName         = invokedMethodName;
        this.invokedMethodDesc         = invokedMethodDesc;
        this.referencedInvokedClass    = referencedInvokedClass;
        this.referencedInvokedMethod   = referencedInvokedMethod;
        this.lambdaExpressionIndex     = lambdaExpressionIndex;
    }


    /**
     * Returns the class name of the converted anonymous class.
     */
    public String getLambdaClassName()
    {
        return String.format("%s$$Lambda$%d",
                             referencedClass.getName(),
                             lambdaExpressionIndex);
    }


    public String getConstructorDescriptor()
    {
        if (isStateless())
        {
            return ClassConstants.METHOD_TYPE_INIT;
        }
        else
        {
            int endIndex = factoryMethodDescriptor.indexOf(TypeConstants.METHOD_ARGUMENTS_CLOSE);

            return factoryMethodDescriptor.substring(0, endIndex + 1) + TypeConstants.VOID;
        }
    }


    /**
     * Returns whether the lambda expression is serializable.
     */
    public boolean isSerializable()
    {
        for (String interfaceName : interfaces)
        {
            if (ClassConstants.NAME_JAVA_IO_SERIALIZABLE.equals(interfaceName))
            {
                return true;
            }
        }
        return false;
    }


    /**
     * Returns whether the lambda expression is actually a method reference.
     */
    public boolean isMethodReference()
    {
        return !isLambdaMethod(invokedMethodName);
    }


    /**
     * Returns whether the lambda expression is stateless.
     */
    public boolean isStateless()
    {
        // The lambda expression is stateless if the factory method does
        // not have arguments.
        return
            ClassUtil.internalMethodParameterCount(factoryMethodDescriptor) == 0;
    }


    /**
     * Returns whether the invoked method is a static interface method.
     */
    public boolean invokesStaticInterfaceMethod()
    {
        // We assume unknown classes are not interfaces.
        return invokedReferenceKind == MethodHandleConstant.REF_INVOKE_STATIC &&
               referencedInvokedClass != null                          &&
               (referencedInvokedClass.getAccessFlags() & AccessConstants.INTERFACE) != 0;
    }


    /**
     * Returns whether the invoked method is a non-static, private synthetic
     * method in an interface.
     */
    boolean referencesPrivateSyntheticInterfaceMethod()
    {
        // We assume unknown classes are not interfaces.
        return (referencedInvokedClass != null)                                                 &&
               (referencedInvokedClass .getAccessFlags() &  AccessConstants.INTERFACE)  != 0 &&
               (referencedInvokedMethod.getAccessFlags() & (AccessConstants.PRIVATE |
                                                            AccessConstants.SYNTHETIC)) != 0 ;
    }


    /**
     * Returns whether an accessor method is needed to access
     * the invoked method from the lambda class.
     */
    public boolean needsAccessorMethod()
    {
        // We assume unknown classes don't need an accessor method.
        return referencedInvokedClass != null &&
               new MemberFinder().findMethod(lambdaClass,
                                             referencedInvokedClass,
                                             invokedMethodName,
                                             invokedMethodDesc) == null;
    }


    /**
     * Returns whether the lambda expression is a method reference
     * to a private constructor.
     */
    public boolean referencesPrivateConstructor()
    {
        return invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL &&
               ClassConstants.METHOD_NAME_INIT.equals(invokedMethodName)   &&
               (referencedInvokedMethod.getAccessFlags() & AccessConstants.PRIVATE) != 0;
    }


    // Small Utility methods.

    private static final String LAMBDA_METHOD_PREFIX = "lambda$";

    private static boolean isLambdaMethod(String methodName)
    {
        return methodName.startsWith(LAMBDA_METHOD_PREFIX);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy