# HG changeset patch # User Jaroslav Tulach # Date 1442301729 -7200 # Node ID cc195dd451210a8e56b3121123d2995f43d053b3 # Parent b59d064835806ad1131917e5e8fbfb367cd34372 Describing difference between createExecute and createInvoke messages. diff -r b59d06483580 -r cc195dd45121 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java Mon Sep 14 18:32:54 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java Tue Sep 15 09:22:09 2015 +0200 @@ -87,6 +87,8 @@ * @return return value, if any * @throws ClassCastException if the createNode has not been created by * {@link Message#createNode()} method. + * @throws IllegalAccessError if the receiver does not support the + * {@link Message#createNode() message represented} by foreignNode */ public static Object execute(Node foreignNode, VirtualFrame frame, TruffleObject receiver, Object... arguments) { ForeignObjectAccessHeadNode fn = (ForeignObjectAccessHeadNode) foreignNode; diff -r b59d06483580 -r cc195dd45121 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java Mon Sep 14 18:32:54 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java Tue Sep 15 09:22:09 2015 +0200 @@ -24,6 +24,7 @@ */ package com.oracle.truffle.api.interop; +import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.interop.ForeignAccess.Factory; @@ -102,9 +103,46 @@ public static Message WRITE = Write.INSTANCE; /** - * Creates an execute message. All messages created by this method are - * {@link Object#equals(java.lang.Object) equal} to each other regardless of the value of - * argumentsLength. + * 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 answer is true one can: + * + *
+     * {@link ForeignAccess}.{@link ForeignAccess#execute(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.frame.VirtualFrame, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object...) execute}(
+     *   {@link Message#createExecute(int) Message.createExecute}(2).{@link Message#createNode()}, {@link VirtualFrame currentFrame}, 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 @@ -137,13 +175,68 @@ public static final Message IS_EXECUTABLE = IsExecutable.INSTANCE; /** - * Creates an execute 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 perform - * {@link #READ} first and on the result invoke {@link #createExecute(int)}. + * 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: + *

+ *

+ * 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();
+     * 
+ * + * To access obj's add method one should use: + * + *
+     * try {
+     *   {@link ForeignAccess}.{@link ForeignAccess#execute(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.frame.VirtualFrame, com.oracle.truffle.api.interop.TruffleObject, java.lang.Object...) execute}(
+     *     {@link Message#createInvoke(int) Message.createInvoke}(2).{@link Message#createNode()}, {@link VirtualFrame currentFrame}, 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 read & execute message + * @return message combining read & execute messages tailored for use with object oriented + * languages */ public static Message createInvoke(int argumentsLength) { return Execute.create(Execute.INVOKE, argumentsLength); @@ -157,7 +250,7 @@ * receiver} and then perform its constructor with appropriate number of arguments. * * @param argumentsLength number of parameters to pass to the target - * @return read & execute message + * @return new instance message */ public static Message createNew(int argumentsLength) { return Execute.create(Execute.NEW, argumentsLength);