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

com.oracle.svm.hosted.analysis.Inflation Maven / Gradle / Ivy

There is a newer version: 19.2.1
Show newest version
/*
 * Copyright (c) 2016, 2017, 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 com.oracle.svm.hosted.analysis;

import static com.oracle.graal.pointsto.reports.AnalysisReportsOptions.PrintAnalysisCallTree;
import static com.oracle.svm.hosted.NativeImageOptions.MaxReachableTypes;
import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.regex.Pattern;

import org.graalvm.compiler.core.common.SuppressSVMWarnings;
import org.graalvm.compiler.graph.NodeSourcePosition;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.word.WordBase;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.meta.HostedProviders;
import com.oracle.graal.pointsto.reports.CallTreePrinter;
import com.oracle.graal.pointsto.util.AnalysisError.TypeNotFoundError;
import com.oracle.svm.core.annotate.UnknownObjectField;
import com.oracle.svm.core.annotate.UnknownPrimitiveField;
import com.oracle.svm.core.graal.meta.SubstrateReplacements;
import com.oracle.svm.core.hub.AnnotatedSuperInfo;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.GenericInfo;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.NativeImageClassLoader;
import com.oracle.svm.hosted.SVMHost;
import com.oracle.svm.hosted.analysis.flow.SVMMethodTypeFlowBuilder;
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;

import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaType;

public class Inflation extends BigBang {
    private Set handledUnknownValueFields;
    private Map genericInterfacesMap;
    private Map annotatedInterfacesMap;
    private Map interfacesEncodings;

    private final Pattern illegalCalleesPattern;
    private final Pattern targetCallersPattern;
    private final AnnotationSubstitutionProcessor annotationSubstitutionProcessor;

    public Inflation(OptionValues options, AnalysisUniverse universe, HostedProviders providers, AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ForkJoinPool executor) {
        super(options, universe, providers, universe.hostVM(), executor, new SubstrateUnsupportedFeatures());
        this.annotationSubstitutionProcessor = annotationSubstitutionProcessor;

        String[] targetCallers = new String[]{"com\\.oracle\\.graal\\.", "org\\.graalvm[^\\.polyglot\\.nativeapi]"};
        targetCallersPattern = buildPrefixMatchPattern(targetCallers);

        String[] illegalCallees = new String[]{"java\\.util\\.stream", "java\\.util\\.Collection\\.stream", "java\\.util\\.Arrays\\.stream"};
        illegalCalleesPattern = buildPrefixMatchPattern(illegalCallees);

        handledUnknownValueFields = new HashSet<>();
        genericInterfacesMap = new HashMap<>();
        annotatedInterfacesMap = new HashMap<>();
        interfacesEncodings = new HashMap<>();
    }

    public SVMAnalysisPolicy svmAnalysisPolicy() {
        return (SVMAnalysisPolicy) super.analysisPolicy();
    }

    @Override
    public MethodTypeFlowBuilder createMethodTypeFlowBuilder(BigBang bb, MethodTypeFlow methodFlow) {
        return new SVMMethodTypeFlowBuilder(bb, methodFlow);
    }

    @Override
    public boolean addRoot(JavaConstant constant, Object root) {
        SubstrateObjectConstant sConstant = (SubstrateObjectConstant) constant;
        return sConstant.setRoot(root);
    }

    @Override
    public Object getRoot(JavaConstant constant) {
        SubstrateObjectConstant sConstant = (SubstrateObjectConstant) constant;
        return sConstant.getRoot();
    }

    @Override
    protected void checkObjectGraph(ObjectScanner objectScanner) {
        universe.getFields().forEach(this::handleUnknownValueField);
        universe.getTypes().forEach(this::checkType);

        /* Scan hubs of all types that end up in the native image. */
        universe.getTypes().stream().filter(type -> type.isInstantiated() || type.isInTypeCheck() || type.isPrimitive()).forEach(type -> scanHub(objectScanner, type));
    }

    @Override
    public SVMHost getHostVM() {
        return (SVMHost) hostVM;
    }

    private void checkType(AnalysisType type) {
        DynamicHub hub = getHostVM().dynamicHub(type);
        if (hub.getGenericInfo() == null) {
            fillGenericInfo(type, hub);
        }
        if (hub.getAnnotatedSuperInfo() == null) {
            fillAnnotatedSuperInfo(type, hub);
        }

        if (type.getJavaKind() == JavaKind.Object) {
            if (type.isArray() && (type.isInstantiated() || type.isInTypeCheck())) {
                hub.getComponentHub().setArrayHub(hub);
            }

            try {
                AnalysisType enclosingType = type.getEnclosingType();
                if (enclosingType != null) {
                    hub.setEnclosingClass(getHostVM().dynamicHub(enclosingType));
                }
            } catch (UnsupportedFeatureException ex) {
                getUnsupportedFeatures().addMessage(type.toJavaName(true), null, ex.getMessage(), null, ex);
            }

            if (hub.getInterfacesEncoding() == null) {
                fillInterfaces(type, hub);
            }

            /*
             * Support for Java annotations.
             */
            try {
                /*
                 * Get the annotations from the wrapped type since AnalysisType.getAnnotations()
                 * defends against JDK-7183985 and we want to get the original behavior.
                 */
                Annotation[] annotations = type.getWrappedWithoutResolve().getAnnotations();
                hub.setAnnotationsEncoding(encodeAnnotations(metaAccess, annotations, hub.getAnnotationsEncoding()));
            } catch (ArrayStoreException e) {
                /* If we hit JDK-7183985 just encode the exception. */
                hub.setAnnotationsEncoding(e);
            }

            /*
             * Support for Java enumerations.
             */
            if (type.getSuperclass() != null && type.getSuperclass().equals(metaAccess.lookupJavaType(Enum.class)) && hub.getEnumConstantsShared() == null) {
                /*
                 * We want to retrieve the enum constant array that is maintained as a private
                 * static field in the enumeration class. We do not want a copy because that would
                 * mean we have the array twice in the native image: as the static field, and in the
                 * enumConstant field of DynamicHub. The only way to get the original value is via a
                 * reflective field access, and we even have to guess the field name.
                 */
                AnalysisField found = null;
                for (AnalysisField f : type.getStaticFields()) {
                    if (f.getName().endsWith("$VALUES")) {
                        if (found != null) {
                            /*
                             * Enumeration has more than one static field with enumeration values.
                             * Bailout and use Class.getEnumConstants() to get the value instead.
                             */
                            found = null;
                            break;
                        }
                        found = f;
                    }
                }
                Enum[] enumConstants;
                if (found == null) {
                    /*
                     * We could not find a unique $VALUES field, so we use the value returned by
                     * Class.getEnumConstants(). This is not ideal since Class.getEnumConstants()
                     * returns a copy of the array, so we will have two arrays with the same content
                     * in the image heap, but it is better than failing image generation.
                     */
                    enumConstants = (Enum[]) type.getJavaClass().getEnumConstants();
                } else {
                    enumConstants = (Enum[]) SubstrateObjectConstant.asObject(getConstantReflectionProvider().readFieldValue(found, null));
                    assert enumConstants != null;
                }
                hub.setEnumConstants(enumConstants);
            }
        }
    }

    @Override
    public void cleanupAfterAnalysis() {
        super.cleanupAfterAnalysis();
        handledUnknownValueFields = null;
        genericInterfacesMap = null;
        annotatedInterfacesMap = null;
        interfacesEncodings = null;
    }

    @Override
    public boolean isValidClassLoader(Object valueObj) {
        return valueObj.getClass().getClassLoader() == null || // boot class loader
                        !(valueObj.getClass().getClassLoader() instanceof NativeImageClassLoader) || valueObj.getClass().getClassLoader() == Thread.currentThread().getContextClassLoader();
    }

    @Override
    public void checkUserLimitations() {
        int maxReachableTypes = MaxReachableTypes.getValue();
        if (maxReachableTypes >= 0) {
            CallTreePrinter callTreePrinter = new CallTreePrinter(this);
            callTreePrinter.buildCallTree();
            int numberOfTypes = callTreePrinter.classesSet(false).size();
            if (numberOfTypes > maxReachableTypes) {
                throw UserError.abort("Reachable " + numberOfTypes + " types but only " + maxReachableTypes + " allowed (because the " + MaxReachableTypes.getName() +
                                " option is set). To see all reachable types use " + PrintAnalysisCallTree.getName() + "; to change the maximum number of allowed types use " +
                                MaxReachableTypes.getName() +
                                ".");
            }
        }
    }

    public AnnotationSubstitutionProcessor getAnnotationSubstitutionProcessor() {
        return annotationSubstitutionProcessor;
    }

    class GenericInterfacesEncodingKey {
        final Type[] interfaces;

        GenericInterfacesEncodingKey(Type[] aInterfaces) {
            this.interfaces = aInterfaces;
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof GenericInterfacesEncodingKey && Arrays.equals(interfaces, ((GenericInterfacesEncodingKey) obj).interfaces);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(interfaces);
        }
    }

    class AnnotatedInterfacesEncodingKey {
        final AnnotatedType[] interfaces;

        AnnotatedInterfacesEncodingKey(AnnotatedType[] aInterfaces) {
            this.interfaces = aInterfaces;
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof AnnotatedInterfacesEncodingKey && Arrays.equals(interfaces, ((AnnotatedInterfacesEncodingKey) obj).interfaces);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(interfaces);
        }
    }

    private void fillGenericInfo(AnalysisType type, DynamicHub hub) {
        Class javaClass = type.getJavaClass();

        TypeVariable[] typeParameters = javaClass.getTypeParameters();

        Type[] allGenericInterfaces;
        try {
            allGenericInterfaces = javaClass.getGenericInterfaces();
        } catch (MalformedParameterizedTypeException | TypeNotPresentException | NoClassDefFoundError t) {
            /*
             * Loading generic interfaces can fail due to missing types. Ignore the exception and
             * return an empty array.
             */
            allGenericInterfaces = new Type[0];
        }

        Type[] genericInterfaces = Arrays.stream(allGenericInterfaces).filter(this::isTypeAllowed).toArray(Type[]::new);
        Type[] cachedGenericInterfaces = genericInterfacesMap.computeIfAbsent(new GenericInterfacesEncodingKey(genericInterfaces), k -> genericInterfaces);
        Type genericSuperClass;
        try {
            genericSuperClass = javaClass.getGenericSuperclass();
        } catch (MalformedParameterizedTypeException | TypeNotPresentException | NoClassDefFoundError t) {
            /*
             * Loading the generic super class can fail due to missing types. Ignore the exception
             * and return null.
             */
            genericSuperClass = null;
        }
        if (!isTypeAllowed(genericSuperClass)) {
            genericSuperClass = null;
        }
        hub.setGenericInfo(GenericInfo.factory(typeParameters, cachedGenericInterfaces, genericSuperClass));
    }

    private void fillAnnotatedSuperInfo(AnalysisType type, DynamicHub hub) {
        Class javaClass = type.getJavaClass();

        AnnotatedType annotatedSuperclass;
        try {
            annotatedSuperclass = javaClass.getAnnotatedSuperclass();
        } catch (MalformedParameterizedTypeException | TypeNotPresentException | NoClassDefFoundError t) {
            /*
             * Loading the annotated super class can fail due to missing types. Ignore the exception
             * and return null.
             */
            annotatedSuperclass = null;
        }
        if (annotatedSuperclass != null && !isTypeAllowed(annotatedSuperclass.getType())) {
            annotatedSuperclass = null;
        }

        AnnotatedType[] allAnnotatedInterfaces;
        try {
            allAnnotatedInterfaces = javaClass.getAnnotatedInterfaces();
        } catch (MalformedParameterizedTypeException | TypeNotPresentException | NoClassDefFoundError t) {
            /*
             * Loading annotated interfaces can fail due to missing types. Ignore the exception and
             * return an empty array.
             */
            allAnnotatedInterfaces = new AnnotatedType[0];
        }

        AnnotatedType[] annotatedInterfaces = Arrays.stream(allAnnotatedInterfaces)
                        .filter(ai -> isTypeAllowed(ai.getType())).toArray(AnnotatedType[]::new);
        AnnotatedType[] cachedAnnotatedInterfaces = annotatedInterfacesMap.computeIfAbsent(
                        new AnnotatedInterfacesEncodingKey(annotatedInterfaces), k -> annotatedInterfaces);
        hub.setAnnotatedSuperInfo(AnnotatedSuperInfo.factory(annotatedSuperclass, cachedAnnotatedInterfaces));
    }

    private boolean isTypeAllowed(Type t) {
        if (t instanceof Class) {
            Optional resolved = metaAccess.optionalLookupJavaType((Class) t);
            return resolved.isPresent() && universe.platformSupported(resolved.get());
        }
        return true;
    }

    class InterfacesEncodingKey {
        final AnalysisType[] aInterfaces;

        InterfacesEncodingKey(AnalysisType[] aInterfaces) {
            this.aInterfaces = aInterfaces;
        }

        DynamicHub[] createHubs() {
            SVMHost svmHost = (SVMHost) hostVM;
            DynamicHub[] hubs = new DynamicHub[aInterfaces.length];
            for (int i = 0; i < hubs.length; i++) {
                hubs[i] = svmHost.dynamicHub(aInterfaces[i]);
            }
            return hubs;
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof InterfacesEncodingKey && Arrays.equals(aInterfaces, ((InterfacesEncodingKey) obj).aInterfaces);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(aInterfaces);
        }
    }

    /**
     * Fill array returned by Class.getInterfaces().
     */
    private void fillInterfaces(AnalysisType type, DynamicHub hub) {
        SVMHost svmHost = (SVMHost) hostVM;

        AnalysisType[] aInterfaces = type.getInterfaces();
        if (aInterfaces.length == 0) {
            hub.setInterfacesEncoding(null);
        } else if (aInterfaces.length == 1) {
            hub.setInterfacesEncoding(svmHost.dynamicHub(aInterfaces[0]));
        } else {
            /*
             * Many interfaces arrays are the same, e.g., all arrays implement the same two
             * interfaces. We want to avoid duplicate arrays with the same content in the native
             * image heap.
             */
            hub.setInterfacesEncoding(interfacesEncodings.computeIfAbsent(new InterfacesEncodingKey(aInterfaces), k -> k.createHubs()));
        }
    }

    private void scanHub(ObjectScanner objectScanner, AnalysisType type) {
        SVMHost svmHost = (SVMHost) hostVM;
        JavaConstant hubConstant = SubstrateObjectConstant.forObject(svmHost.dynamicHub(type));
        objectScanner.scanConstant(hubConstant, "Hub");
    }

    private void handleUnknownValueField(AnalysisField field) {
        if (handledUnknownValueFields.contains(field)) {
            return;
        }

        UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class);
        UnknownPrimitiveField unknownPrimitiveField = field.getAnnotation(UnknownPrimitiveField.class);
        if (unknownObjectField != null) {
            assert !Modifier.isFinal(field.getModifiers()) : "@UnknownObjectField annotated field " + field.format("%H.%n") + " cannot be final";
            assert field.getJavaKind() == JavaKind.Object;

            field.setCanBeNull(unknownObjectField.canBeNull());

            List aAnnotationTypes = extractAnnotationTypes(field, unknownObjectField);

            /*
             * Only if an UnknownValue field is really accessed, we register all the field's
             * sub-types as allocated.
             */
            if (field.isAccessed()) {
                for (AnalysisType type : aAnnotationTypes) {
                    type.registerAsAllocated(null);
                }
            }

            /*
             * Use the annotation types, instead of the declared type, in the UnknownObjectField
             * annotated fields initialization.
             */
            handleUnknownObjectField(field, aAnnotationTypes.toArray(new AnalysisType[aAnnotationTypes.size()]));

        } else if (unknownPrimitiveField != null) {
            assert !Modifier.isFinal(field.getModifiers()) : "@UnknownPrimitiveField annotated field " + field.format("%H.%n") + " cannot be final";
            /*
             * Register a primitive field as containing unknown values(s), i.e., is usually written
             * only in hosted code.
             */

            field.registerAsWritten(null);
        }

        handledUnknownValueFields.add(field);
    }

    private List extractAnnotationTypes(AnalysisField field, UnknownObjectField unknownObjectField) {
        List> annotationTypes = new ArrayList<>(Arrays.asList(unknownObjectField.types()));
        for (String annotationTypeName : unknownObjectField.fullyQualifiedTypes()) {
            try {
                Class annotationType = Class.forName(annotationTypeName);
                annotationTypes.add(annotationType);
            } catch (ClassNotFoundException e) {
                throw shouldNotReachHere("Annotation type not found " + annotationTypeName);
            }
        }

        List aAnnotationTypes = new ArrayList<>();
        AnalysisType declaredType = field.getType();

        for (Class annotationType : annotationTypes) {
            AnalysisType aAnnotationType = metaAccess.lookupJavaType(annotationType);

            assert !WordBase.class.isAssignableFrom(annotationType) : "Annotation type must not be a subtype of WordBase: field: " + field + " | declared type: " + declaredType +
                            " | annotation type: " + annotationType;
            assert declaredType.isAssignableFrom(aAnnotationType) : "Annotation type must be a subtype of the declared type: field: " + field + " | declared type: " + declaredType +
                            " | annotation type: " + annotationType;
            assert aAnnotationType.isArray() || (aAnnotationType.isInstanceClass() && !Modifier.isAbstract(aAnnotationType.getModifiers())) : "Annotation type failure: field: " + field +
                            " | annotation type " + aAnnotationType;

            aAnnotationTypes.add(aAnnotationType);
        }
        return aAnnotationTypes;
    }

    /**
     * Register a field as containing unknown object(s), i.e., is usually written only in hosted
     * code. It can have multiple declared types provided via annotation.
     */
    private void handleUnknownObjectField(AnalysisField aField, AnalysisType... declaredTypes) {
        assert aField.getJavaKind() == JavaKind.Object;

        aField.registerAsWritten(null);

        /* Link the field with all declared types. */
        for (AnalysisType fieldDeclaredType : declaredTypes) {
            TypeFlow fieldDeclaredTypeFlow = fieldDeclaredType.getTypeFlow(this, true);
            if (aField.isStatic()) {
                fieldDeclaredTypeFlow.addUse(this, aField.getStaticFieldFlow());
            } else {
                fieldDeclaredTypeFlow.addUse(this, aField.getInitialInstanceFieldFlow());
                if (fieldDeclaredType.isArray()) {
                    AnalysisType fieldComponentType = fieldDeclaredType.getComponentType();
                    aField.getInitialInstanceFieldFlow().addUse(this, aField.getInstanceFieldFlow());
                    // TODO is there a better way to signal that the elements type flow of a unknown
                    // object field is not empty?
                    if (!fieldComponentType.isPrimitive()) {
                        /*
                         * Write the component type abstract object into the field array elements
                         * type flow, i.e., the array elements type flow of the abstract object of
                         * the field declared type.
                         *
                         * This is required so that the index loads from this array return all the
                         * possible objects that can be stored in the array.
                         */
                        TypeFlow elementsFlow = fieldDeclaredType.getContextInsensitiveAnalysisObject().getArrayElementsFlow(this, true);
                        fieldComponentType.getTypeFlow(this, false).addUse(this, elementsFlow);

                        /*
                         * In the current implementation it is not necessary to do it it recursively
                         * for multidimensional arrays since we don't model individual array
                         * elements, so from the point of view of the static analysis the field's
                         * array elements value is non null (in the case of a n-dimensional array
                         * that value is another array, n-1 dimensional).
                         */
                    }
                }
            }
        }
    }

    public static Object encodeAnnotations(AnalysisMetaAccess metaAccess, Annotation[] allAnnotations, Object oldEncoding) {
        Object newEncoding;
        if (allAnnotations.length == 0) {
            newEncoding = null;
        } else {
            List usedAnnotations = new ArrayList<>();
            for (Annotation annotation : allAnnotations) {
                try {
                    AnalysisType annotationClass = metaAccess.lookupJavaType(annotation.getClass());
                    if (isAnnotationUsed(annotationClass)) {
                        usedAnnotations.add(annotation);
                    }
                } catch (TypeNotFoundError e) {
                    /*
                     * Silently ignore the annotation if its type was not discovered by the static
                     * analysis.
                     */
                }
            }
            if (usedAnnotations.size() == 0) {
                newEncoding = null;
            } else if (usedAnnotations.size() == 1) {
                newEncoding = usedAnnotations.get(0);
            } else {
                newEncoding = usedAnnotations.toArray(new Annotation[usedAnnotations.size()]);
            }
        }

        /*
         * Return newEncoding only if the value is different from oldEncoding. Without this guard,
         * for tests that do runtime compilation, the field appears as being continuously updated
         * during BigBang.checkObjectGraph.
         */
        if (oldEncoding == newEncoding || (oldEncoding instanceof Annotation[] && newEncoding instanceof Annotation[] && Arrays.equals((Annotation[]) oldEncoding, (Annotation[]) newEncoding))) {
            return oldEncoding;
        } else {
            return newEncoding;
        }
    }

    /**
     * We only want annotations in the native image heap that are "used" at run time. In our case,
     * "used" means that the annotation interface is used at least in a type check. This leaves one
     * case where Substrate VM behaves differently than a normal Java VM: When you just query the
     * number of annotations on a class, then we might return a lower number.
     */
    private static boolean isAnnotationUsed(AnalysisType annotationType) {
        if (annotationType.isInstantiated() || annotationType.isInTypeCheck()) {
            return true;
        }
        assert annotationType.getInterfaces().length == 1 : annotationType;

        AnalysisType annotationInterfaceType = annotationType.getInterfaces()[0];
        return annotationInterfaceType.isInstantiated() || annotationInterfaceType.isInTypeCheck();
    }

    @Override
    public boolean trackConcreteAnalysisObjects(AnalysisType type) {
        /*
         * For classes marked as UnknownClass no context sensitive analysis is done, i.e., no
         * concrete objects are tracked.
         *
         * It is assumed that an object of type C can be of any type compatible with C. At the same
         * type fields of type C can be of any type compatible with their declared type.
         */

        if (SVMHost.isUnknownClass(type)) {
            return false;
        }

        if (type.isArray() && SVMHost.isUnknownClass(type.getComponentType())) {
            // TODO are arrays of unknown value types also unknown?
            throw JVMCIError.unimplemented();
        }

        return true;
    }

    /**
     * Builds a pattern that checks if the tested string starts with any of the target prefixes,
     * like so: {@code str1(.*)|str2(.*)|str3(.*)}.
     */
    private static Pattern buildPrefixMatchPattern(String[] targetPrefixes) {
        String patternStr = "";
        for (int i = 0; i < targetPrefixes.length; i++) {
            String prefix = targetPrefixes[i];
            patternStr += prefix;
            patternStr += "(.*)";
            if (i < targetPrefixes.length - 1) {
                patternStr += "|";
            }
        }
        return Pattern.compile(patternStr);
    }

    @Override
    public boolean isCallAllowed(BigBang bb, AnalysisMethod caller, AnalysisMethod callee, NodeSourcePosition srcPosition) {
        String calleeName = callee.format("%H.%n");
        if (illegalCalleesPattern.matcher(calleeName).find()) {
            String callerName = caller.format("%H.%n");
            if (targetCallersPattern.matcher(callerName).find()) {
                SuppressSVMWarnings suppress = caller.getAnnotation(SuppressSVMWarnings.class);
                AnalysisType callerType = caller.getDeclaringClass();
                while (suppress == null && callerType != null) {
                    suppress = callerType.getAnnotation(SuppressSVMWarnings.class);
                    callerType = callerType.getEnclosingType();
                }
                if (suppress != null) {
                    String[] reasons = suppress.value();
                    for (String r : reasons) {
                        if (r.equals("AllowUseOfStreamAPI")) {
                            return true;
                        }
                    }
                }
                String message = "Illegal: Graal/Truffle use of Stream API: " + calleeName;
                int bci = srcPosition.getBCI();
                String trace = caller.asStackTraceElement(bci).toString();
                bb.getUnsupportedFeatures().addMessage(callerName, caller, message, trace);
                return false;
            }
        }
        return true;
    }

    @Override
    public SubstrateReplacements getReplacements() {
        return (SubstrateReplacements) super.getReplacements();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy