# HG changeset patch # User Jaroslav Tulach # Date 1447846432 -3600 # Node ID b4e89154a774fe295cbd117aedb967baa2effb60 # Parent 299c279c87b3556ff5b56bd7e7f270b1ab8da93c# Parent fdf6ad720cdc0d62355a9d9db2e76707e260cb70 Supporting efficient cross-language eval diff -r 299c279c87b3 -r b4e89154a774 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Wed Nov 18 09:02:41 2015 +0100 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Wed Nov 18 12:33:52 2015 +0100 @@ -385,6 +385,25 @@ } /** + * Evaluates source of (potentially different) language. The {@link Source#getMimeType() + * MIME type) is used to identify the {@link TruffleLanguage} to use to perform the + * {@link #parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...)}. + * The names of arguments are parameters for the resulting + * {#link CallTarget} that allow the source to reference + * the actual parameters passed to {@link CallTarget#call(java.lang.Object...)}. + * + * @param source the source to evaluate + * @param argumentNames the names of {@link CallTarget#call(java.lang.Object...)} + * arguments that can be referenced from the source + * @return the call target representing the parsed result + * @throws IOException if the parsing or evaluation fails for some reason + */ + public CallTarget parse(Source source, String... argumentNames) throws IOException { + TruffleLanguage language = API.findLanguageImpl(vm, null, source.getMimeType()); + return language.parse(source, null, argumentNames); + } + + /** * Input associated with {@link com.oracle.truffle.api.vm.PolyglotEngine} this language is * being executed in. * @@ -484,6 +503,11 @@ } @Override + protected TruffleLanguage findLanguageImpl(Object known, Class languageClass, String mimeType) { + return super.findLanguageImpl(known, languageClass, mimeType); + } + + @Override protected Object languageGlobal(TruffleLanguage.Env env) { return env.langCtx.getLanguageGlobal(); } diff -r 299c279c87b3 -r b4e89154a774 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 Nov 18 09:02:41 2015 +0100 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java Wed Nov 18 12:33:52 2015 +0100 @@ -44,10 +44,8 @@ import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.sl.test.instrument.InstrumentationTestMode; import com.oracle.truffle.tck.TruffleTCK; - +import org.junit.After; import static org.junit.Assert.assertTrue; - -import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -161,6 +159,13 @@ } @Override + protected String multiplyCode(String firstName, String secondName) { + return "function multiply(" + firstName + ", " + secondName + ") {\n" + + " return " + firstName + " * " + secondName + ";\n" + + "}\n"; + } + + @Override protected String countInvocations() { return "count"; } @@ -170,6 +175,11 @@ return null; } + @Override + protected String evaluateSource() { + return "interopEval"; + } + // // Ignore tests working on floats and double // diff -r 299c279c87b3 -r b4e89154a774 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Wed Nov 18 09:02:41 2015 +0100 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Wed Nov 18 12:33:52 2015 +0100 @@ -57,12 +57,16 @@ import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.nodes.GraphPrintVisitor; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeUtil; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.vm.PolyglotEngine; @@ -210,7 +214,7 @@ protected SLContext createContext(Env env) { final BufferedReader in = new BufferedReader(new InputStreamReader(env.in())); final PrintWriter out = new PrintWriter(env.out(), true); - SLContext context = new SLContext(this, in, out); + SLContext context = new SLContext(this, env, in, out); for (NodeFactory builtin : builtins) { context.installBuiltin(builtin, true); } @@ -410,7 +414,7 @@ } @Override - protected CallTarget parse(Source code, Node node, String... argumentNames) throws IOException { + protected CallTarget parse(Source code, final Node node, String... argumentNames) throws IOException { final SLContext c = new SLContext(this); final Exception[] failed = {null}; try { @@ -419,10 +423,10 @@ } catch (Exception e) { failed[0] = e; } - return new CallTarget() { + RootNode rootNode = new RootNode(SLLanguage.class, null, null) { @TruffleBoundary @Override - public Object call(Object... arguments) { + public Object execute(VirtualFrame frame) { if (failed[0] instanceof RuntimeException) { throw (RuntimeException) failed[0]; } @@ -432,17 +436,26 @@ Node n = createFindContextNode(); SLContext fillIn = findContext(n); final SLFunctionRegistry functionRegistry = fillIn.getFunctionRegistry(); + int oneAndCnt = 0; + SLFunction oneAndOnly = null; for (SLFunction f : c.getFunctionRegistry().getFunctions()) { RootCallTarget callTarget = f.getCallTarget(); if (callTarget == null) { continue; } - functionRegistry.lookup(f.getName()); + oneAndOnly = functionRegistry.lookup(f.getName()); + oneAndCnt++; functionRegistry.register(f.getName(), (SLRootNode) f.getCallTarget().getRootNode()); } + Object[] arguments = frame.getArguments(); + if (oneAndCnt == 1 && (arguments.length > 0 || node != null)) { + Node callNode = Message.createExecute(arguments.length).createNode(); + return ForeignAccess.execute(callNode, frame, oneAndOnly, arguments); + } return null; } }; + return Truffle.getRuntime().createCallTarget(rootNode); } @Override diff -r 299c279c87b3 -r b4e89154a774 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLEvalBuiltin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLEvalBuiltin.java Wed Nov 18 12:33:52 2015 +0100 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, 2014, 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.sl.builtins; + +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; +import java.io.IOException; + +/** + * Builtin function to parse a text in other language. + */ +@NodeInfo(shortName = "interopEval") +public abstract class SLEvalBuiltin extends SLBuiltinNode { + + public SLEvalBuiltin() { + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "interopEval")); + } + + @Specialization + public Object interopEval(String mimeType, String code) { + Source source = Source.fromText(code, "").withMimeType(mimeType); + try { + return getContext().evalAny(source); + } catch (IOException ex) { + throw new IllegalArgumentException(ex); + } + } +} diff -r 299c279c87b3 -r b4e89154a774 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Wed Nov 18 09:02:41 2015 +0100 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Wed Nov 18 12:33:52 2015 +0100 @@ -41,6 +41,7 @@ package com.oracle.truffle.sl.runtime; import com.oracle.truffle.api.ExecutionContext; +import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.nodes.NodeInfo; @@ -54,6 +55,7 @@ import com.oracle.truffle.sl.builtins.SLAssertTrueBuiltinFactory; import com.oracle.truffle.sl.builtins.SLBuiltinNode; import com.oracle.truffle.sl.builtins.SLDefineFunctionBuiltinFactory; +import com.oracle.truffle.sl.builtins.SLEvalBuiltinFactory; import com.oracle.truffle.sl.builtins.SLHelloEqualsWorldBuiltinFactory; import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltinFactory; import com.oracle.truffle.sl.builtins.SLNewObjectBuiltinFactory; @@ -69,6 +71,7 @@ import com.oracle.truffle.sl.parser.SLNodeFactory; import java.io.BufferedReader; +import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; @@ -90,19 +93,21 @@ private final PrintWriter output; private final SLFunctionRegistry functionRegistry; private final Shape emptyShape; + private final TruffleLanguage.Env env; - public SLContext(SLLanguage language, BufferedReader input, PrintWriter output) { - this(language, input, output, true); + public SLContext(SLLanguage language, TruffleLanguage.Env env, BufferedReader input, PrintWriter output) { + this(language, env, input, output, true); } public SLContext(SLLanguage language) { - this(language, null, null, false); + this(language, null, null, null, false); } - private SLContext(SLLanguage language, BufferedReader input, PrintWriter output, boolean installBuiltins) { + private SLContext(SLLanguage language, TruffleLanguage.Env env, BufferedReader input, PrintWriter output, boolean installBuiltins) { this.language = language; this.input = input; this.output = output; + this.env = env; this.functionRegistry = new SLFunctionRegistry(); installBuiltins(installBuiltins); @@ -150,6 +155,7 @@ installBuiltin(SLAssertTrueBuiltinFactory.getInstance(), registerRootNodes); installBuiltin(SLAssertFalseBuiltinFactory.getInstance(), registerRootNodes); installBuiltin(SLNewObjectBuiltinFactory.getInstance(), registerRootNodes); + installBuiltin(SLEvalBuiltinFactory.getInstance(), registerRootNodes); } public void installBuiltin(NodeFactory factory, boolean registerRootNodes) { @@ -227,4 +233,8 @@ } return a; } + + public Object evalAny(Source source) throws IOException { + return env.parse(source).call(); + } } diff -r 299c279c87b3 -r b4e89154a774 truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TckLanguage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TckLanguage.java Wed Nov 18 12:33:52 2015 +0100 @@ -0,0 +1,147 @@ +/* + * 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.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +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; +import com.oracle.truffle.api.source.Source; +import java.io.IOException; + +@TruffleLanguage.Registration(mimeType = "application/x-tck", name = "TCK", version = "1.0") +public final class TckLanguage extends TruffleLanguage { + public static final TckLanguage INSTANCE = new TckLanguage(); + + @Override + protected Env createContext(Env env) { + return env; + } + + @Override + protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException { + final RootNode root; + final String txt = code.getCode(); + if (txt.startsWith("TCK42:")) { + int nextColon = txt.indexOf(":", 6); + String mimeType = txt.substring(6, nextColon); + Source toParse = Source.fromText(txt.substring(nextColon + 1), "").withMimeType(mimeType); + root = new MultiplyNode(toParse); + } else { + final double value = Double.parseDouble(txt); + root = RootNode.createConstantNode(value); + } + return Truffle.getRuntime().createCallTarget(root); + } + + @Override + protected Object findExportedSymbol(Env context, String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal(Env context) { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + throw new IOException(); + } + + private static final class MultiplyNode extends RootNode implements TruffleObject, ForeignAccess.Factory { + private final Source code; + + public MultiplyNode(Source toParse) { + super(TckLanguage.class, null, null); + this.code = toParse; + } + + @Override + public Object execute(VirtualFrame frame) { + Env env = TckLanguage.INSTANCE.findContext(TckLanguage.INSTANCE.createFindContextNode()); + if (frame.getArguments().length == 0) { + return this; + } + try { + CallTarget call = env.parse(code, (String)frame.getArguments()[1], (String)frame.getArguments()[2]); + return call.call(6, 7); + } catch (IOException ex) { + throw new AssertionError("Cannot parse " + code, ex); + } + } + + @Override + public ForeignAccess getForeignAccess() { + return ForeignAccess.create(this); + } + + @Override + public boolean canHandle(TruffleObject obj) { + return obj instanceof MultiplyNode; + } + + @Override + public CallTarget accessMessage(Message tree) { + if (tree == Message.IS_EXECUTABLE) { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.TRUE)); + } else if (Message.createExecute(2).equals(tree)) { + return Truffle.getRuntime().createCallTarget(this); + } else { + throw new IllegalArgumentException("" + tree); + } + } + + } +} diff -r 299c279c87b3 -r b4e89154a774 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 Nov 18 09:02:41 2015 +0100 +++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java Wed Nov 18 12:33:52 2015 +0100 @@ -38,8 +38,16 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * A collection of tests that can certify language implementation to be compliant with most recent @@ -157,6 +165,31 @@ } /** + * Name of a function to parse source written in some other language. When the function is + * executed, it expects two arguments. First one is MIME type identifying + * {@link TruffleLanguage} and the second one is the source code to parse in that language and + * execute it. The result of the execution is then returned back to the caller. + * + * @return name of globally exported symbol to invoke when one wants to execute some code + */ + protected String evaluateSource() { + throw new UnsupportedOperationException("evaluateSource() method not implemented"); + } + + /** + * Code snippet to multiplyCode two two variables. The test uses the snippet + * as a parameter to your language's {@link TruffleLanguage#parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...)} + * method. + * + * @param firstName name of the first variable to multiplyCode + * @param secondName name of the second variable to multiplyCode + * @return code snippet that multiplies the two variables in your language + */ + protected String multiplyCode(String firstName, String secondName) { + throw new UnsupportedOperationException("multiply(String,String) method not implemeted!"); + } + + /** * Name of a function that counts number of its invocations in current {@link PolyglotEngine} * context. Your function should somehow keep a counter to remember number of its invocations * and always increment it. The first invocation should return 1, the second @@ -594,6 +627,35 @@ assertEquals("Global from the language same with Java obtained one", language.getGlobalObject().get(), global); } + @Test + public void testEvaluateSource() throws Exception { + Language language = vm().getLanguages().get(mimeType()); + assertNotNull("Langugage for " + mimeType() + " found", language); + + PolyglotEngine.Value function = vm().findGlobalSymbol(evaluateSource()); + assertNotNull(evaluateSource() + " found", function); + + double expect = Math.floor(RANDOM.nextDouble() * 100000.0) / 10.0; + Object parsed = function.invoke(null, "application/x-tck", "" + expect).get(); + assertTrue("Expecting numeric result, was:" + expect, parsed instanceof Number); + double value = ((Number)parsed).doubleValue(); + assertEquals("Gets the double", expect, value, 0.01); + } + + @Test + public void multiplyTwoVariables() throws Exception { + final String firstVar = "var" + (char)('A' + RANDOM.nextInt(24)); + final String secondVar = "var" + (char)('0' + RANDOM.nextInt(10)); + String mulCode = multiplyCode(firstVar, secondVar); + Source source = Source.fromText("TCK42:" + mimeType() + ":" + mulCode, "evaluate " + firstVar + " * " + secondVar) + .withMimeType("application/x-tck"); + final PolyglotEngine.Value evalSource = vm().eval(source); + final PolyglotEngine.Value invokeMul = evalSource.invoke(null, firstVar, secondVar); + Object result = invokeMul.get(); + assertTrue("Expecting numeric result, was:" + result, result instanceof Number); + assertEquals("Right value", 42, ((Number)result).intValue()); + } + private PolyglotEngine.Value findGlobalSymbol(String name) throws Exception { PolyglotEngine.Value s = vm().findGlobalSymbol(name); assert s != null : "Symbol " + name + " is not found!";