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

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

Go to download

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

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.truffle.api.interop;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.interop.ForeignAccess.Factory;
import com.oracle.truffle.api.nodes.Node;

/**
 * Inter-operability is based on sending messages. Standard messages are defined as as constants
 * like {@link #IS_NULL} or factory methods in this class, but one can always define their own,
 * specialized messages.
 *
 * @since 0.8 or earlier
 */
public abstract class Message {
    /**
     * One can define their own extended message by subclassing. The expectation is that the
     * subclass will have public constructor and its {@link #equals(java.lang.Object)} and
     * {@link #hashCode()} methods will operate on the class equivalence. Only then the subclass
     * will work properly with {@link #valueOf(java.lang.String)} and
     * {@link #toString(com.oracle.truffle.api.interop.Message)} methods.
     *
     * @since 0.8 or earlier
     */
    protected Message() {
        registerClass(this);
    }

    /**
     * Message to read an object field. The
     * {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) target} created for this
     * message accepts (in addition to a
     * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver}) a single
     * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) argument} identifying a
     * field to read - e.g. either {@link String} or a {@link Number} - if access to an array at
     * particular index is requested.
     * 

* If the object does not support the {@link #READ} message, an * {@link UnsupportedMessageException} has to be thrown. * * If the object does not allow reading a property for a given identifier, an * {@link UnknownIdentifierException} has to be thrown. *

* The code that wants to send this message should use: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendRead(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object) sendRead}(
     *   {@link Message#READ}.{@link Message#createNode()},  receiver, nameOfTheField
     * );
     * 
* * Where receiver is the {@link TruffleObject foreign object} to access and * nameOfTheField is the name (or index) of its field. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.8 or earlier */ public static final Message READ = Read.INSTANCE; /** * Converts {@link TruffleObject truffle value} to Java primitive type. Primitive types are * subclasses of {@link Number}, {@link Boolean}, {@link Character} and {@link String}. Before * sending the {@link #UNBOX} message, it is desirable to send the {@link #IS_BOXED} one and * verify that the object can really be unboxed. *

* If the object does not support the {@link #UNBOX} message, an * {@link UnsupportedMessageException} has to be thrown. *

* To unbox an object, use: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendUnbox(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject) sendUnbox}(
     *   {@link Message#UNBOX}.{@link Message#createNode()},  objectToUnbox
     * );
     * 
* * The returned value should be subclass of {@link Number}, {@link Boolean}, {@link Character} * or {@link String}. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.8 or earlier */ public static final Message UNBOX = Unbox.INSTANCE; /** * Message to write a field. The * {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) target} created for this * message accepts the object to modify as a * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver} and two * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) arguments}. The first * one identifies a field to read - e.g. either {@link String} or an {@link Integer} - if access * to an array at particular index is requested. The second one is the value to assign to such * field. *

* If the object does not support the {@link #WRITE} message, an * {@link UnsupportedMessageException} has to be thrown. * * If the object does not allow writing a property for a given identifier, an * {@link UnknownIdentifierException} has to be thrown. * * If the provided value has an unsupported type and cannot be written, an * {@link UnsupportedTypeException} has to be thrown. *

* Use following style to construct field modification message: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendWrite(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object, java.lang.Object) sendWrite}(
     *   {@link Message#WRITE}.{@link Message#createNode()},  receiver, nameOfTheField, newValue
     * );
     * 
* * Where receiver is the {@link TruffleObject foreign object} to access, * nameOfTheField is the name (or index) of its field and newValue is * the value to assign to the receiver's field. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.8 or earlier */ public static final Message WRITE = Write.INSTANCE; /** * Message to remove a field. The * {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) target} created for this * message accepts the object to modify as a * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver} and one * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) argument} identifying a * field to remove - e.g. either {@link String} or a {@link Number} - if removal of an array * element at particular index is requested. *

* If the object does not support the {@link #REMOVE} message, an * {@link UnsupportedMessageException} has to be thrown. * * If the object does not contain a property for a given identifier, an * {@link UnknownIdentifierException} has to be thrown. *

* Use following style to construct field removal message: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendRemove(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object) sendRemove}(
     *   {@link Message#WRITE}.{@link Message#createNode()},  receiver, nameOfTheField);
     * 
* * Where receiver is the {@link TruffleObject foreign object} to access and * nameOfTheField is the name (or index) of its field. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.32 */ public static final Message REMOVE = Remove.INSTANCE; /** * Creates a non-object oriented execution message. In contrast to {@link #createInvoke(int)} * messages, which are more suitable for dealing with object oriented style of programming, * messages created by this method are more suitable for execution where one can explicitly * control all passed in arguments. *

* To inter-operate with a non-OOP language like C - for example to execute its * function: * *

     * double add(double a, double b) {
     *   return a + b;
     * }
     * 
* * One can obtain reference to the add function (for example by * {@link Env#importSymbol(java.lang.String) importing it as a global symbol}) and store it into * variable addFunction. Then it's time to check the object is executable by * sending it the {@link #IS_EXECUTABLE} message. *

* If the object does not support the EXECUTE message, an * {@link UnsupportedMessageException} has to be thrown. * * If the caller provides a wrong number of arguments, an {@link ArityException} has to be * thrown. * * If one of the provided argument values has an unsupported type, an * {@link UnsupportedTypeException} has to be thrown. *

* Use following style to construct execution message: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendExecute(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object...) sendExecute}(
     *   {@link Message#createExecute(int) Message.createExecute}(2).{@link Message#createNode()},  addFunction, valueOfA, valueOfB
     * );
     * 
* * The valueOfA and valueOfB should be double or * {@link Double} or at least be {@link #UNBOX unboxable} to such type. *

* One can use this method to talk to object oriented language as well, however one needs to pay * attention to provide all necessary arguments manually - usually an OOP language requires the * first argument to represent this or self and only then pass in the * additional arguments. It may be easier to use {@link #createInvoke(int)} message which is * more suitable for object oriented languages and handles (if supported) the arguments * manipulation automatically. *

* * *

* All messages created by this method are {@link Object#equals(java.lang.Object) equal} to each * other regardless of the value of argumentsLength. * * @param argumentsLength number of parameters to pass to the target * @return execute message * @since 0.8 or earlier */ public static Message createExecute(int argumentsLength) { return Execute.create(Execute.EXECUTE, argumentsLength); } /** * Message to check executability of a * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) foreign object}. *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message accepts * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) no arguments} and a * single non-null {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) * receiver}. The call should yield value of {@link Boolean}. Either {@link Boolean#TRUE} if the * receiver can be executed (i.e. accepts {@link #createExecute(int)} message, or * {@link Boolean#FALSE} otherwise. This is the way to send the IS_EXECUTABLE * message: * *

     * {@link Boolean} canBeExecuted = ({@link Boolean}) {@link ForeignAccess}.sendIsExecutable(
     *   {@link Message#IS_EXECUTABLE}.{@link Message#createNode()},  receiver
     * );
     * 
*

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.8 or earlier */ public static final Message IS_EXECUTABLE = IsExecutable.INSTANCE; /** * Message to check the ability to create new instances of a * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) foreign object}. *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message accepts * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) no arguments} and a * single non-null {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) * receiver}. The call should yield value of {@link Boolean}. Either {@link Boolean#TRUE} if the * receiver can be instantiated (i.e. accepts {@link #createNew(int)} message, or * {@link Boolean#FALSE} otherwise. This is the way to send the IS_INSTANTIABLE * message: * *

     * {@link Boolean} canBeinstantiated = ({@link Boolean}) {@link ForeignAccess}.sendIsInstantiable(
     *   {@link Message#IS_INSTANTIABLE}.{@link Message#createNode()},  receiver
     * );
     * 
*

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.30 */ public static final Message IS_INSTANTIABLE = IsInstantiable.INSTANCE; /** * Creates an object oriented execute message. Unlike {@link #createExecute(int)} the receiver * of the message isn't the actual function to invoke, but an object. The object has the * function as a field, or as a field of its class, or whatever is appropriate for an object * oriented language. *

* Languages that don't support object oriented semantics do not and should not implement this * message. When the invoke message isn't supported, the caller is expected to fall back into * following basic operations: *

    *
  • sending {@link #READ} message to access the field
  • *
  • verify the result {@link #IS_EXECUTABLE}, if so continue by
  • *
  • sending {@link #createExecute(int) execute message}
  • *
*

* The last step is problematic, as it is not clear whether to pass just the execution * arguments, or prefix them with the original receiver (aka this or * self). Object oriented languages would in general welcome obtaining the * receiving object as first argument, non-object languages like C would get confused * by doing so. However it is not possible for the caller to find out what language one is * sending message to - only the set of supported messages is known. As a result it is * recommended for object oriented languages to support the {@link #createInvoke(int)} message * and handle the semantics the way it is natural to them. Languages like C shouldn't * implement {@link #createInvoke(int)} and just support primitive operations like * {@link #createExecute(int)} and {@link #READ}. *

* When accessing a method of an object in an object oriented manner, one is supposed to send * the {@link #createInvoke(int)} message first. Only when that fails, fallback to non-object * oriented workflow with {@link #createExecute(int)}. Imagine there is a Java class * with add method and its instance: * *

     * public class Arith {
     *    public double add(double a, double b) {
     *      return a + b;
     *    }
     * }
     * Arith obj = new Arith();
     * 
*

* If the object does not support the INVOKE message, an * {@link UnsupportedMessageException} has to be thrown. * * If the object does not allow invoking a member with the given identifier, an * {@link UnknownIdentifierException} has to be thrown. * * If the caller provides a wrong number of arguments, an {@link ArityException} has to be * thrown. * * If one of the provided argument values has an unsupported type, an * {@link UnsupportedTypeException} has to be thrown. *

* * To access obj's add method one should use: * *

     * try {
     *   {@link ForeignAccess}.{@link ForeignAccess#sendInvoke(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.String, java.lang.Object...) sendInvoke}(
     *     {@link Message#createInvoke(int) Message.createInvoke}(2).{@link Message#createNode()},  obj, "add", valueOfA, valueOfB
     *   );
     * } catch ({@link IllegalArgumentException} ex) {
     *   // access the language via {@link #createExecute(int)}
     * }
     * 
* * The valueOfA and valueOfB should be double or * {@link Double} or at least be {@link #UNBOX unboxable} to such type. *

* All messages created by this method are {@link Object#equals(java.lang.Object) equal} to each * other regardless of the value of argumentsLength. The expected behavior of this * message is to perform {@link #READ} first and on the result invoke * {@link #createExecute(int)}. * * @param argumentsLength number of parameters to pass to the target * @return message combining read & execute messages tailored for use with object oriented * languages * @since 0.8 or earlier */ public static Message createInvoke(int argumentsLength) { return Execute.create(Execute.INVOKE, argumentsLength); } /** * Creates an allocation message. All messages created by this method are * {@link Object#equals(java.lang.Object) equal} to each other regardless of the value of * argumentsLength. The expected behavior of this message is to allocate a new * instance of the {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) * receiver} and then perform its constructor with appropriate number of arguments. To check if * an object supports allocation of new instances, use the {@link #IS_INSTANTIABLE} message. *

* If the object does not support the NEW message, an * {@link UnsupportedMessageException} has to be thrown. * * If the caller provides a wrong number of arguments, an {@link ArityException} has to be * thrown. * * If one of the provided argument values has an unsupported type, an * {@link UnsupportedTypeException} has to be thrown. *

* * @param argumentsLength number of parameters to pass to the target * @return new instance message * @since 0.8 or earlier */ public static Message createNew(int argumentsLength) { return Execute.create(Execute.NEW, argumentsLength); } /** * Check for null message. The Truffle languages are suggested to have their own * object representing null like values in their languages. For purposes of * inter-operability it is essential to canonicalize such values from time to time - sending * this message is a way to recognize such null representing values: * *

     * {@link Boolean} isNull = ({@link Boolean}) {@link ForeignAccess}.sendIsNull(
     *   {@link Message#IS_NULL}.{@link Message#createNode()},  objectToCheckForNull
     * );
     * 
* *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of {@link Boolean}. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.8 or earlier */ public static final Message IS_NULL = IsNull.INSTANCE; /** * Message to check for having a size. If a {@link TruffleObject} indicates it has a * size, it is expected it represents array-like structure and it also properly responds to * {@link #GET_SIZE} message. When HAS_SIZE returns false, it * indicates that {@link #GET_SIZE} message is not supported. *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of {@link Boolean}. * * @since 0.8 or earlier * @see ForeignAccess#sendHasSize(com.oracle.truffle.api.nodes.Node, * com.oracle.truffle.api.interop.TruffleObject) */ public static final Message HAS_SIZE = HasSize.INSTANCE; /** * Getter of the size. If {@link #HAS_SIZE supported}, this message has to return size of * receiver's array like structure as an {@link Integer}. If the {@link #HAS_SIZE} message * returns true implementations for {@link #READ} and {@link #WRITE} messages with * {@link Integer} parameters from range 0 to GET_SIZE - 1 are * required. *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of {@link Integer}. *

* If the object does not support the {@link #GET_SIZE} message, an * {@link UnsupportedMessageException} has to be thrown. *

* * @since 0.8 or earlier * @see ForeignAccess#sendGetSize(com.oracle.truffle.api.nodes.Node, * com.oracle.truffle.api.interop.TruffleObject) */ public static final Message GET_SIZE = GetSize.INSTANCE; /** * Check for value being boxed. Can the {@link TruffleObject foreign object} be converted to one * of the basic Java types? Many languages have a special representation for types like number, * string, etc. To ensure inter-operability, these types should support unboxing - if they do, * they should handle this message and return {@link Boolean#TRUE}. The way to check whether an * object is boxed is: * *

     * {@link Boolean} isBoxed = ({@link Boolean}) {@link ForeignAccess}.sendIsBoxed(
     *   {@link Message#IS_BOXED}.{@link Message#createNode()},  objectToCheck
     * );
     * 
* * Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of {@link Boolean}. If the object responds with * {@link Boolean#TRUE}, it is safe to continue by sending it {@link #UNBOX} message. * * @since 0.8 or earlier */ public static final Message IS_BOXED = IsBoxed.INSTANCE; /** * Message to retrieve flags about a particular key (a property name). The returned value is an * integer containing bit flags. See {@link KeyInfo} for possible flags. This message also * allows a fast check of existence of a property among {@link #KEYS}, the returned value is * 0 iff the key does not exist. The * {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) target} created for this * message accepts (in addition to a * {@link ForeignAccess#getReceiver(com.oracle.truffle.api.frame.Frame) receiver}) a single * {@link ForeignAccess#getArguments(com.oracle.truffle.api.frame.Frame) argument} identifying a * property to get the info of - e.g. either {@link String} or a {@link Number} - if test of an * array at a particular index is requested. *

* The default implementation requests {@link #KEYS} and test if they contain the requested key. * If they do, a default bit mask 0b111 is returned. *

* The code that wants to send this message should use: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendKeyInfo(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object) sendKeyInfo}(
     *   {@link Message#KEY_INFO}.{@link Message#createNode() createNode()},  receiver, nameOfTheField
     * );
     * 
* * Where receiver is the {@link TruffleObject foreign object} to access and * nameOfTheField is the name (or index) of its field. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.26 */ public static final Message KEY_INFO = KeyInfoMsg.INSTANCE; /** * Message to check for having properties. If a {@link TruffleObject} indicates it has * keys, it is expected it represents an object structure with properties and it also * properly responds to {@link #KEYS} message. When HAS_KEYS returns * false, it indicates that {@link #KEYS} message is not supported. *

* The default implementation requests {@link #KEYS} and returns true if the * request was successful and false otherwise. *

* Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of type {@link Boolean}. * * @since 0.30 * @see ForeignAccess#sendHasKeys(com.oracle.truffle.api.nodes.Node, * com.oracle.truffle.api.interop.TruffleObject) */ public static final Message HAS_KEYS = HasKeys.INSTANCE; /** * Obtains list of property names. Checks the properties of a {@link TruffleObject foreign * objects} and obtains list of its property names. Those names can then be used in * {@link #READ} and {@link #WRITE} messages to obtain/assign real values. To check if an object * supports properties, use the {@link #HAS_KEYS} message. *

* Since version 0.26 the {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) * target} created for this message accepts a boolean argument specifying whether internal keys * should be included. Internal keys are extra property keys that are a part of the object, but * are not provided among ordinary keys. They may even not correspond to anything what is an * explicit part of the guest language representation. An example of such internal values are * internal slots in ECMAScript. *

* The return value from using this message is another {@link TruffleObject} that responds to * {@link #HAS_SIZE} message and its indexes 0 to {@link #GET_SIZE} - 1 contain {@link String} * names of individual properties. The properties should be provided in deterministic order. * * @since 0.18 */ public static final Message KEYS = Keys.INSTANCE; /** * Check for a value being a native pointer. Can the {@link TruffleObject foreign object} be * converted to a 64bit pointer value? The way to check whether an object is a pointer is: * *

     * {@link Boolean} isPointer = ({@link Boolean}) {@link ForeignAccess}.sendIsPointer(
     *   {@link Message#IS_POINTER}.{@link Message#createNode()},  objectToCheck
     * );
     * 
* * Calling {@link Factory#accessMessage(com.oracle.truffle.api.interop.Message) the target} * created for this message should yield value of {@link Boolean}. If the object responds with * {@link Boolean#TRUE}, the object can be accessed by {@link #AS_POINTER} message. * * It is expected that objects should only return {@link Boolean#TRUE} here 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 {@link Boolean#FALSE} to {@link #IS_POINTER}, * and wait for the {@link #TO_NATIVE} message to trigger the transformation. * * @since 0.26 or earlier */ public static final Message IS_POINTER = IsPointer.INSTANCE; /** * Converts {@link TruffleObject truffle value} to a raw 64bit pointer value. Before sending the * {@link #AS_POINTER} message, it is desirable to send the {@link #IS_POINTER} one and verify * that the object can really be unwrapped to a raw pointer value. *

* If the object does not support the {@link #AS_POINTER} message, an * {@link UnsupportedMessageException} has to be thrown. *

* To unwrap a pointer value, use: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendAsPointer(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject) sendAsPointer}(
     *   {@link Message#AS_POINTER}.{@link Message#createNode()},  objectAsPointer
     * );
     * 
* * The returned value is a {@link Long} value. *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.26 or earlier */ public static final Message AS_POINTER = AsPointer.INSTANCE; /** * Transforms a {@link TruffleObject truffle value} a new {@link TruffleObject truffle native * value} that represents a raw native pointer. This resulting {@link TruffleObject truffle * native value} returns true for {@link #IS_POINTER} and can be unwrapped using the * {@link #AS_POINTER} message. *

* If an object returns true for {@link #IS_POINTER}, it is still expected that this object * supports the {@link #TO_NATIVE} message. It can just return a reference to itself in that * case. *

* If the object does not support the {@link #TO_NATIVE} message, an * {@link UnsupportedMessageException} has to be thrown. *

* To transform an object to a native value, use: * *

     * {@link ForeignAccess}.{@link ForeignAccess#sendToNative(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject) sendToNative}(
     *   {@link Message#TO_NATIVE}.{@link Message#createNode()},  objectToNative
     * );
     * 
* *

* To achieve good performance it is essential to cache/keep reference to the * {@link Message#createNode() created node}. * * @since 0.26 or earlier */ public static final Message TO_NATIVE = ToNative.INSTANCE; /** * Compares types of two messages. Messages are encouraged to implement this method. All * standard ones ({@link #IS_NULL}, {@link #READ}, etc.) do so. Messages obtained via the same * {@link #createExecute(int) method} are equal, messages obtained by different methods or * fields are not. * * @param message the object to compare to * @return true, if the structure of the message is that same as of this one. * @since 0.8 or earlier */ @Override public abstract boolean equals(Object message); /** * When re-implementing {@link #equals(java.lang.Object)}, it is generally recommended to also * implement hashCode(). * * @return hash code * @since 0.8 or earlier */ @Override public abstract int hashCode(); /** * Creates an AST node for this message. The node can be inserted into AST of your language and * will handle communication with the foreign language. * * @return node to be inserted into your AST and passed back to * {@link ForeignAccess#send(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object...)} * method. * @since 0.8 or earlier */ public final Node createNode() { CompilerAsserts.neverPartOfCompilation(); return InteropAccessNode.create(this); } /** * Converts the message into canonical string representation. The converted string can be * stored, persisted, transfered and later passed to {@link #valueOf(java.lang.String)} to * construct the message again. * * @param message the message to convert * @return canonical string representation * @since 0.9 */ public static String toString(Message message) { if (Message.READ == message) { return "READ"; // NOI18N } if (Message.WRITE == message) { return "WRITE"; // NOI18N } if (Message.REMOVE == message) { return "REMOVE"; // NOI18N } if (Message.UNBOX == message) { return "UNBOX"; // NOI18N } if (Message.GET_SIZE == message) { return "GET_SIZE"; // NOI18N } if (Message.HAS_SIZE == message) { return "HAS_SIZE"; // NOI18N } if (Message.IS_NULL == message) { return "IS_NULL"; // NOI18N } if (Message.IS_BOXED == message) { return "IS_BOXED"; // NOI18N } if (Message.IS_EXECUTABLE == message) { return "IS_EXECUTABLE"; // NOI18N } if (Message.IS_INSTANTIABLE == message) { return "IS_INSTANTIABLE"; // NOI18N } if (Message.HAS_KEYS == message) { return "HAS_KEYS"; // NOI18N } if (Message.KEYS == message) { return "KEYS"; // NOI18N } if (Message.KEY_INFO == message) { return "KEY_INFO"; // NOI18N } if (Message.IS_POINTER == message) { return "IS_POINTER"; // NOI18N } if (Message.AS_POINTER == message) { return "AS_POINTER"; // NOI18N } if (Message.TO_NATIVE == message) { return "TO_NATIVE"; // NOI18N } if (message instanceof Execute) { return ((Execute) message).name(); } return message.getClass().getName(); } /** * Converts string representation into real message. If the string was obtained by * {@link #toString(com.oracle.truffle.api.interop.Message)} method, it is guaranteed to be * successfully recognized (if the classpath of the system remains the same). * * @param messageId canonical string representation of a message * @return the message * @throws IllegalArgumentException if the string does not represent known message * @since 0.9 */ public static Message valueOf(String messageId) { switch (messageId) { case "READ": return Message.READ; case "WRITE": return Message.WRITE; case "REMOVE": return Message.REMOVE; case "UNBOX": return Message.UNBOX; case "GET_SIZE": return Message.GET_SIZE; case "HAS_SIZE": return Message.HAS_SIZE; case "IS_NULL": return Message.IS_NULL; case "IS_BOXED": return Message.IS_BOXED; case "IS_EXECUTABLE": return Message.IS_EXECUTABLE; case "IS_INSTANTIABLE": return Message.IS_INSTANTIABLE; case "HAS_KEYS": return Message.HAS_KEYS; case "KEYS": return Message.KEYS; case "KEY_INFO": return Message.KEY_INFO; case "IS_POINTER": return Message.IS_POINTER; case "AS_POINTER": return Message.AS_POINTER; case "TO_NATIVE": return Message.TO_NATIVE; case "EXECUTE": return Message.createExecute(0); case "NEW": return Message.createNew(0); case "INVOKE": return Message.createInvoke(0); } if (!TruffleOptions.AOT) { initializeMessageClass(messageId); } Message instance = CLASS_TO_MESSAGE.get(messageId); if (instance == null) { throw new IllegalArgumentException("Cannot find existing message instance for " + messageId); } return instance; } @CompilerDirectives.TruffleBoundary private static void initializeMessageClass(String message) throws IllegalArgumentException { try { ClassLoader l = Message.class.getClassLoader(); if (l == null) { l = ClassLoader.getSystemClassLoader(); } Class.forName(message, false, l).newInstance(); } catch (Exception ex) { throw new IllegalArgumentException("Cannot find message for " + message, ex); } } private static final Map CLASS_TO_MESSAGE = new ConcurrentHashMap<>(); @CompilerDirectives.TruffleBoundary private static void registerClass(Message message) { if (message instanceof KnownMessage) { return; } final String key = message.getClass().getName(); CLASS_TO_MESSAGE.putIfAbsent(key, message); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy