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

io.microsphere.invoke.MethodHandleUtils Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 io.microsphere.invoke;

import io.microsphere.lang.function.ThrowableBiFunction;
import io.microsphere.util.BaseUtils;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static io.microsphere.invoke.MethodHandleUtils.LookupKey.buildKey;
import static io.microsphere.invoke.MethodHandleUtils.LookupMode.getModes;
import static io.microsphere.lang.function.ThrowableBiFunction.execute;
import static io.microsphere.reflect.ConstructorUtils.findConstructor;
import static io.microsphere.reflect.ConstructorUtils.getDeclaredConstructor;
import static io.microsphere.reflect.ConstructorUtils.newInstance;
import static io.microsphere.reflect.MemberUtils.isPublic;
import static io.microsphere.reflect.MethodUtils.findMethod;
import static io.microsphere.reflect.MethodUtils.isCallerSensitiveMethod;
import static io.microsphere.util.ArrayUtils.isEmpty;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;

/**
 * The utilities class for {@link MethodHandle}
 *
 * @author Mercy
 * @since 1.0.0
 */
public abstract class MethodHandleUtils extends BaseUtils {

    /**
     * A single-bit mask representing {@code public} access,
     * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}.
     * The value, {@code 0x01}, happens to be the same as the value of the
     * {@code public} {@linkplain Modifier#PUBLIC modifier bit}.
     * 

* A {@code Lookup} with this lookup mode performs cross-module access check * with respect to the {@linkplain MethodHandles.Lookup#lookupClass() lookup class} and * {@linkplain MethodHandles.Lookup#previousLookupClass() previous lookup class} if present. * * @see MethodHandles.Lookup#PUBLIC */ public static final int PUBLIC = Modifier.PUBLIC; /** * A single-bit mask representing {@code private} access, * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value, {@code 0x02}, happens to be the same as the value of the * {@code private} {@linkplain Modifier#PRIVATE modifier bit}. * * @see MethodHandles.Lookup#PRIVATE */ public static final int PRIVATE = Modifier.PRIVATE; /** * A single-bit mask representing {@code protected} access, * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value, {@code 0x04}, happens to be the same as the value of the * {@code protected} {@linkplain Modifier#PROTECTED modifier bit}. * * @see MethodHandles.Lookup#PROTECTED */ public static final int PROTECTED = Modifier.PROTECTED; /** * A single-bit mask representing {@code package} access (default access), * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value is {@code 0x08}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * * @see MethodHandles.Lookup#PACKAGE */ public static final int PACKAGE = Modifier.STATIC; /** * A single-bit mask representing {@code module} access, * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value is {@code 0x10}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} * with this lookup mode can access all public types in the module of the * lookup class and public types in packages exported by other modules * to the module of the lookup class. *

* If this lookup mode is set, the {@linkplain MethodHandles.Lookup#previousLookupClass() * previous lookup class} is always {@code null}. * * @see MethodHandles.Lookup#MODULE * @since 9 */ public static final int MODULE = PACKAGE << 1; /** * A single-bit mask representing {@code unconditional} access * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value is {@code 0x20}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * A {@code Lookup} with this lookup mode assumes {@linkplain * java.lang.Module#canRead(java.lang.Module) readability}. * This lookup mode can access all public members of public types * of all modules when the type is in a package that is {@link * java.lang.Module#isExported(String) exported unconditionally}. * *

* If this lookup mode is set, the {@linkplain MethodHandles.Lookup#previousLookupClass() * previous lookup class} is always {@code null}. * * @see MethodHandles.Lookup#publicLookup() * @see MethodHandles.Lookup#UNCONDITIONAL * @since 9 */ public static final int UNCONDITIONAL = PACKAGE << 2; /** * A single-bit mask representing {@code original} access * which may contribute to the result of {@link MethodHandles.Lookup#lookupModes lookupModes}. * The value is {@code 0x40}, which does not correspond meaningfully to * any particular {@linkplain Modifier modifier bit}. * *

* If this lookup mode is set, the {@code Lookup} object must be * created by the original lookup class by calling * {@link MethodHandles#lookup()} method or by a bootstrap method * invoked by the VM. The {@code Lookup} object with this lookup * mode has {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess() full privilege access}. * * @see MethodHandles.Lookup#ORIGINAL * @since 16 */ public static final int ORIGINAL = PACKAGE << 3; /** * A single-bit mask representing all accesses (public, private, protected and package) * The value, 0x0f, happens to be the same as the value of the modifier bit. */ public static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL | ORIGINAL); /** * {@link MethodHandle} for Not-Found */ public static final MethodHandle NOT_FOUND_METHOD_HANDLE = null; /** * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class)} since JDK 7 */ private static final Constructor lookupConstructor1 = getDeclaredConstructor(MethodHandles.Lookup.class, Class.class); /** * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class, int)} since JDK 7 */ private static final Constructor lookupConstructor2 = findConstructor(MethodHandles.Lookup.class, Class.class, int.class); /** * The {@link Constructor} for {@link MethodHandles.Lookup#Lookup(Class, Class, int)} since JDK 14 */ private static final Constructor lookupConstructor3 = findConstructor(MethodHandles.Lookup.class, Class.class, Class.class, int.class); private static final ConcurrentMap lookupCache = new ConcurrentHashMap<>(); /** * The allowed {@link MethodHandles.Lookup} modes enumeration * * @see MethodHandles.Lookup#PUBLIC * @see MethodHandles.Lookup#PRIVATE * @see MethodHandles.Lookup#PROTECTED * @see MethodHandles.Lookup#PACKAGE * @see MethodHandles.Lookup#MODULE * @see MethodHandles.Lookup#UNCONDITIONAL * @see MethodHandles.Lookup#ORIGINAL * @see MethodHandles.Lookup#TRUSTED * @see MethodHandles.Lookup#ALL_MODES */ public enum LookupMode { /** * @see MethodHandleUtils#PUBLIC */ PUBLIC(MethodHandleUtils.PUBLIC), /** * @see MethodHandleUtils#PRIVATE */ PRIVATE(MethodHandleUtils.PRIVATE), /** * @see MethodHandleUtils#PROTECTED */ PROTECTED(MethodHandleUtils.PROTECTED), /** * @see MethodHandleUtils#PACKAGE */ PACKAGE(MethodHandleUtils.PACKAGE), /** * @see MethodHandleUtils#MODULE */ MODULE(MethodHandleUtils.MODULE), /** * @see MethodHandleUtils#UNCONDITIONAL */ UNCONDITIONAL(MethodHandleUtils.UNCONDITIONAL), /** * @see MethodHandleUtils#ORIGINAL */ ORIGINAL(MethodHandleUtils.ORIGINAL), /** * A single-bit mask representing all accesses (public, private, protected and package) * The value, 0x0f */ ALL(ALL_MODES), /** * -1 */ TRUSTED(-1); private final int value; LookupMode(int value) { this.value = value; } public int getValue() { return value; } public static int getModes(LookupMode... lookupModes) { int modes = 0; for (int i = 0; i < lookupModes.length; i++) { LookupMode lookupMode = lookupModes[i]; modes |= lookupMode.value; } return modes; } } static class LookupKey { final Class lookupClass; final int allowedModes; LookupKey(Class lookupClass, int allowedModes) { this.lookupClass = lookupClass; this.allowedModes = allowedModes; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LookupKey lookupKey = (LookupKey) o; return allowedModes == lookupKey.allowedModes && Objects.equals(lookupClass, lookupKey.lookupClass); } @Override public int hashCode() { return Objects.hash(lookupClass, allowedModes); } static LookupKey buildKey(Class lookupClass, int allowedModes) { return new LookupKey(lookupClass, allowedModes); } } /** * Create an instance of {@link MethodHandles.Lookup} by the specified lookup class * with {@link #ALL_MODES all accesses (public, private, protected and package)} * * @param lookupClass the lookup class * @return non-null */ public static MethodHandles.Lookup lookup(Class lookupClass) { return lookup(lookupClass, LookupMode.ALL); } /** * Create an instance of {@link MethodHandles.Lookup} by the specified lookup class * with {@link #ALL_MODES all access (public, private, protected and package)} * * @param lookupClass the lookup class * @return non-null */ public static MethodHandles.Lookup lookup(Class lookupClass, LookupMode... lookupModes) { int allowedModes = getModes(lookupModes); LookupKey key = buildKey(lookupClass, allowedModes); return lookupCache.computeIfAbsent(key, MethodHandleUtils::newLookup); } /** * The convenient method to find {@link MethodHandles.Lookup#findVirtual(Class, String, MethodType)} * * @param lookupClass the class to be looked up * @param methodName the target method name * @param parameterTypes the types of target method parameters * @return {@link MethodHandle} */ public static MethodHandle findVirtual(Class lookupClass, String methodName, Class... parameterTypes) { return find(lookupClass, methodName, parameterTypes, (lookup, methodType) -> lookup.findVirtual(lookupClass, methodName, methodType)); } /** * The convenient method to find {@link MethodHandles.Lookup#findStatic(Class, String, MethodType)} * * @param lookupClass the class to be looked up * @param methodName the target method name * @param parameterTypes the types of target method parameters * @return {@link MethodHandle} */ public static MethodHandle findStatic(Class lookupClass, String methodName, Class... parameterTypes) { return find(lookupClass, methodName, parameterTypes, (lookup, methodType) -> lookup.findStatic(lookupClass, methodName, methodType)); } protected static MethodHandle find(Class lookupClass, String methodName, Class[] parameterTypes, ThrowableBiFunction function) { Method method = findMethod(lookupClass, methodName, parameterTypes); if (method == null) { return NOT_FOUND_METHOD_HANDLE; } Class returnType = method.getReturnType(); MethodType methodType = isEmpty(parameterTypes) ? methodType(returnType) : methodType(returnType, parameterTypes); MethodHandles.Lookup lookup = isiCandidateMethod(method) ? publicLookup() : lookup(lookupClass); return execute(lookup, methodType, function); } private static boolean isiCandidateMethod(Method method) { return isPublic(method) && !isCallerSensitiveMethod(method); } private static MethodHandles.Lookup newLookup(LookupKey key) { if (lookupConstructor3 != null) { return newInstance(lookupConstructor3, key.lookupClass, key.lookupClass, key.allowedModes); } return newInstance(lookupConstructor2, key.lookupClass, key.allowedModes); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy