changeset 21490:3286fb5fea4a

Introducing standard I/O and error into Env and using TruffleVM to execute SL test cases. Adding SLTckTest to verify SL language interop.
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Tue, 26 May 2015 19:11:36 +0200
parents b3f1d8b23037
children 28a137691ff2 fac827422011
files graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleTCK.java graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleVMSingleThreadedTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package.html graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunction.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java mx/suite.py
diffstat 14 files changed, 449 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleTCK.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleTCK.java	Tue May 26 19:11:36 2015 +0200
@@ -24,16 +24,17 @@
 
 import com.oracle.truffle.api.vm.TruffleVM;
 import java.util.Random;
+import org.junit.Test;
 
 /**
  * A collection of tests that can certify language implementaiton to be complient with most recent
  * requirements of the Truffle infrastructure and tooling. Subclass, implement abstract methods and
  * include in your test suite.
  */
-public abstract class TruffleTCK {
-    private TruffleVM vm;
+public class TruffleTCK { // abstract
+    private TruffleVM tckVM;
 
-    protected TruffleTCK() {
+    public TruffleTCK() { // protected
     }
 
     /**
@@ -45,8 +46,11 @@
      * for internal testing.
      *
      * @return initialized Truffle virtual machine
+     * @throws java.lang.Exception thrown when the VM preparation fails
      */
-    protected abstract TruffleVM prepareVM() throws Exception;
+    protected TruffleVM prepareVM() throws Exception { // abstract
+        return null;
+    }
 
     /**
      * Name of function which will return value 42 as a number. The return value of the method
@@ -55,7 +59,9 @@
      *
      * @return name of globally exported symbol
      */
-    protected abstract String fourtyTwo();
+    protected String fourtyTwo() { // abstract
+        return null;
+    }
 
     /**
      * Name of function to add two integer values together. The symbol will be invoked with two
@@ -64,20 +70,26 @@
      *
      * @return name of globally exported symbol
      */
-    protected abstract String plusInt();
+    protected String plusInt() {  // abstract
+        return null;
+    }
 
     private TruffleVM vm() throws Exception {
-        if (vm == null) {
-            vm = prepareVM();
+        if (tckVM == null) {
+            tckVM = prepareVM();
         }
-        return vm;
+        return tckVM;
     }
 
     //
     // The tests
     //
 
+    @Test
     public void testFortyTwo() throws Exception {
+        if (getClass() == TruffleTCK.class) {
+            return;
+        }
         TruffleVM.Symbol fourtyTwo = findGlobalSymbol(fourtyTwo());
 
         Object res = fourtyTwo.invoke(null);
@@ -89,7 +101,11 @@
         assert 42 == n.intValue() : "The value is 42 =  " + n.intValue();
     }
 
+    @Test
     public void testPlusWithInts() throws Exception {
+        if (getClass() == TruffleTCK.class) {
+            return;
+        }
         Random r = new Random();
         int a = r.nextInt(100);
         int b = r.nextInt(100);
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleVMSingleThreadedTest.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleVMSingleThreadedTest.java	Tue May 26 19:11:36 2015 +0200
@@ -35,10 +35,11 @@
 
     @Before
     public void initInDifferentThread() throws InterruptedException {
+        final TruffleVM.Builder b = TruffleVM.newVM();
         Thread t = new Thread("Initializer") {
             @Override
             public void run() {
-                tvm = TruffleVM.create();
+                tvm = b.build();
             }
         };
         t.start();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Tue May 26 19:11:36 2015 +0200
@@ -29,6 +29,8 @@
 import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.api.vm.TruffleVM.Language;
 import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -135,13 +137,19 @@
     public static final class Env {
         private final TruffleVM vm;
         private final TruffleLanguage lang;
+        private final Reader in;
+        private final Writer err;
+        private final Writer out;
 
-        Env(TruffleVM vm, Constructor<?> langConstructor) {
+        Env(TruffleVM vm, Constructor<?> langConstructor, Writer out, Writer err, Reader in) {
             this.vm = vm;
+            this.in = in;
+            this.err = err;
+            this.out = out;
             try {
                 this.lang = (TruffleLanguage) langConstructor.newInstance(this);
             } catch (Exception ex) {
-                throw new IllegalStateException("Cannot construct language " + langConstructor.getClass().getName(), ex);
+                throw new IllegalStateException("Cannot construct language " + langConstructor.getDeclaringClass().getName(), ex);
             }
         }
 
@@ -157,15 +165,41 @@
         public Object importSymbol(String globalName) {
             return API.importSymbol(vm, lang, globalName);
         }
+
+        /**
+         * Input associated with this {@link TruffleVM}.
+         *
+         * @return reader, never <code>null</code>
+         */
+        public Reader stdIn() {
+            return in;
+        }
+
+        /**
+         * Standard output writer for this {@link TruffleVM}.
+         *
+         * @return writer, never <code>null</code>
+         */
+        public Writer stdOut() {
+            return out;
+        }
+
+        /**
+         * Standard error writer for this {@link TruffleVM}.
+         *
+         * @return writer, never <code>null</code>
+         */
+        public Writer stdErr() {
+            return err;
+        }
     }
 
     private static final AccessAPI API = new AccessAPI();
 
     private static final class AccessAPI extends Accessor {
-
         @Override
-        protected TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz) {
-            Env env = new Env(vm, langClazz);
+        protected TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz, Writer stdOut, Writer stdErr, Reader stdIn) {
+            Env env = new Env(vm, langClazz, stdOut, stdErr, stdIn);
             return env.lang;
         }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Tue May 26 19:11:36 2015 +0200
@@ -29,6 +29,8 @@
 import com.oracle.truffle.api.source.Source;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
+import java.io.Reader;
+import java.io.Writer;
 import java.util.ServiceLoader;
 
 /**
@@ -76,8 +78,8 @@
         }
     }
 
-    protected TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz) {
-        return API.attachEnv(vm, langClazz);
+    protected TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz, Writer stdOut, Writer stdErr, Reader stdIn) {
+        return API.attachEnv(vm, langClazz, stdOut, stdErr, stdIn);
     }
 
     protected Object eval(TruffleLanguage l, Source s) throws IOException {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java	Tue May 26 19:11:36 2015 +0200
@@ -32,8 +32,11 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.lang.reflect.Constructor;
+import java.io.Writer;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
@@ -53,10 +56,10 @@
 import java.util.logging.Logger;
 
 /**
- * Virtual machine for Truffle based languages. Use {@link #create()} to instantiate new isolated
- * virtual machine ready for execution of various languages. All the languages in a single virtual
- * machine see each other exported global symbols and can co-operate. Use {@link #create()} multiple
- * times to create different, isolated virtual machines completely separated from each other.
+ * Virtual machine for Truffle based languages. Use {@link #newVM()} to create new isolated virtual
+ * machine ready for execution of various languages. All the languages in a single virtual machine
+ * see each other exported global symbols and can co-operate. Use {@link #newVM()} multiple times to
+ * create different, isolated virtual machines completely separated from each other.
  * <p>
  * Once instantiated use {@link #eval(java.net.URI)} with a reference to a file or URL or directly
  * pass code snippet into the virtual machine via {@link #eval(java.lang.String, java.lang.String)}.
@@ -65,17 +68,41 @@
  * initialized, it remains so, until the virtual machine isn't garbage collected.
  * <p>
  * The <code>TruffleVM</code> is single-threaded and tries to enforce that. It records the thread it
- * has been {@link #create() created} by and checks that all subsequent calls are coming from the
- * same thread.
+ * has been {@link Builder#build() created} by and checks that all subsequent calls are coming from
+ * the same thread.
  */
 public final class TruffleVM {
     private static final Logger LOG = Logger.getLogger(TruffleVM.class.getName());
     private static final SPIAccessor SPI = new SPIAccessor();
     private final Thread initThread;
     private final Map<String, Language> langs;
+    private final Reader in;
+    private final Writer err;
+    private final Writer out;
 
+    /**
+     * Private & temporary only constructor.
+     */
     private TruffleVM() {
-        initThread = Thread.currentThread();
+        this.initThread = null;
+        this.in = null;
+        this.err = null;
+        this.out = null;
+        this.langs = null;
+    }
+
+    /**
+     * Real constructor used from the builder.
+     *
+     * @param out stdout
+     * @param err stderr
+     * @param in stdin
+     */
+    private TruffleVM(Writer out, Writer err, Reader in) {
+        this.out = out;
+        this.err = err;
+        this.in = in;
+        this.initThread = Thread.currentThread();
         this.langs = new HashMap<>();
         Enumeration<URL> en;
         try {
@@ -111,14 +138,105 @@
     }
 
     /**
-     * Creates new Truffle virtual machine. It searches for {@link Registration languages
-     * registered} in the system class loader and makes them available for later evaluation via
+     * Creation of new Truffle virtual machine. Use the {@link Builder} methods to configure your
+     * virtual machine and then create one using {@link Builder#build()}:
+     *
+     * <pre>
+     * {@link TruffleVM} vm = {@link TruffleVM}.{@link TruffleVM#newVM() newVM()}
+     *     .{@link Builder#stdOut(java.io.Writer) stdOut}({@link Writer yourWriter})
+     *     .{@link Builder#stdErr(java.io.Writer) stdErr}({@link Writer yourWriter})
+     *     .{@link Builder#stdIn(java.io.Reader) stdIn}({@link Reader yourReader})
+     *     .{@link Builder#build() build()};
+     * </pre>
+     *
+     * It searches for {@link Registration languages registered} in the system class loader and
+     * makes them available for later evaluation via
      * {@link #eval(java.lang.String, java.lang.String)} methods.
      *
      * @return new, isolated virtual machine with pre-registered languages
      */
-    public static TruffleVM create() {
-        return new TruffleVM();
+    public static TruffleVM.Builder newVM() {
+        // making Builder non-static inner class is a
+        // nasty trick to avoid the Builder class to appear
+        // in Javadoc next to TruffleVM class
+        TruffleVM vm = new TruffleVM();
+        return vm.new Builder();
+    }
+
+    /**
+     * Builder for a new {@link TruffleVM}. Call various configuration methods in a chain and at the
+     * end create new {@link TruffleVM virtual machine}:
+     *
+     * <pre>
+     * {@link TruffleVM} vm = {@link TruffleVM}.{@link TruffleVM#newVM() newVM()}
+     *     .{@link Builder#stdOut(java.io.Writer) stdOut}({@link Writer yourWriter})
+     *     .{@link Builder#stdErr(java.io.Writer) stdErr}({@link Writer yourWriter})
+     *     .{@link Builder#stdIn(java.io.Reader) stdIn}({@link Reader yourReader})
+     *     .{@link Builder#build() build()};
+     * </pre>
+     */
+    public final class Builder {
+        private Writer out;
+        private Writer err;
+        private Reader in;
+
+        Builder() {
+        }
+
+        /**
+         * Changes the defaut output for languages running in <em>to be created</em>
+         * {@link TruffleVM virtual machine}. The default is to use {@link System#out}.
+         *
+         * @param w the writer to use as output
+         * @return instance of this builder
+         */
+        public Builder stdOut(Writer w) {
+            out = w;
+            return this;
+        }
+
+        /**
+         * Changes the error output for languages running in <em>to be created</em>
+         * {@link TruffleVM virtual machine}. The default is to use {@link System#err}.
+         *
+         * @param w the writer to use as output
+         * @return instance of this builder
+         */
+        public Builder stdErr(Writer w) {
+            err = w;
+            return this;
+        }
+
+        /**
+         * Changes the defaut input for languages running in <em>to be created</em>
+         * {@link TruffleVM virtual machine}. The default is to use {@link System#out}.
+         *
+         * @param r the reader to use as input
+         * @return instance of this builder
+         */
+        public Builder stdIn(Reader r) {
+            in = r;
+            return this;
+        }
+
+        /**
+         * Creates the {@link TruffleVM Truffle virtual machine}. The configuration is taken from
+         * values passed into configuration methods in this class.
+         *
+         * @return new, isolated virtual machine with pre-registered languages
+         */
+        public TruffleVM build() {
+            if (out == null) {
+                out = new OutputStreamWriter(System.out);
+            }
+            if (err == null) {
+                err = new OutputStreamWriter(System.err);
+            }
+            if (in == null) {
+                in = new InputStreamReader(System.in);
+            }
+            return new TruffleVM(out, err, in);
+        }
     }
 
     /**
@@ -336,7 +454,7 @@
                 try {
                     Class<?> langClazz = Class.forName(n, true, loader());
                     Constructor<?> constructor = langClazz.getConstructor(Env.class);
-                    impl = SPI.attachEnv(TruffleVM.this, constructor);
+                    impl = SPI.attachEnv(TruffleVM.this, constructor, out, err, in);
                 } catch (Exception ex) {
                     throw new IllegalStateException("Cannot initialize " + getName() + " language with implementation " + n, ex);
                 }
@@ -368,8 +486,8 @@
         }
 
         @Override
-        public TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz) {
-            return super.attachEnv(vm, langClazz);
+        public TruffleLanguage attachEnv(TruffleVM vm, Constructor<?> langClazz, Writer stdOut, Writer stdErr, Reader stdIn) {
+            return super.attachEnv(vm, langClazz, stdOut, stdErr, stdIn);
         }
 
         @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package.html	Tue May 26 19:11:36 2015 +0200
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Truffle Virtual Machine</title>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    </head>
+    <body>
+        <div>Central place to control <a href="TruffleVM.html">Truffle Virtual Machine</a> and
+        all languages hosted in it.</div>
+    </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Tue May 26 19:11:36 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, 2013, 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.
+ *
+ * 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.sl.test;
+
+import com.oracle.truffle.api.test.vm.TruffleTCK;
+import com.oracle.truffle.api.vm.TruffleVM;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ * This is the way to verify your language implementation is compatible.
+ *
+ */
+public class SLTckTest extends TruffleTCK {
+    @Test
+    public void testVerifyPresence() {
+        TruffleVM vm = TruffleVM.newVM().build();
+        assertTrue("Our language is present", vm.getLanguages().containsKey("application/x-sl"));
+    }
+
+    @Override
+    protected TruffleVM prepareVM() throws Exception {
+        TruffleVM vm = TruffleVM.newVM().build();
+        vm.eval("application/x-sl", // your langage
+                        "function fourtyTwo() {\n" + // your script
+                                        "  return 42;\n" + //
+                                        "}\n" + //
+                                        "function plus(a, b) {\n" + //
+                                        "  return a + b;\n" + //
+                                        "}\n" //
+        );
+        return vm;
+    }
+
+    @Override
+    protected String fourtyTwo() {
+        return "fourtyTwo";
+    }
+
+    @Override
+    protected String plusInt() {
+        return "plus";
+    }
+}
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Tue May 26 19:11:36 2015 +0200
@@ -37,11 +37,9 @@
 import org.junit.runners.model.*;
 
 import com.oracle.truffle.api.dsl.*;
-import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.sl.SLMain;
 import com.oracle.truffle.sl.builtins.*;
-import com.oracle.truffle.sl.factory.*;
-import com.oracle.truffle.sl.runtime.*;
 import com.oracle.truffle.sl.test.SLTestRunner.TestCase;
 
 public final class SLTestRunner extends ParentRunner<TestCase> {
@@ -167,22 +165,16 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         PrintWriter printer = new PrintWriter(out);
         try {
-            SLContext context = SLContextFactory.create(new BufferedReader(new StringReader(repeat(testCase.testInput, repeats))), printer);
-            for (NodeFactory<? extends SLBuiltinNode> builtin : builtins) {
-                context.installBuiltin(builtin);
-            }
-            /*
-             * TruffleVM vm = TruffleVM.create(); String script = readAllLines(testCase.path); for
-             * (int i = 0; i < repeats; i++) { vm.eval("application/x-sl", script); }
-             */
-            final Source source = Source.fromText(readAllLines(testCase.path), testCase.sourceName);
-            SLMain.run(context, source, null, repeats);
+            TruffleVM vm = TruffleVM.newVM().stdIn(new BufferedReader(new StringReader(repeat(testCase.testInput, repeats)))).stdOut(printer).build();
+
+            String script = readAllLines(testCase.path);
+            SLMain.run(vm, testCase.path.toUri(), null, printer, repeats, builtins);
 
             printer.flush();
             String actualOutput = new String(out.toByteArray());
-            Assert.assertEquals(repeat(testCase.expectedOutput, repeats), actualOutput);
+            Assert.assertEquals(script, repeat(testCase.expectedOutput, repeats), actualOutput);
         } catch (Throwable ex) {
-            notifier.fireTestFailure(new Failure(testCase.name, ex));
+            notifier.fireTestFailure(new Failure(testCase.name, new IllegalStateException("Cannot run " + testCase.sourceName, ex)));
         } finally {
             notifier.fireTestFinished(testCase.name);
         }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java	Tue May 26 19:11:36 2015 +0200
@@ -33,8 +33,9 @@
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.tools.*;
 import com.oracle.truffle.api.vm.TruffleVM;
+import com.oracle.truffle.api.vm.TruffleVM.Symbol;
 import com.oracle.truffle.sl.builtins.*;
-import com.oracle.truffle.sl.factory.*;
+import com.oracle.truffle.sl.factory.SLContextFactory;
 import com.oracle.truffle.sl.nodes.*;
 import com.oracle.truffle.sl.nodes.call.*;
 import com.oracle.truffle.sl.nodes.controlflow.*;
@@ -43,6 +44,9 @@
 import com.oracle.truffle.sl.nodes.local.*;
 import com.oracle.truffle.sl.parser.*;
 import com.oracle.truffle.sl.runtime.*;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as
@@ -132,11 +136,17 @@
  */
 @TruffleLanguage.Registration(name = "sl", mimeType = "application/x-sl")
 public class SLMain extends TruffleLanguage {
+    private static SLMain LAST;
+    private static List<NodeFactory<? extends SLBuiltinNode>> builtins = Collections.emptyList();
     private final SLContext context;
 
     public SLMain(Env env) {
         super(env);
-        this.context = SLContextFactory.create(new BufferedReader(new InputStreamReader(System.in)), new PrintWriter(System.out));
+        context = SLContextFactory.create(new BufferedReader(env().stdIn()), new PrintWriter(env().stdOut(), true));
+        LAST = this;
+        for (NodeFactory<? extends SLBuiltinNode> builtin : builtins) {
+            context.installBuiltin(builtin);
+        }
     }
 
     /* Demonstrate per-type tabulation of node execution counts */
@@ -150,7 +160,7 @@
      * The main entry point. Use the mx command "mx sl" to run it with the correct class path setup.
      */
     public static void main(String[] args) throws IOException {
-        TruffleVM vm = TruffleVM.create();
+        TruffleVM vm = TruffleVM.newVM().build();
         assert vm.getLanguages().containsKey("application/x-sl");
 
         int repeats = 1;
@@ -158,12 +168,17 @@
             repeats = Integer.parseInt(args[1]);
         }
 
+        if (args.length == 0) {
+            vm.eval("application/x-sl", new InputStreamReader(System.in));
+        } else {
+            vm.eval(new File(args[0]).toURI());
+        }
+        Symbol main = vm.findGlobalSymbol("main");
+        if (main == null) {
+            throw new SLException("No function main() defined in SL source file.");
+        }
         while (repeats-- > 0) {
-            if (args.length == 0) {
-                vm.eval("application/x-sl", new InputStreamReader(System.in));
-            } else {
-                vm.eval(new File(args[0]).toURI());
-            }
+            main.invoke(null);
         }
     }
 
@@ -171,7 +186,9 @@
      * Parse and run the specified SL source. Factored out in a separate method so that it can also
      * be used by the unit test harness.
      */
-    public static long run(SLContext context, Source source, PrintWriter logOutput, int repeats) {
+    public static long run(TruffleVM context, URI source, PrintWriter logOutput, PrintWriter out, int repeats, List<NodeFactory<? extends SLBuiltinNode>> currentBuiltins) throws IOException {
+        builtins = currentBuiltins;
+
         if (logOutput != null) {
             logOutput.println("== running on " + Truffle.getRuntime().getName());
             // logOutput.println("Source = " + source.getCode());
@@ -200,11 +217,14 @@
         }
 
         /* Parse the SL source file. */
-        Parser.parseSL(context, source);
+        Object result = context.eval(source);
+        if (result != null) {
+            out.println(result);
+        }
 
         /* Lookup our main entry point, which is per definition always named "main". */
-        SLFunction main = context.getFunctionRegistry().lookup("main");
-        if (main.getCallTarget() == null) {
+        Symbol main = context.findGlobalSymbol("main");
+        if (main == null) {
             throw new SLException("No function main() defined in SL source file.");
         }
 
@@ -215,19 +235,19 @@
         /* Change to dump the AST to IGV over the network. */
         boolean dumpASTToIGV = false;
 
-        printScript("before execution", context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV);
+        printScript("before execution", LAST.context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV);
         long totalRuntime = 0;
         try {
             for (int i = 0; i < repeats; i++) {
                 long start = System.nanoTime();
                 /* Call the main entry point, without any arguments. */
                 try {
-                    Object result = main.getCallTarget().call();
+                    result = main.invoke(null);
                     if (result != SLNull.SINGLETON) {
-                        context.getOutput().println(result);
+                        out.println(result);
                     }
                 } catch (UnsupportedSpecializationException ex) {
-                    context.getOutput().println(formatTypeError(ex));
+                    out.println(formatTypeError(ex));
                 }
                 long end = System.nanoTime();
                 totalRuntime += end - start;
@@ -238,7 +258,7 @@
             }
 
         } finally {
-            printScript("after execution", context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV);
+            printScript("after execution", LAST.context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV);
         }
         if (nodeExecCounter != null) {
             nodeExecCounter.print(System.out);
@@ -307,7 +327,7 @@
         if (ex.getNode() != null && ex.getNode().getSourceSection() != null) {
             SourceSection ss = ex.getNode().getSourceSection();
             if (ss != null && !(ss instanceof NullSourceSection)) {
-                result.append(" at ").append(ss.getSource().getName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn());
+                result.append(" at ").append(ss.getSource().getShortName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn());
             }
         }
         result.append(": operation");
@@ -356,17 +376,22 @@
 
     @Override
     protected Object findExportedSymbol(String globalName) {
+        for (SLFunction f : context.getFunctionRegistry().getFunctions()) {
+            if (globalName.equals(f.getName())) {
+                return f;
+            }
+        }
         return null;
     }
 
     @Override
     protected Object getLanguageGlobal() {
-        return null;
+        return context;
     }
 
     @Override
     protected boolean isObjectOfLanguage(Object object) {
-        return false;
+        return object instanceof SLFunction;
     }
 
 }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Tue May 26 19:11:36 2015 +0200
@@ -52,20 +52,22 @@
         StringBuilder str = new StringBuilder();
 
         Truffle.getRuntime().iterateFrames(frameInstance -> {
-            dumpFrame(str, frameInstance.getCallTarget(), frameInstance.getFrame(FrameAccess.READ_ONLY, true));
+            CallTarget callTarget = frameInstance.getCallTarget();
+            Frame frame = frameInstance.getFrame(FrameAccess.READ_ONLY, true);
+            RootNode rn = ((RootCallTarget) callTarget).getRootNode();
+            if (rn.getClass().getName().contains("SLFunctionForeignAccess")) {
+                return 1;
+            }
+            if (str.length() > 0) {
+                str.append(System.getProperty("line.separator"));
+            }
+            str.append("Frame: ").append(rn.toString());
+            FrameDescriptor frameDescriptor = frame.getFrameDescriptor();
+            frameDescriptor.getSlots().stream().forEach((s) -> {
+                str.append(", ").append(s.getIdentifier()).append("=").append(frame.getValue(s));
+            });
             return null;
         });
         return str.toString();
     }
-
-    private static void dumpFrame(StringBuilder str, CallTarget callTarget, Frame frame) {
-        if (str.length() > 0) {
-            str.append(System.getProperty("line.separator"));
-        }
-        str.append("Frame: ").append(((RootCallTarget) callTarget).getRootNode().toString());
-        FrameDescriptor frameDescriptor = frame.getFrameDescriptor();
-        for (FrameSlot s : frameDescriptor.getSlots()) {
-            str.append(", ").append(s.getIdentifier()).append("=").append(frame.getValue(s));
-        }
-    }
 }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue May 26 19:11:36 2015 +0200
@@ -30,7 +30,6 @@
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.object.*;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.builtins.*;
 import com.oracle.truffle.sl.nodes.*;
 import com.oracle.truffle.sl.nodes.local.*;
@@ -158,12 +157,6 @@
      */
     public void executeMain(Source source) {
         Parser.parseSL(this, source);
-        SLFunction main = getFunctionRegistry().lookup("main");
-        if (main.getCallTarget() == null) {
-            throw new SLException("No function main() defined in SL source file.");
-        }
-        main.getCallTarget().call();
-        output.flush();
     }
 
     public DynamicObject createObject() {
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunction.java	Tue May 26 16:46:25 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunction.java	Tue May 26 19:11:36 2015 +0200
@@ -23,6 +23,7 @@
 package com.oracle.truffle.sl.runtime;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.api.utilities.*;
 
 /**
@@ -41,7 +42,7 @@
  * per name exists, the {@link SLFunctionRegistry} creates an instance also when performing name
  * lookup. A function that has been looked up, i.e., used, but not defined, has no call target.
  */
-public final class SLFunction {
+public final class SLFunction implements TruffleObject {
 
     /** The name of the function. */
     private final String name;
@@ -90,4 +91,14 @@
     public String toString() {
         return name;
     }
+
+    /**
+     * In case you want some of your objects to co-operate with other languages, you need to make
+     * them implement {@link TruffleObject} and provide additional {@link SLFunctionForeignAccess
+     * foreign access implementation}.
+     */
+    @Override
+    public ForeignAccessFactory getForeignAccessFactory() {
+        return SLFunctionForeignAccess.INSTANCE;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java	Tue May 26 19:11:36 2015 +0200
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014, 2014, 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.
+ *
+ * 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.sl.runtime;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ForeignAccessFactory;
+import com.oracle.truffle.api.interop.InteropPredicate;
+import com.oracle.truffle.api.interop.exception.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.messages.Message;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.interop.ForeignAccessArguments;
+import com.oracle.truffle.interop.messages.Execute;
+import com.oracle.truffle.interop.messages.Receiver;
+import com.oracle.truffle.sl.nodes.call.SLDispatchNode;
+import com.oracle.truffle.sl.nodes.call.SLDispatchNodeGen;
+import java.math.BigInteger;
+
+/**
+ * Implementation of foreing access for {@link SLFunction}.
+ */
+final class SLFunctionForeignAccess implements ForeignAccessFactory {
+    public static final ForeignAccessFactory INSTANCE = new SLFunctionForeignAccess();
+
+    private SLFunctionForeignAccess() {
+    }
+
+    @Override
+    public InteropPredicate getLanguageCheck() {
+        return (com.oracle.truffle.api.interop.TruffleObject o) -> o instanceof SLFunction;
+    }
+
+    @Override
+    public CallTarget getAccess(Message tree) {
+        if (Execute.create(Receiver.create(), 0).matchStructure(tree)) {
+            return Truffle.getRuntime().createCallTarget(new SLForeignCallerRootNode());
+        } else {
+            throw new UnsupportedMessageException(tree.toString() + " not supported");
+        }
+    }
+
+    private static class SLForeignCallerRootNode extends RootNode {
+        @Child private SLDispatchNode dispatch = SLDispatchNodeGen.create();
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            SLFunction function = (SLFunction) ForeignAccessArguments.getReceiver(frame.getArguments());
+            // the calling convention of interop passes the receiver of a
+            // function call (the this object)
+            // as an implicit 1st argument; we need to ignore this argument for SL
+            Object[] arguments = ForeignAccessArguments.extractUserArguments(1, frame.getArguments());
+            for (int i = 0; i < arguments.length; i++) {
+                if (arguments[i] instanceof Long) {
+                    continue;
+                }
+                if (arguments[i] instanceof BigInteger) {
+                    continue;
+                }
+                if (arguments[i] instanceof Number) {
+                    arguments[i] = ((Number) arguments[i]).longValue();
+                }
+            }
+
+            return dispatch.executeDispatch(frame, function, arguments);
+        }
+
+    }
+
+}
--- a/mx/suite.py	Tue May 26 16:46:25 2015 +0200
+++ b/mx/suite.py	Tue May 26 19:11:36 2015 +0200
@@ -1047,6 +1047,7 @@
       "sourceDirs" : ["src"],
       "dependencies" : [
         "com.oracle.truffle.api.dsl",
+        "com.oracle.truffle.interop",
         "com.oracle.truffle.api.object",
         "FINDBUGS"
       ],
@@ -1060,8 +1061,8 @@
       "subDir" : "graal",
       "sourceDirs" : ["src"],
       "dependencies" : [
-        "com.oracle.truffle.sl",
-        "JUNIT",
+        "com.oracle.truffle.api.test",
+        "com.oracle.truffle.sl"
       ],
       "checkstyle" : "com.oracle.graal.graph",
       "javaCompliance" : "1.8",