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

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

import java.util.List;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.ContextLocal;
import com.oracle.truffle.api.ContextThreadLocal;
import com.oracle.truffle.api.TruffleContext;
import com.oracle.truffle.polyglot.PolyglotEngineImpl.StableLocalLocations;

final class PolyglotLocals {

    static  ContextLocal createLanguageContextLocal(Object factory) {
        return new LanguageContextLocal<>(factory);
    }

    static  ContextLocal createInstrumentContextLocal(Object factory) {
        return new InstrumentContextLocal<>(factory);
    }

    static  ContextThreadLocal createLanguageContextThreadLocal(Object factory) {
        return new LanguageContextThreadLocal<>(factory);
    }

    static  ContextThreadLocal createInstrumentContextThreadLocal(Object factory) {
        return new InstrumentContextThreadLocal<>(factory);
    }

    static void initializeInstrumentContextLocals(List> locals, PolyglotInstrument polyglotInstrument) {
        LocalLocation[] locations;
        if (locals.isEmpty()) {
            locations = PolyglotEngineImpl.EMPTY_LOCATIONS;
        } else {
            for (InstrumentContextLocal local : locals) {
                local.instrument = polyglotInstrument;
            }
            locations = polyglotInstrument.engine.addContextLocals(locals);
        }
        polyglotInstrument.contextLocalLocations = locations;

    }

    static void initializeLanguageContextLocals(List> locals, PolyglotLanguageInstance polyglotLanguageInstance) {
        LocalLocation[] locations;
        if (locals.isEmpty()) {
            locations = PolyglotEngineImpl.EMPTY_LOCATIONS;
        } else {
            for (LanguageContextLocal local : locals) {
                local.languageInstance = polyglotLanguageInstance;
            }
            locations = polyglotLanguageInstance.language.previousContextLocalLocations;
            if (locations != null) {
                /*
                 * We are reusing local locations as there can be an arbitrary number of language
                 * instances per engine. In order to not let the context local array grow
                 * arbitrarily big, we reuse the locations from the previous language instance. We
                 * therefore require TruffleLanguage instances to always create the same number of
                 * context locals with the exact same types.
                 */
                if (locals.size() != locations.length) {
                    throw new IllegalStateException(String.format("Truffle language %s did not create the same number of context locals. " +
                                    "Expected %s locals but were %s.", polyglotLanguageInstance.spi.getClass().getName(), locations.length, locals.size()));
                }
                for (int i = 0; i < locations.length; i++) {
                    locals.get(i).initializeLocation(locations[i]);
                }
            } else {
                PolyglotLanguage language = polyglotLanguageInstance.language;
                PolyglotEngineImpl engine = language.engine;
                language.previousContextLocalLocations = locations = engine.addContextLocals(locals);
                assert locations.length == locals.size();
            }
        }
        assert polyglotLanguageInstance.contextLocals == null : "current context locals can only be initialized once";
        polyglotLanguageInstance.contextLocals = locals;
        polyglotLanguageInstance.contextLocalLocations = locations;
    }

    static void initializeInstrumentContextThreadLocals(List> locals, PolyglotInstrument polyglotInstrument) {
        LocalLocation[] locations;
        if (locals.isEmpty()) {
            locations = PolyglotEngineImpl.EMPTY_LOCATIONS;
        } else {
            for (InstrumentContextThreadLocal local : locals) {
                local.instrument = polyglotInstrument;
            }
            locations = polyglotInstrument.engine.addContextThreadLocals(locals);
        }
        polyglotInstrument.contextThreadLocalLocations = locations;
    }

    static void initializeLanguageContextThreadLocals(List> locals, PolyglotLanguageInstance polyglotLanguageInstance) {
        LocalLocation[] locations;
        if (locals.isEmpty()) {
            locations = PolyglotEngineImpl.EMPTY_LOCATIONS;
        } else {
            for (LanguageContextThreadLocal local : locals) {
                local.languageInstance = polyglotLanguageInstance;
            }

            locations = polyglotLanguageInstance.language.previousContextThreadLocalLocations;
            if (locations != null) {
                /*
                 * We are reusing local locations as there can be an arbitrary number of language
                 * instances per engine. In order to not let the context local array grow
                 * arbitrarily big, we reuse the locations from the previous language instance. We
                 * therefore require TruffleLanguage instances to always create the same number of
                 * context locals with the exact same types.
                 */
                if (locals.size() != locations.length) {
                    throw new IllegalStateException(String.format("Truffle language %s did not create the same number of context thread locals. " +
                                    "Expected %s locals but were %s.", polyglotLanguageInstance.spi.getClass().getName(), locations.length, locals.size()));
                }
                for (int i = 0; i < locations.length; i++) {
                    locals.get(i).initializeLocation(locations[i]);
                }
            } else {
                PolyglotLanguage language = polyglotLanguageInstance.language;
                PolyglotEngineImpl engine = language.engine;
                language.previousContextThreadLocalLocations = locations = engine.addContextThreadLocals(locals);
                assert locations.length == locals.size();
            }
        }

        assert polyglotLanguageInstance.contextThreadLocals == null : "current context locals can only be initialized once";
        polyglotLanguageInstance.contextThreadLocals = locals;
        polyglotLanguageInstance.contextThreadLocalLocations = locations;
    }

    @TruffleBoundary
    static IllegalStateException noCurrentContext() {
        return new IllegalStateException("No current context is entered.");
    }

    @TruffleBoundary
    static boolean assertLanguageCreated(PolyglotContextImpl context, PolyglotLanguage language) {
        if (context == null) {
            throw new IllegalStateException("No current context is entered.");
        }
        if (!context.getContext(language).isCreated()) {
            throw new IllegalStateException(String.format("Language context for language '%s' is not yet created in the context.",
                            language.getId()));
        }
        return true;
    }

    @TruffleBoundary
    static boolean assertInstrumentCreated(PolyglotContextImpl context, PolyglotInstrument instrument) {
        if (context == null) {
            throw new IllegalStateException("No current context is entered.");
        }
        if (!instrument.isInitialized()) {
            throw new IllegalStateException(String.format("Instrument '%s' is not yet created in the  context.",
                            instrument.getId()));
        }
        return true;
    }

    abstract static class AbstractContextLocal extends ContextLocal {

        @CompilationFinal LocalLocation location;

        protected AbstractContextLocal() {
            super(PolyglotImpl.getInstance());
        }

        final void initializeLocation(LocalLocation l) {
            assert this.location == null;
            this.location = l;
        }

        abstract LocalLocation createLocation(int localIndex);

    }

    static final class InstrumentContextLocal extends AbstractContextLocal {

        private PolyglotInstrument instrument;  // effectively final
        private final Object factory;

        protected InstrumentContextLocal(Object factory) {
            this.factory = factory;
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get() {
            PolyglotContextImpl c = PolyglotContextImpl.currentEntered(location.engine);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getLocal(location);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext context) {
            PolyglotContextImpl c = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(context);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getLocal(location);
        }

        @Override
        LocalLocation createLocation(int localIndex) {
            return new Location(this, localIndex);
        }

        private final class Location extends LocalLocation {

            Location(InstrumentContextLocal local, int index) {
                super(local.instrument.engine, index);
            }

            @Override
            Object invokeFactoryImpl(PolyglotContextImpl context, Thread thread) {
                assert thread == null;
                if (context.engine != instrument.engine) {
                    throw new AssertionError("Invalid sharing of locations.");
                }
                return EngineAccessor.INSTRUMENT.invokeContextLocalFactory(factory, context.creatorTruffleContext);
            }
        }

    }

    static final class LanguageContextLocal extends AbstractContextLocal {

        private final Object factory;

        // effectively final
        private PolyglotLanguageInstance languageInstance;

        protected LanguageContextLocal(Object factory) {
            this.factory = factory;
        }

        @Override
        LocalLocation createLocation(int index) {
            /*
             * It is important to not reference the PolyglotLanguageInstance, but only the
             * PolyglotLanguage in the location to avoid memory leaks for the instance. This is
             * somewhat different to instruments, because there can be an arbitrary number of
             * language instances, but there is at most one instrument per engine.
             */
            return new Location(languageInstance.language, index);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get() {
            LocalLocation l = this.location;
            PolyglotContextImpl context = PolyglotContextImpl.currentEntered(l.engine);
            assert assertLanguageCreated(context, languageInstance.language);
            return (T) context.getLocal(l);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext truffleContext) {
            PolyglotContextImpl context = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(truffleContext);
            assert assertLanguageCreated(context, languageInstance.language);
            return (T) context.getLocal(location);
        }

        private static final class Location extends LocalLocation {

            private final PolyglotLanguage language;

            Location(PolyglotLanguage language, int index) {
                super(language.engine, index);
                this.language = language;
            }

            @Override
            Object invokeFactoryImpl(PolyglotContextImpl context, Thread thread) {
                assert thread == null;
                PolyglotLanguageContext languageContext = context.getContext(language);
                List> locals = languageContext.getLanguageInstance().contextLocals;
                for (LanguageContextLocal local : locals) {
                    if (index == local.location.index) {
                        return EngineAccessor.LANGUAGE.invokeContextLocalFactory(local.factory, context.getContextImpl(language));
                    }
                }
                throw new AssertionError("Local index " + index + " not found in language instance locals.");
            }
        }

    }

    abstract static class AbstractContextThreadLocal extends ContextThreadLocal {

        @CompilationFinal LocalLocation location;

        protected AbstractContextThreadLocal() {
            super(PolyglotImpl.getInstance());
        }

        final void initializeLocation(LocalLocation l) {
            assert this.location == null;
            this.location = l;
        }

        abstract LocalLocation createLocation(int localIndex);

    }

    static final class LanguageContextThreadLocal extends AbstractContextThreadLocal {

        // effectively final
        private PolyglotLanguageInstance languageInstance;

        private final Object factory;

        protected LanguageContextThreadLocal(Object factory) {
            this.factory = factory;
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get() {
            PolyglotContextImpl c = PolyglotContextImpl.currentEntered(location.engine);
            assert assertLanguageCreated(c, languageInstance.language);
            return (T) c.getThreadLocal(location);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(Thread t) {
            PolyglotContextImpl c = PolyglotContextImpl.currentEntered(location.engine);
            assert assertLanguageCreated(c, languageInstance.language);
            return (T) c.getThreadLocal(location, t);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext context) {
            PolyglotContextImpl c = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(context);
            assert assertLanguageCreated(c, languageInstance.language);
            return (T) c.getThreadLocal(location);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext context, Thread t) {
            PolyglotContextImpl c = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(context);
            assert assertLanguageCreated(c, languageInstance.language);
            return (T) c.getThreadLocal(location, t);
        }

        @Override
        LocalLocation createLocation(int index) {
            /*
             * It is important to not reference the PolyglotLanguageInstance, but only the
             * PolyglotLanguage in the location to avoid memory leaks for the instance. This is
             * somewhat different to instruments, because there can be an arbitrary number of
             * language instances, but there is at most one instrument per engine.
             */
            return new Location(languageInstance.language, index);
        }

        private static final class Location extends LocalLocation {

            private final PolyglotLanguage language;

            Location(PolyglotLanguage language, int index) {
                super(language.engine, index);
                this.language = language;
            }

            @Override
            Object invokeFactoryImpl(PolyglotContextImpl context, Thread thread) {
                PolyglotLanguageContext languageContext = context.getContext(language);
                List> locals = languageContext.getLanguageInstance().contextThreadLocals;
                for (LanguageContextThreadLocal local : locals) {
                    if (index == local.location.index) {
                        return EngineAccessor.LANGUAGE.invokeContextThreadLocalFactory(local.factory, context.getContextImpl(language), thread);
                    }
                }
                throw new AssertionError("Local index " + index + " not found in language instance locals.");
            }
        }

    }

    static final class InstrumentContextThreadLocal extends AbstractContextThreadLocal {

        private PolyglotInstrument instrument;  // effectively final
        private final Object factory;

        protected InstrumentContextThreadLocal(Object factory) {
            this.factory = factory;
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get() {
            PolyglotContextImpl c = PolyglotContextImpl.currentEntered(location.engine);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getThreadLocal(location);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(Thread t) {
            PolyglotContextImpl c = PolyglotContextImpl.currentEntered(location.engine);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getThreadLocal(location, t);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext context) {
            PolyglotContextImpl c = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(context);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getThreadLocal(location);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get(TruffleContext context, Thread t) {
            PolyglotContextImpl c = (PolyglotContextImpl) EngineAccessor.LANGUAGE.getPolyglotContext(context);
            assert assertInstrumentCreated(c, instrument);
            return (T) c.getThreadLocal(location, t);
        }

        @Override
        LocalLocation createLocation(int localIndex) {
            return new Location(localIndex);
        }

        private final class Location extends LocalLocation {

            Location(int index) {
                super(instrument.engine, index);
            }

            @Override
            Object invokeFactoryImpl(PolyglotContextImpl context, Thread thread) {
                if (context.engine != instrument.engine) {
                    throw new AssertionError("Invalid sharing of locations.");
                }
                return EngineAccessor.INSTRUMENT.invokeContextThreadLocalFactory(factory, context.creatorTruffleContext, thread);
            }
        }

    }

    abstract static class LocalLocation {

        final PolyglotEngineImpl engine;
        final int index;
        @CompilationFinal private volatile Class profiledType;

        private LocalLocation(PolyglotEngineImpl engine, int index) {
            this.engine = engine;
            this.index = index;
        }

        final Object invokeFactory(PolyglotContextImpl context, Thread thread) {
            Object result = invokeFactoryImpl(context, thread);
            Class profileType = this.profiledType;
            assert result != null : "result should already be checked for null";
            if (profileType == null) {
                this.profiledType = result.getClass();
            } else if (profileType != result.getClass()) {
                throw new IllegalStateException(String.format("The return context value type must be stable and exact. Expected %s but got %s for local %s.",
                                profileType, result.getClass(), this));
            }
            return result;
        }

        final Object readLocal(PolyglotContextImpl context, Object[] locals, boolean threadLocal) {
            assert locals != null && index < locals.length && locals[index] != null : invalidLocalMessage(context, locals);
            Object result;
            if (CompilerDirectives.isPartialEvaluationConstant(this)) {
                result = readLocalFast(locals, threadLocal);
            } else {
                result = locals[index];
            }
            assert result.getClass() == profiledType : invalidLocalMessage(context, locals);
            return result;
        }

        private Object readLocalFast(Object[] locals, boolean threadLocal) {
            Object result;
            StableLocalLocations stableLocations = (threadLocal ? this.engine.contextThreadLocalLocations : this.engine.contextLocalLocations);
            LocalLocation[] locations = stableLocations.locations;
            if (!stableLocations.assumption.isValid()) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                result = locals[index];
            } else {
                result = EngineAccessor.RUNTIME.unsafeCast(EngineAccessor.RUNTIME.castArrayFixedLength(locals, locations.length)[index], profiledType, true, true, true);
            }
            return result;
        }

        abstract Object invokeFactoryImpl(PolyglotContextImpl context, Thread thread);

        private String invalidLocalMessage(PolyglotContextImpl context, Object[] locals) {
            if (locals == null) {
                return "Invalid local state: Locals is null. Current context: " + context.toString();
            } else if (index < 0 || index >= locals.length) {
                return "Invalid local state: Locals index is out of bounds " + index + ". Current context: " + context.toString();
            }

            Object value = locals[index];
            if (value == null) {
                return "Invalid local state: Local is not initialized. Engine closed: " + engine.closed + ". Current context: " + context.toString();
            } else if (locals[index].getClass() != profiledType) {
                return "Invalid local state: Invalid profiled type. Expected " + profiledType.getName() + " but was " + value.getClass().getName();
            }
            return "Invalid local state: Unknown reason. Current context: " + context.toString();
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy