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

org.noear.solon.aot.RuntimeNativeMetadata Maven / Gradle / Ivy

/*
 * Copyright 2017-2024 noear.org and authors
 *
 * 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
 *
 *      https://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 org.noear.solon.aot;

import org.noear.snack.ONode;
import org.noear.snack.core.Feature;
import org.noear.snack.core.Options;
import org.noear.solon.Utils;
import org.noear.solon.aot.hint.ExecutableHint;
import org.noear.solon.aot.hint.ExecutableMode;
import org.noear.solon.aot.hint.JdkProxyHint;
import org.noear.solon.aot.hint.MemberCategory;
import org.noear.solon.aot.hint.ReflectionHints;
import org.noear.solon.aot.hint.ResourceHint;
import org.noear.solon.aot.hint.SerializationHint;
import org.noear.solon.core.AppClassLoader;
import org.noear.solon.core.util.ClassUtil;
import org.noear.solon.core.util.ReflectUtil;
import org.noear.solon.core.util.ScanUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * graalvm native 可达性元数据,用于生成 {@code META-INF/native-image/xxx/xxx-config.json} 文件,请注意,这个类是对 graalvm native 的不完整实现,需要时再补充
 *
 * @author songyinyin
 * @link Reachability Metadata
 * @since 2023/4/5 20:50
 */
public class RuntimeNativeMetadata {

    private final Options jsonOptions = Options.def().add(Feature.PrettyFormat).add(Feature.OrderedField);

    private final Map reflection = new LinkedHashMap<>();
    private final Set args = new TreeSet<>();
    private final List includes = new ArrayList<>();
    private final List excludes = new ArrayList<>();
    private final Map serializations = new LinkedHashMap<>();

    private final Map lambdaSerializations = new LinkedHashMap<>();
    private final Map jdkProxys = new LinkedHashMap<>();

    private String applicationClassName;

    /**
     * 注册参数
     *
     * @param args 参数
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerArg(String... args) {
        for (String arg : args) {
            this.args.add(arg);
        }

        return this;
    }

    /**
     * 注册 jdk 代理接口
     */
    public RuntimeNativeMetadata registerJdkProxy(Class type) {
        return registerJdkProxy(type, null);
    }

    /**
     * 注册 jdk 代理接口
     */
    public RuntimeNativeMetadata registerJdkProxy(Class type, String reachableType) {
        if (type.isInterface() && type.isAnnotation() == false) {
            registerJdkProxyDo(type.getName(), reachableType);
        }

        return this;
    }

    private void registerJdkProxyDo(String typeName, String reachableType) {
        if (jdkProxys.containsKey(typeName) == false) {
            JdkProxyHint proxyHint = new JdkProxyHint();
            proxyHint.setReachableType(reachableType);
            proxyHint.setInterfaces(Arrays.asList(typeName));

            jdkProxys.put(typeName, proxyHint);
        }
    }

    /**
     * 注册反射相关
     *
     * @param className 全类名
     * @param typeHint  为该类型添加更多的反射信息
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerReflection(String className, Consumer typeHint) {
        if (Utils.isEmpty(className)) {
            return this;
        }
        ReflectionHints reflectionHints = getReflectionHints(className);
        typeHint.accept(reflectionHints);

        Class clazz = ClassUtil.loadClass(className);
        if (!className.startsWith("java.") && clazz != null) {
            // 如果类存在,且不是jdk中的类,则注册字段相关元数据
            try {
                if (reflectionHints.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS)) {
                    Field[] declaredFields = clazz.getDeclaredFields();
                    if (Arrays.stream(declaredFields).allMatch(e -> ClassUtil.hasClass(e::getType))) {
                        for (Field declaredField : declaredFields) {
                            registerField(declaredField);
                        }
                    }
                } else if (reflectionHints.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS)) {
                    Field[] fields = clazz.getFields();
                    if (Arrays.stream(fields).allMatch(e -> ClassUtil.hasClass(e::getType))) {
                        for (Field field : fields) {
                            registerField(field);
                        }
                    }
                }
            } catch (NoClassDefFoundError ignore) {
                // 该类中使用了不存在的类,不注册字段相关元数据
            }
        }
        return this;
    }

    /**
     * 注册反射相关
     *
     * @param type     类型
     * @param typeHint 为该类型添加更多的反射信息
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerReflection(Class type, Consumer typeHint) {
        return registerReflection(type.getName(), typeHint);
    }

    /**
     * 注册反射相关
     *
     * @param type             类型
     * @param memberCategories 成员类别
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerReflection(Class type, MemberCategory... memberCategories) {
        return registerReflection(type, hints -> hints.getMemberCategories().addAll((Arrays.asList(memberCategories))));
    }

    /**
     * 注册反射相关
     *
     * @param className        全类名
     * @param memberCategories 成员类别
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerReflection(String className, MemberCategory... memberCategories) {
        return registerReflection(className, hints -> hints.getMemberCategories().addAll((Arrays.asList(memberCategories))));
    }

    public RuntimeNativeMetadata registerField(Field field) {
        getReflectionHints(field.getDeclaringClass().getName()).getFields().add(field.getName());
        return this;
    }

    public RuntimeNativeMetadata registerConstructor(Constructor constructor, ExecutableMode mode) {
        getReflectionHints(constructor.getDeclaringClass().getName())
                .getConstructors().add(new ExecutableHint("", constructor.getParameterTypes(), mode));
        return this;
    }

    public RuntimeNativeMetadata registerMethod(Method method, ExecutableMode mode) {
        getReflectionHints(method.getDeclaringClass().getName())
                .getMethods().add(new ExecutableHint(method.getName(), method.getParameterTypes(), mode));
        return this;
    }

    /**
     * 注册类上所有的方法
     */
    public RuntimeNativeMetadata registerAllDeclaredMethod(Class clazz, ExecutableMode mode) {
        Method[] declaredMethods = ReflectUtil.getDeclaredMethods(clazz);
        for (Method declaredMethod : declaredMethods) {
            registerMethod(declaredMethod, mode);
        }
        return this;
    }

    /**
     * 注册默认构造方法,如果不存在则不注册
     */
    public RuntimeNativeMetadata registerDefaultConstructor(Class clazz) {
        if (ClassUtil.loadClass(clazz.getName()) != null && hasDefaultConstructor(clazz)) {
            getReflectionHints(clazz.getName()).getConstructors().add(new ExecutableHint("", null, ExecutableMode.INVOKE));
        }
        return this;
    }

    /**
     * 注册默认构造方法,如果类不存在或不存在默认构造 则不注册
     */
    public RuntimeNativeMetadata registerDefaultConstructor(String className) {
        Class clazz = ClassUtil.loadClass(className);
        if (clazz != null && hasDefaultConstructor(clazz)) {
            return registerDefaultConstructor(clazz);
        }
        return this;
    }

    /**
     * 注册包含的资源
     *
     * @param pattern 正则表达式
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerResourceInclude(String pattern) {
        return registerResourceInclude(pattern, null);
    }

    /**
     * 注册包含的资源
     *
     * @param pattern       正则表达式
     * @param reachableType condition class
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerResourceInclude(String pattern, String reachableType) {
        ResourceHint resourceHint = new ResourceHint();
        resourceHint.setReachableType(reachableType);
        resourceHint.setPattern(pattern);
        includes.add(resourceHint);
        return this;
    }

    /**
     * 注册排除的资源
     *
     * @param pattern 正则表达式
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerResourceExclude(String pattern) {
        return registerResourceExclude(pattern, null);
    }

    /**
     * 注册排除的资源
     *
     * @param pattern       正则表达式
     * @param reachableType condition class
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerResourceExclude(String pattern, String reachableType) {
        ResourceHint resourceHint = new ResourceHint();
        resourceHint.setReachableType(reachableType);
        resourceHint.setPattern(pattern);
        excludes.add(resourceHint);
        return this;
    }

    /**
     * 注册Java序列化
     *
     * @param basePackage 类型基础包
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerSerialization(Package basePackage) {
        String dir = basePackage.getName().replace('.', '/');

        //扫描类文件并处理(采用两段式加载,可以部分bean先处理;剩下的为第二段处理)
        ScanUtil.scan(AppClassLoader.global(), dir, n -> n.endsWith(".class"))
                .stream()
                .forEach(name -> {
                    String className = name.substring(0, name.length() - 6);
                    className = className.replace("/", ".");

                    Class clz = ClassUtil.loadClass(AppClassLoader.global(), className);
                    if (clz != null) {
                        registerSerializationDo(clz.getName(), null);
                    }
                });

        return this;
    }

    /**
     * 注册Java序列化
     *
     * @param type 类型
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerSerialization(Class type) {
        return registerSerialization(type, null);
    }


    /**
     * 注册Java序列化
     *
     * @param name 全类名
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerSerialization(String name) {
        registerSerializationDo(name, null);
        return this;
    }

    /**
     * 注册Java序列化
     *
     * @param type          类型
     * @param reachableType condition class
     * @return {@code this}
     */
    public RuntimeNativeMetadata registerSerialization(Class type, String reachableType) {
        registerSerializationDo(type.getName(), reachableType);
        return this;
    }

    /**
     * 注册Lambda序列化
     */
    public RuntimeNativeMetadata registerLambdaSerialization(Class type) {
        registerLambdaSerializationDo(ReflectUtil.getClassName(type), null, null);
        return this;
    }

    /**
     * 生成 reflect-config.json
     *
     * @see Specifying Reflection Metadata in JSON
     */
    public String toReflectionJson() {
        if (reflection.isEmpty()) {
            return "";
        }
        ONode oNode = new ONode(jsonOptions).asArray();
        for (ReflectionHints hint : reflection.values()) {
            ONode item = oNode.addNew();

            // name
            item.set("name", hint.getName());
            // condition
            if (Utils.isNotEmpty(hint.getReachableType())) {
                item.getOrNew("condition").set("typeReachable", hint.getReachableType());
            }

            List collect = Stream.concat(hint.getConstructors().stream(), hint.getMethods().stream()).collect(Collectors.toList());
            // methods
            List invoke = collect.stream().filter(h -> h.getMode().equals(ExecutableMode.INVOKE)).collect(Collectors.toList());
            if (!invoke.isEmpty()) {
                ONode methods = item.getOrNew("methods").asArray();
                for (ExecutableHint executableHint : invoke) {
                    methods.addNew()
                            .set("name", executableHint.getName())
                            .getOrNew("parameterTypes").asArray().addAll(executableHint.getParameterTypes());
                }
            }
            // queriedMethods
            List introspect = collect.stream().filter(h -> h.getMode().equals(ExecutableMode.INTROSPECT)).collect(Collectors.toList());
            if (!introspect.isEmpty()) {
                ONode queriedMethods = item.getOrNew("queriedMethods").asArray();
                for (ExecutableHint executableHint : introspect) {
                    queriedMethods.addNew()
                            .set("name", executableHint.getName())
                            .getOrNew("parameterTypes").asArray().addAll(executableHint.getParameterTypes());
                }
            }
            // fields
            if (!hint.getFields().isEmpty()) {
                ONode on = item.getOrNew("fields").asArray();
                if (!hint.getFields().isEmpty()) {
                    for (String field : hint.getFields()) {
                        on.addNew().set("name", field);
                    }
                }

            }

            handleCategories(item, hint.getMemberCategories());

        }
        return oNode.toJson();
    }

    /**
     * 生成 resource-config.json
     *
     * @see Resource Metadata in JSON
     */
    public String toResourcesJson() {
        if (includes.isEmpty() && excludes.isEmpty()) {
            return "";
        }
        ONode oNode = new ONode(jsonOptions);

        ONode resources = oNode.getOrNew("resources");

        if (!includes.isEmpty()) {
            ONode includesNode = resources.getOrNew("includes").asArray();
            for (ResourceHint hint : includes) {
                ONode item = includesNode.addNew()
                        .set("pattern", hint.getPattern());
                if (Utils.isNotEmpty(hint.getReachableType())) {
                    item.getOrNew("condition").set("typeReachable", hint.getReachableType());
                }
            }
        }

        if (!excludes.isEmpty()) {
            ONode excludesNode = resources.getOrNew("excludes").asArray();
            for (ResourceHint hint : excludes) {
                ONode item = excludesNode.addNew()
                        .set("pattern", hint.getPattern());
                if (Utils.isNotEmpty(hint.getReachableType())) {
                    item.getOrNew("condition").set("typeReachable", hint.getReachableType());
                }
            }
        }
        return oNode.toJson();
    }


    /**
     * 生成 serialization-config.json
     *
     * @see Serialization Metadata in JSON
     */
    public String toSerializationJson() {
        if (serializations.isEmpty() && lambdaSerializations.isEmpty()) {
            return "";
        }
        ONode oNode = new ONode(jsonOptions);

        ONode types = oNode.getOrNew("types").asArray();
        for (SerializationHint hint : serializations.values()) {
            ONode item = types.addNew().set("name", hint.getName());
            if (Utils.isNotEmpty(hint.getReachableType())) {
                item.getOrNew("condition").set("typeReachable", hint.getReachableType());
            }
        }

        ONode lambdaCapturingTypes = oNode.getOrNew("lambdaCapturingTypes").asArray();
        for (SerializationHint hint : lambdaSerializations.values()) {
            ONode item = lambdaCapturingTypes.addNew().set("name", hint.getName());
            if (Utils.isNotEmpty(hint.getReachableType())) {
                item.getOrNew("condition").set("typeReachable", hint.getReachableType());
            }
            if (Utils.isNotEmpty(hint.getCustomTargetConstructorClass())) {
                item.set("customTargetConstructorClass", hint.getCustomTargetConstructorClass());
            }
        }
        return oNode.toJson();
    }


    public String toJdkProxyJson() {
        if (jdkProxys.isEmpty()) {
            return "";
        }
        ONode oNode = new ONode(jsonOptions).asArray();
        for (JdkProxyHint hint : jdkProxys.values()) {
            if (Utils.isEmpty(hint.getInterfaces())) {
                continue;
            }

            ONode item = oNode.addNew();
            item.set("interfaces", hint.getInterfaces());

            if (Utils.isNotEmpty(hint.getReachableType())) {
                item.getOrNew("condition").set("typeReachable", hint.getReachableType());
            }
        }

        return oNode.toJson();
    }

    public Map getReflection() {
        return reflection;
    }

    public Set getArgs() {
        return args;
    }

    public List getIncludes() {
        return includes;
    }

    public List getExcludes() {
        return excludes;
    }

    public Map getSerializations() {
        return serializations;
    }

    public Map getLambdaSerializations() {
        return lambdaSerializations;
    }

    public Map getJdkProxys() {
        return jdkProxys;
    }

    public String getApplicationClassName() {
        return applicationClassName;
    }

    public void setApplicationClassName(String applicationClassName) {
        this.applicationClassName = applicationClassName;
    }


    private void handleCategories(ONode attributes, Set categories) {
        if (categories.isEmpty()) {
            return;
        }
        for (MemberCategory category : categories) {
            switch (category) {
                case PUBLIC_FIELDS:
                    attributes.set("allPublicFields", true);
                    break;
                case DECLARED_FIELDS:
                    attributes.set("allDeclaredFields", true);
                    break;
                case INTROSPECT_PUBLIC_CONSTRUCTORS:
                    attributes.set("queryAllPublicConstructors", true);
                    break;
                case INTROSPECT_DECLARED_CONSTRUCTORS:
                    attributes.set("queryAllDeclaredConstructors", true);
                    break;
                case INVOKE_PUBLIC_CONSTRUCTORS:
                    attributes.set("allPublicConstructors", true);
                    break;
                case INVOKE_DECLARED_CONSTRUCTORS:
                    attributes.set("allDeclaredConstructors", true);
                    break;
                case INTROSPECT_PUBLIC_METHODS:
                    attributes.set("queryAllPublicMethods", true);
                    break;
                case INTROSPECT_DECLARED_METHODS:
                    attributes.set("queryAllDeclaredMethods", true);
                    break;
                case INVOKE_PUBLIC_METHODS:
                    attributes.set("allPublicMethods", true);
                    break;
                case INVOKE_DECLARED_METHODS:
                    attributes.set("allDeclaredMethods", true);
                    break;
                case PUBLIC_CLASSES:
                    attributes.set("allPublicClasses", true);
                    break;
                case DECLARED_CLASSES:
                    attributes.set("allDeclaredClasses", true);
                    break;
            }
        }
    }

    private void registerSerializationDo(String typeName, String reachableType) {
        if (!serializations.containsKey(typeName)) {
            SerializationHint serializationHint = new SerializationHint();
            serializationHint.setName(typeName);
            serializationHint.setReachableType(reachableType);
            serializations.put(typeName, serializationHint);
        }
    }

    private void registerLambdaSerializationDo(String name, String customTargetConstructorClass, String reachableType) {
        if (!lambdaSerializations.containsKey(name)) {
            SerializationHint serializationHint = new SerializationHint();
            serializationHint.setName(name);
            serializationHint.setCustomTargetConstructorClass(customTargetConstructorClass);
            serializationHint.setReachableType(reachableType);
            lambdaSerializations.put(name, serializationHint);
        }
    }

    private boolean hasDefaultConstructor(Class clazz) {
        if (clazz.isInterface() || clazz.isEnum()) {
            return false;
        }

        return Arrays.stream(clazz.getDeclaredConstructors()).anyMatch(e -> e.getParameterCount() == 0);
    }

    private ReflectionHints getReflectionHints(String className) {
        return reflection.computeIfAbsent(className, k -> {
            ReflectionHints hints = new ReflectionHints();
            hints.setName(className);
            return hints;
        });
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy