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

jdk.graal.compiler.java.StableMethodNameFormatter Maven / Gradle / Ivy

There is a newer version: 24.2.1
Show newest version
/*
 * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.graal.compiler.java;

import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.Equivalence;

import jdk.graal.compiler.util.Digest;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;

/**
 * Formats method names so that different compilations of a method can be correlated. If the method
 * is a lambda, creates a stable name for the lambda by hashing all the invokes in the lambda
 * similarly to {@link LambdaUtils}.
 */
public class StableMethodNameFormatter implements Function {

    /**
     * Separates method names and multi-method keys.
     *
     * For example, consider method {@code java.util.HashMap.size()}. A specialized variant of the
     * method may be created for different compilation scenarios. When a variant of the method is
     * created, it is named {@code java.util.HashMap.size%%key()}. The sequence after the separator
     * ({@code "key"} in this case) is the multi-method key of the variant.
     */
    public static final String MULTI_METHOD_KEY_SEPARATOR = "%%";

    /**
     * A pattern that matches the unstable part of the name of a lambda method that is replaced.
     */
    private static final Pattern LAMBDA_METHOD_PATTERN = Pattern.compile("\\$\\$Lambda\\$\\d+/0x[0-9a-f]+");

    private static final Pattern MH_METHOD_PATTERN = Pattern.compile("LambdaForm\\$[A-Z]*MH.0x[0-9a-f]+");

    /**
     * The part of a lambda method classname that is kept in the stable method name, so that it is
     * still clear that it is a lambda method.
     */
    private static final String LAMBDA_PREFIX = "$$Lambda$";

    private static final String MH_PREFIX = "LambdaForm$";

    /**
     * The format of the methods passed to {@link ResolvedJavaMethod#format(String)}.
     */
    public static final String METHOD_FORMAT = "%H.%n(%p)";

    /**
     * The format of the invoked methods passed to {@link ResolvedJavaMethod#format(String)}, which
     * is {@link Digest#digest hashed} later.
     */
    private static final String INVOKED_METHOD_FORMAT = "%H.%n(%P)%R";

    private final boolean considerMH;

    /**
     * Cached stable method names.
     */
    private final EconomicMap methodName = EconomicMap.create(Equivalence.IDENTITY);

    public StableMethodNameFormatter(boolean considerMH) {
        this.considerMH = considerMH;
    }

    /**
     * Returns a stable method name. If the argument is not a lambda,
     * {@link ResolvedJavaMethod#format(String) the formatted method name} can be taken directly. If
     * the argument is a lambda, then the numbers just after the substring {@code $$Lambda$} are
     * replaced with a hash of invokes similarly to {@link LambdaUtils}. Results are cached
     * (compared by identity of the method).
     *
     * @param method the method to be formatted
     * @return a stable method name.
     */
    @Override
    public String apply(ResolvedJavaMethod method) {
        String result = methodName.get(method);
        if (result != null) {
            return result;
        }
        result = findMethodName(method);
        methodName.put(method, result);
        return result;
    }

    /**
     * Find a stable method for a method that could be lambda (without using the cache).
     *
     * @see #apply(ResolvedJavaMethod)
     * @param method the method to be formatted
     * @return a stable method name
     */
    private String findMethodName(ResolvedJavaMethod method) {
        if (LambdaUtils.isLambdaType(method.getDeclaringClass())) {
            return findStableLambdaMethodName(method);
        }
        if (considerMH && isMethodHandle(method.getDeclaringClass())) {
            return findStableMHName(method);
        }
        return method.format(METHOD_FORMAT);
    }

    public static boolean isMethodHandle(ResolvedJavaType declaringClass) {
        String typeName = declaringClass.getName();
        if (typeName.contains(MH_PREFIX)) {
            return MH_METHOD_PATTERN.matcher(typeName).find();
        }
        return false;
    }

    @SuppressWarnings("try")
    private static String findStableMHName(ResolvedJavaMethod method) {
        String lambdaName = method.format(METHOD_FORMAT);
        Matcher matcher = MH_METHOD_PATTERN.matcher(lambdaName);
        StringBuilder sb = new StringBuilder();
        LambdaUtils.findInvokedMethods(method).forEach((targetMethod) -> sb.append(targetMethod.format(INVOKED_METHOD_FORMAT)));
        return matcher.replaceFirst(Matcher.quoteReplacement(MH_PREFIX + Digest.digest(sb.toString())));
    }

    /**
     * Find a stable method name for a lambda method by replacing the numbers just after the
     * substring {@code $$Lambda$} with a hash of invokes similarly to {@link LambdaUtils}.
     *
     * @param method a lambda method to be formatted
     * @return a stable method name
     */
    @SuppressWarnings("try")
    private static String findStableLambdaMethodName(ResolvedJavaMethod method) {
        String lambdaName = method.format(METHOD_FORMAT);
        Matcher matcher = LAMBDA_METHOD_PATTERN.matcher(lambdaName);
        StringBuilder sb = new StringBuilder();
        LambdaUtils.findInvokedMethods(method).forEach((targetMethod) -> sb.append(targetMethod.format(INVOKED_METHOD_FORMAT)));
        return matcher.replaceFirst(Matcher.quoteReplacement(LAMBDA_PREFIX + Digest.digest(sb.toString())));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy