changeset 21960:a88981c5ce8b

Initial test for Java Interop: Perform callback to Math.min and Math.max via TruffleObject and Message.createExecute(2).
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Thu, 18 Jun 2015 16:09:38 +0200
parents 3e7fc5f743ed
children e61e89bd867a
files mx/suite.py truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInvokeNode.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java
diffstat 6 files changed, 280 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/mx/suite.py	Wed Jun 17 13:39:26 2015 -0700
+++ b/mx/suite.py	Thu Jun 18 16:09:38 2015 +0200
@@ -251,11 +251,13 @@
       "subDir" : "truffle",
       "sourceDirs" : ["src"],
       "dependencies" : [
-        "com.oracle.truffle.api",
+        "com.oracle.truffle.api.dsl",
+        "com.oracle.truffle.api.interop",
         "JUNIT"
       ],
       "checkstyle" : "com.oracle.truffle.api",
       "javaCompliance" : "1.7",
+      "annotationProcessors" : ["com.oracle.truffle.dsl.processor"],
       "workingSets" : "Truffle,Tools",
     },
 
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Wed Jun 17 13:39:26 2015 -0700
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Thu Jun 18 16:09:38 2015 +0200
@@ -49,6 +49,9 @@
             "function plus(a, b) {\n" +
             "  return a + b;\n" +
             "}\n" +
+            "function apply(f) {\n" +
+            "  return f(18, 32) + 10;\n" +
+            "}\n" +
             "function null() {\n" +
             "}\n"
         );
@@ -77,6 +80,11 @@
     }
 
     @Override
+    protected String applyNumbers() {
+        return "apply";
+    }
+
+    @Override
     protected String invalidCode() {
         // @formatter:off
         return
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInvokeNode.java	Wed Jun 17 13:39:26 2015 -0700
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInvokeNode.java	Thu Jun 18 16:09:38 2015 +0200
@@ -23,8 +23,13 @@
 package com.oracle.truffle.sl.nodes.call;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.NodeChild;
+import com.oracle.truffle.api.dsl.NodeChildren;
+import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.sl.nodes.*;
@@ -32,34 +37,26 @@
 
 /**
  * The node for function invocation in SL. Since SL has first class functions, the
- * {@link SLFunction target function} can be computed by an {@link #functionNode arbitrary
- * expression}. This node is responsible for evaluating this expression, as well as evaluating the
- * {@link #argumentNodes arguments}. The actual dispatch is then delegated to a chain of
- * {@link SLDispatchNode} that form a polymorphic inline cache.
+ * {@link SLFunction target function} can be computed by an arbitrary expression. This node is
+ * responsible for evaluating this expression, as well as evaluating the {@link #argumentNodes
+ * arguments}. The actual dispatch is then delegated to a chain of {@link SLDispatchNode} that form
+ * a polymorphic inline cache.
  */
 @NodeInfo(shortName = "invoke")
-public final class SLInvokeNode extends SLExpressionNode {
-
-    public static SLInvokeNode create(SourceSection src, SLExpressionNode function, SLExpressionNode[] arguments) {
-        return new SLInvokeNode(src, function, arguments);
-    }
-
-    @Child private SLExpressionNode functionNode;
+@NodeChildren({@NodeChild(value = "functionNode", type = SLExpressionNode.class)})
+public abstract class SLInvokeNode extends SLExpressionNode {
     @Children private final SLExpressionNode[] argumentNodes;
     @Child private SLDispatchNode dispatchNode;
 
-    private SLInvokeNode(SourceSection src, SLExpressionNode functionNode, SLExpressionNode[] argumentNodes) {
+    SLInvokeNode(SourceSection src, SLExpressionNode[] argumentNodes) {
         super(src);
-        this.functionNode = functionNode;
         this.argumentNodes = argumentNodes;
         this.dispatchNode = SLDispatchNodeGen.create();
     }
 
-    @Override
+    @Specialization
     @ExplodeLoop
-    public Object executeGeneric(VirtualFrame frame) {
-        SLFunction function = evaluateFunction(frame);
-
+    public Object executeGeneric(VirtualFrame frame, SLFunction function) {
         /*
          * The number of arguments is constant for one invoke node. During compilation, the loop is
          * unrolled and the execute methods of all arguments are inlined. This is triggered by the
@@ -72,24 +69,29 @@
         for (int i = 0; i < argumentNodes.length; i++) {
             argumentValues[i] = argumentNodes[i].executeGeneric(frame);
         }
-
         return dispatchNode.executeDispatch(frame, function, argumentValues);
     }
 
-    private SLFunction evaluateFunction(VirtualFrame frame) {
-        try {
-            /*
-             * The function node must evaluate to a SLFunction value, so we call
-             * function-specialized method.
-             */
-            return functionNode.executeFunction(frame);
-        } catch (UnexpectedResultException ex) {
-            /*
-             * The function node evaluated to a non-function result. This is a type error in the SL
-             * program. We report it with the same exception that Truffle DSL generated nodes use to
-             * report type errors.
-             */
-            throw new UnsupportedSpecializationException(this, new Node[]{functionNode}, ex.getResult());
+    @Child private Node crossLanguageCall;
+
+    @Specialization
+    @ExplodeLoop
+    protected Object executeGeneric(VirtualFrame frame, TruffleObject function) {
+        /*
+         * The number of arguments is constant for one invoke node. During compilation, the loop is
+         * unrolled and the execute methods of all arguments are inlined. This is triggered by the
+         * ExplodeLoop annotation on the method. The compiler assertion below illustrates that the
+         * array length is really constant.
+         */
+        CompilerAsserts.compilationConstant(argumentNodes.length);
+
+        Object[] argumentValues = new Object[argumentNodes.length];
+        for (int i = 0; i < argumentNodes.length; i++) {
+            argumentValues[i] = argumentNodes[i].executeGeneric(frame);
         }
+        if (crossLanguageCall == null) {
+            crossLanguageCall = insert(Message.createExecute(argumentValues.length).createNode());
+        }
+        return ForeignAccess.execute(crossLanguageCall, frame, function, argumentValues);
     }
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Wed Jun 17 13:39:26 2015 -0700
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Thu Jun 18 16:09:38 2015 +0200
@@ -275,7 +275,7 @@
         final int startPos = functionNode.getSourceSection().getCharIndex();
         final int endPos = finalToken.charPos + finalToken.val.length();
         final SourceSection src = source.createSection(functionNode.getSourceSection().getIdentifier(), startPos, endPos - startPos);
-        return SLInvokeNode.create(src, functionNode, parameterNodes.toArray(new SLExpressionNode[parameterNodes.size()]));
+        return SLInvokeNodeGen.create(src, parameterNodes.toArray(new SLExpressionNode[parameterNodes.size()]), functionNode);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java	Thu Jun 18 16:09:38 2015 +0200
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015, 2015, 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.tck;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.dsl.NodeChild;
+import com.oracle.truffle.api.dsl.NodeChildren;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+
+final class MaxMinObject implements TruffleObject {
+    private final boolean max;
+
+    public MaxMinObject(boolean max) {
+        this.max = max;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return ForeignAccess.create(MaxMinObject.class, new AF(max));
+    }
+
+    static final class AF implements ForeignAccess.Factory10 {
+        private final boolean max;
+
+        public AF(boolean max) {
+            this.max = max;
+        }
+
+        @Override
+        public CallTarget accessIsNull() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessIsExecutable() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessIsBoxed() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessHasSize() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessGetSize() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessUnbox() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessRead() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessWrite() {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessExecute(int argumentsLength) {
+            if (argumentsLength == 2) {
+                MaxMinNode maxNode = MaxMinObjectFactory.MaxMinNodeGen.create(max, MaxMinObjectFactory.UnboxNodeGen.create(new ReadArgNode(0)),
+                                MaxMinObjectFactory.UnboxNodeGen.create(new ReadArgNode(1)));
+                return Truffle.getRuntime().createCallTarget(maxNode);
+            }
+            return null;
+        }
+
+        @Override
+        public CallTarget accessInvoke(int argumentsLength) {
+            return null;
+        }
+
+        @Override
+        public CallTarget accessMessage(Message unknown) {
+            return null;
+        }
+    }
+
+    static class ReadArgNode extends Node {
+        private final int argIndex;
+
+        public ReadArgNode(int argIndex) {
+            this.argIndex = argIndex;
+        }
+
+        public Object execute(VirtualFrame frame) {
+            return ForeignAccess.getArguments(frame).get(argIndex);
+        }
+    }
+
+    @NodeChildren({@NodeChild(value = "valueNode", type = ReadArgNode.class)})
+    abstract static class UnboxNode extends Node {
+        @Child private Node unbox;
+        @Child private Node isBoxed;
+
+        public abstract Object executeUnbox(VirtualFrame frame);
+
+        @Specialization
+        public int executeUnbox(int value) {
+            return value;
+        }
+
+        @Specialization
+        public long executeUnbox(long value) {
+            return value;
+        }
+
+        @Specialization
+        public String executeUnbox(String value) {
+            return value;
+        }
+
+        @Specialization(guards = "isBoxedPrimitive(frame, foreignValue)")
+        public Object executeUnbox(VirtualFrame frame, TruffleObject foreignValue) {
+            if (unbox == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                unbox = insert(Message.UNBOX.createNode());
+            }
+            return ForeignAccess.execute(unbox, frame, foreignValue);
+        }
+
+        protected final boolean isBoxedPrimitive(VirtualFrame frame, TruffleObject object) {
+            if (isBoxed == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                isBoxed = insert(Message.IS_BOXED.createNode());
+            }
+            return (boolean) ForeignAccess.execute(isBoxed, frame, object);
+        }
+
+    }
+
+    @NodeChildren({@NodeChild(value = "firstNode", type = UnboxNode.class), @NodeChild(value = "secondNode", type = UnboxNode.class)})
+    abstract static class MaxMinNode extends RootNode {
+        private final boolean max;
+
+        MaxMinNode(boolean max) {
+            this.max = max;
+        }
+
+        @Specialization
+        public int execute(int first, int second) {
+            return max ? Math.max(first, second) : Math.min(first, second);
+        }
+
+        @Specialization
+        public long execute(long first, long second) {
+            return max ? Math.max(first, second) : Math.min(first, second);
+        }
+
+        @Specialization
+        public double execute(double first, double second) {
+            return max ? Math.max(first, second) : Math.min(first, second);
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Wed Jun 17 13:39:26 2015 -0700
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Thu Jun 18 16:09:38 2015 +0200
@@ -92,6 +92,16 @@
     protected abstract String plusInt();
 
     /**
+     * Name of a function in your language to perform a callback to foreign function. Your function
+     * should prepare two numbers (18 and 32) and apply them to the function passed in as an
+     * argument of your function. It should then add 10 to the returned value and return the result
+     * back to its caller.
+     *
+     * @return name of globally exported symbol
+     */
+    protected abstract String applyNumbers();
+
+    /**
      * Return a code snippet that is invalid in your language. Its
      * {@link TruffleVM#eval(java.lang.String, java.lang.String) evaluation} should fail and yield
      * an exception.
@@ -161,6 +171,32 @@
         fail("Should yield IOException, but returned " + ret);
     }
 
+    @Test
+    public void testMaxOrMinValue() throws Exception {
+        TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers());
+
+        Object res = apply.invoke(null, new MaxMinObject(true));
+
+        assert res instanceof Number : "result should be a number: " + res;
+
+        Number n = (Number) res;
+
+        assert 42 == n.intValue() : "32 > 18 and plus 10";
+    }
+
+    @Test
+    public void testMaxOrMinValue2() throws Exception {
+        TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers());
+
+        Object res = apply.invoke(null, new MaxMinObject(false));
+
+        assert res instanceof Number : "result should be a number: " + res;
+
+        Number n = (Number) res;
+
+        assert 28 == n.intValue() : "18 < 32 and plus 10";
+    }
+
     private TruffleVM.Symbol findGlobalSymbol(String name) throws Exception {
         TruffleVM.Symbol s = vm().findGlobalSymbol(name);
         assert s != null : "Symbol " + name + " is not found!";