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, 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 static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
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.HashMap;
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.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.options.OptionKey;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.EnvironmentAccess;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.impl.AbstractPolyglotImpl;
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.ThreadLocalAction;
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.ContextReference;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
import com.oracle.truffle.api.TruffleLanguage.Registration;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.TruffleSafepoint;
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.EncapsulatingNodeReference;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
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.PolyglotImpl.EmbedderFileSystemContext;
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.PolyglotThread.ThreadSpawnRootNode;
import com.oracle.truffle.polyglot.SystemThread.InstrumentSystemThread;
import com.oracle.truffle.polyglot.SystemThread.LanguageSystemThread;

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 InteropSupport INTEROP = ACCESSOR.interopSupport();
    static final ExceptionSupport EXCEPTION = ACCESSOR.exceptionSupport();
    static final RuntimeSupport RUNTIME = ACCESSOR.runtimeSupport();
    static final HostSupport HOST = ACCESSOR.hostSupport();

    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 boolean hasCurrentContext() {
            return PolyglotFastThreadLocals.getContext(null) != 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, Object value) {
            return new DefaultLanguageView<>(truffleLanguage, value);
        }

        @TruffleBoundary
        @Override
        public Object getLanguageView(LanguageInfo viewLanguage, Object value) {
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            PolyglotLanguage language = context.engine.findLanguage(viewLanguage);
            PolyglotLanguageContext languageContext = context.getContextInitialized(language, null);
            return languageContext.getLanguageView(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 (PolyglotFastThreadLocals.getContext(null) != 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) {
            return PolyglotImpl.getPolyglotSourceSection(((VMObject) polyglotObject).getImpl(), 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)) {
                    ModuleUtils.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) {
            if (!((LanguageCache) NODES.getLanguageCache(info)).supportsService(serviceClass)) {
                return null;
            }
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            PolyglotLanguage language = context.engine.findLanguage(info);
            PolyglotLanguageContext languageContext = context.getContext(language);
            languageContext.ensureCreated(language);
            return languageContext.lookupService(serviceClass);
        }

        @SuppressWarnings("unchecked")
        @Override
        public > C getCurrentContext(Class languageClass) {
            CompilerAsserts.partialEvaluationConstant(languageClass);
            int index = PolyglotFastThreadLocals.computePELanguageIndex(languageClass, PolyglotFastThreadLocals.LANGUAGE_CONTEXT_OFFSET);
            CompilerAsserts.partialEvaluationConstant(index);
            Object contextImpl = PolyglotFastThreadLocals.getLanguageContext(null, index);
            if (contextImpl == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw PolyglotEngineException.illegalState("There is no current context available.");
            }
            return (C) contextImpl;
        }

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

        @Override
        public Object getHostContext(Object polyglotContext) {
            return ((PolyglotContextImpl) polyglotContext).getHostContextImpl();
        }

        @Override
        public Value asValue(Object polyglotContextImpl, Object guestValue) {
            return ((PolyglotContextImpl) polyglotContextImpl).getHostContext().asValue(guestValue);
        }

        @Override
        public Object enterLanguageFromRuntime(TruffleLanguage language) {
            PolyglotLanguageInstance instance = (PolyglotLanguageInstance) EngineAccessor.LANGUAGE.getPolyglotLanguageInstance(language);
            return PolyglotFastThreadLocals.enterLanguage(instance);
        }

        @Override
        public void leaveLanguageFromRuntime(TruffleLanguage language, Object prev) {
            PolyglotLanguageInstance instance = (PolyglotLanguageInstance) EngineAccessor.LANGUAGE.getPolyglotLanguageInstance(language);
            PolyglotFastThreadLocals.leaveLanguage(instance, prev);
        }

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

        @SuppressWarnings("unchecked")
        @Override
        public > T getCurrentLanguage(Class languageClass) {
            CompilerAsserts.partialEvaluationConstant(languageClass);
            int index = PolyglotFastThreadLocals.computePELanguageIndex(languageClass, PolyglotFastThreadLocals.LANGUAGE_SPI_OFFSET);
            CompilerAsserts.partialEvaluationConstant(index);
            T language = (T) PolyglotFastThreadLocals.getLanguage(null, index, languageClass);
            if (language == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw PolyglotEngineException.illegalState("There is no current context available.");
            }
            return language;
        }

        @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) {
            PolyglotContextImpl context = PolyglotContextImpl.requireContext();
            PolyglotLanguage language = context.engine.findLanguage(info);
            return context.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 Object getCurrentSharingLayer() {
            PolyglotContextImpl context = PolyglotFastThreadLocals.getContext(null);
            if (context == null) {
                return null;
            }
            return context.layer;
        }

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

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

        @Override
        public boolean isEvalRoot(RootNode target) {
            // TODO GR-38632 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
        public Object getInstrumentationHandler(RootNode rootNode) {
            PolyglotSharingLayer sharing = (PolyglotSharingLayer) NODES.getSharingLayer(rootNode);
            if (sharing == null) {
                return null;
            }
            return getInstrumentationHandler(sharing.engine);
        }

        @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);
            }
            return null;
        }

        @Override
        public Object lookupHostSymbol(Object polyglotLanguageContext, TruffleLanguage.Env env, String symbolName) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) polyglotLanguageContext).context;
            if (!context.config.hostLookupAllowed) {
                context.engine.host.throwHostLanguageException("Host class access is not allowed.");
            }
            return context.engine.host.findStaticClass(context.getHostContextImpl(), symbolName);
        }

        @Override
        public Object asHostSymbol(Object polyglotLanguageContext, Class symbolClass) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) polyglotLanguageContext).context;
            return context.engine.host.asHostStaticClass(context.getHostContextImpl(), symbolClass);
        }

        @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 isIOAllowed(Object polyglotLanguageContext, Env env) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            return context.context.config.isAllowIO();
        }

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

        @Override
        public boolean isCurrentNativeAccessAllowed(Node node) {
            PolyglotContextImpl context = PolyglotFastThreadLocals.getContext(PolyglotFastThreadLocals.resolveLayer(node));
            if (context == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalStateException("No current context entered.");
            }
            return 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 = PolyglotFastThreadLocals.getContext(null);
            return currentContext.getPolyglotBindings().as(Map.class);
        }

        @Override
        public Object getPolyglotBindingsObject() {
            PolyglotContextImpl currentContext = PolyglotFastThreadLocals.getContext(null);
            return currentContext.getPolyglotBindingsObject();
        }

        @Override
        public Object toGuestValue(Node node, Object obj, Object languageContext) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.toGuestValue(node, obj, false);
        }

        @Override
        public Object asBoxedGuestValue(Object guestObject, Object polyglotLanguageContext) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) polyglotLanguageContext).context;
            if (PolyglotImpl.isGuestPrimitive(guestObject)) {
                return context.engine.host.toHostObject(context.getHostContextImpl(), guestObject);
            } else if (guestObject instanceof TruffleObject) {
                return guestObject;
            } else {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException("Provided value not an interop value.");
            }
        }

        @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 location, Object polyglotLanguageContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotLanguageContext);
            PolyglotEngineImpl engine = resolveEngine(location, context);
            return engine.enter(context);
        }

        @Override
        public Object[] enterContextAsPolyglotThread(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            return context.enterThreadChanged(true, false, true, false, true);
        }

        @Override
        public void leaveContextAsPolyglotThread(Object polyglotContext, Object[] prev) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            context.leaveThreadChanged(prev, true, true, true);
        }

        @Override
        public Object enterIfNeeded(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            try {
                return context.engine.enterIfNeeded(context, true);
            } catch (Throwable t) {
                throw PolyglotImpl.guestToHostException(context.getHostContext(), t, false);
            }
        }

        @Override
        public void leaveIfNeeded(Object polyglotContext, Object prev) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            try {
                context.engine.leaveIfNeeded(prev, context);
            } catch (Throwable t) {
                throw PolyglotImpl.guestToHostException(context.getHostContext(), t, false);
            }
        }

        @Override
        public boolean initializeInnerContext(Node location, Object polyglotContext, String languageId, boolean allowInternal) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            if (context.parent == null) {
                throw PolyglotEngineException.illegalState("Only created inner contexts can be used to initialize language contexts. " +
                                "Use TruffleLanguage.Env.initializeLanguage(LanguageInfo) instead.");
            }
            PolyglotEngineImpl engine = resolveEngine(location, context);
            try {
                Object[] prev = engine.enter(context);
                try {
                    PolyglotLanguage language = engine.requireLanguage(languageId, allowInternal);
                    PolyglotLanguageContext targetLanguageContext = context.getContext(language);
                    return targetLanguageContext.ensureInitialized(null);
                } finally {
                    engine.leave(prev, context);
                }
            } catch (Throwable t) {
                throw OtherContextGuestObject.toHostOrInnerContextBoundaryException(context.parent, t, context);
            }
        }

        @Override
        public Object evalInternalContext(Node location, Object polyglotContext, Source source, boolean allowInternal) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            if (context.parent == null) {
                throw PolyglotEngineException.illegalState("Only created inner contexts can be used to evaluate sources. " +
                                "Use TruffleLanguage.Env.parseInternal(Source) or TruffleInstrument.Env.parse(Source) instead.");
            }
            PolyglotEngineImpl engine = resolveEngine(location, context);
            try {
                Object[] prev = engine.enter(context);
                try {
                    return evalBoundary(source, prev, context, allowInternal);
                } finally {
                    engine.leave(prev, context);
                }
            } catch (Throwable t) {
                throw OtherContextGuestObject.toHostOrInnerContextBoundaryException(context.parent, t, context);
            }
        }

        @TruffleBoundary
        private static Object evalBoundary(Source source, Object[] prev, PolyglotContextImpl context, boolean allowInternal) {
            PolyglotContextImpl parentEnteredContext = (PolyglotContextImpl) prev[PolyglotFastThreadLocals.CONTEXT_INDEX];
            if (parentEnteredContext != null && parentEnteredContext != context.parent && parentEnteredContext.engine == context.engine) {
                throw PolyglotEngineException.illegalState("Invalid parent context entered. " +
                                "The parent creator context or no context must be entered to evaluate code in an inner context.");
            }
            PolyglotLanguageContext targetContext = context.getContext(context.engine.requireLanguage(source.getLanguage(), allowInternal));
            PolyglotLanguage accessingLanguage = context.creator;
            targetContext.checkAccess(accessingLanguage);
            Object result;
            try {
                CallTarget target = targetContext.parseCached(accessingLanguage, source, null);
                result = target.call(PolyglotImpl.EMPTY_ARGS);
            } catch (Throwable e) {
                throw OtherContextGuestObject.migrateException(context.parent, e, context);
            }
            assert InteropLibrary.isValidValue(result) : "invalid call target return value";
            return context.parent.migrateValue(result, 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((Object[]) prev, context);
            } else {
                leaveInternalContextBoundary(prev, context, engine);
            }
        }

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

        private static PolyglotEngineImpl resolveEngine(Node node, PolyglotContextImpl context) {
            PolyglotEngineImpl engine;
            if (CompilerDirectives.inCompiledCode() && 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);
                PolyglotSharingLayer sharing = (PolyglotSharingLayer) EngineAccessor.NODES.getSharingLayer(root);
                engine = sharing.engine;
                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 PolyglotFastThreadLocals.getContext(null) == impl;
        }

        @Override
        public TruffleContext createInternalContext(Object sourcePolyglotLanguageContext,
                        OutputStream out, OutputStream err, InputStream in, ZoneId timeZone,
                        String[] onlyLanguagesArray, Map config, Map options, Map arguments,
                        Boolean sharingEnabled, boolean initializeCreatorContext, Runnable onCancelledRunnable,
                        Consumer onExitedRunnable, Runnable onClosedRunnable, boolean inheritAccess, Boolean allowCreateThreads,
                        Boolean allowNativeAccess, Boolean allowIO, Boolean allowHostLookup, Boolean allowHostClassLoading,
                        Boolean allowCreateProcess, Boolean allowPolyglotAccess, Boolean allowEnvironmentAccess,
                        Map customEnvironment, Boolean allowInnerContextOptions) {
            PolyglotLanguageContext creator = ((PolyglotLanguageContext) sourcePolyglotLanguageContext);
            PolyglotEngineImpl engine = creator.context.engine;
            PolyglotContextConfig creatorConfig = creator.context.config;
            PolyglotContextImpl impl;
            Set allowedLanguages;
            if (onlyLanguagesArray.length == 0) {
                allowedLanguages = creatorConfig.onlyLanguages;
            } else {
                allowedLanguages = new HashSet<>();
                allowedLanguages.addAll(Arrays.asList(onlyLanguagesArray));
                if (initializeCreatorContext) {
                    allowedLanguages.add(creator.language.getId());
                }
                for (String language : allowedLanguages) {
                    if (!creatorConfig.allowedPublicLanguages.contains(language)) {
                        throw PolyglotEngineException.illegalArgument(String.format(
                                        "The language %s permitted for the created inner context is not installed or was not permitted by the parent context. " + //
                                                        "The parent context only permits the use of the following languages: %s. " + //
                                                        "Ensure the context has access to the permitted language or remove the language from the permitted language list.",
                                        language, creatorConfig.allowedPublicLanguages.toString(), language));
                    }
                }
            }

            Map useOptions;
            if (options == null) {
                useOptions = creatorConfig.originalOptions;
            } else {
                useOptions = new HashMap<>(creatorConfig.originalOptions);
                useOptions.putAll(options);
            }

            if (options != null && !options.isEmpty() && !creatorConfig.innerContextOptionsAllowed) {
                throw PolyglotEngineException.illegalArgument(String.format(
                                "Language options were specified for the inner context but the outer context does not have the required context options privilege for this operation. " +
                                                "Use TruffleLanguage.Env.isInnerContextOptionsAllowed() to check whether the inner context has this privilege. " +
                                                "Use Context.Builder.allowInnerContextOptions(true) to grant inner context option privilege for inner contexts."));
            }

            OutputStream useOut = out;
            if (useOut == null) {
                useOut = creatorConfig.out;
            } else {
                useOut = INSTRUMENT.createDelegatingOutput(out, engine.out);
            }

            OutputStream useErr = err;
            if (useErr == null) {
                useErr = creatorConfig.err;
            } else {
                useErr = INSTRUMENT.createDelegatingOutput(useErr, engine.out);
            }
            final InputStream useIn = in == null ? creatorConfig.in : in;

            boolean useAllowCreateThread = inheritAccess(inheritAccess, allowCreateThreads, creatorConfig.createThreadAllowed);
            boolean useAllowNativeAccess = inheritAccess(inheritAccess, allowNativeAccess, creatorConfig.nativeAccessAllowed);

            FileSystem useFileSystem;
            FileSystem useInternalFileSystem;
            if (inheritAccess(inheritAccess, allowIO, true)) {
                useFileSystem = creatorConfig.fileSystem;
                useInternalFileSystem = creatorConfig.internalFileSystem;
            } else {
                useFileSystem = FileSystems.newNoIOFileSystem();
                useInternalFileSystem = PolyglotEngineImpl.ALLOW_IO ? FileSystems.newLanguageHomeFileSystem() : useFileSystem;
            }

            /*
             * We currently only support one host access configuration per engine. So we need to
             * inherit the host access configuration from the creator. Exposing host values is
             * explicit anyway, by exposing a value to the inner context.
             */
            HostAccess useAllowHostAccess = creatorConfig.hostAccess;

            boolean useAllowHostClassLoading = inheritAccess(inheritAccess, allowHostClassLoading, creatorConfig.hostClassLoadingAllowed);
            boolean useAllowHostLookup = inheritAccess(inheritAccess, allowHostLookup, creatorConfig.hostLookupAllowed);
            Predicate useClassFilter;
            if (useAllowHostLookup) {
                useClassFilter = creatorConfig.classFilter;
            } else {
                useClassFilter = null;
            }

            boolean useAllowCreateProcess = inheritAccess(inheritAccess, allowCreateProcess, creatorConfig.createProcessAllowed);
            ProcessHandler useProcessHandler;
            if (useAllowCreateProcess) {
                useProcessHandler = creatorConfig.processHandler;
            } else {
                useProcessHandler = null;
            }

            PolyglotAccess usePolyglotAccess;
            if (inheritAccess(inheritAccess, allowPolyglotAccess, true)) {
                usePolyglotAccess = creatorConfig.polyglotAccess;
            } else {
                usePolyglotAccess = PolyglotAccess.NONE;
            }

            EnvironmentAccess useEnvironmentAccess;
            if (inheritAccess(inheritAccess, allowEnvironmentAccess, true)) {
                useEnvironmentAccess = creatorConfig.environmentAccess;
            } else {
                useEnvironmentAccess = EnvironmentAccess.NONE;
            }

            Map useCustomEnvironment;
            if (useEnvironmentAccess == EnvironmentAccess.INHERIT && !creatorConfig.customEnvironment.isEmpty()) {
                useCustomEnvironment = new HashMap<>(creatorConfig.customEnvironment);
                if (customEnvironment != null) {
                    useCustomEnvironment.putAll(customEnvironment);
                }
            } else {
                useCustomEnvironment = customEnvironment;
            }

            boolean useAllowInnerContextOptions = inheritAccess(inheritAccess, allowInnerContextOptions, creatorConfig.innerContextOptionsAllowed);

            ZoneId useTimeZone = timeZone == null ? creatorConfig.timeZone : timeZone;

            Map useArguments;
            if (arguments == null) {
                // change: application arguments are not inherited by default
                useArguments = Collections.emptyMap();
            } else {
                useArguments = arguments;
            }

            PolyglotContextConfig innerConfig = new PolyglotContextConfig(engine, sharingEnabled, useOut, useErr, useIn,
                            useAllowHostLookup, usePolyglotAccess, useAllowNativeAccess, useAllowCreateThread, useAllowHostClassLoading,
                            useAllowInnerContextOptions, creatorConfig.allowExperimentalOptions,
                            useClassFilter, useArguments, allowedLanguages, useOptions, useFileSystem, useInternalFileSystem, creatorConfig.logHandler,
                            useAllowCreateProcess, useProcessHandler, useEnvironmentAccess, useCustomEnvironment,
                            useTimeZone, creatorConfig.limits, creatorConfig.hostClassLoader, useAllowHostAccess,
                            creatorConfig.allowValueSharing, false,
                            config, onCancelledRunnable, onExitedRunnable, onClosedRunnable);

            synchronized (creator.context) {
                impl = new PolyglotContextImpl(creator, innerConfig);
                impl.api = creator.getImpl().getAPIAccess().newContext(creator.getImpl().contextDispatch, impl, creator.context.engine.api);
                creator.context.addChildContext(impl);
            }

            synchronized (impl) {
                impl.initializeContextLocals();
                impl.notifyContextCreated();
            }
            if (initializeCreatorContext) {
                impl.initializeInnerContextLanguage(creator.language.getId());
            }
            return impl.creatorTruffleContext;
        }

        private static boolean inheritAccess(boolean inheritAccess, Boolean newPrivilege, boolean creatorPrivilege) {
            if (newPrivilege != null) {
                /*
                 * Explicitly set privilege in the context builder. Still we need to respect the
                 * creator privilege.
                 */
                return newPrivilege && creatorPrivilege;
            } else {
                /*
                 * Not set privilege we can inherit the creator privilege if that is enabled.
                 */
                return inheritAccess && creatorPrivilege;
            }
        }

        @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) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.toHostException(context.getHostContextImpl(), exception);
        }

        @Override
        public boolean isHostException(Object languageContext, Throwable exception) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.isHostException(exception);
        }

        @Override
        public Throwable asHostException(Object languageContext, Throwable exception) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            Throwable host = context.engine.host.unboxHostException(exception);
            if (host == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException("Provided value not a host exception.");
            }
            return host;
        }

        @Override
        public Object getCurrentHostContext() {
            PolyglotContextImpl polyglotContext = PolyglotFastThreadLocals.getContext(null);
            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 = PolyglotFastThreadLocals.getContext(null);
            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, true);
        }

        @Override
        public PolyglotException wrapGuestException(Object polyglotObject, Throwable e) {
            if (polyglotObject instanceof PolyglotContextImpl) {
                PolyglotContextImpl polyglotContext = (PolyglotContextImpl) polyglotObject;
                PolyglotLanguageContext hostLanguageContext = polyglotContext.getHostContext();
                if (polyglotContext.state.isInvalidOrClosed()) {
                    return PolyglotImpl.guestToHostException(hostLanguageContext, e, false);
                } else {
                    PolyglotLanguage language = polyglotContext.getHostContext().language;
                    PolyglotLanguageContext languageContext = polyglotContext.getContextInitialized(language, null);
                    return PolyglotImpl.guestToHostException(languageContext, e, true);
                }
            } else if (polyglotObject instanceof PolyglotEngineImpl) {
                return PolyglotImpl.guestToHostException((PolyglotEngineImpl) polyglotObject, e);
            } else {
                return PolyglotImpl.guestToHostException((PolyglotImpl) polyglotObject, e);
            }
        }

        @Override
        public Set> getProvidedTags(LanguageInfo language) {
            return ((LanguageCache) NODES.getLanguageCache(language)).getProvidedTags();
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T getOrCreateRuntimeData(Object layer) {
            PolyglotSharingLayer useLayer = ((PolyglotSharingLayer) layer);
            PolyglotEngineImpl engine = useLayer != null ? useLayer.engine : null;
            if (engine == null) {
                engine = PolyglotEngineImpl.getFallbackEngine();
            }
            return (T) engine.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 boolean hasNoAccess(FileSystem fs) {
            return FileSystems.hasNoAccess(fs);
        }

        @Override
        public boolean isInternal(TruffleFile file) {
            Object fsContext = EngineAccessor.LANGUAGE.getFileSystemContext(file);
            Object engineObject = EngineAccessor.LANGUAGE.getFileSystemEngineObject(fsContext);
            if (engineObject instanceof PolyglotLanguageContext) {
                return fsContext == ((PolyglotLanguageContext) engineObject).getInternalFileSystemContext();
            } else {
                // embedder sources are never internal
                return false;
            }
        }

        @Override
        public void addToHostClassPath(Object polyglotLanguageContext, TruffleFile entry) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) polyglotLanguageContext).context;
            if (!context.config.hostLookupAllowed) {
                context.engine.host.throwHostLanguageException("Host class access is not allowed.");
            }
            if (!context.config.hostClassLoadingAllowed) {
                context.engine.host.throwHostLanguageException("Host class loading is not allowed.");
            }
            context.engine.host.addToHostClassPath(context.getHostContextImpl(), entry);
        }

        @Override
        public String getLanguageHome(LanguageInfo languageInfo) {
            return ((LanguageCache) NODES.getLanguageCache(languageInfo)).getLanguageHome();
        }

        @Override
        public boolean isInstrumentExceptionsAreThrown(Object polyglotInstrument) {
            // We want to enable this option for testing in general, to ensure tests fail if
            // instruments throw.

            OptionValuesImpl engineOptionValues = getEngine(polyglotInstrument).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.LoggerCache.DEFAULT;
        }

        @Override
        public Object getContextLoggerCache(Object polyglotLanguageContext) {
            return ((PolyglotLanguageContext) polyglotLanguageContext).context.getOrCreateContextLoggers();
        }

        @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 getOuterContext(Object polyglotContext) {
            return getOuterContext((PolyglotContextImpl) polyglotContext);
        }

        static PolyglotContextImpl getOuterContext(PolyglotContextImpl context) {
            PolyglotContextImpl res = context;
            if (res != null) {
                while (res.parent != null) {
                    res = res.parent;
                }
            }
            return res;
        }

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

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

        @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 languageContext, Object obj) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            assert isHostObject(languageContext, obj);
            return context.engine.host.unboxHostObject(obj);
        }

        @Override
        public boolean isHostFunction(Object languageContext, Object obj) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.isHostFunction(obj);
        }

        @Override
        public boolean isHostObject(Object languageContext, Object obj) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.isHostObject(obj);
        }

        @Override
        public boolean isHostSymbol(Object languageContext, Object obj) {
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.isHostSymbol(obj);
        }

        @Override
        public  S lookupService(Object polyglotLanguageContext, LanguageInfo language, LanguageInfo accessingLanguage, Class type) {
            PolyglotLanguage lang = ((PolyglotLanguageContext) polyglotLanguageContext).context.engine.findLanguage(language);
            if (!lang.cache.supportsService(type)) {
                return null;
            }
            PolyglotLanguageContext context = ((PolyglotLanguageContext) polyglotLanguageContext).context.getContext(lang);
            PolyglotLanguage accessingLang = ((PolyglotLanguageContext) polyglotLanguageContext).context.engine.findLanguage(accessingLanguage);
            context.ensureCreated(accessingLang);
            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 , C> ContextReference createContextReference(Class languageClass) {
            return PolyglotFastThreadLocals.createContextReference(languageClass);
        }

        @SuppressWarnings("unchecked")
        @Override
        public > LanguageReference createLanguageReference(Class targetLanguageClass) {
            return (TruffleLanguage.LanguageReference) PolyglotFastThreadLocals.createLanguageReference(targetLanguageClass);
        }

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

        @Override
        public int getAsynchronousStackDepth(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).getEngine().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) {
            PolyglotLanguageContext context = (PolyglotLanguageContext) polyglotLanguageContext;
            return context.getImpl().getRootImpl().isDefaultProcessHandler(context.context.config.processHandler);
        }

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

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

        @Override
        public boolean isCreateProcessSupported() {
            return PolyglotEngineImpl.ALLOW_CREATE_PROCESS;
        }

        @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 = PolyglotFastThreadLocals.getContext(null);
            if (currentContext != null && currentContext.sourcesToInvalidate != null) {
                currentContext.sourcesToInvalidate.add(source);
            }
        }

        @Override
        public void registerOnDispose(Object engineObject, Closeable closeable) {
            if (engineObject instanceof PolyglotLanguageContext) {
                ((PolyglotLanguageContext) engineObject).context.registerOnDispose(closeable);
            } else {
                throw CompilerDirectives.shouldNotReachHere("EngineObject must be PolyglotLanguageContext.");
            }
        }

        @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 = ((PolyglotLanguageContext) polyglotLanguageContext).context.engine.findLanguage(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 skipEngineValidation(RootNode rootNode) {
            return rootNode instanceof HostToGuestRootNode || rootNode instanceof ThreadSpawnRootNode;
        }

        @Override
        public AssertionError invalidSharingError(Node node, Object previousSharingLayer, Object newSharingLayer) throws AssertionError {
            return PolyglotSharingLayer.invalidSharingError(node, (PolyglotSharingLayer) previousSharingLayer, (PolyglotSharingLayer) newSharingLayer);
        }

        @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);
            PolyglotContextImpl.State localContextState = context.state;
            return localContextState.isInvalidOrClosed();
        }

        @Override
        public boolean isContextCancelling(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            PolyglotContextImpl.State localContextState = context.state;
            return localContextState.isCancelling();
        }

        @Override
        public boolean isContextExiting(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            PolyglotContextImpl.State contextState = context.state;
            return contextState.isExiting();
        }

        @Override
        public Future pause(Object polyglotContext) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            return context.pause();
        }

        @Override
        public void resume(Object polyglotContext, Future pauseFuture) {
            PolyglotContextImpl context = ((PolyglotContextImpl) polyglotContext);
            context.resume(pauseFuture);
        }

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

        @Override
        public void clearExplicitContextStack(Object polyglotContext) {
            PolyglotContextImpl context = (PolyglotContextImpl) polyglotContext;
            context.clearExplicitContextStack();
        }

        @Override
        public void initiateCancelOrExit(Object polyglotContext, boolean exit, int exitCode, boolean resourceLimit, String message) {
            PolyglotContextImpl context = (PolyglotContextImpl) polyglotContext;
            context.initiateCancelOrExit(exit, exitCode, resourceLimit, message);
        }

        @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);
                if (entered) {
                    TruffleSafepoint.pollHere(closeLocation != null ? closeLocation : context.uncachedLocation);
                }
            } else {
                synchronized (context) {
                    /*
                     * Closing the context on another thread done as a part of cancelling or exiting
                     * enters the context which could lead to the following IllegalStateException if
                     * the cancelling flag was not checked.
                     */
                    PolyglotContextImpl.State localContextState = context.state;
                    if (context.isActiveNotCancelled(false) && !localContextState.isCancelling() && !localContextState.isExiting()) {
                        /*
                         * 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(true);
                context.finishCleanup();
            }
        }

        @Override
        @CompilerDirectives.TruffleBoundary
        public void closeContext(Object impl, boolean force, boolean resourceExhaused, String resourceExhausedReason) {
            PolyglotContextImpl context = (PolyglotContextImpl) impl;
            if (force) {
                context.cancel(resourceExhaused, resourceExhausedReason);
            } else {
                context.closeAndMaybeWait(false, null);
            }
        }

        @Override
        public void closeEngine(Object polyglotEngine, boolean force) {
            PolyglotEngineImpl engine = (PolyglotEngineImpl) polyglotEngine;
            engine.ensureClosed(force, false);
        }

        @Override
        public  Iterator mergeHostGuestFrames(Object instrumentEnv, StackTraceElement[] hostStack, Iterator guestFrames, boolean inHostLanguage,
                        Function hostFrameConvertor,
                        Function guestFrameConvertor) {
            PolyglotInstrument instrument = (PolyglotInstrument) INSTRUMENT.getPolyglotInstrument(instrumentEnv);
            return new PolyglotExceptionImpl.MergedHostGuestIterator<>(instrument.engine, hostStack, guestFrames, inHostLanguage, hostFrameConvertor, guestFrameConvertor);
        }

        @Override
        public Object createHostAdapterClass(Object languageContext, Object[] types, Object classOverrides) {
            CompilerAsserts.neverPartOfCompilation();
            PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context;
            return context.engine.host.createHostAdapter(context.getHostContextImpl(), types, classOverrides);
        }

        @Override
        public long calculateContextHeapSize(Object polyglotContext, long stopAtBytes, AtomicBoolean cancelled) {
            return ((PolyglotContextImpl) polyglotContext).calculateHeapSize(stopAtBytes, cancelled);
        }

        @Override
        public Future submitThreadLocal(Object polyglotContext, Object sourcePolyglotObject, Thread[] threads, ThreadLocalAction action, boolean needsEnter) {
            String componentId;
            if (sourcePolyglotObject instanceof PolyglotInstrument) {
                componentId = ((PolyglotInstrument) sourcePolyglotObject).getId();
            } else if (sourcePolyglotObject instanceof PolyglotLanguageContext) {
                componentId = ((PolyglotLanguageContext) sourcePolyglotObject).language.getId();
            } else {
                throw CompilerDirectives.shouldNotReachHere("Invalid source component");
            }
            return ((PolyglotContextImpl) polyglotContext).threadLocalActions.submit(threads, componentId, action, needsEnter);
        }

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

        @Override
        public ClassLoader getStaticObjectClassLoader(Object polyglotLanguageInstance, Class referenceClass) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).staticObjectClassLoaders.get(referenceClass);
        }

        @Override
        public void setStaticObjectClassLoader(Object polyglotLanguageInstance, Class referenceClass, ClassLoader cl) {
            ((PolyglotLanguageInstance) polyglotLanguageInstance).staticObjectClassLoaders.put(referenceClass, cl);
        }

        @Override
        public ConcurrentHashMap, Class>, Object> getGeneratorCache(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).generatorCache;
        }

        @Override
        public boolean areStaticObjectSafetyChecksRelaxed(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).getEngine().getEngineOptionValues().get(PolyglotEngineOptions.RelaxStaticObjectSafetyChecks);
        }

        @Override
        public String getStaticObjectStorageStrategy(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).getEngine().getEngineOptionValues().get(PolyglotEngineOptions.StaticObjectStorageStrategy).name();
        }

        @Override
        public void exitContext(Object impl, Node exitLocation, int exitCode) {
            PolyglotContextImpl context = ((PolyglotContextImpl) impl);
            context.closeExited(exitLocation, exitCode);
        }

        @Override
        public Throwable getPolyglotExceptionCause(Object polyglotExceptionImpl) {
            return ((PolyglotExceptionImpl) polyglotExceptionImpl).exception;
        }

        @Override
        public Object getPolyglotExceptionContext(Object polyglotExceptionImpl) {
            return ((PolyglotExceptionImpl) polyglotExceptionImpl).context;
        }

        @Override
        public Object getPolyglotExceptionEngine(Object polyglotExceptionImpl) {
            return ((PolyglotExceptionImpl) polyglotExceptionImpl).engine;
        }

        @Override
        public boolean isCancelExecution(Throwable throwable) {
            return throwable instanceof PolyglotEngineImpl.CancelExecution;
        }

        @Override
        public boolean isExitException(Throwable throwable) {
            return throwable instanceof PolyglotContextImpl.ExitException;
        }

        @Override
        public boolean isInterruptExecution(Throwable throwable) {
            return throwable instanceof PolyglotEngineImpl.InterruptExecution;
        }

        @Override
        public boolean isResourceLimitCancelExecution(Throwable cancelExecution) {
            return ((PolyglotEngineImpl.CancelExecution) cancelExecution).isResourceLimit();
        }

        @Override
        public boolean isPolyglotEngineException(Throwable throwable) {
            return throwable instanceof PolyglotEngineException;
        }

        @Override
        public RuntimeException getPolyglotEngineExceptionCause(Throwable polyglotEngineException) {
            return ((PolyglotEngineException) polyglotEngineException).e;
        }

        @Override
        public RuntimeException createPolyglotEngineException(RuntimeException cause) {
            return new PolyglotEngineException(cause);
        }

        @Override
        public int getExitExceptionExitCode(Throwable exitException) {
            return ((PolyglotContextImpl.ExitException) exitException).getExitCode();
        }

        @Override
        public SourceSection getCancelExecutionSourceLocation(Throwable cancelExecution) {
            return ((PolyglotEngineImpl.CancelExecution) cancelExecution).getSourceLocation();
        }

        @Override
        public ThreadDeath createCancelExecution(SourceSection sourceSection, String message, boolean resourceLimit) {
            return new PolyglotEngineImpl.CancelExecution(sourceSection, message, resourceLimit);
        }

        @Override
        public SourceSection getExitExceptionSourceLocation(Throwable exitException) {
            return ((PolyglotContextImpl.ExitException) exitException).getSourceLocation();
        }

        @Override
        public ThreadDeath createExitException(SourceSection sourceSection, String message, int exitCode) {
            return new PolyglotContextImpl.ExitException(sourceSection, exitCode, message);
        }

        @Override
        public Throwable createInterruptExecution(SourceSection sourceSection) {
            return new PolyglotEngineImpl.InterruptExecution(sourceSection);
        }

        @Override
        public Map readOptionsFromSystemProperties(Map options) {
            return PolyglotEngineImpl.readOptionsFromSystemProperties(options);
        }

        @Override
        public AbstractPolyglotImpl.AbstractHostLanguageService getHostService(Object polyglotEngineImpl) {
            assert polyglotEngineImpl instanceof PolyglotEngineImpl;
            return ((PolyglotEngineImpl) polyglotEngineImpl).host;
        }

        @Override
        public Handler getEngineLogHandler(Object polyglotEngineImpl) {
            return ((PolyglotEngineImpl) polyglotEngineImpl).logHandler;
        }

        @Override
        public Handler getContextLogHandler(Object polyglotContextImpl) {
            return ((PolyglotContextImpl) polyglotContextImpl).config.logHandler;
        }

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

        @Override
        public String getFormatKind(LogRecord logRecord) {
            return PolyglotLoggers.getFormatKind(logRecord);
        }

        @Override
        public boolean isPolyglotThread(Thread thread) {
            return thread instanceof PolyglotThread;
        }

        @Override
        public Object getHostNull() {
            return EngineAccessor.HOST.getHostNull();
        }

        @Override
        public Object getPolyglotSharingLayer(Object polyglotLanguageInstance) {
            return ((PolyglotLanguageInstance) polyglotLanguageInstance).sharing;
        }

        @Override
        public boolean getNeedsAllEncodings() {
            return LanguageCache.getNeedsAllEncodings();
        }

        @Override
        public boolean requireLanguageWithAllEncodings(Object encoding) {
            PolyglotContextImpl context = PolyglotFastThreadLocals.getContext(null);
            if (context == null) {
                // we cannot really check this without an entered context.
                return true;
            }

            boolean needsAllEncodingsFound = false;
            for (PolyglotLanguage language : context.engine.languages) {
                if (language != null && !language.isFirstInstance() && language.cache.isNeedsAllEncodings()) {
                    needsAllEncodingsFound = true;
                    break;
                }
            }
            if (!needsAllEncodingsFound) {
                throw new AssertionError(String.format(
                                "The encoding %s for a new TruffleString was requested but was not configured by any language. " +
                                                "In order to use this encoding configure your language using %s.%s(...needsAllEncodings=true).",
                                encoding, TruffleLanguage.class.getSimpleName(), Registration.class.getSimpleName()));
            }
            return true;
        }

        @Override
        public Object getGuestToHostCodeCache(Object polyglotContextImpl) {
            return ((PolyglotContextImpl) polyglotContextImpl).getHostContext().getLanguageInstance().getGuestToHostCodeCache();
        }

        @Override
        public Object installGuestToHostCodeCache(Object polyglotContextImpl, Object cache) {
            return ((PolyglotContextImpl) polyglotContextImpl).getHostContext().getLanguageInstance().installGuestToHostCodeCache(cache);
        }

        @Override
        public AutoCloseable createPolyglotThreadScope() {
            return PolyglotImpl.getInstance().getRootImpl().createThreadScope();
        }

        @Override
        public Engine getPolyglotEngineAPI(Object polyglotEngineImpl) {
            return ((PolyglotEngineImpl) polyglotEngineImpl).api;
        }

        @Override
        public Context getPolyglotContextAPI(Object polyglotContextImpl) {
            return ((PolyglotContextImpl) polyglotContextImpl).api;
        }

        @Override
        public EncapsulatingNodeReference getEncapsulatingNodeReference(boolean invalidateOnNull) {
            return PolyglotFastThreadLocals.getEncapsulatingNodeReference(invalidateOnNull);
        }

        @Override
        public Thread createInstrumentSystemThread(Object polyglotInstrument, Runnable runnable, ThreadGroup threadGroup) {
            return new InstrumentSystemThread((PolyglotInstrument) polyglotInstrument, runnable, threadGroup);
        }

        @Override
        public Thread createLanguageSystemThread(Object polyglotLanguageContext, Runnable runnable, ThreadGroup threadGroup) {
            PolyglotLanguageContext languageContext = (PolyglotLanguageContext) polyglotLanguageContext;
            // Ensure that thread is entered in correct context
            if (PolyglotContextImpl.requireContext() != languageContext.context) {
                throw new IllegalStateException("Not entered in an Env's context.");
            }
            return new LanguageSystemThread(languageContext, runnable, threadGroup);
        }
    }

    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();
        }
    }

}