com.oracle.truffle.api.interop.InteropLibrary Maven / Gradle / Ivy
Show all versions of truffle-api Show documentation
/*
* 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 extends TruffleLanguage>> getLanguage(Object receiver) throws UnsupportedMessageException {
Env env = getLegacyEnv(receiver, false);
if (env != null) {
return (Class extends TruffleLanguage>>) 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 extends TruffleLanguage>> getLanguage(Object receiver) throws UnsupportedMessageException {
if (CompilerDirectives.inCompiledCode()) {
return delegate.getLanguage(receiver);
}
assert preCondition(receiver);
boolean wasHasLanguage = delegate.hasLanguage(receiver);
try {
Class extends TruffleLanguage>> 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
}