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

com.oracle.truffle.api.library.LibraryFactory 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) 2018, 2021, 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.api.library;

import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.dsl.GeneratedBy;
import com.oracle.truffle.api.library.LibraryExport.DelegateExport;
import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.utilities.FinalBitSet;

import sun.misc.Unsafe;

/**
 * Library factories allow to create instances of libraries used to call library messages. A library
 * factory for a library class can be looked up using the static method {@link #resolve(Class)}.
 * 

* Library instances are either automatically dispatched or manually dispatched. * Automatically dispatched libraries always return true for * {@link Library#accepts(Object)} therefore they can be used with changing receiver values. *

* Manually dispatched libraries are created once for a receiver and are only valid as long as * {@link Library#accepts(Object) accepts} returns true. Once accepts was checked for * an individual value, it is guaranteed that the accepts continues to return true. It is therefore * not necessary to call accepts again for multiple message invocations. To create automatically * dispatched versions of libraries use either {@link #createDispatched(int)} or * {@link #getUncached()}. For calling manually dispatched libraries it is recommended to use * {@link CachedLibrary} instead using the factory manually. *

* Library instances are either cached or uncached. Cached instances are library * instances designed to be used in ASTs. Cached instances are typically {@link Node#isAdoptable() * adoptable} and store additional profiling information for the cached export. This allows to * generate call-site specific profiling information for libray calls. Before a cached instance can * be used it must be {@link Node#insert(Node) adopted} by a parent node. Cached instances of * libraries have a {@link Node#getCost() cost} of {@link NodeCost#MONOMORPHIC} for each manually * cached library. *

* Uncached versions are designed to be used from slow-path runtime methods or whenever call-site * specific profiling is not desired. All uncached versions of a library are annotated with * {@linkplain TruffleBoundary @TruffleBoundary}. Uncached instances always return * false for {@link Node#isAdoptable()}. Uncached instances of libraries have a * {@link Node#getCost() cost} of {@link NodeCost#MEGAMORPHIC}. *

* This class is intended to be sub-classed by generated code only. Do not sub-class * {@link LibraryFactory} manually. * * @see Library * @see LibraryExport * @see CachedLibrary * @see Message * @since 19.0 */ public abstract class LibraryFactory { private static final ConcurrentHashMap, LibraryFactory> LIBRARIES; private static final DefaultExportProvider[] EMPTY_DEFAULT_EXPORT_ARRAY = new DefaultExportProvider[0]; static { LIBRARIES = new ConcurrentHashMap<>(); } /** * Reinitializes the state for native image generation. * * NOTE: this method is called reflectively by downstream projects. */ @SuppressWarnings("unused") private static void reinitializeNativeImageState() { for (Map.Entry, LibraryFactory> entry : LIBRARIES.entrySet()) { LibraryFactory libraryFactory = entry.getValue(); /* Trigger re-initialization of default exports. */ libraryFactory.initDefaultExports(); } } /** * Resets the 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 resetNativeImageState(ClassLoader imageClassLoader) { assert TruffleOptions.AOT : "Only supported during image generation"; for (Map.Entry, LibraryFactory> entry : LIBRARIES.entrySet()) { LibraryFactory libraryFactory = entry.getValue(); removeClassesLoadedDuringImageBuild(libraryFactory.exportCache, imageClassLoader); removeClassesLoadedDuringImageBuild(libraryFactory.uncachedCache, imageClassLoader); removeClassesLoadedDuringImageBuild(libraryFactory.cachedCache, imageClassLoader); /* Reset the default exports. */ LibraryFactory.externalDefaultProviders = null; libraryFactory.afterBuiltinDefaultExports = null; libraryFactory.beforeBuiltinDefaultExports = null; } removeClassesLoadedDuringImageBuild(LIBRARIES, imageClassLoader); removeClassesLoadedDuringImageBuild(ResolvedDispatch.CACHE, imageClassLoader); removeClassesLoadedDuringImageBuild(ResolvedDispatch.REGISTRY, imageClassLoader); } private static void removeClassesLoadedDuringImageBuild(Map, ?> map, ClassLoader imageClassLoader) { Class[] classes = map.keySet().toArray(new Class[0]); for (Class clazz : classes) { if (clazz.getClassLoader() == imageClassLoader) { map.remove(clazz); } } } private final Class libraryClass; private final List messages; private final ConcurrentHashMap, LibraryExport> exportCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap, T> uncachedCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap, T> cachedCache = new ConcurrentHashMap<>(); private final ProxyExports proxyExports = new ProxyExports(); final Map nameToMessages; @CompilationFinal private volatile T uncachedDispatch; final DynamicDispatchLibrary dispatchLibrary; DefaultExportProvider[] beforeBuiltinDefaultExports; DefaultExportProvider[] afterBuiltinDefaultExports; /** * Constructor for generated subclasses. Do not sub-class {@link LibraryFactory} manually. * * @since 19.0 */ @SuppressWarnings("unchecked") protected LibraryFactory(Class libraryClass, List messages) { assert this.getClass().getName().endsWith(LibraryExport.GENERATED_CLASS_SUFFIX); assert this.getClass().getAnnotation(GeneratedBy.class) != null; assert this.getClass().getAnnotation(GeneratedBy.class).value() == libraryClass; this.libraryClass = libraryClass; this.messages = Collections.unmodifiableList(messages); Map messagesMap = new LinkedHashMap<>(); for (Message message : getMessages()) { assert message.library == null; message.library = (LibraryFactory) this; messagesMap.put(message.getSimpleName(), message); } this.nameToMessages = messagesMap; if (libraryClass == DynamicDispatchLibrary.class) { this.dispatchLibrary = null; } else { GenerateLibrary annotation = libraryClass.getAnnotation(GenerateLibrary.class); boolean dynamicDispatchEnabled = annotation == null || libraryClass.getAnnotation(GenerateLibrary.class).dynamicDispatchEnabled(); if (dynamicDispatchEnabled) { this.dispatchLibrary = LibraryFactory.resolve(DynamicDispatchLibrary.class).getUncached(); } else { this.dispatchLibrary = null; } } initDefaultExports(); } /** Lazyily init the default exports data structures. */ private void initDefaultExports() { List providers = getExternalDefaultProviders().get(libraryClass.getName()); List beforeBuiltin = null; List afterBuiltin = null; if (providers != null && !providers.isEmpty()) { for (DefaultExportProvider provider : providers) { List providerList = new ArrayList<>(); if (provider.getPriority() > 0) { if (beforeBuiltin == null) { beforeBuiltin = new ArrayList<>(); } providerList = beforeBuiltin; } else { if (afterBuiltin == null) { afterBuiltin = new ArrayList<>(); } providerList = afterBuiltin; } providerList.add(provider); } } if (beforeBuiltin != null) { beforeBuiltinDefaultExports = beforeBuiltin.toArray(new DefaultExportProvider[beforeBuiltin.size()]); } else { beforeBuiltinDefaultExports = EMPTY_DEFAULT_EXPORT_ARRAY; } if (afterBuiltin != null) { afterBuiltinDefaultExports = afterBuiltin.toArray(new DefaultExportProvider[afterBuiltin.size()]); } else { afterBuiltinDefaultExports = EMPTY_DEFAULT_EXPORT_ARRAY; } } /** * Creates a new cached and automatically dispatched library given a limit. The limit specifies * the number of cached instances that will be automatically dispatched until the dispatched * library rewrites itself to an uncached version of the library. If the limit is zero then the * library will use an uncached version from the start. Negative values will throw an * {@link IllegalArgumentException}. It is discouraged to use {@link Integer#MAX_VALUE} as * parameter to this method. Reasonable values for the limit range from zero to ten. *

* If possible it is recommended to not use this method manually but to use * {@link CachedLibrary} instead. *

* Whenever the limit is reached for a node and the uncached version is in use, the current * enclosing node will be available to the uncached library export of the library using * {@link EncapsulatingNodeReference}. * * @see EncapsulatingNodeReference * @see CachedLibrary * @since 19.0 */ @TruffleBoundary public final T createDispatched(int limit) { if (limit <= 0) { return getUncached(); } else { ensureLibraryInitialized(); return createDispatchImpl(limit); } } /** * Creates a new manually dispatched cached library for a given receiver. The receiver must not * be null. The returned library must be adopted before used. For calling manually * dispatched libraries it is recommended to use {@link CachedLibrary} instead using the factory * manually. * * @see CachedLibrary * @since 19.0 */ @TruffleBoundary public final T create(Object receiver) { Class dispatchClass = dispatch(receiver); T cached = cachedCache.get(dispatchClass); if (cached != null) { assert validateExport(receiver, dispatchClass, cached); return cached; } ensureLibraryInitialized(); LibraryExport export = lookupExport(receiver, dispatchClass); cached = export.createCached(receiver); assert (cached = createAssertionsImpl(export, cached)) != null; if (!cached.isAdoptable()) { assert cached.accepts(receiver) : String.format("Invalid accepts implementation detected in '%s'", dispatchClass.getName()); T otherCached = cachedCache.putIfAbsent(dispatchClass, cached); if (otherCached != null) { return otherCached; } } return cached; } /** * Returns an uncached automatically dispatched version of the library. This is version of a * library is used for slow-path calls. * * @since 19.0 */ public final T getUncached() { T dispatch = this.uncachedDispatch; if (dispatch == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); ensureLibraryInitialized(); dispatch = createUncachedDispatch(); T otherDispatch = this.uncachedDispatch; if (otherDispatch != null) { dispatch = otherDispatch; } else { this.uncachedDispatch = dispatch; } } return dispatch; } private void ensureLibraryInitialized() { CompilerAsserts.neverPartOfCompilation(); /* * This is needed to enforce initialization order. This way the library class is always * initialized before any of the export subclasses. So this method must be invoked before * any instantiation of a library export. */ UNSAFE.ensureClassInitialized(libraryClass); } /** * Returns an uncached manually dispatched library for a given receiver. The receiver must not * be null. * * @see CachedLibrary * @since 19.0 */ @TruffleBoundary public final T getUncached(Object receiver) { Class dispatchClass = dispatch(receiver); T uncached = uncachedCache.get(dispatchClass); if (uncached != null) { assert validateExport(receiver, dispatchClass, uncached); return uncached; } return getUncachedSlowPath(receiver, dispatchClass); } /** * Handles {@link #getUncached(Object)} cache miss. */ private T getUncachedSlowPath(Object receiver, Class dispatchClass) { ensureLibraryInitialized(); LibraryExport export = lookupExport(receiver, dispatchClass); T uncached = export.createUncached(receiver); assert validateExport(receiver, dispatchClass, uncached); assert uncached.accepts(receiver); assert (uncached = createAssertionsImpl(export, uncached)) != null; T otherUncached = uncachedCache.putIfAbsent(dispatchClass, uncached); if (otherUncached != null) { return otherUncached; } return uncached; } private static volatile Map> externalDefaultProviders; private static Map> getExternalDefaultProviders() { Map> providers = externalDefaultProviders; if (providers == null) { synchronized (LibraryFactory.class) { providers = externalDefaultProviders; if (providers == null) { providers = loadExternalDefaultProviders(); } } } return providers; } private static Map> loadExternalDefaultProviders() { Map> providers; providers = new LinkedHashMap<>(); for (DefaultExportProvider provider : LibraryAccessor.engineAccessor().loadServices(DefaultExportProvider.class)) { String libraryClassName = provider.getLibraryClassName(); List providerList = providers.get(libraryClassName); if (providerList == null) { providerList = new ArrayList<>(); providers.put(libraryClassName, providerList); } providerList.add(provider); } for (List providerList : providers.values()) { Collections.sort(providerList, new Comparator() { public int compare(DefaultExportProvider o1, DefaultExportProvider o2) { return Integer.compare(o2.getPriority(), o1.getPriority()); } }); } return providers; } final Class getLibraryClass() { return libraryClass; } final List getMessages() { return messages; } private T createAssertionsImpl(LibraryExport export, T cached) { if (needsAssertions(export)) { return createAssertions(cached); } else { return cached; } } private boolean needsAssertions(LibraryExport export) { Class registerClass = export.registerClass; if (export.isDefaultExport() && registerClass != null && registerClass.getName().equals("com.oracle.truffle.api.interop.DefaultTruffleObjectExports")) { return false; } else { return true; } } private boolean validateExport(Object receiver, Class dispatchClass, T library) { validateExport(receiver, dispatchClass, lookupExport(receiver, dispatchClass)); // this last check should only be a sanity check and not trigger in practice assert library.accepts(receiver) : library.getClass().getName(); return true; } private Class dispatch(Object receiver) { if (receiver == null) { throw new NullPointerException("Null receiver values are not supported by libraries."); } if (dispatchLibrary == null) { return receiver.getClass(); } else { Class dispatch = dispatchLibrary.dispatch(receiver); if (dispatch == null) { return receiver.getClass(); } return dispatch; } } /** * Creates a cached automatically dispatched version of this library. An implementation for this * method is generated, do not implement or call manually. * * @since 19.0 */ protected abstract T createDispatchImpl(int limit); /*** * Creates a uncached automatically dispatched version of this library. An implementation for * this method is generated, do not implement or call manually. * * @since 19.0 */ protected abstract T createUncachedDispatch(); /** * Creates a proxy version of this library. A proxy version is responsible for dispatching to * reflective implementations of messages. An implementation for this method is generated, do * not implement manually. * * @since 19.0 */ protected abstract T createProxy(ReflectionLibrary lib); /** * Creates a delegate version of a library. May be used for cached or uncached versions of a * library. Intended to be used by generated code only, do not use manually. * * @since 20.0 */ protected T createDelegate(T original) { return original; } /** * Creates an assertion version of this library. An implementation for this method is generated, * do not implement manually. * * @since 19.0 */ protected T createAssertions(T delegate) { return delegate; } /** * Returns default exported used for a given receiver. An implementation for this method is * generated, do not implement manually. * * @since 19.0 */ protected abstract Class getDefaultClass(Object receiver); private Class getDefaultClassImpl(Object receiver) { for (DefaultExportProvider defaultExport : beforeBuiltinDefaultExports) { if (defaultExport.getReceiverClass().isInstance(receiver)) { return defaultExport.getDefaultExport(); } } Class defaultClass = getDefaultClass(receiver); if (defaultClass != getLibraryClass()) { return defaultClass; } for (DefaultExportProvider defaultExport : afterBuiltinDefaultExports) { if (defaultExport.getReceiverClass().isInstance(receiver)) { return defaultExport.getDefaultExport(); } } return defaultClass; } /** * Performs a generic dispatch for this library. An implementation for this method is generated, * do not implement manually. * * @since 19.0 */ protected abstract Object genericDispatch(Library library, Object receiver, Message message, Object[] arguments, int parameterOffset) throws Exception; /** * Creates a final bitset of the given messages. Uses an internal index for the messages. An * implementation for this method is generated, do not implement manually. * * @since 20.0 */ protected FinalBitSet createMessageBitSet(@SuppressWarnings({"unused", "hiding"}) Message... enabledMessages) { throw shouldNotReachHere("should be generated"); } /** * Returns true if a message is delegated, otherwise false. Intended * to be used by generated code only, do not use manually. * * @since 20.0 */ protected static boolean isDelegated(Library lib, int index) { boolean result = ((DelegateExport) lib).getDelegateExportMessages().get(index); CompilerAsserts.partialEvaluationConstant(result); return !result; } /** * Reads the delegate for a receiver. Intended to be used by generated code only, do not use * manually. * * @since 20.0 */ protected static Object readDelegate(Library lib, Object receiver) { return ((DelegateExport) lib).readDelegateExport(receiver); } /** * Returns the delegated library to use when messages are delegated. Intended to be used by * generated code only, do not use manually. * * @since 20.0 */ @SuppressWarnings("unchecked") protected static T getDelegateLibrary(T lib, Object delegate) { return (T) ((DelegateExport) lib).getDelegateExportLibrary(delegate); } final LibraryExport lookupExport(Object receiver, Class dispatchedClass) { LibraryExport lib = this.exportCache.get(dispatchedClass); if (lib != null) { return lib; } ResolvedDispatch resolvedLibrary = ResolvedDispatch.lookup(dispatchedClass); lib = resolvedLibrary.getLibrary(libraryClass); if (lib == null) { // dynamic dispatch cannot be reflected. it is not supported. if (libraryClass != DynamicDispatchLibrary.class && resolvedLibrary.getLibrary(ReflectionLibrary.class) != null) { lib = proxyExports; } else { Class defaultClass = getDefaultClassImpl(receiver); lib = ResolvedDispatch.lookup(defaultClass).getLibrary(libraryClass); } } else { assert !lib.isDefaultExport() : String.format("Dynamic dispatch from receiver class '%s' to default export '%s' detected. " + "Use null instead to dispatch to a default export.", receiver.getClass().getName(), dispatchedClass.getName()); validateExport(receiver, dispatchedClass, lib); } LibraryExport concurrent = this.exportCache.putIfAbsent(dispatchedClass, lib); return concurrent != null ? concurrent : lib; } private void validateExport(Object receiver, Class dispatchedClass, LibraryExport exports) throws AssertionError { if (!exports.getReceiverClass().isInstance(receiver)) { throw shouldNotReachHere( String.format("Receiver class %s was dynamically dispatched to incompatible exports %s. Expected receiver class %s.", receiver.getClass().getName(), dispatchedClass.getName(), exports.getReceiverClass().getName())); } } /** * Looks up the resolved library instance for a library class. If a library class was not yet * loaded it will be initialized automatically. If the passed library class is not a valid * library then a {@link IllegalArgumentException} is thrown. * * @see Library * @since 19.0 */ @TruffleBoundary public static LibraryFactory resolve(Class library) { Objects.requireNonNull(library); return resolveImpl(library, true); } @SuppressWarnings("unchecked") private static LibraryFactory resolveImpl(Class library, boolean fail) { LibraryFactory lib = LIBRARIES.get(library); if (lib == null) { loadGeneratedClass(library); lib = LIBRARIES.get(library); if (lib == null) { if (fail) { throw new IllegalArgumentException( String.format("Class '%s' is not a registered library. Truffle libraries must be annotated with @%s to be registered. Did the Truffle annotation processor run?", library.getName(), GenerateLibrary.class.getSimpleName())); } return null; } } return (LibraryFactory) lib; } private static final sun.misc.Unsafe UNSAFE; static { Unsafe unsafe; try { unsafe = Unsafe.getUnsafe(); } catch (SecurityException e) { try { Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafeInstance.setAccessible(true); unsafe = (Unsafe) theUnsafeInstance.get(Unsafe.class); } catch (Exception e2) { throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e2); } } UNSAFE = unsafe; } static LibraryFactory loadGeneratedClass(Class libraryClass) { if (Library.class.isAssignableFrom(libraryClass)) { String generatedClassName = libraryClass.getPackage().getName() + "." + libraryClass.getSimpleName() + "Gen"; try { Class.forName(generatedClassName, true, libraryClass.getClassLoader()); } catch (ClassNotFoundException e) { return null; } return LIBRARIES.get(libraryClass); } return null; } static Message resolveMessage(Class library, String message, boolean fail) { Objects.requireNonNull(message); LibraryFactory lib = resolveImpl(library, fail); if (lib == null) { assert !fail; return null; } return resolveLibraryMessage(lib, message, fail); } private static Message resolveLibraryMessage(LibraryFactory lib, String message, boolean fail) { Message foundMessage = lib.nameToMessages.get(message); if (fail && foundMessage == null) { throw new IllegalArgumentException(String.format("Unknown message '%s' for library '%s' specified.", message, lib.getLibraryClass().getName())); } return foundMessage; } /** * @since 19.0 */ protected static void register(Class libraryClass, LibraryFactory library) { LibraryFactory lib = LIBRARIES.putIfAbsent(libraryClass, library); if (lib != null) { throw shouldNotReachHere("Reflection cannot be installed for a library twice."); } } /*** * {@inheritDoc} * * @since 19.0 */ @Override public String toString() { return "LibraryFactory [library=" + libraryClass.getName() + "]"; } final class ProxyExports extends LibraryExport { protected ProxyExports() { super(libraryClass, Object.class, true); } @Override public T createUncached(Object receiver) { return createProxy(ReflectionLibrary.getFactory().getUncached(receiver)); } @Override protected T createCached(Object receiver) { return createProxy(ReflectionLibrary.getFactory().create(receiver)); } } /** * Helper class representing a single resolved receiver class that exports multiple libraries. */ static final class ResolvedDispatch { private static final ConcurrentHashMap, ResolvedDispatch> CACHE = new ConcurrentHashMap<>(); private static final ConcurrentHashMap, LibraryExport[]> REGISTRY = new ConcurrentHashMap<>(); // the root of every receiver class chain. private static final ResolvedDispatch OBJECT_RECEIVER = new ResolvedDispatch(null, Object.class); private final ResolvedDispatch parent; private final Class dispatchClass; private final Map, LibraryExport> libraries; @SuppressWarnings({"hiding", "unchecked"}) private ResolvedDispatch(ResolvedDispatch parent, Class dispatchClass, LibraryExport... libs) { this.parent = parent; this.dispatchClass = dispatchClass; Map, LibraryExport> libraries = new LinkedHashMap<>(); for (LibraryExport lib : libs) { libraries.put(lib.getLibrary(), lib); } this.libraries = libraries; } @SuppressWarnings("unchecked") LibraryExport getLibrary(Class libraryClass) { LibraryExport lib = libraries.get(libraryClass); if (lib == null && parent != null) { lib = parent.getLibrary(libraryClass); } return (LibraryExport) lib; } /** NOTE: this method is called reflectively by downstream projects. */ @TruffleBoundary static ResolvedDispatch lookup(Class receiverClass) { ResolvedDispatch type = CACHE.get(receiverClass); if (type == null) { type = resolveClass(receiverClass); } return type; } static void register(Class receiverClass, LibraryExport... libs) { for (LibraryExport lib : libs) { lib.registerClass = receiverClass; } LibraryExport[] prevLibs = REGISTRY.put(receiverClass, libs); if (prevLibs != null) { throw new IllegalStateException("Receiver " + receiverClass + " is already registered."); } // eagerly resolve known receivers in AOT mode if (TruffleOptions.AOT) { lookup(receiverClass); } } @Override public String toString() { return "ResolvedDispatch[" + dispatchClass.getName() + "]"; } Set> getLibraries() { return libraries.keySet(); } private static boolean hasExports(Class c) { return c.getAnnotationsByType(ExportLibrary.class).length > 0; } private static ResolvedDispatch resolveClass(Class dispatchClass) { if (dispatchClass == null) { return OBJECT_RECEIVER; } ResolvedDispatch parent = resolveClass(dispatchClass.getSuperclass()); ResolvedDispatch resolved; LibraryExport[] libs = REGISTRY.get(dispatchClass); if (libs == null && hasExports(dispatchClass)) { /* * We can omit loading classes in AOT mode as they are resolved eagerly using the * TruffleFeature. We can also omit if the type was already resolved. */ loadGeneratedClass(dispatchClass); libs = REGISTRY.get(dispatchClass); if (libs == null) { throw shouldNotReachHere(String.format("Libraries for class '%s' could not be resolved. Not registered?", dispatchClass.getName())); } } if (libs != null) { resolved = new ResolvedDispatch(parent, dispatchClass, libs); } else { resolved = parent; } ResolvedDispatch concurrent = CACHE.putIfAbsent(dispatchClass, resolved); if (concurrent != null) { return concurrent; } else { return resolved; } } static void loadGeneratedClass(Class currentReceiverClass) { String generatedClassName = currentReceiverClass.getPackage().getName() + "." + currentReceiverClass.getSimpleName() + "Gen"; try { Class.forName(generatedClassName, true, currentReceiverClass.getClassLoader()); } catch (ClassNotFoundException e) { throw shouldNotReachHere(String.format("Generated class '%s' for class '%s' not found. " + "Did the Truffle annotation processor run?", generatedClassName, currentReceiverClass.getName()), e); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy