# HG changeset patch # User Jaroslav Tulach # Date 1434636578 -7200 # Node ID a88981c5ce8b4ab15fe4a600f9474f11f3ed24cf # Parent 3e7fc5f743edc70d762b6f99af9e92822cdb684a Initial test for Java Interop: Perform callback to Math.min and Math.max via TruffleObject and Message.createExecute(2). diff -r 3e7fc5f743ed -r a88981c5ce8b mx/suite.py --- 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", }, diff -r 3e7fc5f743ed -r a88981c5ce8b truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java --- 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 diff -r 3e7fc5f743ed -r a88981c5ce8b truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInvokeNode.java --- 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); } } diff -r 3e7fc5f743ed -r a88981c5ce8b truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java --- 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); } /** diff -r 3e7fc5f743ed -r a88981c5ce8b truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java --- /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); + } + } + +} diff -r 3e7fc5f743ed -r a88981c5ce8b truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java --- 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!";