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

com.oracle.truffle.api.interop.InteropLibrary Maven / Gradle / Ivy

Go to download

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

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2018, 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.api.interop;

import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
import static com.oracle.truffle.api.interop.AssertUtils.assertString;
import static com.oracle.truffle.api.interop.AssertUtils.preCondition;
import static com.oracle.truffle.api.interop.AssertUtils.validArgument;
import static com.oracle.truffle.api.interop.AssertUtils.validArguments;
import static com.oracle.truffle.api.interop.AssertUtils.validNonInteropArgument;
import static com.oracle.truffle.api.interop.AssertUtils.validReturn;
import static com.oracle.truffle.api.interop.AssertUtils.validScope;
import static com.oracle.truffle.api.interop.AssertUtils.violationInvariant;
import static com.oracle.truffle.api.interop.AssertUtils.violationPost;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRules;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleLanguage.Registration;
import com.oracle.truffle.api.TruffleStackTraceElement;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.impl.Accessor.EngineSupport;
import com.oracle.truffle.api.interop.InteropLibrary.Asserts;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.GenerateLibrary;
import com.oracle.truffle.api.library.GenerateLibrary.Abstract;
import com.oracle.truffle.api.library.GenerateLibrary.DefaultExport;
import com.oracle.truffle.api.library.Library;
import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.TriState;

/**
 * Represents the library that specifies the interoperability message protocol between Truffle
 * languages, tools and embedders. Every method represents one message specified by the protocol. In
 * the following text we will abbreviate interoperability with interop.
 * 

* The interop API differentiates between the source and the target language. The source language is * the language that implements/exports the message implementations. The implementations map types * of the source language to the interop protocol as it is specified by the protocol. For example, * language values that represent arrays or array like structures should implement the messages for * {@link #hasArrayElements(Object) array based access}. This allows the target language to call the * protocol without knowledge of the concrete source language. The target language embeds the * interop protocol semantics as part of their existing language semantics. For example, language * operations that access array elements in the target language should call * {@link #hasArrayElements(Object) array access} messages for interop values. *

* The interop protocol only allows interop values to be used as receivers, return values or * parameters of messages. Allowed Java types of interop values are: *

    *
  • {@link TruffleObject}: Any subclass of {@link TruffleObject} is interpreted depending on the * interop messages it {@link ExportLibrary exports}. Truffle objects are expected but not required * to export interop library messages. *
  • {@link String} and {@link Character} are interpreted as {@link #isString(Object) string} * value. *
  • {@link Boolean} is interpreted as {@link #isBoolean(Object) boolean} value. *
  • {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, {@link Float} and {@link Double} * are interpreted as {@link #isNumber(Object) number} values. *
* Note that {@code null} is never a valid interop value. Instead, use a * {@link TruffleObject} which implements {@link #isNull(Object)} message. *

* The following type combinations are mutually exclusive and cannot return true for * the type check message of the same receiver value: *

    *
  • {@link #isNull(Object) Null} *
  • {@link #isBoolean(Object) Boolean} *
  • {@link #isString(Object) String} *
  • {@link #isNumber(Object) Number} *
  • {@link #isDate(Object) Date}, {@link #isTime(Object) Time} or {@link #isTimeZone(Object) * TimeZone} *
  • {@link #isDuration(Object) Duration} *
  • {@link #isException(Object) Exception} *
  • {@link #isMetaObject(Object) Meta-Object} *
* All receiver values may have none, one or multiple of the following traits: *
    *
  • {@link #isExecutable(Object) executable} *
  • {@link #isInstantiable(Object) instantiable} *
  • {@link #isPointer(Object) pointer} *
  • {@link #hasMembers(Object) members} *
  • {@link #hasArrayElements(Object) array elements} *
  • {@link #hasLanguage(Object) language} *
  • {@link #hasMetaObject(Object) associated metaobject} *
  • {@link #hasDeclaringMetaObject(Object) declaring meta object} *
  • {@link #hasSourceLocation(Object) source location} *
  • {@link #hasIdentity(Object) identity} *
  • {@link #hasScopeParent(Object) scope parent} *
  • {@link #hasExecutableName(Object) executable name} *
  • {@link #hasExceptionMessage(Object) exception message} *
  • {@link #hasExceptionCause(Object) exception cause} *
  • {@link #hasExceptionStackTrace(Object) exception stack trace} *
      *

      *

      Naive and aware dates and times

      *

      * If a date or time value has a {@link #isTimeZone(Object) timezone} then it is called aware * , otherwise naive *

      * An aware time and date has sufficient knowledge of applicable algorithmic and political time * adjustments, such as time zone and daylight saving time information, to locate itself relative to * other aware objects. An aware object is used to represent a specific moment in time that is not * open to interpretation. *

      * A naive time and date does not contain enough information to unambiguously locate itself relative * to other date/time objects. Whether a naive object represents Coordinated Universal Time (UTC), * local time, or time in some other timezone is purely up to the program, just like it is up to the * program whether a particular number represents metres, miles, or mass. Naive objects are easy to * understand and to work with, at the cost of ignoring some aspects of reality. *

      * Interop messages throw {@link InteropException checked exceptions} to indicate error states. The * target language is supposed to always catch those exceptions and translate them into guest * language errors of the target language. Interop message contracts are verified only if assertions * (-ea) are enabled. * * @see com.oracle.truffle.api.library Reference documentation of Truffle Libraries. * @since 19.0 */ @GenerateLibrary(assertions = Asserts.class, receiverType = TruffleObject.class) @DefaultExport(DefaultBooleanExports.class) @DefaultExport(DefaultIntegerExports.class) @DefaultExport(DefaultByteExports.class) @DefaultExport(DefaultShortExports.class) @DefaultExport(DefaultLongExports.class) @DefaultExport(DefaultFloatExports.class) @DefaultExport(DefaultDoubleExports.class) @DefaultExport(DefaultCharacterExports.class) @DefaultExport(DefaultStringExports.class) @SuppressWarnings("unused") public abstract class InteropLibrary extends Library { /** * @since 19.0 */ protected InteropLibrary() { } /** * Returns true if the receiver represents a null like value, else * false. Most object oriented languages have one or many values representing null * values. Invoking this message does not cause any observable side-effects. * * @since 19.0 */ public boolean isNull(Object receiver) { return false; } /** * Returns true if the receiver represents a boolean like value, else * false. Invoking this message does not cause any observable side-effects. * * @see #asBoolean(Object) * @since 19.0 */ // Boolean Messages @Abstract(ifExported = "asBoolean") public boolean isBoolean(Object receiver) { return false; } /** * Returns the Java boolean value if the receiver represents a {@link #isBoolean(Object) * boolean} like value. * * @throws UnsupportedMessageException if and only if {@link #isBoolean(Object)} returns * false for the same receiver. * @see #isBoolean(Object) * @since 19.0 */ @Abstract(ifExported = "isBoolean") public boolean asBoolean(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if the receiver represents an executable value, else * false. Functions, methods or closures are common examples of executable values. * Invoking this message does not cause any observable side-effects. Note that receiver values * which are {@link #isExecutable(Object) executable} might also be * {@link #isInstantiable(Object) instantiable}. * * @see #execute(Object, Object...) * @since 19.0 */ @Abstract(ifExported = "execute") public boolean isExecutable(Object receiver) { return false; } /** * Executes an executable value with the given arguments. * * @throws UnsupportedTypeException if one of the arguments is not compatible to the executable * signature. The exception is thrown on best effort basis, dynamic languages may * throw their own exceptions if the arguments are wrong. * @throws ArityException if the number of expected arguments does not match the number of * actual arguments. * @throws UnsupportedMessageException if and only if {@link #isExecutable(Object)} returns * false for the same receiver. * @see #isExecutable(Object) * @since 19.0 */ @Abstract(ifExported = "isExecutable") public Object execute(Object receiver, Object... arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns {@code true} if the receiver has an executable name. Invoking this message does not * cause any observable side-effects. Returns {@code false} by default. * * @see #getExecutableName(Object) * @since 20.3 */ @Abstract(ifExported = {"getExecutableName"}) public boolean hasExecutableName(Object receiver) { return false; } /** * Returns executable name of the receiver. Throws {@code UnsupportedMessageException} when the * receiver is has no {@link #hasExecutableName(Object) executable name}. The return value is an * interop value that is guaranteed to return true for {@link #isString(Object)}. * * @see #hasExecutableName(Object) * @since 20.3 */ @Abstract(ifExported = {"hasExecutableName"}) public Object getExecutableName(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns {@code true} if the receiver has a declaring meta object. The declaring meta object * is the meta object of the executable or meta object that declares the receiver value. * Invoking this message does not cause any observable side-effects. Returns {@code false} by * default. * * @see #getDeclaringMetaObject(Object) * @since 20.3 */ @Abstract(ifExported = {"getDeclaringMetaObject"}) public boolean hasDeclaringMetaObject(Object receiver) { return false; } /** * Returns declaring meta object. The declaring meta object is the meta object of declaring * executable or meta object. Throws {@code UnsupportedMessageException} when the receiver is * has no {@link #hasDeclaringMetaObject(Object) declaring meta object}. The return value is an * interop value that is guaranteed to return true for * {@link #isMetaObject(Object)}. * * @see #hasDeclaringMetaObject(Object) * @since 20.3 */ @Abstract(ifExported = {"hasDeclaringMetaObject"}) public Object getDeclaringMetaObject(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } // Instantiable Messages /** * Returns true if the receiver represents an instantiable value, else * false. Contructors or {@link #isMetaObject(Object) metaobjects} are typical * examples of instantiable values. Invoking this message does not cause any observable * side-effects. Note that receiver values which are {@link #isExecutable(Object) executable} * might also be {@link #isInstantiable(Object) instantiable}. * * @see #instantiate(Object, Object...) * @see #isMetaObject(Object) * @since 19.0 */ @Abstract(ifExported = "instantiate") public boolean isInstantiable(Object receiver) { return false; } /** * Instantiates the receiver value with the given arguments. The returned object must be * initialized correctly according to the language specification (e.g. by calling the * constructor or initialization routine). * * @throws UnsupportedTypeException if one of the arguments is not compatible to the executable * signature * @throws ArityException if the number of expected arguments does not match the number of * actual arguments. * @throws UnsupportedMessageException if and only if {@link #isInstantiable(Object)} returns * false for the same receiver. * @see #isExecutable(Object) * @since 19.0 */ @Abstract(ifExported = "isInstantiable") public Object instantiate(Object receiver, Object... arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { throw UnsupportedMessageException.create(); } // String Messages /** * Returns true if the receiver represents a string value, else * false. Invoking this message does not cause any observable side-effects. * * @see #asString(Object) * @since 19.0 */ @Abstract(ifExported = "asString") public boolean isString(Object receiver) { return false; } /** * Returns the Java string value if the receiver represents a {@link #isString(Object) string} * like value. * * @throws UnsupportedMessageException if and only if {@link #isString(Object)} returns * false for the same receiver. * @see #isString(Object) * @since 19.0 */ @Abstract(ifExported = "isString") public String asString(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } // Number Messages /** * Returns true if the receiver represents a number value, else * false. Invoking this message does not cause any observable side-effects. * * @see #fitsInByte(Object) * @see #fitsInShort(Object) * @see #fitsInInt(Object) * @see #fitsInLong(Object) * @see #fitsInFloat(Object) * @see #fitsInDouble(Object) * @see #asByte(Object) * @see #asShort(Object) * @see #asInt(Object) * @see #asLong(Object) * @see #asFloat(Object) * @see #asDouble(Object) * @since 19.0 */ @Abstract(ifExported = {"fitsInByte", "fitsInShort", "fitsInInt", "fitsInLong", "fitsInFloat", "fitsInDouble", "asByte", "asShort", "asInt", "asLong", "asFloat", "asDouble"}) public boolean isNumber(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java byte primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asByte(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInByte(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java short primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asShort(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInShort(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java int primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asInt(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInInt(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java long primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asLong(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInLong(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java float primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asFloat(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInFloat(Object receiver) { return false; } /** * Returns true if the receiver represents a number and its value fits * in a Java double primitive without loss of precision, else false. Invoking this * message does not cause any observable side-effects. * * @see #isNumber(Object) * @see #asDouble(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public boolean fitsInDouble(Object receiver) { return false; } /** * Returns the receiver value as Java byte primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInByte(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public byte asByte(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the receiver value as Java short primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInShort(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public short asShort(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the receiver value as Java int primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInInt(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public int asInt(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the receiver value as Java long primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInLong(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public long asLong(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the receiver value as Java float primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInFloat(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public float asFloat(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the receiver value as Java double primitive if the number fits without loss of * precision. Invoking this message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver is not a * {@link #isNumber(Object)} or it does not fit without less of precision. * @see #isNumber(Object) * @see #fitsInDouble(Object) * @since 19.0 */ @Abstract(ifExported = "isNumber") public double asDouble(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } // Member Messages /** * Returns true if the receiver may have members. Therefore, at least one of * {@link #readMember(Object, String)}, {@link #writeMember(Object, String, Object)}, * {@link #removeMember(Object, String)}, {@link #invokeMember(Object, String, Object...)} must * not throw {@link UnsupportedMessageException}. Members are structural elements of a class. * For example, a method or field is a member of a class. Invoking this message does not cause * any observable side-effects. Returns false by default. * * @see #getMembers(Object, boolean) * @see #isMemberReadable(Object, String) * @see #isMemberModifiable(Object, String) * @see #isMemberInvocable(Object, String) * @see #isMemberInsertable(Object, String) * @see #isMemberRemovable(Object, String) * @see #readMember(Object, String) * @see #writeMember(Object, String, Object) * @see #removeMember(Object, String) * @see #invokeMember(Object, String, Object...) * @since 19.0 */ @Abstract(ifExported = {"getMembers", "isMemberReadable", "readMember", "isMemberModifiable", "isMemberInsertable", "writeMember", "isMemberRemovable", "removeMember", "isMemberInvocable", "invokeMember", "isMemberInternal", "hasMemberReadSideEffects", "hasMemberWriteSideEffects", "isScope"}) public boolean hasMembers(Object receiver) { return false; } /** * Returns an array of member name strings. The returned value must return true for * {@link #hasArrayElements(Object)} and every array element must be of type * {@link #isString(Object) string}. The member elements may also provide additional information * like {@link #getSourceLocation(Object) source location} in case of {@link #isScope(Object) * scope} variables, etc. *

      * If the includeInternal argument is true then internal member names are returned * as well. Internal members are implementation specific and should not be exposed to guest * language application. An example of internal members are internal slots in ECMAScript. * * @throws UnsupportedMessageException if and only if the receiver does not have any * {@link #hasMembers(Object) members}. * @see #hasMembers(Object) * @since 19.0 */ @Abstract(ifExported = {"hasMembers", "isScope"}) public Object getMembers(Object receiver, boolean includeInternal) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Short-cut for {@link #getMembers(Object) getMembers(receiver, false)}. Invoking this message * does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver has no * {@link #hasMembers(Object) members}. * @see #getMembers(Object, boolean) * @since 19.0 */ public final Object getMembers(Object receiver) throws UnsupportedMessageException { return getMembers(receiver, false); } /** * Returns true if a given member is {@link #readMember(Object, String) readable}. * This method may only return true if {@link #hasMembers(Object)} returns * true as well and {@link #isMemberInsertable(Object, String)} returns * false. Invoking this message does not cause any observable side-effects. Returns * false by default. * * @see #readMember(Object, String) * @since 19.0 */ @Abstract(ifExported = "readMember") public boolean isMemberReadable(Object receiver, String member) { return false; } /** * Reads the value of a given member. If the member is {@link #isMemberReadable(Object, String) * readable} and {@link #isMemberInvocable(Object, String) invocable} then the result of reading * the member is {@link #isExecutable(Object) executable} and is bound to this receiver. This * method must have not observable side-effects unless * {@link #hasMemberReadSideEffects(Object, String)} returns true. * * @throws UnsupportedMessageException if when the receiver does not support reading at all. An * empty receiver with no readable members supports the read operation (even though * there is nothing to read), therefore it throws {@link UnknownIdentifierException} * for all arguments instead. * @throws UnknownIdentifierException if the given member is not * {@link #isMemberReadable(Object, String) readable}, e.g. when the member with the * given name does not exist. * @see #hasMemberReadSideEffects(Object, String) * @since 19.0 */ @Abstract(ifExported = "isMemberReadable") public Object readMember(Object receiver, String member) throws UnsupportedMessageException, UnknownIdentifierException { throw UnsupportedMessageException.create(); } /** * Returns true if a given member is existing and * {@link #writeMember(Object, String, Object) writable}. This method may only return * true if {@link #hasMembers(Object)} returns true as well and * {@link #isMemberInsertable(Object, String)} returns false. Invoking this message * does not cause any observable side-effects. Returns false by default. * * @see #writeMember(Object, String, Object) * @since 19.0 */ @Abstract(ifExported = "writeMember") public boolean isMemberModifiable(Object receiver, String member) { return false; } /** * Returns true if a given member is not existing and * {@link #writeMember(Object, String, Object) writable}. This method may only return * true if {@link #hasMembers(Object)} returns true as well and * {@link #isMemberExisting(Object, String)} returns false. Invoking this message * does not cause any observable side-effects. Returns false by default. * * @see #writeMember(Object, String, Object) * @since 19.0 */ @Abstract(ifExported = "writeMember") public boolean isMemberInsertable(Object receiver, String member) { return false; } /** * Writes the value of a given member. Writing a member is allowed if is existing and * {@link #isMemberModifiable(Object, String) modifiable}, or not existing and * {@link #isMemberInsertable(Object, String) insertable}. * * This method must have not observable side-effects other than the changed member unless * {@link #hasMemberWriteSideEffects(Object, String) side-effects} are allowed. * * @throws UnsupportedMessageException when the receiver does not support writing at all, e.g. * when it is immutable. * @throws UnknownIdentifierException if the given member is not * {@link #isMemberModifiable(Object, String) modifiable} nor * {@link #isMemberInsertable(Object, String) insertable}. * @throws UnsupportedTypeException if the provided value type is not allowed to be written. * @see #hasMemberWriteSideEffects(Object, String) * @since 19.0 */ @Abstract(ifExported = {"isMemberModifiable", "isMemberInsertable"}) public void writeMember(Object receiver, String member, Object value) throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException { throw UnsupportedMessageException.create(); } /** * Returns true if a given member is existing and removable. This method may only * return true if {@link #hasMembers(Object)} returns true as well and * {@link #isMemberInsertable(Object, String)} returns false. Invoking this message * does not cause any observable side-effects. Returns false by default. * * @see #removeMember(Object, String) * @since 19.0 */ @Abstract(ifExported = "removeMember") public boolean isMemberRemovable(Object receiver, String member) { return false; } /** * Removes a member from the receiver object. Removing member is allowed if is * {@link #isMemberRemovable(Object, String) removable}. * * This method does not have not observable side-effects other than the removed member. * * @throws UnsupportedMessageException when the receiver does not support removing at all, e.g. * when it is immutable. * @throws UnknownIdentifierException if the given member is not * {@link #isMemberRemovable(Object, String)} removable}, e.g. the receiver does not * have a member with the given name. * @see #isMemberRemovable(Object, String) * @since 19.0 */ @Abstract(ifExported = "isMemberRemovable") public void removeMember(Object receiver, String member) throws UnsupportedMessageException, UnknownIdentifierException { throw UnsupportedMessageException.create(); } /** * Returns true if a given member is invocable. This method may only return * true if {@link #hasMembers(Object)} returns true as well and * {@link #isMemberInsertable(Object, String)} returns false. Invoking this message * does not cause any observable side-effects. Returns false by default. * * @see #invokeMember(Object, String, Object...) * @since 19.0 */ @Abstract(ifExported = "invokeMember") public boolean isMemberInvocable(Object receiver, String member) { return false; } /** * Invokes a member for a given receiver and arguments. * * @throws UnknownIdentifierException if the given member does not exist or is not * {@link #isMemberInvocable(Object, String) invocable}. * @throws UnsupportedTypeException if one of the arguments is not compatible to the executable * signature. The exception is thrown on best effort basis, dynamic languages may * throw their own exceptions if the arguments are wrong. * @throws ArityException if the number of expected arguments does not match the number of * actual arguments. * @throws UnsupportedMessageException when the receiver does not support invoking at all, e.g. * when storing executable members is not allowed. * @see #isMemberInvocable(Object, String) * @since 19.0 */ @Abstract(ifExported = "isMemberInvocable") public Object invokeMember(Object receiver, String member, Object... arguments) throws UnsupportedMessageException, ArityException, UnknownIdentifierException, UnsupportedTypeException { throw UnsupportedMessageException.create(); } /** * Returns true if a member is internal. Internal members are not enumerated by * {@link #getMembers(Object, boolean)} by default. Internal members are only relevant to guest * language implementations and tools, but not to guest applications or embedders. An example of * internal members are internal slots in ECMAScript. Invoking this message does not cause any * observable side-effects. Returns false by default. * * @see #getMembers(Object, boolean) * @since 19.0 */ public boolean isMemberInternal(Object receiver, String member) { return false; } /** * Returns true if the member is {@link #isMemberModifiable(Object, String) modifiable} or * {@link #isMemberInsertable(Object, String) insertable}. * * @since 19.0 */ public final boolean isMemberWritable(Object receiver, String member) { return isMemberModifiable(receiver, member) || isMemberInsertable(receiver, member); } /** * Returns true if the member is existing. A member is existing if it is * {@link #isMemberModifiable(Object, String) modifiable}, * {@link #isMemberReadable(Object, String) readable}, {@link #isMemberRemovable(Object, String) * removable} or {@link #isMemberInvocable(Object, String) invocable}. * * @since 19.0 */ public final boolean isMemberExisting(Object receiver, String member) { return isMemberReadable(receiver, member) || isMemberModifiable(receiver, member) || isMemberRemovable(receiver, member) || isMemberInvocable(receiver, member); } /** * Returns true if reading a member may cause a side-effect. Invoking this message * does not cause any observable side-effects. A member read does not cause any side-effects by * default. *

      * For instance in JavaScript a property read may have side-effects if the property has a getter * function. * * @see #readMember(Object, String) * @since 19.0 */ public boolean hasMemberReadSideEffects(Object receiver, String member) { return false; } /** * Returns true if writing a member may cause a side-effect, besides the write * operation of the member. Invoking this message does not cause any observable side-effects. A * member write does not cause any side-effects by default. *

      * For instance in JavaScript a property write may have side-effects if the property has a * setter function. * * @see #writeMember(Object, String, Object) * @since 19.0 */ public boolean hasMemberWriteSideEffects(Object receiver, String member) { return false; } // Array Messages /** * Returns true if the receiver may have array elements. Therefore, At least one of * {@link #readArrayElement(Object, long)}, {@link #writeArrayElement(Object, long, Object)}, * {@link #removeArrayElement(Object, long)} must not throw {#link * {@link UnsupportedMessageException}. For example, the contents of an array or list * datastructure could be interpreted as array elements. Invoking this message does not cause * any observable side-effects. Returns false by default. * * @see #getArraySize(Object) * @since 19.0 */ @Abstract(ifExported = {"readArrayElement", "writeArrayElement", "removeArrayElement", "isArrayElementModifiable", "isArrayElementRemovable", "isArrayElementReadable", "getArraySize"}) public boolean hasArrayElements(Object receiver) { return false; } /** * Reads the value of an array element by index. This method must have not observable * side-effect. * * @throws UnsupportedMessageException when the receiver does not support reading at all. An * empty receiver with no readable array elements supports the read operation (even * though there is nothing to read), therefore it throws * {@link UnknownIdentifierException} for all arguments instead. * @throws InvalidArrayIndexException if the given index is not * {@link #isArrayElementReadable(Object, long) readable}, e.g. when the index is * invalid or the index is out of bounds. * @since 19.0 */ @Abstract(ifExported = {"hasArrayElements"}) public Object readArrayElement(Object receiver, long index) throws UnsupportedMessageException, InvalidArrayIndexException { throw UnsupportedMessageException.create(); } /** * Returns the array size of the receiver. * * @throws UnsupportedMessageException if and only if {@link #hasArrayElements(Object)} returns * false. * @since 19.0 */ @Abstract(ifExported = {"hasArrayElements"}) public long getArraySize(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if a given array element is {@link #readArrayElement(Object, long) * readable}. This method may only return true if {@link #hasArrayElements(Object)} * returns true as well. Invoking this message does not cause any observable * side-effects. Returns false by default. * * @see #readArrayElement(Object, long) * @since 19.0 */ @Abstract(ifExported = {"hasArrayElements"}) public boolean isArrayElementReadable(Object receiver, long index) { return false; } /** * Writes the value of an array element by index. Writing an array element is allowed if is * existing and {@link #isArrayElementModifiable(Object, long) modifiable}, or not existing and * {@link #isArrayElementInsertable(Object, long) insertable}. * * This method must have not observable side-effects other than the changed array element. * * @throws UnsupportedMessageException when the receiver does not support writing at all, e.g. * when it is immutable. * @throws InvalidArrayIndexException if the given index is not * {@link #isArrayElementInsertable(Object, long) insertable} nor * {@link #isArrayElementModifiable(Object, long) modifiable}, e.g. when the index * is invalid or the index is out of bounds and the array does not support growing. * @throws UnsupportedTypeException if the provided value type is not allowed to be written. * @since 19.0 */ @Abstract(ifExported = {"isArrayElementModifiable", "isArrayElementInsertable"}) public void writeArrayElement(Object receiver, long index, Object value) throws UnsupportedMessageException, UnsupportedTypeException, InvalidArrayIndexException { throw UnsupportedMessageException.create(); } /** * Remove an array element from the receiver object. Removing member is allowed if the array * element is {@link #isArrayElementRemovable(Object, long) removable}. This method may only * return true if {@link #hasArrayElements(Object)} returns true as * well and {@link #isArrayElementInsertable(Object, long)} returns false. * * This method does not have observable side-effects other than the removed array element and * shift of remaining elements. If shifting is not supported then the array might allow only * removal of last element. * * @throws UnsupportedMessageException when the receiver does not support removing at all, e.g. * when it is immutable. * @throws InvalidArrayIndexException if the given index is not * {@link #isArrayElementRemovable(Object, long) removable}, e.g. when the index is * invalid, the index is out of bounds, or the array does not support shifting of * remaining elements. * @see #isArrayElementRemovable(Object, long) * @since 19.0 */ @Abstract(ifExported = "isArrayElementRemovable") public void removeArrayElement(Object receiver, long index) throws UnsupportedMessageException, InvalidArrayIndexException { throw UnsupportedMessageException.create(); } /** * Returns true if a given array element index is existing and * {@link #writeArrayElement(Object, long, Object) writable}. This method may only return * true if {@link #hasArrayElements(Object)} returns true as well and * {@link #isArrayElementInsertable(Object, long)} returns false. Invoking this * message does not cause any observable side-effects. Returns false by default. * * @see #writeArrayElement(Object, long, Object) * @see #isArrayElementInsertable(Object, long) * @since 19.0 */ @Abstract(ifExported = "writeArrayElement") public boolean isArrayElementModifiable(Object receiver, long index) { return false; } /** * Returns true if a given array element index is not existing and * {@link #writeArrayElement(Object, long, Object) insertable}. This method may only return * true if {@link #hasArrayElements(Object)} returns true as well and * {@link #isArrayElementExisting(Object, long)}} returns false. Invoking this * message does not cause any observable side-effects. Returns false by default. * * @see #writeArrayElement(Object, long, Object) * @see #isArrayElementModifiable(Object, long) * @since 19.0 */ @Abstract(ifExported = "writeArrayElement") public boolean isArrayElementInsertable(Object receiver, long index) { return false; } /** * Returns true if a given array element index is existing and * {@link #removeArrayElement(Object, long) removable}. This method may only return * true if {@link #hasArrayElements(Object)} returns true as well and * {@link #isArrayElementInsertable(Object, long)}} returns false. Invoking this * message does not cause any observable side-effects. Returns false by default. * * @see #removeArrayElement(Object, long) * @since 19.0 */ @Abstract(ifExported = "removeArrayElement") public boolean isArrayElementRemovable(Object receiver, long index) { return false; } /** * Returns true if the array element is {@link #isArrayElementModifiable(Object, long) * modifiable} or {@link #isArrayElementInsertable(Object, long) insertable}. * * @since 19.0 */ public final boolean isArrayElementWritable(Object receiver, long index) { return isArrayElementModifiable(receiver, index) || isArrayElementInsertable(receiver, index); } /** * Returns true if the array element is existing. An array element is existing if it is, * {@link #isArrayElementModifiable(Object, long) modifiable}, * {@link #isArrayElementReadable(Object, long) readable} or * {@link #isArrayElementRemovable(Object, long) removable}. * * @since 19.0 */ public final boolean isArrayElementExisting(Object receiver, long index) { return isArrayElementModifiable(receiver, index) || isArrayElementReadable(receiver, index) || isArrayElementRemovable(receiver, index); } /** * Returns true if the receiver value represents a native pointer. Native pointers * are represented as 64 bit pointers. Invoking this message does not cause any observable * side-effects. Returns false by default. *

      * It is expected that objects should only return true if the native pointer value * corresponding to this object already exists, and obtaining it is a cheap operation. If an * object can be transformed to a pointer representation, but this hasn't happened yet, the * object is expected to return false with {@link #isPointer(Object)}, and wait for * the {@link #toNative(Object)} message to trigger the transformation. * * @see #asPointer(Object) * @see #toNative(Object) * @since 19.0 */ @Abstract(ifExported = {"asPointer"}) public boolean isPointer(Object receiver) { return false; } /** * Returns the pointer value as long value if the receiver represents a pointer like value. * * @throws UnsupportedMessageException if and only if {@link #isPointer(Object)} returns * false for the same receiver. * @see #isPointer(Object) * @since 19.0 */ @Abstract(ifExported = {"isPointer"}) public long asPointer(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Attempts to transform a {@link TruffleObject receiver} to a value that represents a raw * native pointer. After a successful transformation, the provided receiver returns true for * {@link #isPointer(Object)} and can be unwrapped using the {@link #asPointer(Object)} message. * If transformation cannot be done {@link #isPointer(Object)} will keep returning false. * * @see #isPointer(Object) * @see #asPointer(Object) * @since 19.0 */ public void toNative(Object receiver) { } /** * Returns the receiver as instant if this object represents an {@link #isInstant(Object) * instant}. If a value is an instant then it is also a {@link #isDate(Object) date}, * {@link #isTime(Object) time} and {@link #isTimeZone(Object) timezone}. Using this method may * be more efficient than reconstructing the timestamp from the date, time and timezone data. *

      * Implementers should implement this method if they can provide a more efficient conversion to * Instant than reconstructing it from date, time and timezone date. Implementers must ensure * that the following Java code snippet always holds: * *

           * ZoneId zone = getTimeZone(receiver);
           * LocalDate date = getDate(receiver);
           * LocalTime time = getTime(receiver);
           * assert ZonedDateTime.of(date, time, zone).toInstant().equals(getInstant(receiver));
           * 
      * * @see #isDate(Object) * @see #isTime(Object) * @see #isTimeZone(Object) * @throws UnsupportedMessageException if and only if {@link #isInstant(Object)} returns * false. * @since 20.0.0 beta 2 */ public Instant asInstant(Object receiver) throws UnsupportedMessageException { if (isDate(receiver) && isTime(receiver) && isTimeZone(receiver)) { LocalDate date = asDate(receiver); LocalTime time = asTime(receiver); ZoneId zone = asTimeZone(receiver); return toInstant(date, time, zone); } throw UnsupportedMessageException.create(); } @TruffleBoundary private static Instant toInstant(LocalDate date, LocalTime time, ZoneId zone) { return ZonedDateTime.of(date, time, zone).toInstant(); } /** * Returns true if the receiver represents an instant. If a value is an instant * then it is also a {@link #isDate(Object) date}, {@link #isTime(Object) time} and * {@link #isTimeZone(Object) timezone}. * * This method is short-hand for: * *
           * {@linkplain #isDate(Object) isDate}(v) && {@link #isTime(Object) isTime}(v) && {@link #isTimeZone(Object) isTimeZone}(v)
           * 
      * * @see #isDate(Object) * @see #isTime(Object) * @see #isInstant(Object) * @see #asInstant(Object) * @since 20.0.0 beta 2 */ public final boolean isInstant(Object receiver) { return isDate(receiver) && isTime(receiver) && isTimeZone(receiver); } /** * Returns true if this object represents a timezone, else false. The * interpretation of timezone objects may vary: *
        *
      • If {@link #isDate(Object)} and {@link #isTime(Object)} return true, then the * returned date or time information is aware of this timezone. *
      • If {@link #isDate(Object)} and {@link #isTime(Object)} returns false, then * it represents just timezone information. *
      * Objects with only date information must not have timezone information attached and objects * with only time information must have either none, or {@link ZoneRules#isFixedOffset() fixed * zone} only. If this rule is violated then an {@link AssertionError} is thrown if assertions * are enabled. *

      * If this method is implemented then also {@link #asTimeZone(Object)} must be implemented. * * @see #asTimeZone(Object) * @see #asInstant(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"asTimeZone", "asInstant"}) public boolean isTimeZone(Object receiver) { return false; } /** * Returns the receiver as timestamp if this object represents a {@link #isTimeZone(Object) * timezone}. * * @throws UnsupportedMessageException if and only if {@link #isTimeZone(Object)} returns * false . * @see #isTimeZone(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"isTimeZone", "asInstant"}) public ZoneId asTimeZone(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if this object represents a date, else false. If the * receiver is also a {@link #isTimeZone(Object) timezone} then the date is aware, otherwise it * is naive. * * @see #asDate(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"asDate", "asInstant"}) public boolean isDate(Object receiver) { return false; } /** * Returns the receiver as date if this object represents a {@link #isDate(Object) date}. The * returned date is either aware if the receiver has a {@link #isTimeZone(Object) timezone} * otherwise it is naive. * * @throws UnsupportedMessageException if and only if {@link #isDate(Object)} returns * false. * @see #isDate(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"isTime", "asInstant"}) public LocalDate asDate(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if this object represents a time, else false. If the * receiver is also a {@link #isTimeZone(Object) timezone} then the time is aware, otherwise it * is naive. * * @see #asTime(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"asTime", "asInstant"}) public boolean isTime(Object receiver) { return false; } /** * Returns the receiver as time if this object represents a {@link #isTime(Object) time}. The * returned time is either aware if the receiver has a {@link #isTimeZone(Object) timezone} * otherwise it is naive. * * @throws UnsupportedMessageException if and only if {@link #isTime(Object)} returns * false. * @see #isTime(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"isTime", "asInstant"}) public LocalTime asTime(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if this object represents a duration, else false. * * @see Duration * @see #asDuration(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"asDuration"}) public boolean isDuration(Object receiver) { return false; } /** * Returns the receiver as duration if this object represents a {@link #isDuration(Object) * duration}. * * @throws UnsupportedMessageException if and only if {@link #isDuration(Object)} returns * false . * @see #isDuration(Object) * @since 20.0.0 beta 2 */ @Abstract(ifExported = {"isDuration"}) public Duration asDuration(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if the receiver value represents a throwable exception/error}. * Invoking this message does not cause any observable side-effects. Returns false * by default. *

      * Objects must only return true if they support {@link #throwException} as well. * If this method is implemented then also {@link #throwException(Object)} must be implemented. * * The following simplified {@code TryCatchNode} shows how the exceptions should be handled by * languages. * * {@link InteropLibrarySnippets.TryCatchNode} * * @see #throwException(Object) * @see com.oracle.truffle.api.exception.AbstractTruffleException * @since 19.3 */ @Abstract(ifExported = {"throwException"}) @SuppressWarnings("deprecation") public boolean isException(Object receiver) { // A workaround for missing inheritance feature for default exports. return InteropAccessor.EXCEPTION.isException(receiver) || LegacyTruffleExceptionSupport.isException(receiver); } /** * Throws the receiver object as an exception of the source language, as if it was thrown by the * source language itself. Allows rethrowing exceptions caught by another language. If this * method is implemented then also {@link #isException(Object)} must be implemented. *

      * Any interop value can be an exception value and export {@link #throwException(Object)}. The * exception thrown by this message must extend * {@link com.oracle.truffle.api.exception.AbstractTruffleException}. In future versions this * contract will be enforced using an assertion. *

      * For a sample {@code TryCatchNode} implementation see {@link #isException(Object) * isException}. * * @throws UnsupportedMessageException if and only if {@link #isException(Object)} returns * false for the same receiver. * @see #isException(Object) * @since 19.3 */ @Abstract(ifExported = {"isException"}) public RuntimeException throwException(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { throw InteropAccessor.EXCEPTION.throwException(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { throw LegacyTruffleExceptionSupport.throwException(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns {@link ExceptionType exception type} of the receiver. Throws * {@code UnsupportedMessageException} when the receiver is not an {@link #isException(Object) * exception}. *

      * For a sample {@code TryCatchNode} implementation see {@link #isException(Object) * isException}. * * @see #isException(Object) * @see ExceptionType * @since 20.3 */ @Abstract(ifExported = {"getExceptionExitStatus", "isExceptionIncompleteSource"}) public ExceptionType getExceptionType(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return (ExceptionType) InteropAccessor.EXCEPTION.getExceptionType(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getExceptionType(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns {@code true} if receiver value represents an incomplete source exception. Throws * {@code UnsupportedMessageException} when the receiver is not an {@link #isException(Object) * exception} or the exception is not a {@link ExceptionType#PARSE_ERROR}. * * @see #isException(Object) * @see #getExceptionType(Object) * @since 20.3 */ public boolean isExceptionIncompleteSource(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.isExceptionIncompleteSource(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.isExceptionIncompleteSource(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns exception exit status of the receiver. Throws {@code UnsupportedMessageException} * when the receiver is not an {@link #isException(Object) exception} of the * {@link ExceptionType#EXIT exit type}. A return value zero indicates that the execution of the * application was successful, a non-zero value that it failed. The individual interpretation of * non-zero values depends on the application. * * @see #isException(Object) * @see #getExceptionType(Object) * @see ExceptionType * @since 20.3 */ public int getExceptionExitStatus(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.getExceptionExitStatus(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getExceptionExitStatus(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns {@code true} if the receiver is an exception with an attached internal cause. * Invoking this message does not cause any observable side-effects. Returns {@code false} by * default. * * @see #isException(Object) * @see #getExceptionCause(Object) * @since 20.3 */ @Abstract(ifExported = {"getExceptionCause"}) public boolean hasExceptionCause(Object receiver) { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.hasExceptionCause(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.hasExceptionCause(receiver); } else { return false; } } /** * Returns the internal cause of the receiver. Throws {@code UnsupportedMessageException} when * the receiver is not an {@link #isException(Object) exception} or has no internal cause. The * return value of this message is guaranteed to return true for * {@link #isException(Object)}. * * * @see #isException(Object) * @see #hasExceptionCause(Object) * @since 20.3 */ @Abstract(ifExported = {"hasExceptionCause"}) public Object getExceptionCause(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.getExceptionCause(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getExceptionCause(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns {@code true} if the receiver is an exception that has an exception message. Invoking * this message does not cause any observable side-effects. Returns {@code false} by default. * * @see #isException(Object) * @see #getExceptionMessage(Object) * @since 20.3 */ @Abstract(ifExported = {"getExceptionMessage"}) public boolean hasExceptionMessage(Object receiver) { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.hasExceptionMessage(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.hasExceptionMessage(receiver); } else { return false; } } /** * Returns exception message of the receiver. Throws {@code UnsupportedMessageException} when * the receiver is not an {@link #isException(Object) exception} or has no exception message. * The return value of this message is guaranteed to return true for * {@link #isString(Object)}. * * @see #isException(Object) * @see #hasExceptionMessage(Object) * @since 20.3 */ @Abstract(ifExported = {"hasExceptionMessage"}) public Object getExceptionMessage(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.getExceptionMessage(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getExceptionMessage(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns {@code true} if the receiver is an exception and has a stack trace. Invoking this * message does not cause any observable side-effects. Returns {@code false} by default. * * @see #isException(Object) * @see #getExceptionStackTrace(Object) * @since 20.3 */ @Abstract(ifExported = {"getExceptionStackTrace"}) public boolean hasExceptionStackTrace(Object receiver) { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.hasExceptionStackTrace(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.hasExceptionStackTrace(receiver); } else { return false; } } /** * Returns the exception stack trace of the receiver that is of type exception. Returns an * {@link #hasArrayElements(Object) array} of objects with potentially * {@link #hasExecutableName(Object) executable name}, {@link #hasDeclaringMetaObject(Object) * declaring meta object} and {@link #hasSourceLocation(Object) source location} of the caller. * Throws {@code UnsupportedMessageException} when the receiver is not an * {@link #isException(Object) exception} or has no stack trace. Invoking this message or * accessing the stack trace elements array must not cause any observable side-effects. *

      * The default implementation of {@link #getExceptionStackTrace(Object)} calls * {@link TruffleStackTrace#getStackTrace(Throwable)} on the underlying exception object and * {@link TruffleStackTraceElement#getGuestObject()} to access an interop capable object of the * underlying stack trace element. * * @see #isException(Object) * @see #hasExceptionStackTrace(Object) * @since 20.3 */ @Abstract(ifExported = {"hasExceptionStackTrace"}) public Object getExceptionStackTrace(Object receiver) throws UnsupportedMessageException { // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.getExceptionStackTrace(receiver); } else if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getExceptionStackTrace(receiver); } else { throw UnsupportedMessageException.create(); } } /** * Returns true if the receiver value has a declared source location attached, else * false. Returning a source location for a value is optional and typically impacts * the capabilities of tools like debuggers to jump to the declaration of a value. *

      * Examples for values that may provide a source location: *

        *
      • {@link #isMetaObject(Object) Metaobjects} like classes or types. *
      • First class {@link #isExecutable(Object) executables}, like functions, closures or * promises. *
      • Allocation sites for instances. Note that in most languages it is very expensive to track * the allocation site of an instance and it is therefore not recommended to support this * feature by default, but ideally behind an optional language option. *
          *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #getSourceLocation(Object)} must be implemented. * * @see #getSourceLocation(Object) * @since 20.1 */ @Abstract(ifExported = {"getSourceLocation"}) @TruffleBoundary public boolean hasSourceLocation(Object receiver) { Env env = getLegacyEnv(receiver, false); if (env != null) { SourceSection location = InteropAccessor.ACCESSOR.languageSupport().legacyFindSourceLocation(env, receiver); if (location != null) { return true; } } // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.hasSourceLocation(receiver); } if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.hasSourceLocation(receiver); } return false; } /** * Returns the declared source location of the receiver value. Throws an * {@link UnsupportedMessageException} if the value does not have a declared source location. * See {@link #hasSourceLocation(Object)} for further details on potential interpretations. * Throws {@link UnsupportedMessageException} by default. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #hasSourceLocation(Object)} must be implemented. * * @throws UnsupportedMessageException if and only if {@link #hasSourceLocation(Object)} returns * false for the same receiver. * @since 20.1 */ @Abstract(ifExported = {"hasSourceLocation"}) @TruffleBoundary public SourceSection getSourceLocation(Object receiver) throws UnsupportedMessageException { Env env = getLegacyEnv(receiver, false); if (env != null) { SourceSection location = InteropAccessor.ACCESSOR.languageSupport().legacyFindSourceLocation(env, receiver); if (location != null) { return location; } } // A workaround for missing inheritance feature for default exports. if (InteropAccessor.EXCEPTION.isException(receiver)) { return InteropAccessor.EXCEPTION.getSourceLocation(receiver); } if (LegacyTruffleExceptionSupport.isException(receiver)) { return LegacyTruffleExceptionSupport.getSourceLocation(receiver); } throw UnsupportedMessageException.create(); } /** * Returns true if the receiver originates from a language, else false * . Primitive values or other shared interop value representations that are not associated with * a language may return false. Values that originate from a language should return * true. Returns false by default. *

          * The associated language allows tools to identify the original language of a value. If an * instrument requests a * {@link com.oracle.truffle.api.instrumentation.TruffleInstrument.Env#getLanguageView(LanguageInfo, Object) * language view} then values that are already associated with a language will just return the * same value. Otherwise {@link TruffleLanguage#getLanguageView(Object, Object)} will be invoked * on the language. The returned language may be also exposed to embedders in the future. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #getLanguage(Object)} and {@link #toDisplayString(Object, boolean)} must be * implemented. * * @see #getLanguage(Object) * @see #toDisplayString(Object) * @since 20.1 */ @Abstract(ifExported = {"getLanguage", "isScope"}) @TruffleBoundary public boolean hasLanguage(Object receiver) { return getLegacyEnv(receiver, false) != null; } /** * Returns the the original language of the receiver value. The returned language class must be * non-null and represent a valid {@link Registration registered} language class. For more * details see {@link #hasLanguage(Object)}. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #hasLanguage(Object)} must be implemented. * * @throws UnsupportedMessageException if and only if {@link #hasLanguage(Object)} returns * false for the same receiver. * @since 20.1 */ @SuppressWarnings("unchecked") @Abstract(ifExported = {"hasLanguage"}) @TruffleBoundary public Class> getLanguage(Object receiver) throws UnsupportedMessageException { Env env = getLegacyEnv(receiver, false); if (env != null) { return (Class>) InteropAccessor.ACCESSOR.languageSupport().getSPI(env).getClass(); } throw UnsupportedMessageException.create(); } /** * Returns true if the receiver value has a metaobject associated. The metaobject * represents a description of the object, reveals its kind and its features. Some information * that a metaobject might define includes the base object's type, interface, class, methods, * attributes, etc. Should return false when no metaobject is known for this type. * Returns false by default. *

          * An example, for Java objects the returned metaobject is the {@link Object#getClass() class} * instance. In JavaScript this could be the function or class that is associated with the * object. *

          * Metaobjects for primitive values or values of other languages may be provided using * {@link TruffleLanguage#getLanguageView(Object, Object) language views}. While an object is * associated with a metaobject in one language, the metaobject might be a different when viewed * from another language. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #getMetaObject(Object)} must be implemented. * * @see #getMetaObject(Object) * @see #isMetaObject(Object) * @since 20.1 */ @Abstract(ifExported = {"getMetaObject"}) @TruffleBoundary public boolean hasMetaObject(Object receiver) { Env env = getLegacyEnv(receiver, false); if (env != null) { Object metaObject = InteropAccessor.ACCESSOR.languageSupport().legacyFindMetaObject(env, receiver); if (metaObject != null) { return true; } } return false; } /** * Returns the metaobject that is associated with this value. The metaobject represents a * description of the object, reveals its kind and its features. Some information that a * metaobject might define includes the base object's type, interface, class, methods, * attributes, etc. When no metaobject is known for this type. Throws * {@link UnsupportedMessageException} by default. *

          * The returned object must return true for {@link #isMetaObject(Object)} and * provide implementations for {@link #getMetaSimpleName(Object)}, * {@link #getMetaQualifiedName(Object)}, and {@link #isMetaInstance(Object, Object)}. For all * values with metaobjects it must at hold that * isMetaInstance(getMetaObject(value), value) == * true. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #hasMetaObject(Object)} must be implemented. * * @throws UnsupportedMessageException if and only if {@link #hasMetaObject(Object)} returns * false for the same receiver. * * @see #hasMetaObject(Object) * @since 20.1 */ @Abstract(ifExported = {"hasMetaObject"}) @TruffleBoundary public Object getMetaObject(Object receiver) throws UnsupportedMessageException { Env env = getLegacyEnv(receiver, false); if (env != null) { Object metaObject = InteropAccessor.ACCESSOR.languageSupport().legacyFindMetaObject(env, receiver); if (metaObject != null) { return new LegacyMetaObjectWrapper(receiver, metaObject); } } throw UnsupportedMessageException.create(); } /** * Converts the receiver to a human readable {@link #isString(Object) string}. Each language may * have special formating conventions - even primitive values may not follow the traditional * Java rules. The format of the returned string is intended to be interpreted by humans not * machines and should therefore not be relied upon by machines. By default the receiver class * name and its {@link System#identityHashCode(Object) identity hash code} is used as string * representation. *

          * String representations for primitive values or values of other languages may be provided * using {@link TruffleLanguage#getLanguageView(Object, Object) language views}. It is common * that languages provide different string representations for primitive and foreign values. To * convert the result value to a Java string use {@link InteropLibrary#asString(Object)}. * * @param allowSideEffects whether side-effects are allowed in the production of the string. * @see TruffleLanguage#getLanguageView(Object, Object) * @since 20.1 */ @Abstract(ifExported = {"hasLanguage", "getLanguage", "isScope"}) @TruffleBoundary public Object toDisplayString(Object receiver, boolean allowSideEffects) { Env env = getLegacyEnv(receiver, false); if (env != null && allowSideEffects) { return InteropAccessor.ACCESSOR.languageSupport().legacyToString(env, receiver); } else { return receiver.getClass().getTypeName() + "@" + Integer.toHexString(System.identityHashCode(receiver)); } } private static Env getLegacyEnv(Object receiver, boolean nullForhost) { return InteropAccessor.ACCESSOR.engineSupport().getLegacyLanguageEnv(receiver, nullForhost); } /** * Converts the receiver to a human readable {@link #isString(Object) string} of the language. * Short-cut for {@link #toDisplayString(Object) toDisplayString}(true). * * @see #toDisplayString(Object, boolean) * @since 20.1 */ public final Object toDisplayString(Object receiver) { return toDisplayString(receiver, true); } /** * Returns true if the receiver value represents a metaobject. Metaobjects may be * values that naturally occur in a language or they may be returned by * {@link #getMetaObject(Object)}. A metaobject represents a description of the object, reveals * its kind and its features. If a receiver is a metaobject it is often also * {@link #isInstantiable(Object) instantiable}, but this is not a requirement. *

          * Sample interpretations: In Java an instance of the type {@link Class} is a metaobject. * In JavaScript any function instance is a metaobject. For example, the metaobject of a * JavaScript class is the associated constructor function. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #getMetaQualifiedName(Object)}, {@link #getMetaSimpleName(Object)} and * {@link #isMetaInstance(Object, Object)} must be implemented as well. * * @since 20.1 */ @Abstract(ifExported = {"getMetaQualifiedName", "getMetaSimpleName", "isMetaInstance"}) public boolean isMetaObject(Object receiver) { return false; } /** * Returns the qualified name of a metaobject as {@link #isString(Object) string}. *

          * Sample interpretations: The qualified name of a Java class includes the package name * and its class name. JavaScript does not have the notion of qualified name and therefore * returns the {@link #getMetaSimpleName(Object) simple name} instead. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isMetaObject(Object)} must be implemented as well. * * @throws UnsupportedMessageException if and only if {@link #isMetaObject(Object)} returns * false for the same receiver. * * @since 20.1 */ @Abstract(ifExported = {"isMetaObject"}) public Object getMetaQualifiedName(Object metaObject) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the simple name of a metaobject as {@link #isString(Object) string}. *

          * Sample interpretations: The simple name of a Java class is the class name. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isMetaObject(Object)} must be implemented as well. * * @throws UnsupportedMessageException if and only if {@link #isMetaObject(Object)} returns * false for the same receiver. * * @since 20.1 */ @Abstract(ifExported = {"isMetaObject"}) public Object getMetaSimpleName(Object metaObject) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if the given instance is of the provided receiver metaobject, else * false. *

          * Sample interpretations: A Java object is an instance of its returned * {@link Object#getClass() class}. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isMetaObject(Object)} must be implemented as well. * * @param instance the instance object to check. * @throws UnsupportedMessageException if and only if {@link #isMetaObject(Object)} returns * false for the same receiver. * @since 20.1 */ @Abstract(ifExported = {"isMetaObject"}) public boolean isMetaInstance(Object receiver, Object instance) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns {@link TriState#TRUE TRUE} if the receiver is or {@link TriState#FALSE FALSE} if the * receiver is not identical to the other value. Returns {@link TriState#UNDEFINED * UNDEFINED} if the operation is not specified. *

          * Sample interpretations: *

            *
          • A Java object might be of the identical instance as another Java object. Typically * compared using the == operator. *
          *

          * Any implementation, with the exception of an implementation that returns * {@link TriState#UNDEFINED UNDEFINED} unconditionally, must guarantee the following * properties: *

            *
          • It is reflexive: for any value {@code x}, {@code lib.isIdenticalOrUndefined(x, x)} * always returns {@link TriState#TRUE TRUE}. This is necessary to ensure that the * {@link #hasIdentity(Object)} contract has reliable results. *
          • It is symmetric: for any values {@code x} and {@code y}, * {@code lib.isIdenticalOrUndefined(x, y)} returns {@link TriState#TRUE TRUE} if and only if * {@code lib.isIdenticalOrUndefined(y, x)} returns {@link TriState#TRUE TRUE}. *
          • It is transitive: for any values {@code x}, {@code y}, and {@code z}, if * {@code lib.isIdenticalOrUndefined(x, y)} returns {@link TriState#TRUE TRUE} and * {@code lib.isIdenticalOrUndefined(y, z)} returns {@link TriState#TRUE TRUE}, then * {@code lib.isIdentical(x, z, zLib)} returns {@link TriState#TRUE TRUE}. *
          • It is consistent: for any values {@code x} and {@code y}, multiple invocations of * {@code lib.isIdenticalOrUndefined(x, y)} consistently returns the same value. *
          *

          * Note that the target language identical semantics typically does not map directly to interop * identical implementation. Instead target language identity is specified by the language * operation, may take multiple other rules into account and may only fallback to interop * identical for values without dedicated interop type. For example, in many languages * primitives like numbers or strings may be identical, in the target language sense, still * identity can only be exposed for objects and non-primitive values. Primitive values like * {@link Integer} can never be interop identical to other boxed language integers as this would * violate the symmetric property. *

          * Example receiver class MyObject which uses an explicit identity field to compute whether two * values are identical. * *

               * static class MyObject {
               *
               *     final Object identity;
               *
               *     MyObject(Object identity) {
               *         this.identity = identity;
               *     }
               *
               *     @ExportMessage
               *     static final class IsIdenticalOrUndefined {
               *         @Specialization
               *         static TriState doMyObject(MyObject receiver, MyObject other) {
               *             return receiver.identity == other.identity ? TriState.TRUE : TriState.FALSE;
               *         }
               *
               *         @Fallback
               *         static TriState doOther(MyObject receiver, Object other) {
               *             return TriState.UNDEFINED;
               *         }
               *     }
               *     // ...
               * }
               * 
          * *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #identityHashCode(Object)} must be implemented. * * @param other the other value to compare to * * @since 20.2 */ @Abstract(ifExported = {"isIdentical", "identityHashCode"}) protected TriState isIdenticalOrUndefined(Object receiver, Object other) { return TriState.UNDEFINED; } /** * Returns true if two values represent the the identical value, else * false. Two values are identical if and only if they have specified identity * semantics in the target language and refer to the identical instance. *

          * By default, an interop value does not support identical comparisons, and will return * false for any invocation of this method. Use {@link #hasIdentity(Object)} to * find out whether a receiver supports identity comparisons. *

          * This method has the following properties: *

            *
          • It is not reflexive: for any value {@code x}, * {@code lib.isIdentical(x, x, lib)} may return {@code false} if the object does not support * identity, else true. This method is reflexive if {@code x} supports identity. A * value supports identity if {@code lib.isIdentical(x, x, lib)} returns true. The * method {@link #hasIdentity(Object)} may be used to document this intent explicitly. *
          • It is symmetric: for any values {@code x} and {@code y}, * {@code lib.isIdentical(x, y, yLib)} returns {@code true} if and only if * {@code lib.isIdentical(y, x, xLib)} returns {@code true}. *
          • It is transitive: for any values {@code x}, {@code y}, and {@code z}, if * {@code lib.isIdentical(x, y, yLib)} returns {@code true} and * {@code lib.isIdentical(y, z, zLib)} returns {@code true}, then * {@code lib.isIdentical(x, z, zLib)} returns {@code true}. *
          • It is consistent: for any values {@code x} and {@code y}, multiple invocations of * {@code lib.isIdentical(x, y, yLib)} consistently returns {@code true} or consistently return * {@code false}. *
          *

          * Note that the target language identical semantics typically does not map directly to interop * identical implementation. Instead target language identity is specified by the language * operation, may take multiple other rules into account and may only fallback to interop * identical for values without dedicated interop type. For example, in many languages * primitives like numbers or strings may be identical, in the target language sense, still * identity can only be exposed for objects and non-primitive values. Primitive values like * {@link Integer} can never be interop identical to other boxed language integers as this would * violate the symmetric property. *

          * This method performs double dispatch by forwarding calls to * {@link #isIdenticalOrUndefined(Object, Object)} with receiver and other value first and then * with reversed parameters if the result was {@link TriState#UNDEFINED undefined}. This allows * the receiver and the other value to negotiate identity semantics. This method is supposed to * be exported only if the receiver represents a wrapper that forwards messages. In such a case * the isIdentical message should be forwarded to the delegate value. Otherwise, the * {@link #isIdenticalOrUndefined(Object, Object)} should be exported instead. *

          * This method must not cause any observable side-effects. *

          * Cached usage example: * *

               * abstract class IsIdenticalUsage extends Node {
               *
               *     abstract boolean execute(Object left, Object right);
               *
               *     @Specialization(limit = "3")
               *     public boolean isIdentical(Object left, Object right,
               *                     @CachedLibrary("left") InteropLibrary leftInterop,
               *                     @CachedLibrary("right") InteropLibrary rightInterop) {
               *         return leftInterop.isIdentical(left, right, rightInterop);
               *     }
               * }
               * 
          *

          * Uncached usage example: * *

               * @TruffleBoundary
               * public static boolean isIdentical(Object left, Object right) {
               *     return InteropLibrary.getUncached(left).isIdentical(left, right,
               *                     InteropLibrary.getUncached(right));
               * }
               * 
          * * For a full example please refer to the SLEqualNode of the SimpleLanguage example * implementation. * * @since 20.2 */ public boolean isIdentical(Object receiver, Object other, InteropLibrary otherInterop) { TriState result = this.isIdenticalOrUndefined(receiver, other); if (result == TriState.UNDEFINED) { result = otherInterop.isIdenticalOrUndefined(other, receiver); } return result == TriState.TRUE; } /** * Returns true if and only if the receiver specifies identity, else * false. This method is a short-cut for * this.isIdentical(receiver, receiver, this) != TriState.UNDEFINED. This message * cannot be exported. To add identity support to the receiver export * {@link #isIdenticalOrUndefined(Object, Object)} instead. * * @see #isIdenticalOrUndefined(Object, Object) * @since 20.2 */ public final boolean hasIdentity(Object receiver) { return this.isIdentical(receiver, receiver, this); } /** * Returns an identity hash code for the receiver if it has {@link #hasIdentity(Object) * identity}. If the receiver has no identity then an {@link UnsupportedMessageException} is * thrown. The identity hash code may be used by languages to store foreign values with identity * in an identity hash map. *

          *

            *
          • Whenever it is invoked on the same object more than once during an execution of a guest * context, the identityHashCode method must consistently return the same integer. This integer * need not remain consistent from one execution context of a guest application to another * execution context of the same application. *
          • If two objects are the same according to the * {@link #isIdentical(Object, Object, InteropLibrary)} message, then calling the * identityHashCode method on each of the two objects must produce the same integer result. *
          • As much as is reasonably practical, the identityHashCode message does return distinct * integers for objects that are not the same. *
          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isIdenticalOrUndefined(Object, Object)} must be implemented. * * @throws UnsupportedMessageException if and only if {@link #hasIdentity(Object)} returns * false for the same receiver. * @see #isIdenticalOrUndefined(Object, Object) * @see #isIdentical(Object, Object, InteropLibrary) * @since 20.2 */ @Abstract(ifExported = "isIdenticalOrUndefined") public int identityHashCode(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns true if the value represents a scope object, else false. * The scope object contains variables as {@link #getMembers(Object) members} and has a * {@link InteropLibrary#toDisplayString(Object, boolean) scope display name}. It needs to be * associated with a {@link #getLanguage(Object) language}. The scope may return a * {@link InteropLibrary#getSourceLocation(Object) source location} that indicates the range of * the scope in the source code. The scope may have {@link #hasScopeParent(Object) parent * scopes}. *

          * The {@link #getMembers(Object) members} of a scope represent all visible flattened variables, * including all parent scopes, if any. The variables of the current scope must be listed first * in {@link #getMembers(Object)}. Variables of the {@link InteropLibrary#getScopeParent(Object) * parent scope} must be listed afterwards, even if they contain duplicates. This allows to * resolve which variables are redeclared in sub scopes. *

          * Every {@link #getMembers(Object) member} may not be just a String literal, but a * {@link #isString(Object) string object} that provides also a * {@link #getSourceLocation(Object) source location} of its declaration. When different * variables of the same name are in different scopes, they will be represented by different * member elements providing the same {@link #asString(Object) name}. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #hasMembers(Object)} and {@link #toDisplayString(Object, boolean)} must be * implemented and {@link #hasSourceLocation(Object)} is recommended. * * @see #getLanguage(Object) * @see #getMembers(Object) * @see #hasScopeParent(Object) * @since 20.3 */ @Abstract(ifExported = "hasScopeParent") public boolean isScope(Object receiver) { return false; } /** * Returns true if this scope has an enclosing parent scope, else * false. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isScope(Object)} and {@link #getScopeParent(Object)} must be implemented. * * @see #isScope(Object) * @see #getScopeParent(Object) * @since 20.3 */ @Abstract(ifExported = "getScopeParent") public boolean hasScopeParent(Object receiver) { return false; } /** * Returns the parent scope object if it {@link #hasScopeParent(Object) has the parent}. The * returned object must be a {@link #isScope(Object) scope} and must provide a reduced list of * {@link #getMembers(Object) member} variables, omitting all variables that are local to the * current scope. *

          * This method must not cause any observable side-effects. If this method is implemented then * also {@link #isScope(Object)} and {@link #getScopeParent(Object)} must be implemented. * * @throws UnsupportedMessageException if and only if {@link #hasScopeParent(Object)} returns * false for the same receiver. * @see #isScope(Object) * @see #hasScopeParent(Object) * @since 20.3 */ @Abstract(ifExported = "hasScopeParent") public Object getScopeParent(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } /** * Returns the library factory for the interop library. Short-cut for * {@link LibraryFactory#resolve(Class) ResolvedLibrary.resolve(InteropLibrary.class)}. * * @see LibraryFactory#resolve(Class) * @since 19.0 */ public static LibraryFactory getFactory() { return FACTORY; } /** * Returns the uncached automatically dispatched version of the interop library. This is a * short-cut for calling InteropLibrary.getFactory().getUncached(). * * @see LibraryFactory#getUncached() * @since 20.2 */ public static InteropLibrary getUncached() { return UNCACHED; } /** * Returns the uncached manually dispatched version of the interop library. This is a short-cut * for calling InteropLibrary.getFactory().getUncached(v). * * @see LibraryFactory#getUncached(Object) * @since 20.2 */ public static InteropLibrary getUncached(Object v) { return FACTORY.getUncached(v); } /** * Utility for libraries to require adoption before cached versions of nodes can be executed. * Only fails if assertions (-ea) are enabled. * * @since 19.0 */ protected final boolean assertAdopted() { assert this.getRootNode() != null : "Invalid library usage. Cached library must be adopted by a RootNode before it is executed."; return true; } static final LibraryFactory FACTORY = LibraryFactory.resolve(InteropLibrary.class); static final InteropLibrary UNCACHED = FACTORY.getUncached(); static class Asserts extends InteropLibrary { @Child private InteropLibrary delegate; public enum Type { NULL, BOOLEAN, DATE_TIME_ZONE, DURATION, STRING, NUMBER, POINTER, META_OBJECT; } Asserts(InteropLibrary delegate) { this.delegate = delegate; } private static boolean isMultiThreaded(Object receiver) { EngineSupport engine = InteropAccessor.ACCESSOR.engineSupport(); if (engine == null) { return false; } return engine.isMultiThreaded(receiver); } @Override public boolean accepts(Object receiver) { assert preCondition(receiver); return delegate.accepts(receiver); } @Override public boolean isNull(Object receiver) { assert preCondition(receiver); boolean result = delegate.isNull(receiver); assert !result || notOtherType(receiver, Type.NULL); return result; } private boolean notOtherType(Object receiver, Type type) { if (receiver instanceof LegacyMetaObjectWrapper) { // ignore other type assertions for legacy meta object wrapper return true; } assert type == Type.NULL || !delegate.isNull(receiver) : violationInvariant(receiver); assert type == Type.BOOLEAN || !delegate.isBoolean(receiver) : violationInvariant(receiver); assert type == Type.STRING || !delegate.isString(receiver) : violationInvariant(receiver); assert type == Type.NUMBER || !delegate.isNumber(receiver) : violationInvariant(receiver); assert type == Type.DATE_TIME_ZONE || (!delegate.isDate(receiver) && !delegate.isTime(receiver) && !delegate.isTimeZone(receiver)) : violationInvariant(receiver); assert type == Type.DURATION || !delegate.isDuration(receiver) : violationInvariant(receiver); assert type == Type.META_OBJECT || !delegate.isMetaObject(receiver) : violationInvariant(receiver); return true; } @Override public boolean isBoolean(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.isBoolean(receiver); } assert preCondition(receiver); boolean result = delegate.isBoolean(receiver); if (result) { try { delegate.asBoolean(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !result || notOtherType(receiver, Type.BOOLEAN); return result; } @Override public boolean asBoolean(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asBoolean(receiver); } assert preCondition(receiver); boolean wasBoolean = delegate.isBoolean(receiver); try { boolean result = delegate.asBoolean(receiver); assert wasBoolean : violationInvariant(receiver); assert notOtherType(receiver, Type.BOOLEAN); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasBoolean : violationInvariant(receiver); throw e; } } @Override public boolean isExecutable(Object receiver) { assert preCondition(receiver); boolean result = delegate.isExecutable(receiver); return result; } @Override public Object execute(Object receiver, Object... arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.execute(receiver, arguments); } assert preCondition(receiver); assert validArguments(receiver, arguments); boolean wasExecutable = delegate.isExecutable(receiver); try { Object result = delegate.execute(receiver, arguments); assert wasExecutable : violationInvariant(receiver, arguments); assert validReturn(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof ArityException || e instanceof UnsupportedTypeException : violationInvariant(receiver, arguments); assert !(e instanceof UnsupportedMessageException) || !wasExecutable : violationInvariant(receiver, arguments); throw e; } } @Override public boolean isInstantiable(Object receiver) { assert preCondition(receiver); boolean result = delegate.isInstantiable(receiver); return result; } @Override public Object instantiate(Object receiver, Object... arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.instantiate(receiver, arguments); } assert preCondition(receiver); assert validArguments(receiver, arguments); boolean wasInstantiable = delegate.isInstantiable(receiver); try { Object result = delegate.instantiate(receiver, arguments); assert wasInstantiable : violationInvariant(receiver, arguments); assert validReturn(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof ArityException || e instanceof UnsupportedTypeException : violationInvariant(receiver, arguments); assert !(e instanceof UnsupportedMessageException) || !wasInstantiable : violationInvariant(receiver, arguments); throw e; } } @Override public boolean isString(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.isString(receiver); } assert preCondition(receiver); boolean result = delegate.isString(receiver); if (result) { try { delegate.asString(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !result || notOtherType(receiver, Type.STRING); return result; } @Override public String asString(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asString(receiver); } assert preCondition(receiver); boolean wasString = delegate.isString(receiver); try { String result = delegate.asString(receiver); assert wasString : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasString : violationInvariant(receiver); throw e; } } @Override public boolean isNumber(Object receiver) { assert preCondition(receiver); boolean result = delegate.isNumber(receiver); assert !result || notOtherType(receiver, Type.NUMBER); return result; } @Override public boolean fitsInByte(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInByte(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInByte(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInShort(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInInt(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInLong(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInFloat(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInDouble(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asByte(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public boolean fitsInShort(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInShort(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInShort(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInInt(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInLong(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInFloat(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInDouble(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asShort(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public boolean fitsInInt(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInInt(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInInt(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInLong(receiver) : violationInvariant(receiver); assert !fits || delegate.fitsInDouble(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asInt(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public boolean fitsInLong(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInLong(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInLong(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asLong(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public boolean fitsInFloat(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInFloat(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInFloat(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asFloat(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public boolean fitsInDouble(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.fitsInDouble(receiver); } assert preCondition(receiver); boolean fits = delegate.fitsInDouble(receiver); assert !fits || delegate.isNumber(receiver) : violationInvariant(receiver); if (fits) { try { delegate.asDouble(receiver); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } assert !fits || notOtherType(receiver, Type.NUMBER); return fits; } @Override public byte asByte(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { byte result = delegate.asByte(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInByte(receiver) : violationInvariant(receiver); assert result == delegate.asShort(receiver) : violationInvariant(receiver); assert result == delegate.asInt(receiver) : violationInvariant(receiver); assert result == delegate.asLong(receiver) : violationInvariant(receiver); assert result == delegate.asFloat(receiver) : violationInvariant(receiver); assert result == delegate.asDouble(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public short asShort(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { short result = delegate.asShort(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInShort(receiver) : violationInvariant(receiver); assert result == delegate.asInt(receiver) : violationInvariant(receiver); assert result == delegate.asLong(receiver) : violationInvariant(receiver); assert result == delegate.asFloat(receiver) : violationInvariant(receiver); assert result == delegate.asDouble(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public int asInt(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { int result = delegate.asInt(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInInt(receiver) : violationInvariant(receiver); assert result == delegate.asLong(receiver) : violationInvariant(receiver); assert result == delegate.asDouble(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public long asLong(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { long result = delegate.asLong(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInLong(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public float asFloat(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { float result = delegate.asFloat(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInFloat(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public double asDouble(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { double result = delegate.asDouble(receiver); assert delegate.isNumber(receiver) : violationInvariant(receiver); assert delegate.fitsInDouble(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); throw e; } } @Override public boolean hasMembers(Object receiver) { assert preCondition(receiver); return delegate.hasMembers(receiver); } @Override public Object readMember(Object receiver, String identifier) throws UnsupportedMessageException, UnknownIdentifierException { if (CompilerDirectives.inCompiledCode()) { return delegate.readMember(receiver, identifier); } assert preCondition(receiver); assert validArgument(receiver, identifier); boolean wasReadable = delegate.isMemberReadable(receiver, identifier); try { Object result = delegate.readMember(receiver, identifier); assert delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert wasReadable || isMultiThreaded(receiver) : violationInvariant(receiver, identifier); assert validReturn(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof UnknownIdentifierException : violationPost(receiver, e); throw e; } } @Override public void writeMember(Object receiver, String identifier, Object value) throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException { if (CompilerDirectives.inCompiledCode()) { delegate.writeMember(receiver, identifier, value); return; } assert preCondition(receiver); assert validArgument(receiver, identifier); assert validArgument(receiver, value); boolean wasWritable = (delegate.isMemberModifiable(receiver, identifier) || delegate.isMemberInsertable(receiver, identifier)); try { delegate.writeMember(receiver, identifier, value); assert delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert wasWritable || isMultiThreaded(receiver) : violationInvariant(receiver, identifier); } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof UnknownIdentifierException || e instanceof UnsupportedTypeException : violationPost(receiver, e); throw e; } } @Override public void removeMember(Object receiver, String identifier) throws UnsupportedMessageException, UnknownIdentifierException { if (CompilerDirectives.inCompiledCode()) { delegate.removeMember(receiver, identifier); return; } assert preCondition(receiver); assert validArgument(receiver, identifier); boolean wasRemovable = delegate.isMemberRemovable(receiver, identifier); try { delegate.removeMember(receiver, identifier); assert delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert wasRemovable || isMultiThreaded(receiver) : violationInvariant(receiver, identifier); } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof UnknownIdentifierException : violationPost(receiver, e); throw e; } } @Override public Object invokeMember(Object receiver, String identifier, Object... arguments) throws UnsupportedMessageException, ArityException, UnknownIdentifierException, UnsupportedTypeException { if (CompilerDirectives.inCompiledCode()) { return delegate.invokeMember(receiver, identifier, arguments); } assert preCondition(receiver); assert validArgument(receiver, identifier); assert validArguments(receiver, arguments); boolean wasInvocable = delegate.isMemberInvocable(receiver, identifier); try { Object result = delegate.invokeMember(receiver, identifier, arguments); assert delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert wasInvocable || isMultiThreaded(receiver) : violationInvariant(receiver, identifier); assert validReturn(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof ArityException || e instanceof UnknownIdentifierException || e instanceof UnsupportedTypeException : violationPost(receiver, e); throw e; } } @Override public Object getMembers(Object receiver, boolean internal) throws UnsupportedMessageException { assert preCondition(receiver); try { Object result = delegate.getMembers(receiver, internal); assert validReturn(receiver, result); assert isMultiThreaded(receiver) || assertMemberKeys(receiver, result, internal); assert !delegate.hasScopeParent(receiver) || assertScopeMembers(receiver, result, delegate.getMembers(delegate.getScopeParent(receiver), internal)); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationPost(receiver, e); throw e; } } private static boolean assertMemberKeys(Object receiver, Object result, boolean internal) { assert result != null : violationPost(receiver, result); InteropLibrary uncached = InteropLibrary.getFactory().getUncached(result); assert uncached.hasArrayElements(result) : violationPost(receiver, result); long arraySize; try { arraySize = uncached.getArraySize(result); } catch (UnsupportedMessageException e) { assert false : violationPost(receiver, e); return true; } for (long i = 0; i < arraySize; i++) { assert uncached.isArrayElementReadable(result, i) : violationPost(receiver, result); Object element; try { element = uncached.readArrayElement(result, i); } catch (UnsupportedMessageException | InvalidArrayIndexException e) { assert false : violationPost(receiver, result); return true; } assert InteropLibrary.getFactory().getUncached().isString(element) : violationPost(receiver, element); try { InteropLibrary.getFactory().getUncached().asString(element); } catch (UnsupportedMessageException e) { assert false : violationInvariant(result, i); } } return true; } private static boolean assertScopeMembers(Object receiver, Object allMembers, Object parentMembers) { assert parentMembers != null : violationPost(receiver, parentMembers); InteropLibrary allUncached = InteropLibrary.getUncached(allMembers); InteropLibrary parentUncached = InteropLibrary.getUncached(parentMembers); assert allUncached.hasArrayElements(allMembers) : violationPost(receiver, allMembers); assert parentUncached.hasArrayElements(parentMembers) : violationPost(receiver, parentMembers); long allSize; long parentSize; try { allSize = allUncached.getArraySize(allMembers); parentSize = parentUncached.getArraySize(parentMembers); } catch (UnsupportedMessageException e) { assert false : violationPost(receiver, e); return true; } assert AssertUtils.validScopeMemberLengths(allSize, parentSize, allMembers, parentMembers); long currentSize = allSize - parentSize; for (long i = 0; i < parentSize; i++) { assert allUncached.isArrayElementReadable(allMembers, i + currentSize) : violationPost(receiver, allMembers); assert parentUncached.isArrayElementReadable(parentMembers, i) : violationPost(receiver, parentMembers); Object allElement; Object parentElement; try { allElement = allUncached.readArrayElement(allMembers, i + currentSize); } catch (UnsupportedMessageException | InvalidArrayIndexException e) { assert false : violationPost(receiver, allMembers); return true; } try { parentElement = parentUncached.readArrayElement(parentMembers, i); } catch (UnsupportedMessageException | InvalidArrayIndexException e) { assert false : violationPost(receiver, parentMembers); return true; } assert InteropLibrary.getUncached().isString(allElement) : violationPost(receiver, allElement); assert InteropLibrary.getUncached().isString(parentElement) : violationPost(receiver, parentElement); String allElementName; String parentElementName; try { allElementName = InteropLibrary.getUncached().asString(allElement); } catch (UnsupportedMessageException e) { assert false : violationInvariant(allElement); return true; } try { parentElementName = InteropLibrary.getUncached().asString(parentElement); } catch (UnsupportedMessageException e) { assert false : violationInvariant(parentElement); return true; } assert AssertUtils.validScopeMemberNames(allElementName, parentElementName, allMembers, parentMembers, i + currentSize, i); } return true; } @Override public boolean hasMemberReadSideEffects(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.hasMemberReadSideEffects(receiver, identifier); assert !result || delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert !result || (delegate.isMemberReadable(receiver, identifier) || isMultiThreaded(receiver)) : violationInvariant(receiver, identifier); return result; } @Override public boolean hasMemberWriteSideEffects(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.hasMemberWriteSideEffects(receiver, identifier); assert !result || delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); assert !result || (delegate.isMemberWritable(receiver, identifier) || isMultiThreaded(receiver)) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberReadable(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberReadable(receiver, identifier); assert !result || delegate.hasMembers(receiver) && !delegate.isMemberInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberModifiable(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberModifiable(receiver, identifier); assert !result || delegate.hasMembers(receiver) && !delegate.isMemberInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberInsertable(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberInsertable(receiver, identifier); assert !result || delegate.hasMembers(receiver) && !delegate.isMemberExisting(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberRemovable(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberRemovable(receiver, identifier); assert !result || delegate.hasMembers(receiver) && !delegate.isMemberInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberInvocable(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberInvocable(receiver, identifier); assert !result || delegate.hasMembers(receiver) && !delegate.isMemberInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isMemberInternal(Object receiver, String identifier) { assert preCondition(receiver); assert validArgument(receiver, identifier); boolean result = delegate.isMemberInternal(receiver, identifier); assert !result || delegate.hasMembers(receiver) : violationInvariant(receiver, identifier); return result; } @Override public boolean hasArrayElements(Object receiver) { assert preCondition(receiver); return delegate.hasArrayElements(receiver); } @Override public Object readArrayElement(Object receiver, long index) throws UnsupportedMessageException, InvalidArrayIndexException { if (CompilerDirectives.inCompiledCode()) { return delegate.readArrayElement(receiver, index); } assert preCondition(receiver); boolean wasReadable = delegate.isArrayElementReadable(receiver, index); try { Object result = delegate.readArrayElement(receiver, index); assert delegate.hasArrayElements(receiver) : violationInvariant(receiver, index); assert wasReadable || isMultiThreaded(receiver) : violationInvariant(receiver, index); assert validReturn(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof InvalidArrayIndexException : violationPost(receiver, e); throw e; } } @Override public void writeArrayElement(Object receiver, long index, Object value) throws UnsupportedMessageException, UnsupportedTypeException, InvalidArrayIndexException { if (CompilerDirectives.inCompiledCode()) { delegate.writeArrayElement(receiver, index, value); return; } assert preCondition(receiver); assert validArgument(receiver, value); boolean wasWritable = delegate.isArrayElementModifiable(receiver, index) || delegate.isArrayElementInsertable(receiver, index); try { delegate.writeArrayElement(receiver, index, value); assert delegate.hasArrayElements(receiver) : violationInvariant(receiver, index); assert wasWritable || isMultiThreaded(receiver) : violationInvariant(receiver, index); } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof UnsupportedTypeException || e instanceof InvalidArrayIndexException : violationPost(receiver, e); throw e; } } @Override public void removeArrayElement(Object receiver, long index) throws UnsupportedMessageException, InvalidArrayIndexException { if (CompilerDirectives.inCompiledCode()) { delegate.removeArrayElement(receiver, index); return; } assert preCondition(receiver); boolean wasRemovable = delegate.isArrayElementRemovable(receiver, index); try { delegate.removeArrayElement(receiver, index); assert delegate.hasArrayElements(receiver) : violationInvariant(receiver, index); assert wasRemovable || isMultiThreaded(receiver) : violationInvariant(receiver, index); } catch (InteropException e) { assert e instanceof UnsupportedMessageException || e instanceof InvalidArrayIndexException : violationPost(receiver, e); throw e; } } @Override public long getArraySize(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); try { long result = delegate.getArraySize(receiver); assert delegate.hasArrayElements(receiver) : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationPost(receiver, e); throw e; } } @Override public boolean isArrayElementReadable(Object receiver, long identifier) { assert preCondition(receiver); boolean result = delegate.isArrayElementReadable(receiver, identifier); assert !result || delegate.hasArrayElements(receiver) && !delegate.isArrayElementInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isArrayElementModifiable(Object receiver, long identifier) { assert preCondition(receiver); boolean result = delegate.isArrayElementModifiable(receiver, identifier); assert !result || delegate.hasArrayElements(receiver) && !delegate.isArrayElementInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isArrayElementInsertable(Object receiver, long identifier) { assert preCondition(receiver); boolean result = delegate.isArrayElementInsertable(receiver, identifier); assert !result || delegate.hasArrayElements(receiver) && !delegate.isArrayElementExisting(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isArrayElementRemovable(Object receiver, long identifier) { assert preCondition(receiver); boolean result = delegate.isArrayElementRemovable(receiver, identifier); assert !result || delegate.hasArrayElements(receiver) && !delegate.isArrayElementInsertable(receiver, identifier) : violationInvariant(receiver, identifier); return result; } @Override public boolean isPointer(Object receiver) { assert preCondition(receiver); boolean result = delegate.isPointer(receiver); return result; } @Override public void toNative(Object receiver) { assert preCondition(receiver); boolean wasPointer = delegate.isPointer(receiver); delegate.toNative(receiver); assert !wasPointer || delegate.isPointer(receiver) : violationInvariant(receiver); } @Override public long asPointer(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asPointer(receiver); } assert preCondition(receiver); boolean wasPointer = delegate.isPointer(receiver); try { long result = delegate.asPointer(receiver); assert wasPointer : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasPointer : violationInvariant(receiver); throw e; } } @Override public LocalDate asDate(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asDate(receiver); } assert preCondition(receiver); boolean hasDate = delegate.isDate(receiver); try { LocalDate result = delegate.asDate(receiver); assert hasDate : violationInvariant(receiver); assert !delegate.isTimeZone(receiver) || delegate.isTime(receiver) : violationInvariant(receiver); assert notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !hasDate : violationInvariant(receiver); assert !delegate.isTimeZone(receiver) || !delegate.isTime(receiver) || hasFixedTimeZone(receiver) : violationInvariant(receiver); throw e; } } @Override public LocalTime asTime(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asTime(receiver); } assert preCondition(receiver); boolean hasTime = delegate.isTime(receiver); try { LocalTime result = delegate.asTime(receiver); assert hasTime : violationInvariant(receiver); assert !delegate.isTimeZone(receiver) || delegate.isDate(receiver) || hasFixedTimeZone(receiver) : violationInvariant(receiver); assert notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !hasTime : violationInvariant(receiver); assert !delegate.isTimeZone(receiver) || !delegate.isDate(receiver) : violationInvariant(receiver); throw e; } } @Override public ZoneId asTimeZone(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asTimeZone(receiver); } assert preCondition(receiver); boolean hasTimeZone = delegate.isTimeZone(receiver); try { ZoneId result = delegate.asTimeZone(receiver); assert hasTimeZone : violationInvariant(receiver); assert ((delegate.isDate(receiver) || result.getRules().isFixedOffset()) && delegate.isTime(receiver)) || (!delegate.isDate(receiver) && !delegate.isTime(receiver)) : violationInvariant(receiver); assert notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !hasTimeZone : violationInvariant(receiver); throw e; } } private boolean hasFixedTimeZone(Object receiver) { try { return delegate.asTimeZone(receiver).getRules().isFixedOffset(); } catch (InteropException e) { throw shouldNotReachHere(violationInvariant(receiver)); } } @Override public Duration asDuration(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asDuration(receiver); } assert preCondition(receiver); boolean wasDuration = delegate.isDuration(receiver); try { Duration result = delegate.asDuration(receiver); assert wasDuration : violationInvariant(receiver); assert notOtherType(receiver, Type.DURATION); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasDuration : violationInvariant(receiver); throw e; } } @Override public Instant asInstant(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.asInstant(receiver); } assert preCondition(receiver); boolean hasDateAndTime = delegate.isDate(receiver) && delegate.isTime(receiver) && delegate.isTimeZone(receiver); try { Instant result = delegate.asInstant(receiver); assert hasDateAndTime : violationInvariant(receiver); assert ZonedDateTime.of(delegate.asDate(receiver), delegate.asTime(receiver), delegate.asTimeZone(receiver)).// toInstant().equals(result) : violationInvariant(receiver); assert notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !hasDateAndTime : violationInvariant(receiver); throw e; } } @Override public boolean isDate(Object receiver) { assert preCondition(receiver); boolean result = delegate.isDate(receiver); assert !delegate.isTimeZone(receiver) || (delegate.isTime(receiver) && result) || ((!delegate.isTime(receiver) || hasFixedTimeZone(receiver)) && !result) : violationInvariant(receiver); assert !result || notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } @Override public boolean isTime(Object receiver) { assert preCondition(receiver); boolean result = delegate.isTime(receiver); assert !delegate.isTimeZone(receiver) || ((delegate.isDate(receiver) || hasFixedTimeZone(receiver)) && result) || (!delegate.isDate(receiver) && !result) : violationInvariant(receiver); assert !result || notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } @Override public boolean isTimeZone(Object receiver) { assert preCondition(receiver); boolean result = delegate.isTimeZone(receiver); assert !result || ((delegate.isDate(receiver) || hasFixedTimeZone(receiver)) && delegate.isTime(receiver)) || (!delegate.isDate(receiver) && !delegate.isTime(receiver)) : violationInvariant(receiver); assert !result || notOtherType(receiver, Type.DATE_TIME_ZONE); return result; } @Override public boolean isDuration(Object receiver) { assert preCondition(receiver); boolean result = delegate.isDuration(receiver); assert !result || notOtherType(receiver, Type.DURATION); return result; } @Override public boolean isException(Object receiver) { assert preCondition(receiver); boolean result = delegate.isException(receiver); return result; } @Override public ExceptionType getExceptionType(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); ExceptionType result = delegate.getExceptionType(receiver); return result; } @Override public boolean isExceptionIncompleteSource(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.isExceptionIncompleteSource(receiver); } assert preCondition(receiver); boolean wasParseError; try { wasParseError = delegate.getExceptionType(receiver) == ExceptionType.PARSE_ERROR; } catch (UnsupportedMessageException e) { wasParseError = false; } try { boolean result = delegate.isExceptionIncompleteSource(receiver); assert !result || wasParseError : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasParseError : violationInvariant(receiver); throw e; } } @Override public int getExceptionExitStatus(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getExceptionExitStatus(receiver); } assert preCondition(receiver); boolean wasExit; try { wasExit = delegate.getExceptionType(receiver) == ExceptionType.EXIT; } catch (UnsupportedMessageException e) { wasExit = false; } try { int result = delegate.getExceptionExitStatus(receiver); assert wasExit : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasExit : violationInvariant(receiver); throw e; } } @Override public RuntimeException throwException(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.throwException(receiver); } assert preCondition(receiver); boolean wasException = delegate.isException(receiver); boolean wasTruffleException = false; boolean unsupported = false; try { throw delegate.throwException(receiver); } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasException : violationInvariant(receiver); unsupported = true; throw e; } catch (Throwable e) { wasTruffleException = LegacyTruffleExceptionSupport.isTruffleException(e); throw e; } finally { if (!unsupported) { assert wasException : violationInvariant(receiver); assert wasTruffleException : violationInvariant(receiver); } } } @Override public boolean hasExceptionCause(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasExceptionCause(receiver); return result; } @Override public Object getExceptionCause(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getExceptionCause(receiver); } assert preCondition(receiver); boolean wasHasExceptionCause = delegate.hasExceptionCause(receiver); try { Object result = delegate.getExceptionCause(receiver); assert wasHasExceptionCause : violationInvariant(receiver); assert assertException(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasExceptionCause : violationInvariant(receiver); throw e; } } private static boolean assertException(Object receiver, Object exception) { InteropLibrary uncached = InteropLibrary.getUncached(exception); assert uncached.isException(exception) : violationPost(receiver, exception); return true; } @Override public boolean hasExceptionMessage(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasExceptionMessage(receiver); return result; } @Override public Object getExceptionMessage(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getExceptionMessage(receiver); } assert preCondition(receiver); boolean wasHasExceptionMessage = delegate.hasExceptionMessage(receiver); try { Object result = delegate.getExceptionMessage(receiver); assert wasHasExceptionMessage : violationInvariant(receiver); assert assertString(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasExceptionMessage : violationInvariant(receiver); throw e; } } @Override public boolean hasExceptionStackTrace(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasExceptionStackTrace(receiver); return result; } @Override public Object getExceptionStackTrace(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getExceptionStackTrace(receiver); } assert preCondition(receiver); boolean wasHasExceptionStackTrace = delegate.hasExceptionStackTrace(receiver); try { Object result = delegate.getExceptionStackTrace(receiver); assert wasHasExceptionStackTrace : violationInvariant(receiver); assert verifyStackTrace(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasExceptionStackTrace : violationInvariant(receiver); throw e; } } private static boolean verifyStackTrace(Object receiver, Object stackTrace) { assert stackTrace != null : violationPost(receiver, stackTrace); InteropLibrary stackTraceLib = InteropLibrary.getFactory().getUncached(stackTrace); assert stackTraceLib.hasArrayElements(stackTrace) : violationPost(receiver, stackTrace); return true; } @Override public boolean hasExecutableName(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasExecutableName(receiver); return result; } @Override public Object getExecutableName(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getExecutableName(receiver); } assert preCondition(receiver); boolean wasHasExecutableName = delegate.hasExecutableName(receiver); try { Object result = delegate.getExecutableName(receiver); assert wasHasExecutableName : violationInvariant(receiver); assert assertString(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasExecutableName : violationInvariant(receiver); throw e; } } @Override public boolean hasDeclaringMetaObject(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasDeclaringMetaObject(receiver); return result; } @Override public Object getDeclaringMetaObject(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getDeclaringMetaObject(receiver); } assert preCondition(receiver); boolean wasHasDeclaringMetaObject = delegate.hasDeclaringMetaObject(receiver); try { Object result = delegate.getDeclaringMetaObject(receiver); assert wasHasDeclaringMetaObject : violationInvariant(receiver); assert verifyDeclaringMetaObject(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasDeclaringMetaObject : violationInvariant(receiver); throw e; } } private static boolean verifyDeclaringMetaObject(Object receiver, Object meta) { assert meta != null : violationPost(receiver, meta); InteropLibrary metaLib = InteropLibrary.getFactory().getUncached(meta); assert metaLib.isMetaObject(meta) : violationPost(receiver, meta); try { assert metaLib.getMetaSimpleName(meta) != null : violationPost(receiver, meta); assert metaLib.getMetaQualifiedName(meta) != null : violationPost(receiver, meta); } catch (UnsupportedMessageException e) { assert false : violationPost(receiver, meta); } return true; } @Override public Object toDisplayString(Object receiver, boolean allowSideEffects) { assert preCondition(receiver); assert validNonInteropArgument(receiver, allowSideEffects); Object result = delegate.toDisplayString(receiver, allowSideEffects); assert assertString(receiver, result); return result; } @Override public boolean hasSourceLocation(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.hasSourceLocation(receiver); } assert preCondition(receiver); boolean result = delegate.hasSourceLocation(receiver); if (result) { try { assert delegate.getSourceLocation(receiver) != null : violationPost(receiver, result); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } else { assert assertHasNoSourceSection(receiver); } return result; } private boolean assertHasNoSourceSection(Object receiver) { try { delegate.getSourceLocation(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } return true; } @Override public SourceSection getSourceLocation(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getSourceLocation(receiver); } assert preCondition(receiver); boolean wasHasSourceLocation = delegate.hasSourceLocation(receiver); try { SourceSection result = delegate.getSourceLocation(receiver); assert wasHasSourceLocation : violationInvariant(receiver); assert result != null : violationPost(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasSourceLocation : violationInvariant(receiver); throw e; } } @Override public boolean hasLanguage(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasLanguage(receiver); if (result) { try { assert delegate.getLanguage(receiver) != null : violationPost(receiver, result); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } } else { assert assertHasNoLanguage(receiver); } return result; } private boolean assertHasNoLanguage(Object receiver) { try { delegate.getLanguage(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } return true; } @Override public Class> getLanguage(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getLanguage(receiver); } assert preCondition(receiver); boolean wasHasLanguage = delegate.hasLanguage(receiver); try { Class> result = delegate.getLanguage(receiver); assert wasHasLanguage : violationInvariant(receiver); assert result != null : violationPost(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasLanguage : violationInvariant(receiver); throw e; } } @Override public boolean hasMetaObject(Object receiver) { assert preCondition(receiver); boolean result = delegate.hasMetaObject(receiver); if (result) { assert assertHasMetaObject(receiver, result); } else { assert assertHasNoMetaObject(receiver); } return result; } private boolean assertHasMetaObject(Object receiver, boolean result) { try { Object meta = delegate.getMetaObject(receiver); assert verifyMetaObject(receiver, meta); } catch (InteropException e) { assert false : violationInvariant(receiver); } catch (Exception e) { } return true; } private static boolean verifyMetaObject(Object receiver, Object meta) throws UnsupportedMessageException { assert meta != null : violationPost(receiver, meta); InteropLibrary metaLib = InteropLibrary.getFactory().getUncached(meta); assert metaLib.isMetaObject(meta) : violationPost(receiver, meta); assert metaLib.isMetaInstance(meta, receiver) : violationPost(receiver, meta); assert metaLib.getMetaSimpleName(meta) != null : violationPost(receiver, meta); assert metaLib.getMetaQualifiedName(meta) != null : violationPost(receiver, meta); return true; } private boolean assertHasNoMetaObject(Object receiver) { try { delegate.getMetaObject(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } return true; } @Override public Object getMetaObject(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getMetaObject(receiver); } assert preCondition(receiver); boolean wasHasMetaObject = delegate.hasMetaObject(receiver); try { Object result = delegate.getMetaObject(receiver); assert wasHasMetaObject : violationInvariant(receiver); assert verifyMetaObject(receiver, result); assert result != null : violationPost(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasHasMetaObject : violationInvariant(receiver); throw e; } } @Override public boolean isMetaObject(Object receiver) { assert preCondition(receiver); boolean result = delegate.isMetaObject(receiver); if (result) { assert assertMetaObject(receiver); } else { assert assertNoMetaObject(receiver); assert !result || notOtherType(receiver, Type.META_OBJECT); } return result; } private boolean assertNoMetaObject(Object receiver) { try { delegate.isMetaInstance(receiver, receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } try { delegate.getMetaSimpleName(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } try { delegate.getMetaQualifiedName(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } return true; } private boolean assertMetaObject(Object receiver) { try { delegate.isMetaInstance(receiver, receiver); } catch (UnsupportedMessageException e) { assert false : violationInvariant(receiver); } try { assert assertString(receiver, delegate.getMetaSimpleName(receiver)) : violationInvariant(receiver); } catch (UnsupportedMessageException e) { assert false : violationInvariant(receiver); } try { assert assertString(receiver, delegate.getMetaQualifiedName(receiver)) : violationInvariant(receiver); } catch (UnsupportedMessageException e) { assert false : violationInvariant(receiver); } return true; } @Override public Object getMetaQualifiedName(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getMetaQualifiedName(receiver); } assert preCondition(receiver); boolean wasMetaObject = delegate.isMetaObject(receiver); try { Object result = delegate.getMetaQualifiedName(receiver); assert wasMetaObject : violationInvariant(receiver); assert assertString(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasMetaObject : violationInvariant(receiver); throw e; } } @Override public Object getMetaSimpleName(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getMetaSimpleName(receiver); } assert preCondition(receiver); boolean wasMetaObject = delegate.isMetaObject(receiver); try { Object result = delegate.getMetaSimpleName(receiver); assert wasMetaObject : violationInvariant(receiver); assert assertString(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasMetaObject : violationInvariant(receiver); throw e; } } @Override public boolean isMetaInstance(Object receiver, Object instance) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.isMetaInstance(receiver, instance); } assert preCondition(receiver); assert validArgument(receiver, instance); boolean wasMetaObject = delegate.isMetaObject(receiver); try { boolean result = delegate.isMetaInstance(receiver, instance); assert wasMetaObject : violationInvariant(receiver); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !wasMetaObject : violationInvariant(receiver); throw e; } } @Override protected TriState isIdenticalOrUndefined(Object receiver, Object other) { assert preCondition(receiver); assert validArgument(receiver, other); TriState result = delegate.isIdenticalOrUndefined(receiver, other); assert verifyIsSameOrUndefined(delegate, result, receiver, other); return result; } static boolean verifyIsSameOrUndefined(InteropLibrary library, TriState result, Object receiver, Object other) { if (result != TriState.UNDEFINED) { int hashCode = 0; try { hashCode = library.identityHashCode(receiver); } catch (Exception t) { throw shouldNotReachHere(t); } } return true; } @Override public int identityHashCode(Object receiver) throws UnsupportedMessageException { assert preCondition(receiver); int result; try { result = delegate.identityHashCode(receiver); assert delegate.hasIdentity(receiver) : violationInvariant(receiver); } catch (UnsupportedMessageException e) { assert !delegate.hasIdentity(receiver) : violationInvariant(receiver); throw e; } return result; } @Override public boolean isIdentical(Object receiver, Object other, InteropLibrary otherInterop) { assert preCondition(receiver); assert validArgument(receiver, other); assert otherInterop != null; boolean result = delegate.isIdentical(receiver, other, otherInterop); assert verifyIsSame(result, receiver, other, otherInterop); return result; } boolean verifyIsSame(boolean result, Object receiver, Object other, InteropLibrary otherInterop) { try { InteropLibrary otherDelegate = otherInterop; if (otherInterop instanceof Asserts) { // avoid recursions otherDelegate = ((Asserts) otherInterop).delegate; } // verify symmetric property assert result == otherDelegate.isIdentical(other, receiver, delegate) : violationInvariant(receiver); if (result) { // if true identity hash code must be equal assert delegate.identityHashCode(receiver) == otherDelegate.identityHashCode(other) : violationInvariant(receiver); } // verify reflexivity TriState state = delegate.isIdenticalOrUndefined(receiver, other); if (state != TriState.UNDEFINED) { assert delegate.isIdentical(receiver, receiver, delegate) : violationInvariant(receiver); } // also check isIdenticalOrUndefined results as they would be skipped with the // isIdentical default implementation. verifyIsSameOrUndefined(delegate, state, receiver, other); verifyIsSameOrUndefined(otherDelegate, otherDelegate.isIdenticalOrUndefined(other, receiver), other, receiver); } catch (UnsupportedMessageException e) { throw shouldNotReachHere(e); } return true; } @Override public boolean isScope(Object receiver) { assert preCondition(receiver); boolean result = delegate.isScope(receiver); assert !result || delegate.hasMembers(receiver) : violationInvariant(receiver); assert !result || delegate.hasLanguage(receiver) : violationInvariant(receiver); return result; } @Override public boolean hasScopeParent(Object receiver) { if (CompilerDirectives.inCompiledCode()) { return delegate.hasScopeParent(receiver); } assert preCondition(receiver); boolean result = delegate.hasScopeParent(receiver); if (result) { assert delegate.isScope(receiver) : violationInvariant(receiver); try { assert validScope(delegate.getScopeParent(receiver)); } catch (UnsupportedMessageException e) { assert false : violationInvariant(receiver); } } else { try { delegate.getScopeParent(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException e) { } } return result; } @Override public Object getScopeParent(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { return delegate.getScopeParent(receiver); } assert preCondition(receiver); boolean hadScopeParent = delegate.hasScopeParent(receiver); try { Object result = delegate.getScopeParent(receiver); assert hadScopeParent : violationInvariant(receiver); assert delegate.isScope(receiver) : violationInvariant(receiver); assert validScope(result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationInvariant(receiver); assert !hadScopeParent : violationInvariant(receiver); throw e; } } } } class InteropLibrarySnippets { abstract static class StatementNode extends Node { abstract void executeVoid(VirtualFrame frame); } static class BlockNode extends StatementNode { @Children private StatementNode[] children; BlockNode(StatementNode... children) { this.children = children; } @Override @ExplodeLoop void executeVoid(VirtualFrame frame) { for (StatementNode child : children) { child.executeVoid(frame); } } } // BEGIN: InteropLibrarySnippets.TryCatchNode static final class TryCatchNode extends StatementNode { @Node.Child private BlockNode block; @Node.Child private BlockNode catchBlock; @Node.Child private BlockNode finallyBlock; @Node.Child private InteropLibrary exceptions; private final BranchProfile exceptionProfile; TryCatchNode(BlockNode block, BlockNode catchBlock, BlockNode finallyBlock) { this.block = block; this.catchBlock = catchBlock; this.finallyBlock = finallyBlock; this.exceptions = InteropLibrary.getFactory().createDispatched(5); this.exceptionProfile = BranchProfile.create(); } @Override void executeVoid(VirtualFrame frame) { Throwable exception = null; try { block.executeVoid(frame); } catch (Throwable ex) { exception = executeCatchBlock(frame, ex, catchBlock); } // Java finally blocks that execute nodes are not allowed for // compilation as code in finally blocks is duplicated // by the Java bytecode compiler. This can lead to // exponential code growth in worst cases. if (finallyBlock != null) { finallyBlock.executeVoid(frame); } if (exception != null) { if (exception instanceof ControlFlowException) { throw (ControlFlowException) exception; } try { throw exceptions.throwException(exception); } catch (UnsupportedMessageException ie) { throw CompilerDirectives.shouldNotReachHere(ie); } } } @SuppressWarnings("unchecked") private Throwable executeCatchBlock( VirtualFrame frame, Throwable ex, BlockNode catchBlk) throws T { if (ex instanceof ControlFlowException) { // run finally blocks for control flow return ex; } exceptionProfile.enter(); if (exceptions.isException(ex)) { if (catchBlk != null) { try { catchBlk.executeVoid(frame); return null; } catch (Throwable catchEx) { return executeCatchBlock(frame, catchEx, null); } } else { // run finally blocks for any interop exception return ex; } } else { // do not run finally blocks for internal errors or unwinds throw (T) ex; } } } // END: InteropLibrarySnippets.TryCatchNode }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy