# HG changeset patch # User Jaroslav Tulach # Date 1432294870 -7200 # Node ID 99942eac9c6d090d9356d8c5ef9bd95edb5830b0 # Parent d4db9d812c8da34a36a599638568e1c3d663ee7b Introducing TruffleVM - a central place to invoke code in any registered TruffleLanguage. diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleTCK.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleTCK.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012, 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. + * + * 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.api.test.vm; + +import com.oracle.truffle.api.vm.TruffleVM; +import java.util.Random; + +/** + * 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; + + protected TruffleTCK() { + } + + /** + * This methods is called before first test is executed. It's purpose is to set a TruffleVM with + * your language up, so it is ready for testing. + * {@link TruffleVM#eval(java.lang.String, java.lang.String) Execute} any scripts you need, and + * prepare global symbols with proper names. The symbols will then be looked up by the + * infastructure (using the names provided by you from methods like {@link #plusInt()}) and used + * for internal testing. + * + * @return initialized Truffle virtual machine + */ + protected abstract TruffleVM prepareVM() throws Exception; + + /** + * Name of function which will return value 42 as a number. The return value of the method + * should be instance of {@link Number} and its {@link Number#intValue()} should return + * 42. + * + * @return name of globally exported symbol + */ + protected abstract String fourtyTwo(); + + /** + * Name of function to add two integer values together. The symbol will be invoked with two + * parameters of type {@link Integer} and expects result of type {@link Number} which's + * {@link Number#intValue()} is equivalent of param1 + param2. + * + * @return name of globally exported symbol + */ + protected abstract String plusInt(); + + private TruffleVM vm() throws Exception { + if (vm == null) { + vm = prepareVM(); + } + return vm; + } + + // + // The tests + // + + public void testFortyTwo() throws Exception { + TruffleVM.Symbol fourtyTwo = findGlobalSymbol(fourtyTwo()); + + Object res = fourtyTwo.invoke(null); + + assert res instanceof Number : "should yield a number, but was: " + res; + + Number n = (Number) res; + + assert 42 == n.intValue() : "The value is 42 = " + n.intValue(); + } + + public void testPlusWithInts() throws Exception { + Random r = new Random(); + int a = r.nextInt(100); + int b = r.nextInt(100); + + TruffleVM.Symbol plus = findGlobalSymbol(plusInt()); + + Object res = plus.invoke(null, a, b); + + assert res instanceof Number : "+ on two ints should yield a number, but was: " + res; + + Number n = (Number) res; + + assert a + b == n.intValue() : "The value is correct: (" + a + " + " + b + ") = " + n.intValue(); + } + + private TruffleVM.Symbol findGlobalSymbol(String name) throws Exception { + TruffleVM.Symbol s = vm().findGlobalSymbol(name); + assert s != null : "Symbol " + name + " is not found!"; + return s; + } +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleVMSingleThreadedTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/TruffleVMSingleThreadedTest.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, 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. + * + * 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.api.test.vm; + +import com.oracle.truffle.api.vm.TruffleVM; +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; + +public class TruffleVMSingleThreadedTest { + TruffleVM tvm; + + @Before + public void initInDifferentThread() throws InterruptedException { + Thread t = new Thread("Initializer") { + @Override + public void run() { + tvm = TruffleVM.create(); + } + }; + t.start(); + t.join(); + } + + @Test(expected = IllegalStateException.class) + public void evalURI() throws IOException, URISyntaxException { + tvm.eval(new URI("http://unknown.js")); + } + + @Test(expected = IllegalStateException.class) + public void evalString() throws IOException { + tvm.eval("text/javascript", "1 + 1"); + } + + @Test(expected = IllegalStateException.class) + public void evalReader() throws IOException { + try (StringReader sr = new StringReader("1 + 1")) { + tvm.eval("text/javascript", sr); + } + } + + @Test(expected = IllegalStateException.class) + public void findGlobalSymbol() { + tvm.findGlobalSymbol("doesNotExists"); + } +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,183 @@ +/* + * Copyright (c) 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. 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.api; + +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; +import com.oracle.truffle.api.vm.TruffleVM.Language; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An entry point for everyone who wants to implement a Truffle based language. By providing + * implementation of this type and registering it using {@link Registration} annotation, your + * language becomes accessible to users of the {@link TruffleVM Truffle virtual machine} - all they + * will need to do is to include your JAR into their application and all the Truffle goodies (multi + * language support, multi tennat hosting, debugging, etc.) will be made available to them. + */ +public abstract class TruffleLanguage { + private Env env; + + /** + * The annotation to use to register your language to the {@link TruffleVM Truffle} system. By + * annotating your implementation of {@link TruffleLanguage} by this annotation you are just a + * one JAR drop to the classpath away from your users. Once they include your JAR in + * their application, your language will be available to the {@link TruffleVM Truffle virtual + * machine}. + */ + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.TYPE) + public @interface Registration { + /** + * Unique name of your language. This name will be exposed to users via the + * {@link Language#getName()} getter. + * + * @return identifier of your language + */ + String name(); + + /** + * List of mimetypes associated with your language. Users will use them (directly or + * inderectly) when {@link TruffleVM#eval(java.lang.String, java.lang.String) executing} + * their code snippets or their {@link TruffleVM#eval(java.net.URI) files}. + * + * @return array of mime types assigned to your language files + */ + String[] mimeType(); + } + + @SuppressWarnings("all") + void attachEnv(Env env) { + this.env = env; + } + + protected final Env env() { + if (this.env == null) { + throw new NullPointerException("Accessing env before initialization is finished"); + } + return this.env; + } + + protected abstract Object eval(Source code) throws IOException; + + /** + * Called when some other language is seeking for a global symbol. This method is supposed to do + * lazy binding, e.g. there is no need to export symbols in advance, it is fine to wait until + * somebody asks for it (by calling this method). + *

+ * The exported object can either be TruffleObject (e.g. a native object from the + * other language) to support interoperability between languages or one of Java primitive + * wrappers ( {@link Integer}, {@link Double}, {@link Short}, etc.). + * + * @param globalName the name of the global symbol to find + * @return an exported object or null, if the symbol does not represent anything + * meaningful in this language + */ + protected abstract Object findExportedSymbol(String globalName); + + /** + * Returns global object for the language. + *

+ * The object is expected to be TruffleObject (e.g. a native object from the other + * language) but technically it can be one of Java primitive wrappers ({@link Integer}, + * {@link Double}, {@link Short}, etc.). + * + * @return the global object or null if the language does not support such concept + */ + protected abstract Object getLanguageGlobal(); + + /** + * Checks whether the object is provided by this language. + * + * @param object the object to check + * @return true if this language can deal with such object in native way + */ + protected abstract boolean isObjectOfLanguage(Object object); + + /** + * Represents execution environment of the {@link TruffleLanguage}. Each active + * {@link TruffleLanguage} receives instance of the environment before any code is executed upon + * it. The environment has knowledge of all active languages and can exchange symbols between + * them. + */ + public static final class Env { + private final TruffleVM vm; + private final TruffleLanguage lang; + + Env(TruffleVM vm, TruffleLanguage lang) { + this.vm = vm; + this.lang = lang; + } + + /** + * Asks the environment to go through other registered languages and find whether they + * export global symbol of specified name. The expected return type is either + * TruffleObject, or one of wrappers of Java primitive types ({@link Integer}, + * {@link Double}). + * + * @param globalName the name of the symbol to search for + * @return object representing the symbol or null + */ + public Object importSymbol(String globalName) { + return API.importSymbol(vm, lang, globalName); + } + } + + private static final AccessAPI API = new AccessAPI(); + + private static final class AccessAPI extends Accessor { + + @Override + protected Env attachEnv(TruffleVM vm, TruffleLanguage l) { + Env env = new Env(vm, l); + l.attachEnv(env); + return env; + } + + @Override + public Object importSymbol(TruffleVM vm, TruffleLanguage queryingLang, String globalName) { + return super.importSymbol(vm, queryingLang, globalName); + } + + @Override + protected Object eval(TruffleLanguage l, Source s) throws IOException { + return l.eval(s); + } + + @Override + protected Object findExportedSymbol(TruffleLanguage l, String globalName) { + return l.findExportedSymbol(globalName); + } + + @Override + protected Object languageGlobal(TruffleLanguage l) { + return l.getLanguageGlobal(); + } + } +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 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. 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.api.impl; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.vm.TruffleVM; +import com.oracle.truffle.api.source.Source; +import java.io.IOException; +import java.util.ServiceLoader; + +/** + * Communication between TruffleVM and TruffleLanguage API/SPI. + */ +public abstract class Accessor { + private static Accessor API; + private static Accessor SPI; + static { + TruffleLanguage lng = new TruffleLanguage() { + @Override + protected Object eval(Source code) throws IOException { + return null; + } + + @Override + protected Object findExportedSymbol(String globalName) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + }; + lng.hashCode(); + } + + protected Accessor() { + if (this.getClass().getSimpleName().endsWith("API")) { + if (API != null) { + throw new IllegalStateException(); + } + API = this; + } else { + if (SPI != null) { + throw new IllegalStateException(); + } + SPI = this; + } + } + + protected Env attachEnv(TruffleVM vm, TruffleLanguage l) { + return API.attachEnv(vm, l); + } + + protected Object eval(TruffleLanguage l, Source s) throws IOException { + return API.eval(l, s); + } + + protected Object importSymbol(TruffleVM vm, TruffleLanguage queryingLang, String globalName) { + return SPI.importSymbol(vm, queryingLang, globalName); + } + + protected Object findExportedSymbol(TruffleLanguage l, String globalName) { + return API.findExportedSymbol(l, globalName); + } + + protected Object languageGlobal(TruffleLanguage l) { + return API.languageGlobal(l); + } + + protected Object invoke(Object obj, Object[] args) throws IOException { + for (SymbolInvoker si : ServiceLoader.load(SymbolInvoker.class)) { + return si.invoke(obj, args); + } + throw new IOException("No symbol invoker found!"); + } + +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/SymbolInvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/SymbolInvoker.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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. 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.api.impl; + +import java.io.IOException; + +/** + * XXX: Temporary class to make unit tests pass without messing with Message implementations and + * associated nodes too much. + */ +public abstract class SymbolInvoker { + protected abstract Object invoke(Object symbol, Object... args) throws IOException; +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,388 @@ +/* + * Copyright (c) 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. 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.api.vm; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.source.Source; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +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. + *

+ * 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)}. + * Support for individual languages is initialized on demand - e.g. once a file of certain mime type + * is about to be processed, its appropriate engine (if found), is initialized. Once an engine gets + * initialized, it remains so, until the virtual machine isn't garbage collected. + *

+ * The TruffleVM 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. + */ +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 langs; + + private TruffleVM() { + initThread = Thread.currentThread(); + this.langs = new HashMap<>(); + Enumeration en; + try { + en = loader().getResources("META-INF/truffle/language"); + } catch (IOException ex) { + throw new IllegalStateException("Cannot read list of Truffle languages", ex); + } + while (en.hasMoreElements()) { + URL u = en.nextElement(); + Properties p; + try { + p = new Properties(); + try (InputStream is = u.openStream()) { + p.load(is); + } + } catch (IOException ex) { + LOG.log(Level.CONFIG, "Cannot process " + u + " as language definition", ex); + continue; + } + Language l = new Language(p); + for (String mimeType : l.getMimeTypes()) { + langs.put(mimeType, l); + } + } + } + + static ClassLoader loader() { + ClassLoader l = TruffleVM.class.getClassLoader(); + if (l == null) { + l = ClassLoader.getSystemClassLoader(); + } + return l; + } + + /** + * 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 + * {@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(); + } + + /** + * Descriptions of languages supported in this Truffle virtual machine. + * + * @return an immutable map with keys being mimetypes and values the {@link Language + * descriptions} of associated languages + */ + public Map getLanguages() { + return Collections.unmodifiableMap(langs); + } + + /** + * Evaluates file located on a given URL. Is equivalent to loading the content of a file and + * executing it via {@link #eval(java.lang.String, java.lang.String)} with a mime type guess + * based on the file's extension and/or content. + * + * @param location the location of a file to execute + * @return result of a processing the file, possibly null + * @throws IOException exception to signal I/O problems or problems with processing the file's + * content + */ + public Object eval(URI location) throws IOException { + checkThread(); + Source s; + String mimeType; + if (location.getScheme().equals("file")) { + File file = new File(location); + s = Source.fromFileName(file.getPath(), true); + mimeType = file.getName().endsWith(".c") ? "text/x-c" : Files.probeContentType(file.toPath()); + } else { + URL url = location.toURL(); + s = Source.fromURL(url, location.toString()); + URLConnection conn = url.openConnection(); + mimeType = conn.getContentType(); + } + TruffleLanguage l = getTruffleLang(mimeType); + if (l == null) { + throw new IOException("No language for " + location + " with mime type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return SPI.eval(l, s); + } + + /** + * Evaluates code snippet. Chooses a language registered for a given mime type (throws + * {@link IOException} if there is none). And passes the specified code to it for execution. + * + * @param mimeType mime type of the code snippet - chooses the right language + * @param reader the source of code snippet to execute + * @return result of an exceution, possibly null + * @throws IOException thrown to signal errors while processing the code + */ + public Object eval(String mimeType, Reader reader) throws IOException { + checkThread(); + TruffleLanguage l = getTruffleLang(mimeType); + if (l == null) { + throw new IOException("No language for mime type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return SPI.eval(l, Source.fromReader(reader, mimeType)); + } + + /** + * Evaluates code snippet. Chooses a language registered for a given mime type (throws + * {@link IOException} if there is none). And passes the specified code to it for execution. + * + * @param mimeType mime type of the code snippet - chooses the right language + * @param code the code snippet to execute + * @return result of an exceution, possibly null + * @throws IOException thrown to signal errors while processing the code + */ + public Object eval(String mimeType, String code) throws IOException { + checkThread(); + TruffleLanguage l = getTruffleLang(mimeType); + if (l == null) { + throw new IOException("No language for mime type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return SPI.eval(l, Source.fromText(code, mimeType)); + } + + /** + * Looks global symbol provided by one of initialized languages up. First of all execute your + * program via one of your {@link #eval(java.lang.String, java.lang.String)} and then look + * expected symbol up using this method. + *

+ * The names of the symbols are language dependant, but for example the Java language bindings + * follow the specification for method references: + *

+ * Once an symbol is obtained, it remembers values for fast acces and is ready for being + * invoked. + * + * @param globalName the name of the symbol to find + * @return found symbol or null if it has not been found + */ + public Symbol findGlobalSymbol(String globalName) { + checkThread(); + Object obj = null; + Object global = null; + for (Language dl : langs.values()) { + TruffleLanguage l = dl.getImpl(); + obj = SPI.findExportedSymbol(l, globalName); + if (obj != null) { + global = SPI.languageGlobal(l); + break; + } + } + return obj == null ? null : new Symbol(obj, global); + } + + private void checkThread() { + if (initThread != Thread.currentThread()) { + throw new IllegalStateException("TruffleVM created on " + initThread.getName() + " but used on " + Thread.currentThread().getName()); + } + } + + private TruffleLanguage getTruffleLang(String mimeType) { + checkThread(); + Language l = langs.get(mimeType); + return l == null ? null : l.getImpl(); + } + + /** + * Represents {@link TruffleVM#findGlobalSymbol(java.lang.String) global symbol} provided by one + * of the initialized languages in {@link TruffleVM Truffle virtual machine}. + */ + public class Symbol { + private final Object obj; + private final Object global; + + Symbol(Object obj, Object global) { + this.obj = obj; + this.global = global; + } + + /** + * Invokes the symbol. If the symbol represents a function, then it should be invoked with + * provided arguments. If the symbol represents a field, then first argument (if provided) + * should set the value to the field; the return value should be the actual value of the + * field when the invoke method returns. + * + * @param thiz this/self in language that support such concept; use null to let + * the language use default this/self or ignore the value + * @param args arguments to pass when invoking the symbol + * @return the value returned by invoking the symbol + * @throws IOException signals problem during execution + */ + public Object invoke(Object thiz, Object... args) throws IOException { + List arr = new ArrayList<>(); + if (thiz == null) { + if (global != null) { + arr.add(global); + } + } else { + arr.add(thiz); + } + arr.addAll(Arrays.asList(args)); + return SPI.invoke(obj, arr.toArray()); + } + } + + /** + * Description of a language registered in {@link TruffleVM Truffle virtual machine}. Languages + * are registered by {@link Registration} annotation which stores necessary information into a + * descriptor inside of the language's JAR file. When a new {@link TruffleVM} is created, it + * reads all available descritors and creates {@link Language} objects to represent them. One + * can obtain a {@link #getName() name} or list of supported {@link #getMimeTypes() mimetypes} + * for each language. The actual language implementation is not initialized until + * {@link TruffleVM#eval(java.lang.String, java.lang.String) a code is evaluated} in it. + */ + public final class Language { + private final Properties props; + private TruffleLanguage impl; + + Language(Properties props) { + this.props = props; + } + + /** + * Mimetypes recognized by the language. + * + * @return returns immutable set of recognized mimetypes + */ + public Set getMimeTypes() { + TreeSet ts = new TreeSet<>(); + for (int i = 0;; i++) { + String mt = props.getProperty("mimeType." + i); + if (mt == null) { + break; + } + ts.add(mt); + } + return Collections.unmodifiableSet(ts); + } + + /** + * Human readable name of the language. Think of C, Ruby, JS, etc. + * + * @return string giving the language a name + */ + public String getName() { + return props.getProperty("name"); + } + + TruffleLanguage getImpl() { + if (impl == null) { + String n = props.getProperty("className"); + try { + TruffleLanguage lang = (TruffleLanguage) Class.forName(n, true, loader()).newInstance(); + SPI.attachEnv(TruffleVM.this, lang); + impl = lang; + } catch (Exception ex) { + throw new IllegalStateException("Cannot initialize " + getName() + " language with implementation " + n, ex); + } + } + return impl; + } + + @Override + public String toString() { + return "[" + getName() + " for " + getMimeTypes() + "]"; + } + } // end of Language + + private static class SPIAccessor extends Accessor { + @Override + public Object importSymbol(TruffleVM vm, TruffleLanguage ownLang, String globalName) { + Set uniqueLang = new LinkedHashSet<>(vm.langs.values()); + for (Language dl : uniqueLang) { + TruffleLanguage l = dl.getImpl(); + if (l == ownLang) { + continue; + } + Object obj = SPI.findExportedSymbol(l, globalName); + if (obj != null) { + return obj; + } + } + return null; + } + + @Override + public Env attachEnv(TruffleVM vm, TruffleLanguage l) { + return super.attachEnv(vm, l); + } + + @Override + public Object eval(TruffleLanguage l, Source s) throws IOException { + return super.eval(l, s); + } + + @Override + public Object findExportedSymbol(TruffleLanguage l, String globalName) { + return super.findExportedSymbol(l, globalName); + } + + @Override + public Object languageGlobal(TruffleLanguage l) { + return super.languageGlobal(l); + } + + @Override + public Object invoke(Object obj, Object[] args) throws IOException { + return super.invoke(obj, args); + } + } // end of SPIAccessor +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.dsl.processor/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.truffle.dsl.processor/src/META-INF/services/javax.annotation.processing.Processor Fri May 22 10:50:43 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/META-INF/services/javax.annotation.processing.Processor Fri May 22 13:41:10 2015 +0200 @@ -1,2 +1,3 @@ com.oracle.truffle.dsl.processor.TruffleProcessor com.oracle.truffle.dsl.processor.verify.VerifyTruffleProcessor +com.oracle.truffle.dsl.processor.LanguageRegistrationProcessor diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012, 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. + * + * 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.dsl.processor; + +import com.oracle.truffle.api.TruffleLanguage.Registration; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Properties; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +@SupportedAnnotationTypes("com.oracle.truffle.api.*") +public final class LanguageRegistrationProcessor extends AbstractProcessor { + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + private void createProviderFile(TypeElement language, Registration annotation) { + String filename = "META-INF/truffle/language"; + try { + FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, language); + Properties p = new Properties(); + String className = processingEnv.getElementUtils().getBinaryName(language).toString(); + p.setProperty("name", annotation.name()); + p.setProperty("className", className); + String[] mimes = annotation.mimeType(); + for (int i = 0; i < mimes.length; i++) { + p.setProperty("mimeType." + i, mimes[i]); + } + try (OutputStream os = file.openOutputStream()) { + p.store(os, "Generated by " + LanguageRegistrationProcessor.class.getName()); + } + } catch (IOException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), language); + } + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(Registration.class)) { + Registration annotation = e.getAnnotation(Registration.class); + if (annotation != null && e.getKind() == ElementKind.CLASS) { + if (!e.getModifiers().contains(Modifier.PUBLIC)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Registered language class must be public", e); + } + createProviderFile((TypeElement) e, annotation); + } + } + + return true; + } +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.interop/src/META-INF/services/com.oracle.truffle.api.impl.SymbolInvoker --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.interop/src/META-INF/services/com.oracle.truffle.api.impl.SymbolInvoker Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,1 @@ +com.oracle.truffle.interop.SymbolInvokerImpl diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/SymbolInvokerImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/SymbolInvokerImpl.java Fri May 22 13:41:10 2015 +0200 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 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. 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.interop; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.impl.SymbolInvoker; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.interop.messages.Execute; +import com.oracle.truffle.interop.messages.Receiver; +import com.oracle.truffle.interop.node.ForeignObjectAccessNode; +import java.io.IOException; + +public final class SymbolInvokerImpl extends SymbolInvoker { + static final FrameDescriptor UNUSED_FRAMEDESCRIPTOR = new FrameDescriptor(); + + @Override + protected Object invoke(Object symbol, Object... arr) throws IOException { + ForeignObjectAccessNode executeMain = ForeignObjectAccessNode.getAccess(Execute.create(Receiver.create(), arr.length)); + CallTarget callTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(executeMain, (TruffleObject) symbol, arr)); + VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(arr, UNUSED_FRAMEDESCRIPTOR); + return callTarget.call(frame); + } + + private static class TemporaryRoot extends RootNode { + @Child private ForeignObjectAccessNode foreignAccess; + private final TruffleObject function; + private final Object[] args; + + public TemporaryRoot(ForeignObjectAccessNode foreignAccess, TruffleObject function, Object... args) { + this.foreignAccess = foreignAccess; + this.function = function; + this.args = args; + } + + @Override + public Object execute(VirtualFrame frame) { + return foreignAccess.executeForeign(frame, function, args); + } + } + +} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleGlobalScope.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleGlobalScope.java Fri May 22 10:50:43 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * Copyright (c) 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.interop; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.interop.*; - -public interface TruffleGlobalScope { - void exportTruffleObject(Object identifier, TruffleObject object); - - FrameSlot getFrameSlot(Object identifier); - - TruffleObject getTruffleObject(FrameSlot slot); - - boolean contains(Object identifier); -} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java Fri May 22 10:50:43 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (c) 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. 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.interop; - -import com.oracle.truffle.api.interop.*; - -public interface TruffleLanguage { - String getLanguageName(); - - Object run(String[] arguments); - - void setTruffleGlobalScope(TruffleGlobalScope globalScope); - - TruffleObject getLanguageGlobal(); - - boolean isObjectOfLanguage(TruffleObject object); - -} diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Fri May 22 10:50:43 2015 +0200 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Fri May 22 13:41:10 2015 +0200 @@ -37,8 +37,8 @@ import org.junit.runners.model.*; import com.oracle.truffle.api.dsl.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.sl.*; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.sl.SLMain; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.factory.*; import com.oracle.truffle.sl.runtime.*; @@ -171,6 +171,10 @@ for (NodeFactory 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); diff -r d4db9d812c8d -r 99942eac9c6d graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Fri May 22 10:50:43 2015 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Fri May 22 13:41:10 2015 +0200 @@ -32,6 +32,7 @@ import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.tools.*; +import com.oracle.truffle.api.vm.TruffleVM; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.factory.*; import com.oracle.truffle.sl.nodes.*; @@ -129,7 +130,13 @@ * default printer. * */ -public class SLMain { +@TruffleLanguage.Registration(name = "sl", mimeType = "application/x-sl") +public class SLMain extends TruffleLanguage { + private final SLContext context; + + public SLMain() { + this.context = SLContextFactory.create(new BufferedReader(new InputStreamReader(System.in)), System.out); + } /* Demonstrate per-type tabulation of node execution counts */ private static boolean nodeExecCounts = false; @@ -142,22 +149,21 @@ * 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 { - - SLContext context = SLContextFactory.create(new BufferedReader(new InputStreamReader(System.in)), System.out); - - Source source; - if (args.length == 0) { - source = Source.fromReader(new InputStreamReader(System.in), "stdin"); - } else { - source = Source.fromFileName(args[0]); - } + TruffleVM vm = TruffleVM.create(); + assert vm.getLanguages().containsKey("application/x-sl"); int repeats = 1; if (args.length >= 2) { repeats = Integer.parseInt(args[1]); } - run(context, source, System.out, repeats); + while (repeats-- > 0) { + if (args.length == 0) { + vm.eval("application/x-sl", new InputStreamReader(System.in)); + } else { + vm.eval(new File(args[0]).toURI()); + } + } } /** @@ -341,4 +347,25 @@ return result.toString(); } + @Override + protected Object eval(Source code) throws IOException { + context.executeMain(code); + return null; + } + + @Override + protected Object findExportedSymbol(String globalName) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + }