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

com.oracle.truffle.polyglot.InstrumentCache Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.polyglot;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import com.oracle.truffle.api.InternalResource;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration;
import com.oracle.truffle.api.instrumentation.provider.TruffleInstrumentProvider;
import com.oracle.truffle.polyglot.EngineAccessor.AbstractClassLoaderSupplier;
import com.oracle.truffle.polyglot.EngineAccessor.StrongClassLoaderSupplier;
import org.graalvm.polyglot.SandboxPolicy;

final class InstrumentCache {
    private static final List nativeImageCache = TruffleOptions.AOT ? new ArrayList<>() : null;
    private static Map, List> runtimeCaches = new HashMap<>();

    private final String className;
    private final String id;
    private final String name;
    private final String version;
    private final String website;
    private final boolean internal;
    private final Set services;
    private final ProviderAdapter providerAdapter;
    private final SandboxPolicy sandboxPolicy;
    private final Map internalResources;

    /**
     * Initializes state for native image generation.
     *
     * NOTE: this method is called reflectively by downstream projects.
     *
     * @param imageClassLoader class loader passed by the image builder.
     */
    @SuppressWarnings("unused")
    private static void initializeNativeImageState(ClassLoader imageClassLoader) {
        nativeImageCache.addAll(doLoad(List.of(new StrongClassLoaderSupplier(imageClassLoader))));
    }

    /**
     * Collect tools included in a native image.
     *
     * NOTE: this method is called reflectively by TruffleBaseFeature
     */
    @SuppressWarnings("unused")
    private static Set collectInstruments() {
        assert TruffleOptions.AOT : "Only supported during image generation";
        Set res = new HashSet<>();
        for (InstrumentCache instrumentCache : nativeImageCache) {
            res.add(instrumentCache.id);
        }
        return res;
    }

    /**
     * Initializes state for native image generation.
     *
     * NOTE: this method is called reflectively by downstream projects.
     */
    @SuppressWarnings("unused")
    private static void resetNativeImageState() {
        nativeImageCache.clear();
        runtimeCaches.clear();
    }

    private InstrumentCache(String id, String name, String version, String className, boolean internal, Set services,
                    ProviderAdapter providerAdapter, String website, SandboxPolicy sandboxPolicy, Map internalResources) {
        this.id = id;
        this.name = name;
        this.version = version;
        this.website = website;
        this.className = className;
        this.internal = internal;
        this.services = services;
        this.providerAdapter = providerAdapter;
        this.sandboxPolicy = sandboxPolicy;
        this.internalResources = internalResources;
    }

    boolean isInternal() {
        return internal;
    }

    static List load() {
        if (TruffleOptions.AOT) {
            return nativeImageCache;
        }
        synchronized (InstrumentCache.class) {
            List classLoaders = EngineAccessor.locatorOrDefaultLoaders();
            List cache = runtimeCaches.get(classLoaders);
            if (cache == null) {
                cache = doLoad(classLoaders);
                runtimeCaches.put(classLoaders, cache);
            }
            return cache;
        }
    }

    static Collection internalInstruments() {
        Set result = new HashSet<>();
        for (InstrumentCache i : load()) {
            if (i.isInternal()) {
                result.add(i);
            }
        }
        return result;
    }

    static List doLoad(List suppliers) {
        List list = new ArrayList<>();
        Set classNamesUsed = new HashSet<>();
        ClassLoader truffleClassLoader = InstrumentCache.class.getClassLoader();
        boolean usesTruffleClassLoader = false;
        Map>> optionalResources = InternalResourceCache.loadOptionalInternalResources(suppliers);
        for (AbstractClassLoaderSupplier supplier : suppliers) {
            ClassLoader loader = supplier.get();
            if (loader == null || !isValidLoader(loader)) {
                continue;
            }
            usesTruffleClassLoader |= truffleClassLoader == loader;
            loadProviders(loader).filter((p) -> supplier.accepts(p.getProviderClass())).forEach((p) -> loadInstrumentImpl(p, list, classNamesUsed, optionalResources));
            if (supplier.supportsLegacyProviders()) {
                loadLegacyProviders(loader).filter((p) -> supplier.accepts(p.getProviderClass())).forEach((p) -> loadInstrumentImpl(p, list, classNamesUsed, optionalResources));
            }
        }
        /*
         * Resolves a missing debugger instrument when the GuestLangToolsClassLoader does not define
         * module. If the ClassLoader does not define module it has no ServiceCatalog. The
         * ServiceLoader does not load module services from parent classloader. This code can be
         * removed if we add system classloader into GraalVMLocator.
         */
        if (!usesTruffleClassLoader) {
            Module truffleModule = InstrumentCache.class.getModule();
            loadProviders(truffleClassLoader).//
                            filter((p) -> p.getProviderClass().getModule().equals(truffleModule)).//
                            forEach((p) -> loadInstrumentImpl(p, list, classNamesUsed, optionalResources));
        }
        list.sort(Comparator.comparing(InstrumentCache::getId));
        return list;
    }

    @SuppressWarnings("deprecation")
    private static Stream loadLegacyProviders(ClassLoader loader) {
        ModuleUtils.exportToUnnamedModuleOf(loader);
        return StreamSupport.stream(ServiceLoader.load(TruffleInstrument.Provider.class, loader).spliterator(), false).map(LegacyProvider::new);
    }

    private static Stream loadProviders(ClassLoader loader) {
        return StreamSupport.stream(ServiceLoader.load(TruffleInstrumentProvider.class, loader).spliterator(), false).map(ModuleAwareProvider::new);
    }

    private static void loadInstrumentImpl(ProviderAdapter providerAdapter, List list, Set classNamesUsed,
                    Map>> optionalResources) {
        Class providerClass = providerAdapter.getProviderClass();
        Module providerModule = providerClass.getModule();
        ModuleUtils.exportTransitivelyTo(providerModule);
        Registration reg = providerClass.getAnnotation(Registration.class);
        if (reg == null) {
            emitWarning("Warning Truffle instrument ignored: Provider %s is missing @Registration annotation.", providerClass);
            return;
        }
        String className = providerAdapter.getInstrumentClassName();
        String name = reg.name();
        String id = reg.id();
        if (id == null || id.isEmpty()) {
            /* use class name default id */
            int lastIndex = className.lastIndexOf('$');
            if (lastIndex == -1) {
                lastIndex = className.lastIndexOf('.');
            }
            id = className.substring(lastIndex + 1);
        }
        String version = reg.version();
        String website = reg.website();
        SandboxPolicy sandboxPolicy = reg.sandbox();
        boolean internal = reg.internal();
        Set servicesClassNames = new TreeSet<>(providerAdapter.getServicesClassNames());
        Map resources = new HashMap<>();
        for (String resourceId : providerAdapter.getInternalResourceIds()) {
            resources.put(resourceId, new InternalResourceCache(id, resourceId, () -> providerAdapter.createInternalResource(resourceId)));
        }
        for (Map.Entry> resourceSupplier : optionalResources.getOrDefault(id, Map.of()).entrySet()) {
            InternalResourceCache resource = resourceSupplier.getValue().get();
            InternalResourceCache old = resources.put(resourceSupplier.getKey(), resource);
            if (old != null) {
                throw InternalResourceCache.throwDuplicateOptionalResourceException(old, resource);
            }
        }
        // we don't want multiple instruments with the same class name
        if (!classNamesUsed.contains(className)) {
            classNamesUsed.add(className);
            list.add(new InstrumentCache(id, name, version, className, internal, servicesClassNames, providerAdapter, website, sandboxPolicy, Collections.unmodifiableMap(resources)));
        }
    }

    private static boolean isValidLoader(ClassLoader loader) {
        try {
            Class truffleInstrumentClassAsSeenByLoader = Class.forName(TruffleInstrument.class.getName(), true, loader);
            return truffleInstrumentClassAsSeenByLoader == TruffleInstrument.class;
        } catch (ClassNotFoundException ex) {
            return false;
        }
    }

    String getId() {
        return id;
    }

    String getName() {
        return name;
    }

    String getClassName() {
        return className;
    }

    String getVersion() {
        return version;
    }

    TruffleInstrument loadInstrument() {
        return providerAdapter.create();
    }

    boolean supportsService(Class clazz) {
        return services.contains(clazz.getName()) || services.contains(clazz.getCanonicalName());
    }

    String[] services() {
        return services.toArray(new String[0]);
    }

    InternalResourceCache getResourceCache(String resourceId) {
        return internalResources.get(resourceId);
    }

    Collection getResourceIds() {
        return internalResources.keySet();
    }

    String getWebsite() {
        return website;
    }

    SandboxPolicy getSandboxPolicy() {
        return sandboxPolicy;
    }

    private static void emitWarning(String message, Object... args) {
        PrintStream out = System.err;
        out.printf(message + "%n", args);
    }

    private interface ProviderAdapter {
        Class getProviderClass();

        TruffleInstrument create();

        String getInstrumentClassName();

        Collection getServicesClassNames();

        List getInternalResourceIds();

        InternalResource createInternalResource(String resourceId);
    }

    /**
     * Provider adapter for deprecated {@code TruffleInstrument.Provider}. GR-46292 Remove the
     * deprecated {@code TruffleInstrument.Provider} and this adapter. When removed, the
     * {@link ModuleAwareProvider} should also be removed.
     */
    @SuppressWarnings("deprecation")
    private static final class LegacyProvider implements ProviderAdapter {

        private final TruffleInstrument.Provider provider;

        LegacyProvider(TruffleInstrument.Provider provider) {
            Objects.requireNonNull(provider, "Provider must be non null");
            this.provider = provider;
        }

        @Override
        public Class getProviderClass() {
            return provider.getClass();
        }

        @Override
        public TruffleInstrument create() {
            return provider.create();
        }

        @Override
        public String getInstrumentClassName() {
            return provider.getInstrumentClassName();
        }

        @Override
        public Collection getServicesClassNames() {
            return provider.getServicesClassNames();
        }

        @Override
        public List getInternalResourceIds() {
            return List.of();
        }

        @Override
        public InternalResource createInternalResource(String resourceId) {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * Provider adapter for {@link TruffleInstrumentProvider}. When the {@link LegacyProvider} is
     * removed, this class should also be removed.
     */
    private static final class ModuleAwareProvider implements ProviderAdapter {

        private final TruffleInstrumentProvider provider;

        ModuleAwareProvider(TruffleInstrumentProvider provider) {
            Objects.requireNonNull(provider, "Provider must be non null");
            this.provider = provider;
        }

        @Override
        public Class getProviderClass() {
            return provider.getClass();
        }

        @Override
        public TruffleInstrument create() {
            return (TruffleInstrument) EngineAccessor.INSTRUMENT_PROVIDER.create(provider);
        }

        @Override
        public String getInstrumentClassName() {
            return EngineAccessor.INSTRUMENT_PROVIDER.getInstrumentClassName(provider);
        }

        @Override
        public Collection getServicesClassNames() {
            return EngineAccessor.INSTRUMENT_PROVIDER.getServicesClassNames(provider);
        }

        @Override
        public List getInternalResourceIds() {
            return EngineAccessor.INSTRUMENT_PROVIDER.getInternalResourceIds(provider);
        }

        @Override
        public InternalResource createInternalResource(String resourceId) {
            return EngineAccessor.INSTRUMENT_PROVIDER.createInternalResource(provider, resourceId);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy