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

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

Go to download

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

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

import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
import static com.oracle.truffle.polyglot.EngineAccessor.RUNTIME;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.SourceSection;
import org.graalvm.polyglot.TypeLiteral;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.polyglot.PolyglotLanguageContext.ToGuestValueNode;
import com.oracle.truffle.polyglot.PolyglotLanguageContext.ToGuestValuesNode;
import com.oracle.truffle.polyglot.PolyglotLanguageContext.ToHostValueNode;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsDateNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsDurationNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsInstantNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsNativePointerNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsTimeNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.AsTimeZoneNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.CanExecuteNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.CanInstantiateNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.CanInvokeNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetArrayElementNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetArraySizeNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMemberKeysNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMemberNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMetaQualifiedNameNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMetaSimpleNameNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasArrayElementsNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasMemberNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasMembersNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsDateNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsDurationNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsExceptionNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsMetaInstanceNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsMetaObjectNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsNativePointerNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsNullNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsTimeNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsTimeZoneNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.NewInstanceNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.PutMemberNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.RemoveArrayElementNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.RemoveMemberNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.SetArrayElementNodeGen;
import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.ThrowExceptionNodeGen;

abstract class PolyglotValue extends AbstractValueImpl {

    private static final String TRUNCATION_SUFFIX = "...";

    private static final String UNKNOWN = "Unknown";

    protected final PolyglotLanguageContext languageContext;

    static final InteropLibrary UNCACHED_INTEROP = InteropLibrary.getFactory().getUncached();

    PolyglotValue(PolyglotLanguageContext languageContext) {
        super(languageContext.getEngine().impl);
        this.languageContext = languageContext;
    }

    PolyglotValue(PolyglotImpl polyglot, PolyglotLanguageContext languageContext) {
        super(polyglot);
        this.languageContext = languageContext;
    }

    @Override
    public final Context getContext() {
        if (languageContext == null) {
            return null;
        }
        return languageContext.context.currentApi;
    }

    @Override
    public Value getArrayElement(Object receiver, long index) {
        try {
            return getArrayElementUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static final Value getArrayElementUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "getArrayElement(long)", "hasArrayElements()");
    }

    @Override
    public void setArrayElement(Object receiver, long index, Object value) {
        try {
            setArrayElementUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static void setArrayElementUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "setArrayElement(long, Object)", "hasArrayElements()");
    }

    @Override
    public boolean removeArrayElement(Object receiver, long index) {
        try {
            throw removeArrayElementUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static RuntimeException removeArrayElementUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "removeArrayElement(long, Object)", null);
    }

    @Override
    public long getArraySize(Object receiver) {
        try {
            return getArraySizeUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static long getArraySizeUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "getArraySize()", "hasArrayElements()");
    }

    @Override
    public Value getMember(Object receiver, String key) {
        try {
            return getMemberUnsupported(languageContext, receiver, key);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static Value getMemberUnsupported(PolyglotLanguageContext context, Object receiver, @SuppressWarnings("unused") String key) {
        throw unsupported(context, receiver, "getMember(String)", "hasMembers()");
    }

    @Override
    public void putMember(Object receiver, String key, Object member) {
        try {
            putMemberUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static RuntimeException putMemberUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "putMember(String, Object)", "hasMembers()");
    }

    @Override
    public boolean removeMember(Object receiver, String key) {
        try {
            throw removeMemberUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static RuntimeException removeMemberUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "removeMember(String, Object)", null);
    }

    @Override
    public Value execute(Object receiver, Object[] arguments) {
        try {
            throw executeUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public Value execute(Object receiver) {
        try {
            throw executeUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static RuntimeException executeUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "execute(Object...)", "canExecute()");
    }

    @Override
    public Value newInstance(Object receiver, Object[] arguments) {
        try {
            return newInstanceUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static Value newInstanceUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "newInstance(Object...)", "canInstantiate()");
    }

    @Override
    public void executeVoid(Object receiver, Object[] arguments) {
        try {
            executeVoidUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public void executeVoid(Object receiver) {
        try {
            executeVoidUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static void executeVoidUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw unsupported(context, receiver, "executeVoid(Object...)", "canExecute()");
    }

    @Override
    public Value invoke(Object receiver, String identifier, Object[] arguments) {
        try {
            throw invokeUnsupported(languageContext, receiver, identifier);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public Value invoke(Object receiver, String identifier) {
        try {
            throw invokeUnsupported(languageContext, receiver, identifier);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @TruffleBoundary
    static RuntimeException invokeUnsupported(PolyglotLanguageContext context, Object receiver, String identifier) {
        throw unsupported(context, receiver, "invoke(" + identifier + ", Object...)", "canInvoke(String)");
    }

    @Override
    public String asString(Object receiver) {
        try {
            return asStringUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final String asStringUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, String.class, "asString()", "isString()", "Invalid coercion.");
    }

    @Override
    public boolean asBoolean(Object receiver) {
        try {
            return asBooleanUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    private static boolean isNullUncached(Object receiver) {
        return InteropLibrary.getFactory().getUncached().isNull(receiver);
    }

    protected final boolean asBooleanUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, boolean.class, "asBoolean()", "isBoolean()", "Invalid or lossy primitive coercion.");
    }

    private  T invalidCastPrimitive(Object receiver, Class clazz, String asMethodName, String isMethodName, String detail) {
        Object prev = hostEnter(languageContext);
        try {
            if (isNullUncached(receiver)) {
                throw nullCoercion(languageContext, receiver, clazz, asMethodName, isMethodName);
            } else {
                throw cannotConvert(languageContext, receiver, clazz, asMethodName, isMethodName, detail);
            }
        } finally {
            hostLeave(languageContext, prev);
        }
    }

    @Override
    public int asInt(Object receiver) {
        try {
            return asIntUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final int asIntUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, int.class, "asInt()", "fitsInInt()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public long asLong(Object receiver) {
        try {
            return asLongUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final long asLongUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, long.class, "asLong()", "fitsInLong()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public double asDouble(Object receiver) {
        try {
            return asDoubleUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final double asDoubleUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, double.class, "asDouble()", "fitsInDouble()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public float asFloat(Object receiver) {
        try {
            return asFloatUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final float asFloatUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, float.class, "asFloat()", "fitsInFloat()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public byte asByte(Object receiver) {
        try {
            return asByteUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final byte asByteUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, byte.class, "asByte()", "fitsInByte()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public short asShort(Object receiver) {
        try {
            return asShortUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final short asShortUnsupported(Object receiver) {
        return invalidCastPrimitive(receiver, short.class, "asShort()", "fitsInShort()", "Invalid or lossy primitive coercion.");
    }

    @Override
    public long asNativePointer(Object receiver) {
        try {
            return asNativePointerUnsupported(languageContext, receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    static long asNativePointerUnsupported(PolyglotLanguageContext context, Object receiver) {
        throw cannotConvert(context, receiver, long.class, "asNativePointer()", "isNativeObject()", "Value cannot be converted to a native pointer.");
    }

    @Override
    public Object asHostObject(Object receiver) {
        try {
            return asHostObjectUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final Object asHostObjectUnsupported(Object receiver) {
        throw cannotConvert(languageContext, receiver, null, "asHostObject()", "isHostObject()", "Value is not a host object.");
    }

    @Override
    public Object asProxyObject(Object receiver) {
        try {
            return asProxyObjectUnsupported(receiver);
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected final Object asProxyObjectUnsupported(Object receiver) {
        throw cannotConvert(languageContext, receiver, null, "asProxyObject()", "isProxyObject()", "Value is not a proxy object.");
    }

    @Override
    public LocalDate asDate(Object receiver) {
        try {
            if (isNullUncached(receiver)) {
                return null;
            } else {
                throw cannotConvert(languageContext, receiver, null, "asDate()", "isDate()", "Value does not contain date information.");
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public LocalTime asTime(Object receiver) {
        try {
            if (isNullUncached(receiver)) {
                return null;
            } else {
                throw cannotConvert(languageContext, receiver, null, "asTime()", "isTime()", "Value does not contain time information.");
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public ZoneId asTimeZone(Object receiver) {
        try {
            if (isNullUncached(receiver)) {
                return null;
            } else {
                throw cannotConvert(languageContext, receiver, null, "asTimeZone()", "isTimeZone()", "Value does not contain time zone information.");
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public Instant asInstant(Object receiver) {
        try {
            if (isNullUncached(receiver)) {
                return null;
            } else {
                throw cannotConvert(languageContext, receiver, null, "asInstant()", "isInstant()", "Value does not contain instant information.");
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public Duration asDuration(Object receiver) {
        try {
            if (isNullUncached(receiver)) {
                return null;
            } else {
                throw cannotConvert(languageContext, receiver, null, "asDuration()", "isDuration()", "Value does not contain duration information.");
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public RuntimeException throwException(Object receiver) {
        try {
            throw unsupported(languageContext, receiver, "throwException()", "isException()");
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public final Value getMetaObject(Object receiver) {
        try {
            Object prev = hostEnter(languageContext);
            try {
                return getMetaObjectImpl(receiver);
            } finally {
                hostLeave(languageContext, prev);
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected Value getMetaObjectImpl(Object receiver) {
        InteropLibrary lib = InteropLibrary.getFactory().getUncached(receiver);
        if (lib.hasMetaObject(receiver)) {
            try {
                return asValue(lib.getMetaObject(receiver));
            } catch (UnsupportedMessageException e) {
                throw shouldNotReachHere("Unexpected unsupported message.", e);
            }
        }
        return null;
    }

    private Value asValue(Object value) {
        if (languageContext == null) {
            return PolyglotImpl.getInstance().asValue(PolyglotContextImpl.currentNotEntered(), value);
        } else {
            return languageContext.asValue(value);
        }
    }

    static Object hostEnter(PolyglotLanguageContext languageContext) {
        if (languageContext == null) {
            return null;
        }
        return languageContext.context.engine.enterIfNeeded(languageContext.context);
    }

    static void hostLeave(PolyglotLanguageContext languageContext, Object prev) {
        if (languageContext == null) {
            return;
        }
        languageContext.context.engine.leaveIfNeeded(prev, languageContext.context);
    }

    @TruffleBoundary
    protected static RuntimeException unsupported(PolyglotLanguageContext languageContext, Object receiver, String message, String useToCheck) {
        Object prev = hostEnter(languageContext);
        try {
            String polyglotMessage;
            if (useToCheck != null) {
                polyglotMessage = String.format("Unsupported operation %s.%s for %s. You can ensure that the operation is supported using %s.%s.",
                                Value.class.getSimpleName(), message, getValueInfo(languageContext, receiver), Value.class.getSimpleName(), useToCheck);
            } else {
                polyglotMessage = String.format("Unsupported operation %s.%s for %s.",
                                Value.class.getSimpleName(), message, getValueInfo(languageContext, receiver));
            }
            throw PolyglotEngineException.unsupported(polyglotMessage);
        } finally {
            hostLeave(languageContext, prev);
        }
    }

    private static final int CHARACTER_LIMIT = 140;

    private static final InteropLibrary INTEROP = InteropLibrary.getFactory().getUncached();

    @TruffleBoundary
    static String getValueInfo(PolyglotLanguageContext languageContext, Object receiver) {
        if (languageContext == null) {
            return receiver.toString();
        } else if (receiver == null) {
            assert false : "receiver should never be null";
            return "null";
        }
        Object prev = hostEnter(languageContext);
        try {
            PolyglotContextImpl context = languageContext.context;
            PolyglotLanguage displayLanguage = EngineAccessor.EngineImpl.findObjectLanguage(context.engine, receiver);
            Object view;
            if (displayLanguage == null) {
                displayLanguage = context.engine.hostLanguage;
                view = context.getHostContext().getLanguageView(receiver);
            } else {
                view = receiver;
            }

            String valueToString;
            String metaObjectToString = UNKNOWN;
            try {
                InteropLibrary uncached = InteropLibrary.getFactory().getUncached(view);
                if (uncached.hasMetaObject(view)) {
                    Object qualifiedName = INTEROP.getMetaQualifiedName(uncached.getMetaObject(view));
                    metaObjectToString = truncateString(INTEROP.asString(qualifiedName), CHARACTER_LIMIT);
                }
                valueToString = truncateString(INTEROP.asString(uncached.toDisplayString(view)), CHARACTER_LIMIT);
            } catch (UnsupportedMessageException e) {
                throw shouldNotReachHere(e);
            }
            String languageName = null;
            boolean hideType = false;
            if (displayLanguage.isHost()) {
                languageName = "Java"; // java is our host language for now

                // hide meta objects of null
                if (UNKNOWN.equals(metaObjectToString) && INTEROP.isNull(receiver)) {
                    hideType = true;
                }
            } else {
                languageName = displayLanguage.getName();
            }
            if (hideType) {
                return String.format("'%s'(language: %s)", valueToString, languageName);
            } else {
                return String.format("'%s'(language: %s, type: %s)", valueToString, languageName, metaObjectToString);
            }
        } finally {
            hostLeave(languageContext, prev);
        }

    }

    private static String truncateString(String s, int i) {
        if (s.length() > i) {
            return s.substring(0, i - TRUNCATION_SUFFIX.length()) + TRUNCATION_SUFFIX;
        } else {
            return s;
        }
    }

    @TruffleBoundary
    protected static RuntimeException nullCoercion(PolyglotLanguageContext languageContext, Object receiver, Class targetType, String message, String useToCheck) {
        Object prev = hostEnter(languageContext);
        try {
            String valueInfo = getValueInfo(languageContext, receiver);
            throw PolyglotEngineException.nullPointer(String.format("Cannot convert null value %s to Java type '%s' using %s.%s. " +
                            "You can ensure that the operation is supported using %s.%s.",
                            valueInfo, targetType, Value.class.getSimpleName(), message, Value.class.getSimpleName(), useToCheck));
        } finally {
            hostLeave(languageContext, prev);
        }
    }

    @TruffleBoundary
    protected static RuntimeException cannotConvert(PolyglotLanguageContext languageContext, Object receiver, Class targetType, String message, String useToCheck, String reason) {
        Object prev = hostEnter(languageContext);
        try {
            String valueInfo = getValueInfo(languageContext, receiver);
            String targetTypeString = "";
            if (targetType != null) {
                targetTypeString = String.format("to Java type '%s'", targetType.getTypeName());
            }
            throw PolyglotEngineException.classCast(
                            String.format("Cannot convert %s %s using %s.%s: %s You can ensure that the value can be converted using %s.%s.",
                                            valueInfo, targetTypeString, Value.class.getSimpleName(), message, reason, Value.class.getSimpleName(), useToCheck));
        } finally {
            hostLeave(languageContext, prev);
        }
    }

    @TruffleBoundary
    protected static RuntimeException invalidArrayIndex(PolyglotLanguageContext context, Object receiver, long index) {
        String message = String.format("Invalid array index %s for array %s.", index, getValueInfo(context, receiver));
        throw PolyglotEngineException.arrayIndexOutOfBounds(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidArrayValue(PolyglotLanguageContext context, Object receiver, long identifier, Object value) {
        throw PolyglotEngineException.classCast(
                        String.format("Invalid array value %s for array %s and index %s.",
                                        getValueInfo(context, value), getValueInfo(context, receiver), identifier));
    }

    @TruffleBoundary
    protected static RuntimeException invalidMemberKey(PolyglotLanguageContext context, Object receiver, String identifier) {
        String message = String.format("Invalid member key '%s' for object %s.", identifier, getValueInfo(context, receiver));
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidMemberValue(PolyglotLanguageContext context, Object receiver, String identifier, Object value) {
        String message = String.format("Invalid member value %s for object %s and member key '%s'.", getValueInfo(context, value), getValueInfo(context, receiver), identifier);
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidExecuteArgumentType(PolyglotLanguageContext context, Object receiver, UnsupportedTypeException e) {
        String originalMessage = e.getMessage() == null ? "" : e.getMessage() + " ";
        String[] formattedArgs = formatArgs(context, e.getSuppliedValues());
        throw PolyglotEngineException.illegalArgument(String.format("Invalid argument when executing %s. %sProvided arguments: %s.",
                        getValueInfo(context, receiver),
                        originalMessage,
                        Arrays.asList(formattedArgs)));
    }

    @TruffleBoundary
    protected static RuntimeException invalidInvokeArgumentType(PolyglotLanguageContext context, Object receiver, String member, UnsupportedTypeException e) {
        String originalMessage = e.getMessage() == null ? "" : e.getMessage();
        String[] formattedArgs = formatArgs(context, e.getSuppliedValues());
        String message = String.format("Invalid argument when invoking '%s' on %s. %sProvided arguments: %s.",
                        member,
                        getValueInfo(context, receiver),
                        originalMessage,
                        Arrays.asList(formattedArgs));
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidInstantiateArgumentType(PolyglotLanguageContext context, Object receiver, Object[] arguments) {
        String[] formattedArgs = formatArgs(context, arguments);
        String message = String.format("Invalid argument when instantiating %s with arguments %s.", getValueInfo(context, receiver), Arrays.asList(formattedArgs));
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidInstantiateArity(PolyglotLanguageContext context, Object receiver, Object[] arguments, int expected, int actual) {
        String[] formattedArgs = formatArgs(context, arguments);
        String message = String.format("Invalid argument count when instantiating %s with arguments %s. Expected %d argument(s) but got %d.",
                        getValueInfo(context, receiver), Arrays.asList(formattedArgs), expected, actual);
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidExecuteArity(PolyglotLanguageContext context, Object receiver, Object[] arguments, int expected, int actual) {
        String[] formattedArgs = formatArgs(context, arguments);
        String message = String.format("Invalid argument count when executing %s with arguments %s. Expected %d argument(s) but got %d.",
                        getValueInfo(context, receiver), Arrays.asList(formattedArgs), expected, actual);
        throw PolyglotEngineException.illegalArgument(message);
    }

    @TruffleBoundary
    protected static RuntimeException invalidInvokeArity(PolyglotLanguageContext context, Object receiver, String member, Object[] arguments, int expected, int actual) {
        String[] formattedArgs = formatArgs(context, arguments);
        String message = String.format("Invalid argument count when invoking '%s' on %s with arguments %s. Expected %d argument(s) but got %d.",
                        member,
                        getValueInfo(context, receiver), Arrays.asList(formattedArgs), expected, actual);
        throw PolyglotEngineException.illegalArgument(message);
    }

    private static String[] formatArgs(PolyglotLanguageContext context, Object[] arguments) {
        String[] formattedArgs = new String[arguments.length];
        for (int i = 0; i < arguments.length; i++) {
            formattedArgs[i] = getValueInfo(context, arguments[i]);
        }
        return formattedArgs;
    }

    @Override
    public final String toString(Object receiver) {
        try {
            Object prev = hostEnter(languageContext);
            try {
                return toStringImpl(receiver);
            } finally {
                hostLeave(languageContext, prev);
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    protected String toStringImpl(Object receiver) throws AssertionError {
        InteropLibrary lib = InteropLibrary.getFactory().getUncached(receiver);
        Object result = lib.toDisplayString(receiver);
        InteropLibrary resultLib = InteropLibrary.getFactory().getUncached(result);
        try {
            return resultLib.asString(result);
        } catch (UnsupportedMessageException e) {
            throw shouldNotReachHere("toDisplayString must be coercible to java.lang.String, but is not.", e);
        }
    }

    @Override
    public SourceSection getSourceLocation(Object receiver) {
        try {
            if (languageContext == null) {
                return null;
            }
            Object prev = hostEnter(languageContext);
            try {
                InteropLibrary lib = InteropLibrary.getFactory().getUncached(receiver);
                com.oracle.truffle.api.source.SourceSection result = null;
                if (lib.hasSourceLocation(receiver)) {
                    try {
                        result = lib.getSourceLocation(receiver);
                    } catch (UnsupportedMessageException e) {
                    }
                }
                if (result == null) {
                    return null;
                }
                return languageContext.getImpl().getPolyglotSourceSection(result);
            } finally {
                hostLeave(languageContext, prev);
            }
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public boolean isMetaObject(Object receiver) {
        return false;
    }

    @Override
    public boolean equalsImpl(Object receiver, Object obj) {
        if (receiver == obj) {
            return true;
        }
        return HostWrapper.equals(languageContext, receiver, obj);
    }

    @Override
    public int hashCodeImpl(Object receiver) {
        return HostWrapper.hashCode(languageContext, receiver);
    }

    @Override
    public boolean isMetaInstance(Object receiver, Object instance) {
        try {
            throw unsupported(languageContext, receiver, "isMetaInstance(Object)", "isMetaObject()");
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public String getMetaQualifiedName(Object receiver) {
        try {
            throw unsupported(languageContext, receiver, "getMetaQualifiedName()", "isMetaObject()");
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    @Override
    public String getMetaSimpleName(Object receiver) {
        try {
            throw unsupported(languageContext, receiver, "getMetaSimpleName()", "isMetaObject()");
        } catch (Throwable e) {
            throw PolyglotImpl.guestToHostException((languageContext), e);
        }
    }

    static CallTarget createTarget(InteropNode root) {
        CallTarget target = Truffle.getRuntime().createCallTarget(root);
        Class[] types = root.getArgumentTypes();
        if (types != null) {
            RUNTIME.initializeProfile(target, types);
        }
        return target;
    }

    static PolyglotValue createInteropValue(PolyglotLanguageContext languageContext, TruffleObject receiver, Class receiverType) {
        PolyglotLanguageInstance languageInstance = languageContext.getLanguageInstance();
        InteropCodeCache cache = languageInstance.valueCodeCache.get(receiverType);
        if (cache == null) {
            cache = new InteropCodeCache(languageInstance, receiver, receiverType);
            languageInstance.valueCodeCache.put(receiverType, cache);
        }
        return new InteropValue(languageContext, cache);
    }

    static PolyglotValue createHostNull(PolyglotImpl polyglot) {
        return new HostNull(polyglot);
    }

    static void createDefaultValues(PolyglotImpl polyglot, PolyglotLanguageContext context, Map, PolyglotValue> valueCache) {
        addDefaultValue(polyglot, context, valueCache, false);
        addDefaultValue(polyglot, context, valueCache, "");
        addDefaultValue(polyglot, context, valueCache, 'a');
        addDefaultValue(polyglot, context, valueCache, (byte) 0);
        addDefaultValue(polyglot, context, valueCache, (short) 0);
        addDefaultValue(polyglot, context, valueCache, 0);
        addDefaultValue(polyglot, context, valueCache, 0L);
        addDefaultValue(polyglot, context, valueCache, 0F);
        addDefaultValue(polyglot, context, valueCache, 0D);
    }

    static void addDefaultValue(PolyglotImpl polyglot, PolyglotLanguageContext context, Map, PolyglotValue> valueCache, Object primitive) {
        valueCache.put(primitive.getClass(), new PrimitiveValue(polyglot, context, primitive));
    }

    @SuppressWarnings("unused")
    static class InteropCodeCache {

        final CallTarget isNativePointer;
        final CallTarget asNativePointer;
        final CallTarget hasArrayElements;
        final CallTarget getArrayElement;
        final CallTarget setArrayElement;
        final CallTarget removeArrayElement;
        final CallTarget getArraySize;
        final CallTarget hasMembers;
        final CallTarget hasMember;
        final CallTarget getMember;
        final CallTarget putMember;
        final CallTarget removeMember;
        final CallTarget isNull;
        final CallTarget canExecute;
        final CallTarget execute;
        final CallTarget canInstantiate;
        final CallTarget newInstance;
        final CallTarget executeNoArgs;
        final CallTarget executeVoid;
        final CallTarget executeVoidNoArgs;
        final CallTarget canInvoke;
        final CallTarget invoke;
        final CallTarget invokeNoArgs;
        final CallTarget getMemberKeys;
        final CallTarget isDate;
        final CallTarget asDate;
        final CallTarget isTime;
        final CallTarget asTime;
        final CallTarget isTimeZone;
        final CallTarget asTimeZone;
        final CallTarget asInstant;
        final CallTarget isDuration;
        final CallTarget asDuration;
        final CallTarget isException;
        final CallTarget throwException;
        final CallTarget isMetaObject;
        final CallTarget isMetaInstance;
        final CallTarget getMetaQualifiedName;
        final CallTarget getMetaSimpleName;

        final boolean isProxy;
        final boolean isHost;

        final CallTarget asClassLiteral;
        final CallTarget asTypeLiteral;
        final Class receiverType;
        final PolyglotLanguageInstance languageInstance;

        InteropCodeCache(PolyglotLanguageInstance languageInstance, TruffleObject receiverObject, Class receiverType) {
            Objects.requireNonNull(receiverType);
            this.languageInstance = languageInstance;
            this.receiverType = receiverType;
            this.asClassLiteral = createTarget(new AsClassLiteralNode(this));
            this.asTypeLiteral = createTarget(new AsTypeLiteralNode(this));
            this.isNativePointer = createTarget(IsNativePointerNodeGen.create(this));
            this.asNativePointer = createTarget(AsNativePointerNodeGen.create(this));
            this.hasArrayElements = createTarget(HasArrayElementsNodeGen.create(this));
            this.getArrayElement = createTarget(GetArrayElementNodeGen.create(this));
            this.setArrayElement = createTarget(SetArrayElementNodeGen.create(this));
            this.removeArrayElement = createTarget(RemoveArrayElementNodeGen.create(this));
            this.getArraySize = createTarget(GetArraySizeNodeGen.create(this));
            this.hasMember = createTarget(HasMemberNodeGen.create(this));
            this.getMember = createTarget(GetMemberNodeGen.create(this));
            this.putMember = createTarget(PutMemberNodeGen.create(this));
            this.removeMember = createTarget(RemoveMemberNodeGen.create(this));
            this.isNull = createTarget(IsNullNodeGen.create(this));
            this.execute = createTarget(new ExecuteNode(this));
            this.executeNoArgs = createTarget(new ExecuteNoArgsNode(this));
            this.executeVoid = createTarget(new ExecuteVoidNode(this));
            this.executeVoidNoArgs = createTarget(new ExecuteVoidNoArgsNode(this));
            this.newInstance = createTarget(NewInstanceNodeGen.create(this));
            this.canInstantiate = createTarget(CanInstantiateNodeGen.create(this));
            this.canExecute = createTarget(CanExecuteNodeGen.create(this));
            this.canInvoke = createTarget(CanInvokeNodeGen.create(this));
            this.invoke = createTarget(new InvokeNode(this));
            this.invokeNoArgs = createTarget(new InvokeNoArgsNode(this));
            this.hasMembers = createTarget(HasMembersNodeGen.create(this));
            this.isProxy = PolyglotProxy.isProxyGuestObject(receiverObject);
            this.isHost = HostObject.isInstance(receiverObject);
            this.getMemberKeys = createTarget(GetMemberKeysNodeGen.create(this));
            this.isDate = createTarget(IsDateNodeGen.create(this));
            this.asDate = createTarget(AsDateNodeGen.create(this));
            this.isTime = createTarget(IsTimeNodeGen.create(this));
            this.asTime = createTarget(AsTimeNodeGen.create(this));
            this.isTimeZone = createTarget(IsTimeZoneNodeGen.create(this));
            this.asTimeZone = createTarget(AsTimeZoneNodeGen.create(this));
            this.asInstant = createTarget(AsInstantNodeGen.create(this));
            this.isDuration = createTarget(IsDurationNodeGen.create(this));
            this.asDuration = createTarget(AsDurationNodeGen.create(this));
            this.isException = createTarget(IsExceptionNodeGen.create(this));
            this.throwException = createTarget(ThrowExceptionNodeGen.create(this));
            this.isMetaObject = createTarget(IsMetaObjectNodeGen.create(this));
            this.isMetaInstance = createTarget(IsMetaInstanceNodeGen.create(this));
            this.getMetaQualifiedName = createTarget(GetMetaQualifiedNameNodeGen.create(this));
            this.getMetaSimpleName = createTarget(GetMetaSimpleNameNodeGen.create(this));
        }

        abstract static class IsDateNode extends InteropNode {

            protected IsDateNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isDate";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isDate(receiver);
            }
        }

        abstract static class AsDateNode extends InteropNode {

            protected AsDateNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "asDate";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.asDate(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.isNull(receiver)) {
                        return null;
                    } else {
                        throw cannotConvert(context, receiver, null, "asDate()", "isDate()", "Value does not contain date information.");
                    }
                }
            }
        }

        abstract static class IsTimeNode extends InteropNode {

            protected IsTimeNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isTime";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isTime(receiver);
            }
        }

        abstract static class AsTimeNode extends InteropNode {

            protected AsTimeNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "asTime";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.asTime(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.isNull(receiver)) {
                        return null;
                    } else {
                        throw cannotConvert(context, receiver, null, "asTime()", "isTime()", "Value does not contain time information.");
                    }
                }
            }
        }

        abstract static class IsTimeZoneNode extends InteropNode {

            protected IsTimeZoneNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isTimeZone";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isTimeZone(receiver);
            }
        }

        abstract static class AsTimeZoneNode extends InteropNode {

            protected AsTimeZoneNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "asTimeZone";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.asTimeZone(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.isNull(receiver)) {
                        return null;
                    } else {
                        throw cannotConvert(context, receiver, null, "asTimeZone()", "isTimeZone()", "Value does not contain time-zone information.");
                    }
                }
            }
        }

        abstract static class IsDurationNode extends InteropNode {

            protected IsDurationNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isDuration";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isDuration(receiver);
            }
        }

        abstract static class AsDurationNode extends InteropNode {

            protected AsDurationNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "asDuration";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.asDuration(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.isNull(receiver)) {
                        return null;
                    } else {
                        throw cannotConvert(context, receiver, null, "asDuration()", "isDuration()", "Value does not contain duration information.");
                    }
                }
            }
        }

        abstract static class AsInstantNode extends InteropNode {

            protected AsInstantNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "getInstant";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.asInstant(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.isNull(receiver)) {
                        return null;
                    } else {
                        throw cannotConvert(context, receiver, null, "asInstant()", "hasInstant()", "Value does not contain instant information.");
                    }
                }
            }
        }

        private static class AsClassLiteralNode extends InteropNode {

            @Child ToHostNode toHost = ToHostNodeGen.create();

            protected AsClassLiteralNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Class.class};
            }

            @Override
            protected String getOperationName() {
                return "as";
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                return toHost.execute(receiver, (Class) args[ARGUMENT_OFFSET], null, context, true);
            }

        }

        private static class AsTypeLiteralNode extends InteropNode {

            @Child ToHostNode toHost = ToHostNodeGen.create();

            protected AsTypeLiteralNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, TypeLiteral.class};
            }

            @Override
            protected String getOperationName() {
                return "as";
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                TypeLiteral typeLiteral = (TypeLiteral) args[ARGUMENT_OFFSET];
                return toHost.execute(receiver, typeLiteral.getRawType(), typeLiteral.getType(), context, true);
            }

        }

        abstract static class IsNativePointerNode extends InteropNode {

            protected IsNativePointerNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isNativePointer";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary natives) {
                return natives.isPointer(receiver);
            }

        }

        abstract static class AsNativePointerNode extends InteropNode {

            protected AsNativePointerNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "asNativePointer";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary natives,
                            @Cached BranchProfile unsupported) {
                try {
                    return natives.asPointer(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw cannotConvert(context, receiver, long.class, "asNativePointer()", "isNativeObject()", "Value cannot be converted to a native pointer.");
                }
            }

        }

        abstract static class HasArrayElementsNode extends InteropNode {

            protected HasArrayElementsNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "hasArrayElements";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary arrays) {
                return arrays.hasArrayElements(receiver);
            }

        }

        abstract static class GetMemberKeysNode extends InteropNode {

            protected GetMemberKeysNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "getMemberKeys";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached("createToHost()") ToHostValueNode toHost,
                            @Cached BranchProfile unsupported) {
                try {
                    return toHost.execute(context, objects.getMembers(receiver));
                } catch (UnsupportedMessageException e) {
                    return null;
                }
            }
        }

        abstract static class GetArrayElementNode extends InteropNode {

            protected GetArrayElementNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class};
            }

            @Override
            protected String getOperationName() {
                return "getArrayElement";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary arrays,
                            @Cached("createToHost()") ToHostValueNode toHost,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile unknown) {
                long index = (long) args[ARGUMENT_OFFSET];
                try {
                    return toHost.execute(context, arrays.readArrayElement(receiver, index));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    return getArrayElementUnsupported(context, receiver);
                } catch (InvalidArrayIndexException e) {
                    unknown.enter();
                    throw invalidArrayIndex(context, receiver, index);
                }
            }
        }

        abstract static class SetArrayElementNode extends InteropNode {
            protected SetArrayElementNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class, null};
            }

            @Override
            protected String getOperationName() {
                return "setArrayElement";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary arrays,
                            @Cached ToGuestValueNode toGuestValue,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile invalidIndex,
                            @Cached BranchProfile invalidValue) {
                long index = (long) args[ARGUMENT_OFFSET];
                Object value = toGuestValue.execute(context, args[ARGUMENT_OFFSET + 1]);
                try {
                    arrays.writeArrayElement(receiver, index, value);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    setArrayElementUnsupported(context, receiver);
                } catch (UnsupportedTypeException e) {
                    invalidValue.enter();
                    throw invalidArrayValue(context, receiver, index, value);
                } catch (InvalidArrayIndexException e) {
                    invalidIndex.enter();
                    throw invalidArrayIndex(context, receiver, index);
                }
                return null;
            }
        }

        abstract static class RemoveArrayElementNode extends InteropNode {

            protected RemoveArrayElementNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class};
            }

            @Override
            protected String getOperationName() {
                return "removeArrayElement";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary arrays,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile invalidIndex) {
                long index = (long) args[ARGUMENT_OFFSET];
                Object value;
                try {
                    arrays.removeArrayElement(receiver, index);
                    value = Boolean.TRUE;
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw removeArrayElementUnsupported(context, receiver);
                } catch (InvalidArrayIndexException e) {
                    invalidIndex.enter();
                    throw invalidArrayIndex(context, receiver, index);
                }
                return value;
            }

        }

        abstract static class GetArraySizeNode extends InteropNode {

            protected GetArraySizeNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "getArraySize";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary arrays,
                            @Cached BranchProfile unsupported) {
                try {
                    return arrays.getArraySize(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    return getArraySizeUnsupported(context, receiver);
                }
            }

        }

        abstract static class GetMemberNode extends InteropNode {

            protected GetMemberNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class};
            }

            @Override
            protected String getOperationName() {
                return "getMember";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached("createToHost()") ToHostValueNode toHost,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile unknown) {
                String key = (String) args[ARGUMENT_OFFSET];
                Object value;
                try {
                    assert key != null : "should be handled already";
                    value = toHost.execute(context, objects.readMember(receiver, key));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (objects.hasMembers(receiver)) {
                        value = null;
                    } else {
                        return getMemberUnsupported(context, receiver, key);
                    }
                } catch (UnknownIdentifierException e) {
                    unknown.enter();
                    value = null;
                }
                return value;
            }

        }

        abstract static class PutMemberNode extends InteropNode {

            protected PutMemberNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected String getOperationName() {
                return "putMember";
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class, null};
            }

            @Specialization
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary(limit = "CACHE_LIMIT") InteropLibrary objects,
                            @Cached ToGuestValueNode toGuestValue,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile invalidValue,
                            @Cached BranchProfile unknown) {
                String key = (String) args[ARGUMENT_OFFSET];
                Object originalValue = args[ARGUMENT_OFFSET + 1];
                Object value = toGuestValue.execute(context, originalValue);
                assert key != null;
                try {
                    objects.writeMember(receiver, key, value);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw putMemberUnsupported(context, receiver);
                } catch (UnknownIdentifierException e) {
                    unknown.enter();
                    throw invalidMemberKey(context, receiver, key);
                } catch (UnsupportedTypeException e) {
                    invalidValue.enter();
                    throw invalidMemberValue(context, receiver, key, value);
                }
                return null;
            }
        }

        abstract static class RemoveMemberNode extends InteropNode {

            protected RemoveMemberNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected String getOperationName() {
                return "removeMember";
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class};
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported,
                            @Cached BranchProfile unknown) {
                String key = (String) args[ARGUMENT_OFFSET];
                Object value;
                try {
                    assert key != null : "should be handled already";
                    objects.removeMember(receiver, key);
                    value = Boolean.TRUE;
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    if (!objects.hasMembers(receiver) || objects.isMemberExisting(receiver, key)) {
                        throw removeMemberUnsupported(context, receiver);
                    } else {
                        value = Boolean.FALSE;
                    }
                } catch (UnknownIdentifierException e) {
                    unknown.enter();
                    value = Boolean.FALSE;
                }
                return value;
            }

        }

        abstract static class IsNullNode extends InteropNode {

            protected IsNullNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isNull";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary values) {
                return values.isNull(receiver);
            }

        }

        abstract static class HasMembersNode extends InteropNode {

            protected HasMembersNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "hasMembers";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.hasMembers(receiver);
            }

        }

        private abstract static class AbstractMemberInfoNode extends InteropNode {

            protected AbstractMemberInfoNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected final Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class};
            }

        }

        abstract static class HasMemberNode extends AbstractMemberInfoNode {

            protected HasMemberNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected String getOperationName() {
                return "hasMember";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                String key = (String) args[ARGUMENT_OFFSET];
                return objects.isMemberExisting(receiver, key);
            }
        }

        abstract static class CanInvokeNode extends AbstractMemberInfoNode {

            protected CanInvokeNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected String getOperationName() {
                return "canInvoke";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary objects) {
                String key = (String) args[ARGUMENT_OFFSET];
                return objects.isMemberInvocable(receiver, key);
            }

        }

        abstract static class CanExecuteNode extends InteropNode {

            protected CanExecuteNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected String getOperationName() {
                return "canExecute";
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary executables) {
                return executables.isExecutable(receiver);
            }

        }

        abstract static class CanInstantiateNode extends InteropNode {

            protected CanInstantiateNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "canInstantiate";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary instantiables) {
                return instantiables.isInstantiable(receiver);
            }

        }

        private abstract static class AbstractExecuteNode extends InteropNode {

            @Child private InteropLibrary executables = InteropLibrary.getFactory().createDispatched(CACHE_LIMIT);
            private final ToGuestValuesNode toGuestValues = ToGuestValuesNode.create();
            private final BranchProfile invalidArgument = BranchProfile.create();
            private final BranchProfile arity = BranchProfile.create();
            private final BranchProfile unsupported = BranchProfile.create();

            protected AbstractExecuteNode(InteropCodeCache interop) {
                super(interop);
            }

            protected final Object executeShared(PolyglotLanguageContext context, Object receiver, Object[] args) {
                Object[] guestArguments = toGuestValues.apply(context, args);
                try {
                    return executables.execute(receiver, guestArguments);
                } catch (UnsupportedTypeException e) {
                    invalidArgument.enter();
                    throw invalidExecuteArgumentType(context, receiver, e);
                } catch (ArityException e) {
                    arity.enter();
                    throw invalidExecuteArity(context, receiver, guestArguments, e.getExpectedArity(), e.getActualArity());
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw executeUnsupported(context, receiver);
                }
            }

        }

        private static class ExecuteVoidNode extends AbstractExecuteNode {

            protected ExecuteVoidNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Object[].class};
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                executeShared(context, receiver, (Object[]) args[ARGUMENT_OFFSET]);
                return null;
            }

            @Override
            protected String getOperationName() {
                return "executeVoid";
            }

        }

        private static class ExecuteVoidNoArgsNode extends AbstractExecuteNode {

            private static final Object[] NO_ARGS = new Object[0];

            protected ExecuteVoidNoArgsNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                executeShared(context, receiver, NO_ARGS);
                return null;
            }

            @Override
            protected String getOperationName() {
                return "executeVoid";
            }

        }

        private static class ExecuteNode extends AbstractExecuteNode {

            private final ToHostValueNode toHostValue;

            protected ExecuteNode(InteropCodeCache interop) {
                super(interop);
                this.toHostValue = ToHostValueNode.create(interop.languageInstance.language.getImpl());
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Object[].class};
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                return toHostValue.execute(context, executeShared(context, receiver, (Object[]) args[ARGUMENT_OFFSET]));
            }

            @Override
            protected String getOperationName() {
                return "execute";
            }

        }

        private static class ExecuteNoArgsNode extends AbstractExecuteNode {

            private final ToHostValueNode toHostValue;

            protected ExecuteNoArgsNode(InteropCodeCache interop) {
                super(interop);
                this.toHostValue = ToHostValueNode.create(interop.languageInstance.language.getImpl());
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                return toHostValue.execute(context, executeShared(context, receiver, ExecuteVoidNoArgsNode.NO_ARGS));
            }

            @Override
            protected String getOperationName() {
                return "execute";
            }

        }

        abstract static class NewInstanceNode extends InteropNode {

            private final ToGuestValuesNode toGuestValues = ToGuestValuesNode.create();
            private final ToHostValueNode toHostValue;

            protected NewInstanceNode(InteropCodeCache interop) {
                super(interop);
                this.toHostValue = ToHostValueNode.create(interop.languageInstance.language.getImpl());
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Object[].class};
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, //
                            @CachedLibrary("receiver") InteropLibrary instantiables,
                            @Cached ToGuestValuesNode toGuestValues,
                            @Cached("createToHost()") ToHostValueNode toHostValue,
                            @Cached BranchProfile arity,
                            @Cached BranchProfile invalidArgument,
                            @Cached BranchProfile unsupported) {
                Object[] instantiateArguments = toGuestValues.apply(context, (Object[]) args[ARGUMENT_OFFSET]);
                try {
                    return toHostValue.execute(context, instantiables.instantiate(receiver, instantiateArguments));
                } catch (UnsupportedTypeException e) {
                    invalidArgument.enter();
                    throw invalidInstantiateArgumentType(context, receiver, instantiateArguments);
                } catch (ArityException e) {
                    arity.enter();
                    throw invalidInstantiateArity(context, receiver, instantiateArguments, e.getExpectedArity(), e.getActualArity());
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    return newInstanceUnsupported(context, receiver);
                }
            }

            @Override
            protected String getOperationName() {
                return "newInstance";
            }

        }

        private abstract static class AbstractInvokeNode extends InteropNode {

            @Child private InteropLibrary objects = InteropLibrary.getFactory().createDispatched(CACHE_LIMIT);
            private final ToHostValueNode toHostValue;
            private final BranchProfile invalidArgument = BranchProfile.create();
            private final BranchProfile arity = BranchProfile.create();
            private final BranchProfile unsupported = BranchProfile.create();
            private final BranchProfile unknownIdentifier = BranchProfile.create();

            protected AbstractInvokeNode(InteropCodeCache interop) {
                super(interop);
                this.toHostValue = ToHostValueNode.create(interop.languageInstance.language.getImpl());
            }

            protected final Object executeShared(PolyglotLanguageContext context, Object receiver, String key, Object[] guestArguments) {
                try {
                    return toHostValue.execute(context, objects.invokeMember(receiver, key, guestArguments));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw invokeUnsupported(context, receiver, key);
                } catch (UnknownIdentifierException e) {
                    unknownIdentifier.enter();
                    throw invalidMemberKey(context, receiver, key);
                } catch (UnsupportedTypeException e) {
                    invalidArgument.enter();
                    throw invalidInvokeArgumentType(context, receiver, key, e);
                } catch (ArityException e) {
                    arity.enter();
                    throw invalidInvokeArity(context, receiver, key, guestArguments, e.getExpectedArity(), e.getActualArity());
                }
            }

        }

        private static class InvokeNode extends AbstractInvokeNode {

            private final ToGuestValuesNode toGuestValues = ToGuestValuesNode.create();

            protected InvokeNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class, Object[].class};
            }

            @Override
            protected String getOperationName() {
                return "invoke";
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                String key = (String) args[ARGUMENT_OFFSET];
                Object[] guestArguments = toGuestValues.apply(context, (Object[]) args[ARGUMENT_OFFSET + 1]);
                return executeShared(context, receiver, key, guestArguments);
            }

        }

        private static class InvokeNoArgsNode extends AbstractInvokeNode {

            protected InvokeNoArgsNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, String.class};
            }

            @Override
            protected String getOperationName() {
                return "invoke";
            }

            @Override
            protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) {
                String key = (String) args[ARGUMENT_OFFSET];
                return executeShared(context, receiver, key, ExecuteVoidNoArgsNode.NO_ARGS);
            }

        }

        abstract static class IsExceptionNode extends InteropNode {

            protected IsExceptionNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isException";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isException(receiver);
            }
        }

        abstract static class ThrowExceptionNode extends InteropNode {

            protected ThrowExceptionNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "throwException";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached BranchProfile unsupported) {
                try {
                    throw objects.throwException(receiver);
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw unsupported(context, receiver, "throwException()", "isException()");
                }
            }
        }

        abstract static class IsMetaObjectNode extends InteropNode {

            protected IsMetaObjectNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "isMetaObject";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static boolean doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects) {
                return objects.isMetaObject(receiver);
            }
        }

        abstract static class GetMetaQualifiedNameNode extends InteropNode {

            protected GetMetaQualifiedNameNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "getMetaQualifiedName";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static String doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @CachedLibrary(limit = "1") InteropLibrary toString,
                            @Cached BranchProfile unsupported) {
                try {
                    return toString.asString(objects.getMetaQualifiedName(receiver));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw unsupported(context, receiver, "throwException()", "isException()");
                }
            }
        }

        abstract static class GetMetaSimpleNameNode extends InteropNode {

            protected GetMetaSimpleNameNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType};
            }

            @Override
            protected String getOperationName() {
                return "getMetaSimpleName";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static String doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @CachedLibrary(limit = "1") InteropLibrary toString,
                            @Cached BranchProfile unsupported) {
                try {
                    return toString.asString(objects.getMetaSimpleName(receiver));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw unsupported(context, receiver, "throwException()", "isException()");
                }
            }
        }

        abstract static class IsMetaInstanceNode extends InteropNode {

            protected IsMetaInstanceNode(InteropCodeCache interop) {
                super(interop);
            }

            @Override
            protected Class[] getArgumentTypes() {
                return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, null};
            }

            @Override
            protected String getOperationName() {
                return "isMetaInstance";
            }

            @Specialization(limit = "CACHE_LIMIT")
            static boolean doCached(PolyglotLanguageContext context, Object receiver, Object[] args,
                            @CachedLibrary("receiver") InteropLibrary objects,
                            @Cached ToGuestValueNode toGuest,
                            @Cached BranchProfile unsupported) {
                try {
                    return objects.isMetaInstance(receiver, toGuest.execute(context, args[ARGUMENT_OFFSET]));
                } catch (UnsupportedMessageException e) {
                    unsupported.enter();
                    throw unsupported(context, receiver, "throwException()", "isException()");
                }
            }
        }

    }

    static final class PrimitiveValue extends PolyglotValue {

        private final InteropLibrary interop;

        PrimitiveValue(PolyglotImpl polyglot, PolyglotLanguageContext context, Object primitiveValue) {
            super(polyglot, context);
            /*
             * No caching needed for primitives. We do that to avoid the overhead of crossing a
             * Truffle call boundary.
             */
            this.interop = InteropLibrary.getFactory().getUncached(primitiveValue);
        }

        @Override
        public boolean isString(Object receiver) {
            return interop.isString(receiver);
        }

        @Override
        public boolean isBoolean(Object receiver) {
            return interop.isBoolean(receiver);
        }

        @Override
        public boolean asBoolean(Object receiver) {
            try {
                return interop.asBoolean(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asBoolean(receiver);
            }
        }

        @Override
        public String asString(Object receiver) {
            try {
                return interop.asString(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asString(receiver);
            }
        }

        @Override
        public boolean isNumber(Object receiver) {
            return interop.isNumber(receiver);
        }

        @Override
        public boolean fitsInByte(Object receiver) {
            return interop.fitsInByte(receiver);
        }

        @Override
        public boolean fitsInShort(Object receiver) {
            return interop.fitsInShort(receiver);
        }

        @Override
        public boolean fitsInInt(Object receiver) {
            return interop.fitsInInt(receiver);
        }

        @Override
        public boolean fitsInLong(Object receiver) {
            return interop.fitsInLong(receiver);
        }

        @Override
        public boolean fitsInFloat(Object receiver) {
            return interop.fitsInFloat(receiver);
        }

        @Override
        public boolean fitsInDouble(Object receiver) {
            return interop.fitsInDouble(receiver);
        }

        @Override
        public byte asByte(Object receiver) {
            try {
                return interop.asByte(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asByte(receiver);
            }
        }

        @Override
        public short asShort(Object receiver) {
            try {
                return interop.asShort(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asShort(receiver);
            }
        }

        @Override
        public int asInt(Object receiver) {
            try {
                return interop.asInt(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asInt(receiver);
            }
        }

        @Override
        public long asLong(Object receiver) {
            try {
                return interop.asLong(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asLong(receiver);
            }
        }

        @Override
        public float asFloat(Object receiver) {
            try {
                return interop.asFloat(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asFloat(receiver);
            }
        }

        @Override
        public double asDouble(Object receiver) {
            try {
                return interop.asDouble(receiver);
            } catch (UnsupportedMessageException e) {
                return super.asDouble(receiver);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T as(Object receiver, Class targetType) {
            try {
                return (T) ToHostNodeGen.getUncached().execute(receiver, targetType, targetType, languageContext, true);
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException(languageContext, e);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T as(Object receiver, TypeLiteral targetType) {
            return as(receiver, targetType.getRawType());
        }

        @Override
        public Value getMetaObjectImpl(Object receiver) {
            return super.getMetaObjectImpl(getLanguageView(receiver));
        }

        @Override
        protected String toStringImpl(Object receiver) throws AssertionError {
            return super.toStringImpl(getLanguageView(receiver));
        }

        private Object getLanguageView(Object receiver) {
            if (languageContext == null) {
                return receiver;
            }
            return languageContext.getLanguageViewNoCheck(receiver);
        }

    }

    private static final class HostNull extends PolyglotValue {

        private final PolyglotImpl polyglot;

        HostNull(PolyglotImpl polyglot) {
            super(polyglot, null);
            this.polyglot = polyglot;
        }

        @Override
        public boolean isNull(Object receiver) {
            return true;
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T as(Object receiver, Class targetType) {
            if (targetType == Value.class) {
                return (T) polyglot.hostNull;
            }
            return null;
        }

        @SuppressWarnings("cast")
        @Override
        public  T as(Object receiver, TypeLiteral targetType) {
            return as(receiver, (Class) targetType.getRawType());
        }

    }

    abstract static class InteropNode extends HostToGuestRootNode {

        protected static final int CACHE_LIMIT = 5;

        protected final InteropCodeCache polyglot;

        protected abstract String getOperationName();

        protected InteropNode(InteropCodeCache polyglot) {
            this.polyglot = polyglot;
        }

        protected abstract Class[] getArgumentTypes();

        @Override
        protected Class getReceiverType() {
            return polyglot.receiverType;
        }

        protected final ToHostValueNode createToHost() {
            return ToHostValueNode.create(getImpl());
        }

        @Override
        public final String getName() {
            return "org.graalvm.polyglot.Value<" + polyglot.receiverType.getSimpleName() + ">." + getOperationName();
        }

        protected final PolyglotImpl getImpl() {
            return polyglot.languageInstance.language.getImpl();
        }

        @Override
        public final String toString() {
            return getName();
        }

    }

    /**
     * Host value implementation used when a Value needs to be created but not context is available.
     * If a context is available the normal interop value implementation is used.
     */
    static final class HostValue extends PolyglotValue {

        HostValue(PolyglotImpl polyglot) {
            super(polyglot, null);
        }

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

        @Override
        public Object asHostObject(Object receiver) {
            return HostObject.valueOf(receiver);
        }

        @Override
        public boolean isProxyObject(Object receiver) {
            return PolyglotProxy.isProxyGuestObject(receiver);
        }

        @Override
        public Object asProxyObject(Object receiver) {
            return PolyglotProxy.toProxyHostObject((TruffleObject) receiver);
        }

        @Override
        public  T as(Object receiver, Class targetType) {
            return asImpl(receiver, targetType);
        }

        @SuppressWarnings("cast")
        @Override
        public  T as(Object receiver, TypeLiteral targetType) {
            return asImpl(receiver, (Class) targetType.getRawType());
        }

         T asImpl(Object receiver, Class targetType) {
            Object hostValue;
            if (isProxyObject(receiver)) {
                hostValue = asProxyObject(receiver);
            } else if (isHostObject(receiver)) {
                hostValue = asHostObject(receiver);
            } else {
                throw new ClassCastException();
            }
            return targetType.cast(hostValue);
        }

    }

    private static final class InteropValue extends PolyglotValue {

        private final InteropCodeCache cache;

        InteropValue(PolyglotLanguageContext context, InteropCodeCache codeCache) {
            super(context);
            this.cache = codeCache;
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T as(Object receiver, Class targetType) {
            return (T) RUNTIME.callProfiled(cache.asClassLiteral, languageContext, receiver, targetType);
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T as(Object receiver, TypeLiteral targetType) {
            return (T) RUNTIME.callProfiled(cache.asTypeLiteral, languageContext, receiver, targetType);
        }

        @Override
        public boolean isNativePointer(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isNativePointer, languageContext, receiver);
        }

        @Override
        public boolean hasArrayElements(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.hasArrayElements, languageContext, receiver);
        }

        @Override
        public Value getArrayElement(Object receiver, long index) {
            return (Value) RUNTIME.callProfiled(cache.getArrayElement, languageContext, receiver, index);
        }

        @Override
        public void setArrayElement(Object receiver, long index, Object value) {
            RUNTIME.callProfiled(cache.setArrayElement, languageContext, receiver, index, value);
        }

        @Override
        public boolean removeArrayElement(Object receiver, long index) {
            return (boolean) RUNTIME.callProfiled(cache.removeArrayElement, languageContext, receiver, index);
        }

        @Override
        public long getArraySize(Object receiver) {
            return (long) RUNTIME.callProfiled(cache.getArraySize, languageContext, receiver);
        }

        @Override
        public boolean hasMembers(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.hasMembers, languageContext, receiver);
        }

        @Override
        public Value getMember(Object receiver, String key) {
            return (Value) RUNTIME.callProfiled(cache.getMember, languageContext, receiver, key);
        }

        @Override
        public boolean hasMember(Object receiver, String key) {
            return (boolean) RUNTIME.callProfiled(cache.hasMember, languageContext, receiver, key);
        }

        @Override
        public void putMember(Object receiver, String key, Object member) {
            RUNTIME.callProfiled(cache.putMember, languageContext, receiver, key, member);
        }

        @Override
        public boolean removeMember(Object receiver, String key) {
            return (boolean) RUNTIME.callProfiled(cache.removeMember, languageContext, receiver, key);
        }

        @Override
        public Set getMemberKeys(Object receiver) {
            Value keys = (Value) RUNTIME.callProfiled(cache.getMemberKeys, languageContext, receiver);
            if (keys == null) {
                // unsupported
                return Collections.emptySet();
            }
            return new MemberSet(receiver, keys);
        }

        @Override
        public long asNativePointer(Object receiver) {
            return (long) RUNTIME.callProfiled(cache.asNativePointer, languageContext, receiver);
        }

        @Override
        public boolean isDate(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isDate, languageContext, receiver);
        }

        @Override
        public LocalDate asDate(Object receiver) {
            return (LocalDate) RUNTIME.callProfiled(cache.asDate, languageContext, receiver);
        }

        @Override
        public boolean isTime(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isTime, languageContext, receiver);
        }

        @Override
        public LocalTime asTime(Object receiver) {
            return (LocalTime) RUNTIME.callProfiled(cache.asTime, languageContext, receiver);
        }

        @Override
        public boolean isTimeZone(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isTimeZone, languageContext, receiver);
        }

        @Override
        public ZoneId asTimeZone(Object receiver) {
            return (ZoneId) RUNTIME.callProfiled(cache.asTimeZone, languageContext, receiver);
        }

        @Override
        public Instant asInstant(Object receiver) {
            return (Instant) RUNTIME.callProfiled(cache.asInstant, languageContext, receiver);
        }

        @Override
        public boolean isDuration(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isDuration, languageContext, receiver);
        }

        @Override
        public Duration asDuration(Object receiver) {
            return (Duration) RUNTIME.callProfiled(cache.asDuration, languageContext, receiver);
        }

        @Override
        public boolean isHostObject(Object receiver) {
            return cache.isHost;
        }

        @Override
        public boolean isProxyObject(Object receiver) {
            return cache.isProxy;
        }

        @Override
        public Object asProxyObject(Object receiver) {
            if (cache.isProxy) {
                return PolyglotProxy.toProxyHostObject((TruffleObject) receiver);
            } else {
                return super.asProxyObject(receiver);
            }
        }

        @Override
        public Object asHostObject(Object receiver) {
            if (cache.isHost) {
                return HostObject.valueOf(receiver);
            } else {
                return super.asHostObject(receiver);
            }
        }

        @Override
        public boolean isNull(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isNull, languageContext, receiver);
        }

        @Override
        public boolean canExecute(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.canExecute, languageContext, receiver);
        }

        @Override
        public void executeVoid(Object receiver, Object[] arguments) {
            RUNTIME.callProfiled(cache.executeVoid, languageContext, receiver, arguments);
        }

        @Override
        public void executeVoid(Object receiver) {
            RUNTIME.callProfiled(cache.executeVoidNoArgs, languageContext, receiver);
        }

        @Override
        public Value execute(Object receiver, Object[] arguments) {
            return (Value) RUNTIME.callProfiled(cache.execute, languageContext, receiver, arguments);
        }

        @Override
        public Value execute(Object receiver) {
            return (Value) RUNTIME.callProfiled(cache.executeNoArgs, languageContext, receiver);
        }

        @Override
        public boolean canInstantiate(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.canInstantiate, languageContext, receiver);
        }

        @Override
        public Value newInstance(Object receiver, Object[] arguments) {
            return (Value) RUNTIME.callProfiled(cache.newInstance, languageContext, receiver, arguments);
        }

        @Override
        public boolean canInvoke(String identifier, Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.canInvoke, languageContext, receiver, identifier);
        }

        @Override
        public Value invoke(Object receiver, String identifier, Object[] arguments) {
            return (Value) RUNTIME.callProfiled(cache.invoke, languageContext, receiver, identifier, arguments);
        }

        @Override
        public Value invoke(Object receiver, String identifier) {
            return (Value) RUNTIME.callProfiled(cache.invokeNoArgs, languageContext, receiver, identifier);
        }

        @Override
        public boolean isException(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isException, languageContext, receiver);
        }

        @Override
        public RuntimeException throwException(Object receiver) {
            RUNTIME.callProfiled(cache.throwException, languageContext, receiver);
            throw super.throwException(receiver);
        }

        @Override
        public boolean isNumber(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.isNumber(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInByte(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.fitsInByte(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public byte asByte(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asByte(receiver);
                } catch (UnsupportedMessageException e) {
                    return asByteUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean isString(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.isString(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public String asString(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    if (isNullUncached(receiver)) {
                        return null;
                    }
                    return UNCACHED_INTEROP.asString(receiver);
                } catch (UnsupportedMessageException e) {
                    return asStringUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInInt(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.fitsInInt(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public int asInt(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asInt(receiver);
                } catch (UnsupportedMessageException e) {
                    return asIntUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean isBoolean(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return InteropLibrary.getFactory().getUncached().isBoolean(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean asBoolean(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return InteropLibrary.getFactory().getUncached().asBoolean(receiver);
                } catch (UnsupportedMessageException e) {
                    return asBooleanUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInFloat(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return InteropLibrary.getFactory().getUncached().fitsInFloat(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public float asFloat(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asFloat(receiver);
                } catch (UnsupportedMessageException e) {
                    return asFloatUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInDouble(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.fitsInDouble(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public double asDouble(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asDouble(receiver);
                } catch (UnsupportedMessageException e) {
                    return asDoubleUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInLong(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.fitsInLong(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public long asLong(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asLong(receiver);
                } catch (UnsupportedMessageException e) {
                    return asLongUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean fitsInShort(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.fitsInShort(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public short asShort(Object receiver) {
            try {
                Object c = hostEnter(languageContext);
                try {
                    return UNCACHED_INTEROP.asShort(receiver);
                } catch (UnsupportedMessageException e) {
                    return asShortUnsupported(receiver);
                } finally {
                    hostLeave(languageContext, c);
                }
            } catch (Throwable e) {
                throw PolyglotImpl.guestToHostException((languageContext), e);
            }
        }

        @Override
        public boolean isMetaObject(Object receiver) {
            return (boolean) RUNTIME.callProfiled(cache.isMetaObject, languageContext, receiver);
        }

        @Override
        public boolean isMetaInstance(Object receiver, Object instance) {
            return (boolean) RUNTIME.callProfiled(cache.isMetaInstance, languageContext, receiver, instance);
        }

        @Override
        public String getMetaQualifiedName(Object receiver) {
            return (String) RUNTIME.callProfiled(cache.getMetaQualifiedName, languageContext, receiver);
        }

        @Override
        public String getMetaSimpleName(Object receiver) {
            return (String) RUNTIME.callProfiled(cache.getMetaSimpleName, languageContext, receiver);
        }

        private final class MemberSet extends AbstractSet {

            private final Object receiver;
            private final Value keys;
            private int cachedSize = -1;

            MemberSet(Object receiver, Value keys) {
                this.receiver = receiver;
                this.keys = keys;
            }

            @Override
            public boolean contains(Object o) {
                if (!(o instanceof String)) {
                    return false;
                }
                return hasMember(receiver, (String) o);
            }

            @Override
            public Iterator iterator() {
                return new Iterator() {

                    int index = 0;

                    public boolean hasNext() {
                        return index < size();
                    }

                    public String next() {
                        if (index >= size()) {
                            throw new NoSuchElementException();
                        }
                        Value arrayElement = keys.getArrayElement(index++);
                        if (arrayElement.isString()) {
                            return arrayElement.asString();
                        } else {
                            return null;
                        }
                    }
                };
            }

            @Override
            public int size() {
                int size = this.cachedSize;
                if (size != -1) {
                    return size;
                }
                cachedSize = size = (int) keys.getArraySize();
                return size;
            }

        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy