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

com.oracle.truffle.polyglot.EngineAccessor 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) 2017, 2020, 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 static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.nio.file.Path;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.options.OptionKey;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.FileSystem;
import org.graalvm.polyglot.io.ProcessHandler;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.ContextLocal;
import com.oracle.truffle.api.ContextThreadLocal;
import com.oracle.truffle.api.InstrumentInfo;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleContext;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleFile.FileTypeDetector;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.impl.TruffleLocator;
import com.oracle.truffle.api.instrumentation.ContextsListener;
import com.oracle.truffle.api.instrumentation.ThreadsListener;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInterface;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.polyglot.HostAdapterFactory.AdapterResult;
import com.oracle.truffle.polyglot.HostLanguage.HostContext;
import com.oracle.truffle.polyglot.PolyglotImpl.VMObject;
import com.oracle.truffle.polyglot.PolyglotLocals.InstrumentContextLocal;
import com.oracle.truffle.polyglot.PolyglotLocals.InstrumentContextThreadLocal;
import com.oracle.truffle.polyglot.PolyglotLocals.LanguageContextLocal;
import com.oracle.truffle.polyglot.PolyglotLocals.LanguageContextThreadLocal;
import com.oracle.truffle.polyglot.PolyglotSource.EmbedderFileSystemContext;

final class EngineAccessor extends Accessor {

    static final EngineAccessor ACCESSOR = new EngineAccessor();

    static final NodeSupport NODES = ACCESSOR.nodeSupport();
    static final SourceSupport SOURCE = ACCESSOR.sourceSupport();
    static final InstrumentSupport INSTRUMENT = ACCESSOR.instrumentSupport();
    static final LanguageSupport LANGUAGE = ACCESSOR.languageSupport();
    static final JDKSupport JDKSERVICES = ACCESSOR.jdkSupport();
    static final InteropSupport INTEROP = ACCESSOR.interopSupport();
    static final ExceptionSupport EXCEPTION = ACCESSOR.exceptionSupport();
    static final RuntimeSupport RUNTIME = ACCESSOR.runtimeSupport();

    private static List locatorLoaders() {
        if (ImageInfo.inImageRuntimeCode()) {
            return Collections.emptyList();
        }
        List loaders = TruffleLocator.loaders();
        if (loaders == null) {
            return null;
        }
        List suppliers = new ArrayList<>(loaders.size());
        for (ClassLoader loader : loaders) {
            suppliers.add(new StrongClassLoaderSupplier(loader));
        }
        return suppliers;
    }

    private static List defaultLoaders() {
        return Arrays. asList(
                        new StrongClassLoaderSupplier(EngineAccessor.class.getClassLoader()),
                        new StrongClassLoaderSupplier(ClassLoader.getSystemClassLoader()),
                        new WeakClassLoaderSupplier(Thread.currentThread().getContextClassLoader()));
    }

    static List locatorOrDefaultLoaders() {
        List loaders = locatorLoaders();
        if (loaders == null) {
            loaders = defaultLoaders();
        }
        return loaders;
    }

    private EngineAccessor() {
    }

    @Override
    protected void initializeNativeImageTruffleLocator() {
        super.initializeNativeImageTruffleLocator();
    }

    static final class EngineImpl extends EngineSupport {

        private EngineImpl() {
        }

        @Override
        public boolean isDisposed(Object polyglotLanguageContext) {
            return getEngine(polyglotLanguageContext).closed;
        }

        @Override
        public TruffleLanguage.ContextReference getCurrentContextReference(Object polyglotLanguage) {
            return ((PolyglotLanguage) polyglotLanguage).getContextReference();
        }

        @Override
        public boolean hasCurrentContext() {
            return PolyglotContextImpl.currentNotEntered() != null;
        }

        @Override
        public boolean isPolyglotEvalAllowed(Object polyglotLanguageContext) {
            PolyglotLanguageContext languageContext = ((PolyglotLanguageContext) polyglotLanguageContext);
            return languageContext.isPolyglotEvalAllowed(null);
        }

        @Override
        public boolean isPolyglotBindingsAccessAllowed(Object polyglotLanguageContext) {
            PolyglotLanguageContext languageContext = ((PolyglotLanguageContext) polyglotLanguageContext);
            return languageContext.isPolyglotBindingsAccessAllowed();
        }

        @Override
        public ZoneId getTimeZone(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).context.config.getTimeZone();
        }

        @Override
        public Object getPolyglotEngine(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).language.engine;
        }

        @Override
        public  Object getDefaultLanguageView(TruffleLanguage truffleLanguage, C context, Object value) {
            return new DefaultLanguageView<>(truffleLanguage, context, value);
        }

        @Override
        public Object getLanguageView(LanguageInfo viewLanguage, Object value) {
            PolyglotLanguage language = (PolyglotLanguage) NODES.getPolyglotLanguage(viewLanguage);
            PolyglotLanguageContext context = PolyglotContextImpl.requireContext().getContextInitialized(language, null);
            return context.getLanguageView(value);
        }

        @Override
        public Object getScopedView(LanguageInfo viewLanguage, Node location, Frame frame, Object value) {
            PolyglotLanguage language = (PolyglotLanguage) NODES.getPolyglotLanguage(viewLanguage);
            PolyglotLanguageContext context = PolyglotContextImpl.requireContext().getContextInitialized(language, null);
            return context.getScopedView(location, frame, value);
        }

        @Override
        public LanguageInfo getLanguageInfo(Object polyglotInstrument, Class> languageClass) {
            return ((PolyglotInstrument) polyglotInstrument).engine.getLanguage(languageClass, true).info;
        }

        @Override
        public CallTarget parseForLanguage(Object sourceLanguageContext, Source source, String[] argumentNames, boolean allowInternal) {
            PolyglotLanguageContext sourceContext = (PolyglotLanguageContext) sourceLanguageContext;
            if (PolyglotContextImpl.currentNotEntered() != sourceContext.context) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw PolyglotEngineException.illegalState("The context is not entered.");
            }
            PolyglotLanguage targetLanguage = sourceContext.context.engine.findLanguage(sourceContext, source.getLanguage(), source.getMimeType(), true, allowInternal);
            PolyglotLanguageContext targetContext = sourceContext.context.getContextInitialized(targetLanguage, sourceContext.language);
            targetContext.checkAccess(sourceContext.getLanguageInstance().language);
            return targetContext.parseCached(sourceContext.language, source, argumentNames);
        }

        @Override
        public Env getEnvForInstrument(String languageId, String mimeType) {
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            PolyglotLanguage foundLanguage = context.engine.findLanguage(null, languageId, mimeType, true, true);
            return context.getContextInitialized(foundLanguage, null).env;
        }

        @Override
        public org.graalvm.polyglot.SourceSection createSourceSection(Object polyglotObject, org.graalvm.polyglot.Source source, SourceSection sectionImpl) {
            PolyglotImpl impl = ((VMObject) polyglotObject).getImpl();
            return impl.getPolyglotSourceSection(sectionImpl);
        }

        @Override
        public TruffleFile getTruffleFile(String path) {
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            return EngineAccessor.LANGUAGE.getTruffleFile(context.getHostContext().getPublicFileSystemContext(), path);
        }

        @Override
        public TruffleFile getTruffleFile(URI uri) {
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            return EngineAccessor.LANGUAGE.getTruffleFile(context.getHostContext().getPublicFileSystemContext(), uri);
        }

        @Override
        public  Iterable loadServices(Class type) {
            Map, T> found = new LinkedHashMap<>();
            // Library providers exported by Truffle are not on the GuestLangToolsLoader path.
            if (type.getClassLoader() == Truffle.class.getClassLoader()) {
                for (T service : ServiceLoader.load(type, type.getClassLoader())) {
                    found.putIfAbsent(service.getClass(), service);
                }
            }
            // Search guest languages and tools.
            for (AbstractClassLoaderSupplier loaderSupplier : EngineAccessor.locatorOrDefaultLoaders()) {
                ClassLoader loader = loaderSupplier.get();
                if (seesTheSameClass(loader, type)) {
                    EngineAccessor.JDKSERVICES.exportTo(loader, null);
                    for (T service : ServiceLoader.load(type, loader)) {
                        found.putIfAbsent(service.getClass(), service);
                    }
                }
            }
            return found.values();
        }

        private static boolean seesTheSameClass(ClassLoader loader, Class type) {
            try {
                return loader != null && loader.loadClass(type.getName()) == type;
            } catch (ClassNotFoundException ex) {
                return false;
            }
        }

        @Override
        public  T lookup(InstrumentInfo info, Class serviceClass) {
            PolyglotInstrument instrument = (PolyglotInstrument) LANGUAGE.getPolyglotInstrument(info);
            return instrument.lookupInternal(serviceClass);
        }

        @Override
        public  S lookup(LanguageInfo info, Class serviceClass) {
            PolyglotLanguage language = (PolyglotLanguage) NODES.getPolyglotLanguage(info);
            if (!language.cache.supportsService(serviceClass)) {
                return null;
            }
            PolyglotLanguageContext languageContext = PolyglotContextImpl.requireContext().getContext(language);
            languageContext.ensureCreated(language);
            return languageContext.lookupService(serviceClass);
        }

        @SuppressWarnings("unchecked")
        @Override
        public > C getCurrentContext(Class languageClass) {
            CompilerAsserts.partialEvaluationConstant(languageClass);
            PolyglotLanguageContext context = PolyglotContextImpl.requireContext().getLanguageContext(languageClass);
            TruffleLanguage.Env env = context.env;
            if (env == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw PolyglotEngineException.illegalState("Current context is not yet initialized or already disposed.");
            }
            return (C) LANGUAGE.getContext(env);
        }

        @Override
        public TruffleContext getTruffleContext(Object polyglotLanguageContext) {
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) polyglotLanguageContext;
            return languageContext.context.currentTruffleContext;
        }

        @Override
        public TruffleContext getCurrentCreatorTruffleContext() {
            PolyglotContextImpl context = PolyglotContextImpl.currentNotEntered();
            return context != null ? context.creatorTruffleContext : null;
        }

        @SuppressWarnings("unchecked")
        @Override
        public > T getCurrentLanguage(Class languageClass) {
            CompilerAsserts.partialEvaluationConstant(languageClass);
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            TruffleLanguage.Env env = context.getLanguageContext(languageClass).env;
            if (env == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw PolyglotEngineException.illegalState("Current context is not yet initialized or already disposed.");
            }
            return (T) EngineAccessor.LANGUAGE.getLanguage(env);
        }

        @Override
        public Map getInternalLanguages(Object polyglotObject) {
            if (polyglotObject instanceof PolyglotLanguageContext) {
                return ((PolyglotLanguageContext) polyglotObject).getAccessibleLanguages(true);
            } else {
                return getEngine(polyglotObject).idToInternalLanguageInfo;
            }
        }

        @Override
        public Map getPublicLanguages(Object polyglotObject) {
            return ((PolyglotLanguageContext) polyglotObject).getAccessibleLanguages(false);
        }

        @Override
        public Map getInstruments(Object polyglotObject) {
            return getEngine(polyglotObject).idToInternalInstrumentInfo;
        }

        private static PolyglotEngineImpl getEngine(Object polyglotObject) throws AssertionError {
            if (!(polyglotObject instanceof PolyglotImpl.VMObject)) {
                throw shouldNotReachHere();
            }
            return ((PolyglotImpl.VMObject) polyglotObject).getEngine();
        }

        @Override
        public TruffleLanguage.Env getEnvForInstrument(LanguageInfo info) {
            PolyglotLanguage language = (PolyglotLanguage) NODES.getPolyglotLanguage(info);
            return PolyglotContextImpl.requireContext().getContextInitialized(language, null).env;
        }

        static PolyglotLanguage findObjectLanguage(PolyglotEngineImpl engine, Object value) {
            InteropLibrary lib = InteropLibrary.getFactory().getUncached(value);
            if (lib.hasLanguage(value)) {
                try {
                    return engine.getLanguage(lib.getLanguage(value), false);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                return null;
            }
        }

        static PolyglotLanguage getLanguageView(PolyglotEngineImpl engine, Object value) {
            InteropLibrary lib = InteropLibrary.getFactory().getUncached(value);
            if (lib.hasLanguage(value)) {
                try {
                    return engine.getLanguage(lib.getLanguage(value), false);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                return null;
            }
        }

        static boolean isPrimitive(final Object value) {
            final Class valueClass = value.getClass();
            return valueClass == Boolean.class || valueClass == Byte.class || valueClass == Short.class || valueClass == Integer.class || valueClass == Long.class ||
                            valueClass == Float.class || valueClass == Double.class ||
                            valueClass == Character.class || valueClass == String.class;
        }

        @Override
        public Env getLegacyLanguageEnv(Object obj, boolean nullForHost) {
            PolyglotContextImpl context = PolyglotContextImpl.currentNotEntered();
            if (context == null) {
                return null;
            }
            PolyglotLanguage language = findLegacyLanguage(context, obj);
            if (language == null) {
                return null;
            }
            return context.getContext(language).env;
        }

        private static PolyglotLanguage findLegacyLanguage(PolyglotContextImpl context, Object value) {
            PolyglotLanguage foundLanguage = null;
            for (PolyglotLanguageContext searchContext : context.contexts) {
                if (searchContext.isCreated()) {
                    final TruffleLanguage.Env searchEnv = searchContext.env;
                    if (EngineAccessor.LANGUAGE.isObjectOfLanguage(searchEnv, value)) {
                        foundLanguage = searchContext.language;
                        break;
                    }
                }
            }
            return foundLanguage;
        }

        @Override
        public Object getCurrentPolyglotEngine() {
            PolyglotContextImpl context = PolyglotContextImpl.currentNotEntered();
            if (context == null) {
                return null;
            }
            return context.engine;
        }

        @Override
        public boolean isMultiThreaded(Object guestObject) {
            PolyglotContextImpl context = PolyglotContextImpl.currentNotEntered();
            if (context == null) {
                return true;
            }
            if (isPrimitive(guestObject)) {
                return false;
            } else if (guestObject instanceof HostObject || guestObject instanceof PolyglotBindings) {
                return true;
            }
            PolyglotLanguage language = findObjectLanguage(context.engine, guestObject);
            if (language == null) {
                // be conservative
                return true;
            }
            return context.singleThreaded.isValid();
        }

        @Override
        public boolean isEvalRoot(RootNode target) {
            // TODO no eval root nodes anymore on the stack for the polyglot api
            return false;
        }

        @Override
        public RuntimeException engineToLanguageException(Throwable t) {
            return PolyglotImpl.engineToLanguageException(t);
        }

        @Override
        public RuntimeException engineToInstrumentException(Throwable t) {
            return PolyglotImpl.engineToInstrumentException(t);
        }

        @Override
        public Object getCurrentFileSystemContext() {
            return PolyglotContextImpl.requireContext().getHostContext().getPublicFileSystemContext();
        }

        @Override
        public Object getPublicFileSystemContext(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).getPublicFileSystemContext();
        }

        @Override
        public Object getInternalFileSystemContext(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).getInternalFileSystemContext();
        }

        @Override
        public Map> getEngineFileTypeDetectors(Object engineFileSystemObject) {
            if (engineFileSystemObject instanceof PolyglotLanguageContext) {
                return ((PolyglotLanguageContext) engineFileSystemObject).context.engine.getFileTypeDetectorsSupplier().get();
            } else if (engineFileSystemObject instanceof EmbedderFileSystemContext) {
                return ((EmbedderFileSystemContext) engineFileSystemObject).fileTypeDetectors.get();
            } else {
                throw new AssertionError();
            }
        }

        @Override
        public Set getValidMimeTypes(Object engineObject, String language) {
            LanguageCache lang = getLanguageCache(engineObject, language);
            if (lang != null) {
                return lang.getMimeTypes();
            } else {
                return Collections.emptySet();
            }
        }

        private static LanguageCache getLanguageCache(Object engineObject, String language) throws AssertionError {
            if (engineObject instanceof PolyglotLanguageContext) {
                PolyglotLanguage polyglotLanguage = ((PolyglotLanguageContext) engineObject).context.engine.idToLanguage.get(language);
                if (polyglotLanguage != null) {
                    return polyglotLanguage.cache;
                } else {
                    return null;
                }
            } else if (engineObject instanceof EmbedderFileSystemContext) {
                return ((EmbedderFileSystemContext) engineObject).cachedLanguages.get(language);
            } else {
                throw new AssertionError();
            }
        }

        @Override
        public boolean isCharacterBasedSource(Object fsEngineObject, String language, String mimeType) {
            LanguageCache cache = getLanguageCache(fsEngineObject, language);
            if (cache == null) {
                return true;
            }
            String useMimeType = mimeType;
            if (useMimeType == null) {
                useMimeType = cache.getDefaultMimeType();
            }
            if (useMimeType == null || !cache.getMimeTypes().contains(useMimeType)) {
                return true;
            }
            return cache.isCharacterMimeType(useMimeType);
        }

        @Override
        public boolean isMimeTypeSupported(Object polyglotLanguageContext, String mimeType) {
            PolyglotEngineImpl engine = getEngine(polyglotLanguageContext);
            for (PolyglotLanguage language : engine.idToLanguage.values()) {
                if (language.cache.getMimeTypes().contains(mimeType)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public Object getInstrumentationHandler(Object polyglotObject) {
            return getEngine(polyglotObject).instrumentationHandler;
        }

        @Override
        @CompilerDirectives.TruffleBoundary
        public Object importSymbol(Object polyglotLanguageContext, TruffleLanguage.Env env, String symbolName) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            Value value = context.context.polyglotBindings.get(symbolName);
            if (value != null) {
                return context.getAPIAccess().getReceiver(value);
            } else {
                value = context.context.findLegacyExportedSymbol(symbolName);
                if (value != null) {
                    return context.getAPIAccess().getReceiver(value);
                }
            }
            return null;
        }

        @Override
        public Object lookupHostSymbol(Object polyglotLanguageContext, TruffleLanguage.Env env, String symbolName) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            HostLanguage.HostContext hostContext = ((PolyglotLanguageContext) polyglotLanguageContext).context.getHostContextImpl();
            Class clazz = hostContext.findClass(symbolName);
            if (clazz == null) {
                return null;
            }
            return HostObject.forStaticClass(clazz, context);
        }

        @Override
        public Object asHostSymbol(Object polyglotLanguageContext, Class symbolClass) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            return HostObject.forStaticClass(symbolClass, context);
        }

        @Override
        public boolean isHostAccessAllowed(Object polyglotLanguageContext, TruffleLanguage.Env env) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            return context.context.config.hostLookupAllowed;
        }

        @Override
        public boolean isNativeAccessAllowed(Object polyglotLanguageContext, TruffleLanguage.Env env) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            return context.context.config.nativeAccessAllowed;
        }

        @Override
        public boolean inContextPreInitialization(Object polyglotObject) {
            PolyglotContextImpl polyglotContext;
            if (polyglotObject instanceof PolyglotContextImpl) {
                polyglotContext = (PolyglotContextImpl) polyglotObject;
            } else if (polyglotObject instanceof PolyglotLanguageContext) {
                polyglotContext = ((PolyglotLanguageContext) polyglotObject).context;
            } else if (polyglotObject instanceof EmbedderFileSystemContext) {
                return false;
            } else {
                throw shouldNotReachHere();
            }
            return polyglotContext.inContextPreInitialization;
        }

        @Override
        @CompilerDirectives.TruffleBoundary
        public void exportSymbol(Object polyglotLanguageContext, String symbolName, Object value) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            if (value == null) {
                context.context.getPolyglotGuestBindings().remove(symbolName);
                return;
            }
            if (!PolyglotImpl.isGuestPrimitive(value) && !(value instanceof TruffleObject)) {
                throw new IllegalArgumentException("Invalid exported value. Must be an interop value.");
            }
            context.context.getPolyglotGuestBindings().put(symbolName, context.asValue(value));
        }

        @SuppressWarnings("unchecked")
        @Override
        public Map getExportedSymbols() {
            PolyglotContextImpl currentContext = PolyglotContextImpl.currentNotEntered();
            return currentContext.getPolyglotBindings().as(Map.class);
        }

        @Override
        public Object getPolyglotBindingsObject() {
            PolyglotContextImpl currentContext = PolyglotContextImpl.currentNotEntered();
            return currentContext.getPolyglotBindingsObject();
        }

        @Override
        public Object toGuestValue(Object obj, Object context) {
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) context;
            if (obj instanceof Value) {
                PolyglotValue valueImpl = (PolyglotValue) languageContext.getImpl().getAPIAccess().getImpl((Value) obj);
                languageContext = valueImpl.languageContext;
            }
            return languageContext.toGuestValue(obj);
        }

        @Override
        public Object asBoxedGuestValue(Object guestObject, Object polyglotLanguageContext) {
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) polyglotLanguageContext;
            if (PolyglotImpl.isGuestPrimitive(guestObject)) {
                return HostObject.forObject(guestObject, languageContext);
            } else if (guestObject instanceof TruffleObject) {
                return guestObject;
            } else {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException("Provided value not an interop value.");
            }
        }

        @Override
        @SuppressWarnings("deprecation")
        public Iterable createDefaultLexicalScope(Node node, Frame frame, Class> language) {
            return LegacyDefaultScope.lexicalScope(node, frame, language);
        }

        @Override
        @SuppressWarnings("deprecation")
        public Iterable createDefaultTopScope(Object global) {
            return LegacyDefaultScope.topScope(global);
        }

        @Override
        public Object getDefaultVariables(RootNode root, Frame frame, Class> language) {
            return DefaultScope.getVariables(root, frame, language);
        }

        @Override
        public Object getDefaultArguments(Object[] frameArguments, Class> language) {
            return DefaultScope.getArguments(frameArguments, language);
        }

        @Override
        public void reportAllLanguageContexts(Object polyglotEngine, Object contextsListener) {
            ((PolyglotEngineImpl) polyglotEngine).reportAllLanguageContexts((ContextsListener) contextsListener);
        }

        @Override
        public void reportAllContextThreads(Object polyglotEngine, Object threadsListener) {
            ((PolyglotEngineImpl) polyglotEngine).reportAllContextThreads((ThreadsListener) threadsListener);
        }

        @Override
        public TruffleContext getParentContext(Object polyglotContext) {
            PolyglotContextImpl parent = ((PolyglotContextImpl) polyglotContext).parent;
            if (parent != null) {
                return parent.currentTruffleContext;
            } else {
                return null;
            }
        }

        @Override
        public Object enterInternalContext(Node node, Object polyglotLanguageContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotLanguageContext);
            PolyglotEngineImpl engine = resolveEngine(node, context);
            if (CompilerDirectives.isPartialEvaluationConstant(engine)) {
                return engine.enter(context);
            } else {
                return enterInternalContextBoundary(context, engine);
            }
        }

        @TruffleBoundary
        private static Object enterInternalContextBoundary(PolyglotContextImpl context, PolyglotEngineImpl engine) {
            return engine.enter(context);
        }

        @Override
        public void leaveInternalContext(Node node, Object impl, Object prev) {
            CompilerAsserts.partialEvaluationConstant(node);
            PolyglotContextImpl context = ((PolyglotContextImpl) impl);
            PolyglotEngineImpl engine = resolveEngine(node, context);
            if (CompilerDirectives.isPartialEvaluationConstant(engine)) {
                engine.leave((PolyglotContextImpl) prev, context);
            } else {
                leaveInternalContextBoundary(prev, context, engine);
            }
        }

        @TruffleBoundary
        private static void leaveInternalContextBoundary(Object prev, PolyglotContextImpl context, PolyglotEngineImpl engine) {
            engine.leave((PolyglotContextImpl) prev, context);
        }

        private static PolyglotEngineImpl resolveEngine(Node node, PolyglotContextImpl context) {
            PolyglotEngineImpl engine;
            if (node != null) {
                RootNode root = node.getRootNode();
                if (root == null) {
                    CompilerDirectives.transferToInterpreterAndInvalidate();
                    throw new IllegalStateException("Passed node is not yet adopted. Adopt it first.");
                }
                CompilerAsserts.partialEvaluationConstant(root);
                engine = (PolyglotEngineImpl) EngineAccessor.NODES.getPolyglotEngine(root);
                CompilerAsserts.partialEvaluationConstant(engine);
                assert engine != null : "root node engine must not be null";
            } else {
                engine = context.engine;
            }
            return engine;
        }

        @Override
        public boolean isContextEntered(Object impl) {
            return PolyglotContextImpl.currentNotEntered() == impl;
        }

        @Override
        public TruffleContext createInternalContext(Object sourcePolyglotLanguageContext, Map config) {
            PolyglotLanguageContext creator = ((PolyglotLanguageContext) sourcePolyglotLanguageContext);
            PolyglotContextImpl impl;
            synchronized (creator.context) {
                impl = new PolyglotContextImpl(creator, config);
                impl.creatorApi = impl.getAPIAccess().newContext(impl);
                impl.currentApi = impl.getAPIAccess().newContext(impl);
            }
            synchronized (impl) {
                impl.initializeContextLocals();
                impl.engine.initializeMultiContext(creator.context);
                impl.notifyContextCreated();
                impl.initializeLanguage(creator.language.getId());
            }
            return impl.creatorTruffleContext;
        }

        @Override
        public boolean isCreateThreadAllowed(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).context.config.createThreadAllowed;
        }

        @Override
        public Thread createThread(Object polyglotLanguageContext, Runnable runnable, Object innerContextImpl, ThreadGroup group, long stackSize) {
            if (!isCreateThreadAllowed(polyglotLanguageContext)) {
                throw PolyglotEngineException.illegalState("Creating threads is not allowed.");
            }

            PolyglotLanguageContext threadContext = (PolyglotLanguageContext) polyglotLanguageContext;
            if (innerContextImpl != null) {
                PolyglotContextImpl innerContext = (PolyglotContextImpl) innerContextImpl;
                threadContext = innerContext.getContext(threadContext.language);
            }
            PolyglotThread newThread = new PolyglotThread(threadContext, runnable, group, stackSize);
            threadContext.context.checkMultiThreadedAccess(newThread);
            return newThread;
        }

        @Override
        public RuntimeException wrapHostException(Node location, Object languageContext, Throwable exception) {
            return PolyglotImpl.hostToGuestException((PolyglotLanguageContext) languageContext, exception);
        }

        @Override
        public boolean isHostException(Throwable exception) {
            return exception instanceof HostException;
        }

        @Override
        public Throwable asHostException(Throwable exception) {
            if (!(exception instanceof HostException)) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException("Provided value not a host exception.");
            }
            return ((HostException) exception).getOriginal();
        }

        @Override
        public Object getCurrentHostContext() {
            PolyglotContextImpl polyglotContext = PolyglotContextImpl.currentNotEntered();
            return polyglotContext == null ? null : polyglotContext.getHostContext();
        }

        @Override
        public Object getPolyglotBindingsForLanguage(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).getPolyglotGuestBindings();
        }

        @Override
        public Object findMetaObjectForLanguage(Object polyglotLanguageContext, Object value) {
            InteropLibrary lib = InteropLibrary.getFactory().getUncached(value);
            if (lib.hasMetaObject(value)) {
                try {
                    return lib.getMetaObject(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere("Unexpected unsupported message.", e);
                }
            }
            return null;
        }

        @SuppressWarnings("cast")
        @Override
        public PolyglotException wrapGuestException(String languageId, Throwable e) {
            PolyglotContextImpl pc = PolyglotContextImpl.currentNotEntered();
            if (pc == null) {
                return null;
            }
            PolyglotLanguage language = pc.engine.findLanguage(null, languageId, null, true, true);
            PolyglotLanguageContext languageContext = pc.getContextInitialized(language, null);
            return (PolyglotException) PolyglotImpl.guestToHostException(languageContext, e);
        }

        @Override
        public Set> getProvidedTags(LanguageInfo language) {
            return ((PolyglotLanguage) NODES.getPolyglotLanguage(language)).cache.getProvidedTags();
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T getOrCreateRuntimeData(Object polyglotEngine) {
            PolyglotEngineImpl useEngine = (PolyglotEngineImpl) polyglotEngine;
            if (useEngine == null) {
                useEngine = PolyglotEngineImpl.getFallbackEngine();
            }
            return (T) useEngine.runtimeData;
        }

        @Override
        public OptionValues getEngineOptionValues(Object polyglotEngine) {
            return ((PolyglotEngineImpl) polyglotEngine).engineOptionValues;
        }

        @Override
        public Collection findCallTargets(Object polyglotEngine) {
            return INSTRUMENT.getLoadedCallTargets(((PolyglotEngineImpl) polyglotEngine).instrumentationHandler);
        }

        @Override
        public void preinitializeContext(Object polyglotEngine) {
            ((PolyglotEngineImpl) polyglotEngine).preInitialize();
        }

        @Override
        public void finalizeStore(Object polyglotEngine) {
            ((PolyglotEngineImpl) polyglotEngine).finalizeStore();
        }

        @Override
        public Object getEngineLock(Object polyglotEngine) {
            return ((PolyglotEngineImpl) polyglotEngine).lock;
        }

        @Override
        public boolean isInternal(FileSystem fs) {
            return FileSystems.isInternal(fs);
        }

        @Override
        public boolean hasAllAccess(FileSystem fs) {
            return FileSystems.hasAllAccess(fs);
        }

        @Override
        public void addToHostClassPath(Object polyglotLanguageContext, TruffleFile entry) {
            HostLanguage.HostContext hostContext = ((PolyglotLanguageContext) polyglotLanguageContext).context.getHostContextImpl();
            hostContext.addToHostClasspath(entry);
        }

        @Override
        public String getLanguageHome(Object engineObject) {
            return ((PolyglotLanguage) engineObject).cache.getLanguageHome();
        }

        @Override
        public boolean isInstrumentExceptionsAreThrown(Object polyglotEngine) {
            // We want to enable this option for testing in general, to ensure tests fail if
            // instruments throw.
            OptionValuesImpl engineOptionValues = getEngine(polyglotEngine).engineOptionValues;
            return areAssertionsEnabled() && !engineOptionValues.hasBeenSet(PolyglotEngineOptions.InstrumentExceptionsAreThrown) ||
                            engineOptionValues.get(PolyglotEngineOptions.InstrumentExceptionsAreThrown);
        }

        @SuppressWarnings("all")
        private static boolean areAssertionsEnabled() {
            boolean assertsEnabled = false;
            // Next assignment will be executed when asserts are enabled.
            assert assertsEnabled = true;
            return assertsEnabled;
        }

        @Override
        public Object createDefaultLoggerCache() {
            return PolyglotLoggers.defaultSPI();
        }

        @Override
        public Handler getLogHandler(Object loggerCache) {
            return ((PolyglotLoggers.LoggerCache) loggerCache).getLogHandler();
        }

        @Override
        public LogRecord createLogRecord(Object loggerCache, Level level, String loggerName, String message, String className, String methodName, Object[] parameters, Throwable thrown) {
            return ((PolyglotLoggers.LoggerCache) loggerCache).createLogRecord(level, loggerName, message, className, methodName, parameters, thrown);
        }

        @Override
        public Object getCurrentOuterContext() {
            return PolyglotLoggers.getCurrentOuterContext();
        }

        @Override
        public Map getLogLevels(final Object loggerCache) {
            return ((PolyglotLoggers.LoggerCache) loggerCache).getLogLevels();
        }

        @Override
        public Object getLoggerOwner(Object loggerCache) {
            return ((PolyglotLoggers.LoggerCache) loggerCache).getEngine();
        }

        @Override
        public Set getLanguageIds() {
            return LanguageCache.languages().keySet();
        }

        @Override
        public Set getInstrumentIds() {
            Set ids = new HashSet<>();
            for (InstrumentCache cache : InstrumentCache.load()) {
                ids.add(cache.getId());
            }
            return ids;
        }

        @Override
        public Set getInternalIds() {
            return PolyglotLoggers.getInternalIds();
        }

        @Override
        public Object asHostObject(Object obj) {
            assert isHostObject(obj);
            return HostObject.valueOf(obj);
        }

        @Override
        public boolean isHostFunction(Object obj) {
            return HostFunction.isInstance(obj);
        }

        @Override
        public boolean isHostObject(Object obj) {
            return HostObject.isInstance(obj);
        }

        @Override
        public boolean isHostSymbol(Object obj) {
            if (HostObject.isHostObjectInstance(obj)) {
                return ((HostObject) obj).isStaticClass();
            }
            return false;
        }

        @Override
        public  S lookupService(Object polyglotLanguageContext, LanguageInfo language, LanguageInfo accessingLanguage, Class type) {
            PolyglotLanguage lang = (PolyglotLanguage) NODES.getPolyglotLanguage(language);
            if (!lang.cache.supportsService(type)) {
                return null;
            }
            PolyglotLanguageContext context = ((PolyglotLanguageContext) polyglotLanguageContext).context.getContext(lang);
            context.ensureCreated((PolyglotLanguage) NODES.getPolyglotLanguage(accessingLanguage));
            return context.lookupService(type);
        }

        @Override
        public TruffleLogger getLogger(Object polyglotInstrument, String loggerName) {
            PolyglotInstrument instrument = (PolyglotInstrument) polyglotInstrument;
            String id = instrument.getId();
            PolyglotEngineImpl engine = getEngine(polyglotInstrument);
            Object loggerCache = engine.getOrCreateEngineLoggers();
            return LANGUAGE.getLogger(id, loggerName, loggerCache);
        }

        @Override
        public Object convertPrimitive(Object value, Class requestedType) {
            return ToHostNode.convertLossLess(value, requestedType, InteropLibrary.getFactory().getUncached());
        }

        @SuppressWarnings("unchecked")
        @Override
        @CompilerDirectives.TruffleBoundary
        public , C> TruffleLanguage.ContextReference lookupContextReference(Object polyglotEngine, TruffleLanguage sourceLanguageSPI,
                        Class targetLanguageClass) {
            assert sourceLanguageSPI == null || sourceLanguageSPI.getClass() != targetLanguageClass;
            PolyglotLanguageInstance instance = ((PolyglotEngineImpl) polyglotEngine).getCurrentLanguageInstance(targetLanguageClass);
            return (TruffleLanguage.ContextReference) instance.lookupContextSupplier(resolveLanguage(sourceLanguageSPI));
        }

        @SuppressWarnings("unchecked")
        @Override
        public , C> TruffleLanguage.ContextReference getDirectContextReference(Object polyglotEngine, TruffleLanguage sourceLanguageSPI,
                        Class targetLanguageClass) {
            assert sourceLanguageSPI == null || sourceLanguageSPI.getClass() == targetLanguageClass;
            return (TruffleLanguage.ContextReference) resolveLanguageInstance(sourceLanguageSPI).getDirectContextSupplier();
        }

        @SuppressWarnings("unchecked")
        @Override
        public > TruffleLanguage.LanguageReference getDirectLanguageReference(Object polyglotEngine, TruffleLanguage sourceLanguageSPI,
                        Class targetLanguageClass) {
            assert sourceLanguageSPI == null || sourceLanguageSPI.getClass() == targetLanguageClass;
            return (TruffleLanguage.LanguageReference) resolveLanguageInstance(sourceLanguageSPI).getDirectLanguageReference();
        }

        @SuppressWarnings("unchecked")
        @Override
        @CompilerDirectives.TruffleBoundary
        public > TruffleLanguage.LanguageReference lookupLanguageReference(Object polyglotEngineImpl, TruffleLanguage sourceLanguageSPI,
                        Class targetLanguageClass) {
            assert sourceLanguageSPI == null || sourceLanguageSPI.getClass() != targetLanguageClass;
            PolyglotLanguageInstance instance = ((PolyglotEngineImpl) polyglotEngineImpl).getCurrentLanguageInstance(targetLanguageClass);
            return (TruffleLanguage.LanguageReference) instance.lookupLanguageSupplier(resolveLanguage(sourceLanguageSPI));
        }

        private static PolyglotLanguageInstance resolveLanguageInstance(TruffleLanguage sourceLanguageSPI) {
            if (sourceLanguageSPI == null) {
                return null;
            }
            return ((PolyglotLanguageInstance) EngineAccessor.LANGUAGE.getPolyglotLanguageInstance(sourceLanguageSPI));
        }

        private static PolyglotLanguage resolveLanguage(TruffleLanguage sourceLanguageSPI) {
            if (sourceLanguageSPI == null) {
                return null;
            }
            return ((PolyglotLanguageInstance) EngineAccessor.LANGUAGE.getPolyglotLanguageInstance(sourceLanguageSPI)).language;
        }

        @Override
        public FileSystem getFileSystem(Object polyglotContext) {
            return ((PolyglotContextImpl) polyglotContext).config.fileSystem;
        }

        @Override
        public int getAsynchronousStackDepth(Object polylgotLanguage) {
            return ((PolyglotLanguage) polylgotLanguage).engine.getAsynchronousStackDepth();
        }

        @Override
        public void setAsynchronousStackDepth(Object polyglotInstrument, int depth) {
            getEngine(polyglotInstrument).setAsynchronousStackDepth((PolyglotInstrument) polyglotInstrument, depth);
        }

        @Override
        public boolean isCreateProcessAllowed(Object polylgotLanguageContext) {
            return ((PolyglotLanguageContext) polylgotLanguageContext).context.config.createProcessAllowed;
        }

        @Override
        public Map getProcessEnvironment(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).context.config.getEnvironment();
        }

        @Override
        public Process createSubProcess(Object polyglotLanguageContext, List cmd, String cwd, Map environment, boolean redirectErrorStream,
                        ProcessHandler.Redirect inputRedirect, ProcessHandler.Redirect outputRedirect, ProcessHandler.Redirect errorRedirect) throws IOException {
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) polyglotLanguageContext;
            OutputStream stdOut = languageContext.getImpl().getIO().getOutputStream(outputRedirect);
            OutputStream stdErr = languageContext.getImpl().getIO().getOutputStream(errorRedirect);
            ProcessHandler.Redirect useOutputRedirect = stdOut == null ? outputRedirect : ProcessHandler.Redirect.PIPE;
            ProcessHandler.Redirect useErrorRedirect = stdErr == null ? errorRedirect : ProcessHandler.Redirect.PIPE;
            ProcessHandler.ProcessCommand command = languageContext.getImpl().getIO().newProcessCommand(cmd, cwd, environment, redirectErrorStream, inputRedirect, useOutputRedirect, useErrorRedirect);
            Process process = languageContext.context.config.processHandler.start(command);
            return ProcessHandlers.decorate(
                            languageContext,
                            cmd,
                            process,
                            stdOut,
                            stdErr);
        }

        @Override
        public boolean hasDefaultProcessHandler(Object polyglotLanguageContext) {
            return ProcessHandlers.isDefault(((PolyglotLanguageContext) polyglotLanguageContext).context.config.processHandler);
        }

        @Override
        public ProcessHandler.Redirect createRedirectToOutputStream(Object polyglotLanguageContext, OutputStream stream) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).getImpl().getIO().createRedirectToStream(stream);
        }

        @Override
        public boolean isIOAllowed() {
            return PolyglotEngineImpl.ALLOW_IO;
        }

        @Override
        public String getUnparsedOptionValue(OptionValues optionValues, OptionKey optionKey) {
            if (!(optionValues instanceof OptionValuesImpl)) {
                throw new IllegalArgumentException(String.format("Only %s is supported.", OptionValuesImpl.class.getName()));
            }
            return ((OptionValuesImpl) optionValues).getUnparsedOptionValue(optionKey);
        }

        @Override
        public String getRelativePathInLanguageHome(TruffleFile truffleFile) {
            return FileSystems.getRelativePathInLanguageHome(truffleFile);
        }

        @Override
        public void onSourceCreated(Source source) {
            PolyglotContextImpl currentContext = PolyglotContextImpl.currentNotEntered();
            if (currentContext != null && currentContext.sourcesToInvalidate != null) {
                currentContext.sourcesToInvalidate.add(source);
            }
        }

        @Override
        public String getReinitializedPath(TruffleFile truffleFile) {
            FileSystem fs = EngineAccessor.LANGUAGE.getFileSystem(truffleFile);
            Path path = EngineAccessor.LANGUAGE.getPath(truffleFile);
            return ((FileSystems.PreInitializeContextFileSystem) fs).pathToString(path);
        }

        @Override
        public URI getReinitializedURI(TruffleFile truffleFile) {
            FileSystem fs = EngineAccessor.LANGUAGE.getFileSystem(truffleFile);
            Path path = EngineAccessor.LANGUAGE.getPath(truffleFile);
            return ((FileSystems.PreInitializeContextFileSystem) fs).absolutePathtoURI(path);
        }

        @Override
        public boolean initializeLanguage(Object polyglotLanguageContext, LanguageInfo targetLanguage) {
            PolyglotLanguage targetPolyglotLanguage = (PolyglotLanguage) NODES.getPolyglotLanguage(targetLanguage);
            PolyglotLanguageContext targetLanguageContext = ((PolyglotLanguageContext) polyglotLanguageContext).context.getContext(targetPolyglotLanguage);
            PolyglotLanguage accessingPolyglotLanguage = ((PolyglotLanguageContext) polyglotLanguageContext).language;
            try {
                targetLanguageContext.checkAccess(accessingPolyglotLanguage);
            } catch (PolyglotEngineException notAccessible) {
                if (notAccessible.e instanceof IllegalArgumentException) {
                    throw new SecurityException(notAccessible.e.getMessage());
                }
                throw notAccessible;
            }
            return targetLanguageContext.ensureInitialized(accessingPolyglotLanguage);
        }

        @Override
        public boolean isHostToGuestRootNode(RootNode rootNode) {
            return rootNode instanceof HostToGuestRootNode;
        }

        @Override
        public AssertionError invalidSharingError(Object polyglotEngine) throws AssertionError {
            return PolyglotReferences.invalidSharingError((PolyglotEngineImpl) polyglotEngine);
        }

        @Override
        public  ContextLocal createInstrumentContextLocal(Object factory) {
            return PolyglotLocals.createInstrumentContextLocal(factory);
        }

        @Override
        public  ContextThreadLocal createInstrumentContextThreadLocal(Object factory) {
            return PolyglotLocals.createInstrumentContextThreadLocal(factory);
        }

        @Override
        public  ContextLocal createLanguageContextLocal(Object factory) {
            return PolyglotLocals.createLanguageContextLocal(factory);
        }

        @Override
        public  ContextThreadLocal createLanguageContextThreadLocal(Object factory) {
            return PolyglotLocals.createLanguageContextThreadLocal(factory);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void initializeInstrumentContextLocal(List> locals, Object polyglotInstrument) {
            PolyglotLocals.initializeInstrumentContextLocals((List>) locals, (PolyglotInstrument) polyglotInstrument);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void initializeInstrumentContextThreadLocal(List> local, Object polyglotInstrument) {
            PolyglotLocals.initializeInstrumentContextThreadLocals((List>) local, (PolyglotInstrument) polyglotInstrument);
        }

        @Override
        public boolean isPolyglotObject(Object polyglotObject) {
            return PolyglotImpl.getInstance() == polyglotObject;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void initializeLanguageContextLocal(List> locals, Object polyglotLanguageInstance) {
            PolyglotLocals.initializeLanguageContextLocals((List>) locals, (PolyglotLanguageInstance) polyglotLanguageInstance);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void initializeLanguageContextThreadLocal(List> local, Object polyglotLanguageInstance) {
            PolyglotLocals.initializeLanguageContextThreadLocals((List>) local, (PolyglotLanguageInstance) polyglotLanguageInstance);
        }

        @Override
        public OptionValues getInstrumentContextOptions(Object polyglotInstrument, Object polyglotContext) {
            PolyglotInstrument instrument = (PolyglotInstrument) polyglotInstrument;
            PolyglotContextImpl context = (PolyglotContextImpl) polyglotContext;
            return context.getInstrumentContextOptions(instrument);
        }

        @Override
        public boolean isContextClosed(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            if (context.invalid && context.closingThread != Thread.currentThread()) {
                return true;
            }
            return context.closed;
        }

        @Override
        public boolean isContextActive(Object polyglotContext) {
            PolyglotContextImpl context = (PolyglotContextImpl) polyglotContext;
            return context.isActive(Thread.currentThread());
        }

        @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
        @Override
        @CompilerDirectives.TruffleBoundary
        public void closeContext(Object impl, boolean force, Node closeLocation, boolean resourceExhaused, String resourceExhausedReason) {
            PolyglotContextImpl context = (PolyglotContextImpl) impl;
            if (force) {
                boolean isActive = isContextActive(context);
                boolean entered = isContextEntered(context);
                if (isActive && !entered) {
                    throw PolyglotEngineException.illegalState(String.format("The context is currently active on the current thread but another different context is entered as top-most context. " +
                                    "Leave or close the top-most context first or close the context on a separate thread to resolve this problem."));
                }
                context.cancel(resourceExhaused, resourceExhausedReason, !entered);
                if (entered) {
                    throw context.createCancelException(closeLocation);
                }
            } else {
                if (context.isActiveNotCancelled(false)) {
                    /*
                     * Polyglot threads are still allowed to run at this point. They are required to
                     * be finished after finalizeContext.
                     */
                    throw new IllegalStateException("The context is currently active and cannot be closed. Make sure no thread is running or call closeCancelled on the context to resolve this.");
                }
                context.closeImpl(false, false, true);
            }
        }

        @Override
        public  Iterator mergeHostGuestFrames(StackTraceElement[] hostStack, Iterator guestFrames, boolean inHostLanguage, Function hostFrameConvertor,
                        Function guestFrameConvertor) {
            return new PolyglotExceptionImpl.MergedHostGuestIterator<>(hostStack, guestFrames, inHostLanguage, hostFrameConvertor, guestFrameConvertor);
        }

        @Override
        public Object createHostAdapterClass(Object polyglotLanguageContext, Class[] types, Object classOverrides) {
            CompilerAsserts.neverPartOfCompilation();
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) polyglotLanguageContext;
            PolyglotEngineImpl engine = languageContext.getEngine();
            HostContext hostContext = languageContext.context.getHostContextImpl();
            AdapterResult adapter = HostAdapterFactory.getAdapterClassFor(engine, hostContext, types, classOverrides);
            if (!adapter.isSuccess()) {
                throw adapter.throwException();
            }
            return asHostSymbol(polyglotLanguageContext, adapter.getAdapterClass());
        }

        @Override
        @SuppressWarnings("deprecation")
        public Iterable findLibraryLocalScopesToLegacy(Node node, Frame frame) {
            return LegacyScopesBridge.findLibraryLocalScopesToLegacy(node, frame);
        }

        @Override
        @SuppressWarnings("deprecation")
        public Iterable topScopesToLegacy(Object scope) {
            return LegacyScopesBridge.topScopesToLegacy(scope);
        }

        @Override
        @SuppressWarnings("deprecation")
        public boolean legacyScopesHasScope(NodeInterface node, Iterator legacyScopes) {
            return LegacyScopesBridge.legacyScopesHasScope(node, legacyScopes);
        }

        @Override
        @SuppressWarnings("deprecation")
        public Object legacyScopes2ScopeObject(NodeInterface node, Iterator legacyScopes, Class> language) {
            return LegacyScopesBridge.legacyScopes2ScopeObject(node, legacyScopes, language);
        }

    }

    abstract static class AbstractClassLoaderSupplier implements Supplier {

        private final int hashCode;

        AbstractClassLoaderSupplier(ClassLoader loader) {
            this.hashCode = loader == null ? 0 : loader.hashCode();
        }

        @Override
        public final int hashCode() {
            return hashCode;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (!(obj instanceof AbstractClassLoaderSupplier)) {
                return false;
            }
            AbstractClassLoaderSupplier supplier = (AbstractClassLoaderSupplier) obj;
            return Objects.equals(get(), supplier.get());
        }
    }

    static final class StrongClassLoaderSupplier extends AbstractClassLoaderSupplier {

        private final ClassLoader classLoader;

        StrongClassLoaderSupplier(ClassLoader classLoader) {
            super(classLoader);
            this.classLoader = classLoader;
        }

        @Override
        public ClassLoader get() {
            return classLoader;
        }
    }

    private static final class WeakClassLoaderSupplier extends AbstractClassLoaderSupplier {

        private final Reference classLoaderRef;

        WeakClassLoaderSupplier(ClassLoader classLoader) {
            super(classLoader);
            this.classLoaderRef = new WeakReference<>(classLoader);
        }

        @Override
        public ClassLoader get() {
            return classLoaderRef.get();
        }
    }

}