Mercurial > hg > truffle
changeset 22128:f879b1fe3773
Separating the TruffleVM into its own project makes it possible to cleanup various system parts interations
line wrap: on
line diff
--- a/mx.truffle/eclipse-settings/org.eclipse.jdt.core.prefs Thu Sep 03 16:29:30 2015 +0200 +++ b/mx.truffle/eclipse-settings/org.eclipse.jdt.core.prefs Thu Sep 03 16:38:45 2015 +0200 @@ -54,7 +54,7 @@ org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
--- a/mx.truffle/suite.py Thu Sep 03 16:29:30 2015 +0200 +++ b/mx.truffle/suite.py Thu Sep 03 16:38:45 2015 +0200 @@ -43,7 +43,16 @@ "sourceDirs" : ["src"], "javaCompliance" : "1.7", "workingSets" : "API,Truffle", - "netbeans.project.properties" : "main.class=com.oracle.truffle.api.impl.Accessor", + }, + + "com.oracle.truffle.api.vm" : { + "subDir" : "truffle", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.truffle.api", + ], + "javaCompliance" : "1.7", + "workingSets" : "API,Truffle", }, "com.oracle.truffle.api.test" : { @@ -51,6 +60,7 @@ "sourceDirs" : ["src"], "dependencies" : [ "com.oracle.truffle.api.interop", + "com.oracle.truffle.api.vm", "mx:JUNIT", ], "checkstyle" : "com.oracle.truffle.dsl.processor", @@ -132,8 +142,7 @@ "subDir" : "truffle", "sourceDirs" : ["src"], "dependencies" : [ - "com.oracle.truffle.api.dsl", - "com.oracle.truffle.api.interop", + "TRUFFLE_API", "mx:JUNIT" ], "annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"], @@ -145,7 +154,7 @@ "com.oracle.truffle.tools" : { "subDir" : "truffle", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.api"], + "dependencies" : ["TRUFFLE_API"], "checkstyle" : "com.oracle.truffle.api", "javaCompliance" : "1.7", "workingSets" : "Truffle,Tools", @@ -228,8 +237,8 @@ "javaCompliance" : "1.7", "dependencies" : [ "com.oracle.truffle.api.dsl", + "com.oracle.truffle.api.vm", "com.oracle.truffle.object.basic", - "com.oracle.truffle.tools" ], "distDependencies" : [ ], @@ -286,6 +295,7 @@ "javaCompliance" : "1.7", "dependencies" : [ "com.oracle.truffle.tools.debug.shell", + "com.oracle.truffle.tools" ], "exclude" : ["JLINE"], "distDependencies" : [
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/AccessorTest.java Thu Sep 03 16:38:45 2015 +0200 @@ -62,7 +62,7 @@ } Object findLanguageByClass(TruffleVM vm) throws IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { - Method find = Accessor.class.getDeclaredMethod("findLanguage", TruffleVM.class, Class.class); + Method find = Accessor.class.getDeclaredMethod("findLanguage", Object.class, Class.class); find.setAccessible(true); TruffleLanguage.Env env = (TruffleLanguage.Env) find.invoke(API, vm, ExportImportLanguage1.class); Field f = env.getClass().getDeclaredField("langCtx");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/EventConsumer.java Thu Sep 03 16:38:45 2015 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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.api.vm; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; + +/** + * {@link TruffleVM} generates various events and delivers them to + * {@link TruffleVM.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) registered} handlers. + * Each handler is registered for a particular type of event. Examples of events include + * {@link ExecutionEvent} or {@link SuspendedEvent} useful when debugging {@link TruffleLanguage + * Truffle language}s. + * + * @param <Event> type of event to observe and handle + */ +public abstract class EventConsumer<Event> { + final Class<Event> type; + + /** + * Creates new handler for specified event type. + * + * @param eventType type of events to handle + */ + public EventConsumer(Class<Event> eventType) { + this.type = eventType; + } + + /** + * Called by the {@link TruffleVM} when event of requested type appears. + * + * @param event the instance of an event of the request type + */ + protected abstract void on(Event event); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/LanguageCache.java Thu Sep 03 16:38:45 2015 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, 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.api.vm; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.vm.TruffleVM.Language; +import java.util.HashMap; +import java.util.Map; + +/** + * Ahead-of-time initialization. If the JVM is started with -Dcom.oracle.truffle.aot=true, it + * populates cache with languages found in application classloader. + */ +final class LanguageCache { + private static final boolean PRELOAD; + private static final Map<String, TruffleLanguage<?>> CACHE; + static { + Map<String, TruffleLanguage<?>> map = null; + if (Boolean.getBoolean("com.oracle.truffle.aot")) { // NOI18N + map = new HashMap<>(); + for (Language description : TruffleVM.newVM().build().getLanguages().values()) { + TruffleLanguage<?> language = description.getImpl(); + map.put(language.getClass().getName(), language); + } + } + CACHE = map; + PRELOAD = CACHE != null; + } + + static TruffleLanguage<?> find(String name, ClassLoader loader) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + if (PRELOAD) { + return CACHE.get(name); + } else { + Class<?> langClazz = Class.forName(name, true, loader); + return (TruffleLanguage<?>) langClazz.getField("INSTANCE").get(null); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Thu Sep 03 16:38:45 2015 +0200 @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2014, 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.api.vm; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.debug.*; +import com.oracle.truffle.api.impl.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.source.*; +import java.io.*; +import java.net.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.logging.*; + +/** + * <em>Virtual machine</em> for Truffle based languages. Term virtual machine is a bit overloaded, + * so don't think of <em>Java virtual machine</em> here - while we are running and using + * {@link TruffleVM} inside of a <em>JVM</em> there can be multiple instances (some would say + * tenants) of {@link TruffleVM} running next to each other in a single <em>JVM</em> with a complete + * mutual isolation. There is 1:N mapping between <em>JVM</em> and {@link TruffleVM}. + * <p> + * It would not be correct to think of a {@link TruffleVM} as a runtime for a single Truffle + * language (Ruby, Python, R, C, JavaScript, etc.) either. {@link TruffleVM} can host as many of + * Truffle languages as {@link Registration registered on a class path} of your <em>JVM</em> + * application. {@link TruffleVM} orchestrates these languages, manages exchange of objects and + * calls among them. While it may happen that there is just one activated language inside of a + * {@link TruffleVM}, the greatest strength of {@link TruffleVM} is in interoperability between all + * Truffle languages. There is 1:N mapping between {@link TruffleVM} and {@link TruffleLanguage + * Truffle language implementations}. + * <p> + * 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 cooperate. 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)}. + * 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. + * <p> + * The <code>TruffleVM</code> is single-threaded and tries to enforce that. It records the thread it + * has been {@link Builder#build() created} by and checks that all subsequent calls are coming from + * the same thread. There is 1:1 mapping between {@link TruffleVM} and a thread that can tell it + * what to do. + */ +@SuppressWarnings("rawtypes") +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 Executor executor; + private final Map<String, Language> langs; + private final Reader in; + private final Writer err; + private final Writer out; + private final EventConsumer<?>[] handlers; + private final Map<String, Object> globals; + private Debugger debugger; + + /** + * Private & temporary only constructor. + */ + private TruffleVM() { + this.initThread = null; + this.in = null; + this.err = null; + this.out = null; + this.langs = null; + this.handlers = null; + this.globals = null; + this.executor = null; + } + + /** + * Real constructor used from the builder. + */ + private TruffleVM(Executor executor, Map<String, Object> globals, Writer out, Writer err, Reader in, EventConsumer<?>[] handlers) { + this.executor = executor; + this.out = out; + this.err = err; + this.in = in; + this.handlers = handlers; + this.initThread = Thread.currentThread(); + this.globals = new HashMap<>(globals); + this.langs = new HashMap<>(); + Enumeration<URL> 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; + } + for (int cnt = 1;; cnt++) { + String prefix = "language" + cnt + "."; + if (p.getProperty(prefix + "name") == null) { + break; + } + Language l = new Language(prefix, 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; + } + + /** + * 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.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; + private final List<EventConsumer<?>> handlers = new ArrayList<>(); + private final Map<String, Object> globals = new HashMap<>(); + private Executor executor; + + Builder() { + } + + /** + * Changes the default 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 default 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; + } + + /** + * Registers another instance of {@link EventConsumer} into the to be created + * {@link TruffleVM}. + * + * @param handler the handler to register + * @return instance of this builder + */ + public Builder onEvent(EventConsumer<?> handler) { + handler.getClass(); + handlers.add(handler); + return this; + } + + /** + * Adds global named symbol into the configuration of to-be-built {@link TruffleVM}. This + * symbol will be accessible to all languages via {@link Env#importSymbol(java.lang.String)} + * and will take precedence over {@link TruffleLanguage#findExportedSymbol symbols exported + * by languages itself}. Repeated use of <code>globalSymbol</code> is possible; later + * definition of the same name overrides the previous one. + * + * @param name name of the symbol to register + * @param obj value of the object - expected to be primitive wrapper, {@link String} or + * <code>TruffleObject</code> for mutual inter-operability + * @return instance of this builder + * @see TruffleVM#findGlobalSymbol(java.lang.String) + */ + public Builder globalSymbol(String name, Object obj) { + globals.put(name, obj); + return this; + } + + /** + * Provides own executor for running {@link TruffleVM} scripts. By default + * {@link TruffleVM#eval(com.oracle.truffle.api.source.Source)} and + * {@link Symbol#invoke(java.lang.Object, java.lang.Object...)} are executed synchronously + * in the calling thread. Sometimes, however it is more beneficial to run them + * asynchronously - the easiest way to do so is to provide own executor when configuring the + * { {@link #executor(java.util.concurrent.Executor) the builder}. The executor is expected + * to execute all {@link Runnable runnables} passed into its + * {@link Executor#execute(java.lang.Runnable)} method in the order they arrive and in a + * single (yet arbitrary) thread. + * + * @param executor the executor to use for internal execution inside the {@link #build() to + * be created} {@link TruffleVM} + * @return instance of this builder + */ + @SuppressWarnings("hiding") + public Builder executor(Executor executor) { + this.executor = executor; + 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); + } + Executor nonNullExecutor = executor != null ? executor : new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }; + return new TruffleVM(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0])); + } + } + + /** + * Descriptions of languages supported in this Truffle virtual machine. + * + * @return an immutable map with keys being MIME types and values the {@link Language + * descriptions} of associated languages + */ + public Map<String, Language> 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 <code>null</code> + * @throws IOException exception to signal I/O problems or problems with processing the file's + * content + * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} + */ + @Deprecated + 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); + if (file.getName().endsWith(".c")) { + mimeType = "text/x-c"; + } else if (file.getName().endsWith(".sl")) { + mimeType = "application/x-sl"; + } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { + mimeType = "application/x-r"; + } else { + mimeType = Files.probeContentType(file.toPath()); + } + } else { + URL url = location.toURL(); + s = Source.fromURL(url, location.toString()); + URLConnection conn = url.openConnection(); + mimeType = conn.getContentType(); + } + Language l = langs.get(mimeType); + if (l == null) { + throw new IOException("No language for " + location + " with MIME type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return eval(l, s).get(); + } + + /** + * 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 execution, possibly <code>null</code> + * @throws IOException thrown to signal errors while processing the code + * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} + */ + @Deprecated + public Object eval(String mimeType, Reader reader) throws IOException { + checkThread(); + Language l = langs.get(mimeType); + if (l == null) { + throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return eval(l, Source.fromReader(reader, mimeType)).get(); + } + + /** + * 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 execution, possibly <code>null</code> + * @throws IOException thrown to signal errors while processing the code + * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} + */ + @Deprecated + public Object eval(String mimeType, String code) throws IOException { + checkThread(); + Language l = langs.get(mimeType); + if (l == null) { + throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return eval(l, Source.fromText(code, mimeType)).get(); + } + + /** + * Evaluates provided source. Chooses language registered for a particular + * {@link Source#getMimeType() MIME type} (throws {@link IOException} if there is none). The + * language is then allowed to parse and execute the source. + * + * @param source code snippet to execute + * @return a {@link Symbol} object that holds result of an execution, never <code>null</code> + * @throws IOException thrown to signal errors while processing the code + */ + public Symbol eval(Source source) throws IOException { + String mimeType = source.getMimeType(); + checkThread(); + Language l = langs.get(mimeType); + if (l == null) { + throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); + } + return eval(l, source); + } + + private Symbol eval(final Language l, final Source s) throws IOException { + final Debugger[] fillIn = {debugger}; + final Object[] result = {null, null}; + final CountDownLatch ready = new CountDownLatch(1); + final TruffleLanguage[] lang = {null}; + executor.execute(new Runnable() { + @Override + public void run() { + TruffleLanguage<?> langImpl = l.getImpl(); + lang[0] = langImpl; + TruffleVM.findDebuggerSupport(langImpl); + evalImpl(fillIn, s, result, langImpl, ready); + } + }); + exceptionCheck(result); + return new Symbol(lang[0], result, ready); + } + + private void evalImpl(Debugger[] fillIn, Source s, Object[] result, TruffleLanguage<?> l, CountDownLatch ready) { + try (Closeable d = SPI.executionStart(this, fillIn, s)) { + if (debugger == null) { + debugger = fillIn[0]; + } + result[0] = SPI.eval(l, s); + } catch (IOException ex) { + result[1] = ex; + } finally { + ready.countDown(); + } + } + + /** + * 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. + * <p> + * The names of the symbols are language dependent, but for example the Java language bindings + * follow the specification for method references: + * <ul> + * <li>"java.lang.Exception::new" is a reference to constructor of {@link Exception} + * <li>"java.lang.Integer::valueOf" is a reference to static method in {@link Integer} class + * </ul> + * Once an symbol is obtained, it remembers values for fast access and is ready for being + * invoked. + * + * @param globalName the name of the symbol to find + * @return found symbol or <code>null</code> if it has not been found + */ + public Symbol findGlobalSymbol(final String globalName) { + checkThread(); + final TruffleLanguage<?>[] lang = {null}; + final Object[] obj = {globals.get(globalName), null}; + final CountDownLatch ready = new CountDownLatch(1); + if (obj[0] == null) { + executor.execute(new Runnable() { + @Override + public void run() { + findGlobalSymbolImpl(obj, globalName, lang, ready); + } + }); + try { + ready.await(); + } catch (InterruptedException ex) { + LOG.log(Level.SEVERE, null, ex); + } + } else { + ready.countDown(); + } + return obj[0] == null ? null : new Symbol(lang[0], obj, ready); + } + + private void findGlobalSymbolImpl(Object[] obj, String globalName, TruffleLanguage<?>[] lang, CountDownLatch ready) { + if (obj[0] == null) { + for (Language dl : langs.values()) { + TruffleLanguage<?> l = dl.getImpl(); + obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, true); + if (obj[0] != null) { + lang[0] = l; + break; + } + } + } + if (obj[0] == null) { + for (Language dl : langs.values()) { + TruffleLanguage<?> l = dl.getImpl(); + obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, false); + if (obj[0] != null) { + lang[0] = l; + break; + } + } + } + ready.countDown(); + } + + private void checkThread() { + if (initThread != Thread.currentThread()) { + throw new IllegalStateException("TruffleVM created on " + initThread.getName() + " but used on " + Thread.currentThread().getName()); + } + } + + @SuppressWarnings("unchecked") + void dispatch(Object ev) { + Class type = ev.getClass(); + if (type == SuspendedEvent.class) { + dispatchSuspendedEvent((SuspendedEvent) ev); + } + if (type == ExecutionEvent.class) { + dispatchExecutionEvent((ExecutionEvent) ev); + } + dispatch(type, ev); + } + + @SuppressWarnings("unused") + void dispatchSuspendedEvent(SuspendedEvent event) { + } + + @SuppressWarnings("unused") + void dispatchExecutionEvent(ExecutionEvent event) { + } + + @SuppressWarnings("unchecked") + <Event> void dispatch(Class<Event> type, Event event) { + for (EventConsumer handler : handlers) { + if (handler.type == type) { + handler.on(event); + } + } + } + + static void exceptionCheck(Object[] result) throws RuntimeException, IOException { + if (result[1] instanceof IOException) { + throw (IOException) result[1]; + } + if (result[1] instanceof RuntimeException) { + throw (RuntimeException) result[1]; + } + } + + /** + * 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 TruffleLanguage<?> language; + private final Object[] result; + private final CountDownLatch ready; + private CallTarget target; + + Symbol(TruffleLanguage<?> language, Object[] result, CountDownLatch ready) { + this.language = language; + this.result = result; + this.ready = ready; + } + + /** + * Obtains the object represented by this symbol. The <em>raw</em> object can either be a + * wrapper about primitive type (e.g. {@link Number}, {@link String}, {@link Character}, + * {@link Boolean}) or a <em>TruffleObject</em> representing more complex object from a + * language. The method can return <code>null</code>. + * + * @return the object or <code>null</code> + * @throws IOException in case it is not possible to obtain the value of the object + */ + public Object get() throws IOException { + waitForSymbol(); + exceptionCheck(result); + return result[0]; + } + + /** + * 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 <code>invoke</code> method returns. + * + * @param thiz this/self in language that support such concept; use <code>null</code> to let + * the language use default this/self or ignore the value + * @param args arguments to pass when invoking the symbol + * @return symbol wrapper around the value returned by invoking the symbol, never + * <code>null</code> + * @throws IOException signals problem during execution + */ + public Symbol invoke(final Object thiz, final Object... args) throws IOException { + get(); + final Debugger[] fillIn = {debugger}; + final CountDownLatch done = new CountDownLatch(1); + final Object[] res = {null, null}; + executor.execute(new Runnable() { + @Override + public void run() { + invokeImpl(fillIn, thiz, args, res, done); + } + }); + exceptionCheck(res); + return new Symbol(language, res, done); + } + + private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) { + try (final Closeable c = SPI.executionStart(TruffleVM.this, fillIn, null)) { + if (debugger == null) { + debugger = fillIn[0]; + } + List<Object> arr = new ArrayList<>(); + if (thiz == null && language != null) { + Object global = SPI.languageGlobal(SPI.findLanguage(TruffleVM.this, language.getClass())); + if (global != null) { + arr.add(global); + } + } else { + arr.add(thiz); + } + arr.addAll(Arrays.asList(args)); + for (;;) { + try { + if (target == null) { + target = SPI.createCallTarget(language, result[0], arr.toArray()); + } + res[0] = target.call(arr.toArray()); + break; + } catch (SymbolInvoker.ArgumentsMishmashException ex) { + target = null; + } + } + } catch (IOException ex) { + res[1] = ex; + } catch (RuntimeException ex) { + res[1] = ex; + } finally { + done.countDown(); + } + } + + private void waitForSymbol() throws InterruptedIOException { + checkThread(); + try { + ready.await(); + } catch (InterruptedException ex) { + throw (InterruptedIOException) new InterruptedIOException(ex.getMessage()).initCause(ex); + } + } + } + + /** + * 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 descriptors and creates {@link Language} objects to represent them. One + * can obtain a {@link #getName() name} or list of supported {@link #getMimeTypes() MIME types} + * 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; + private TruffleLanguage.Env env; + private final String prefix; + private String shortName; + + Language(String prefix, Properties props) { + this.prefix = prefix; + this.props = props; + } + + /** + * MIME types recognized by the language. + * + * @return returns immutable set of recognized MIME types + */ + public Set<String> getMimeTypes() { + TreeSet<String> ts = new TreeSet<>(); + for (int i = 0;; i++) { + String mt = props.getProperty(prefix + "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(prefix + "name"); + } + + /** + * Name of the language version. + * + * @return string specifying the language version + */ + public String getVersion() { + return props.getProperty(prefix + "version"); + } + + /** + * Human readable string that identifies the language and version. + * + * @return string describing the specific language version + */ + public String getShortName() { + if (shortName == null) { + shortName = getName() + "(" + getVersion() + ")"; + } + return shortName; + } + + TruffleLanguage<?> getImpl() { + if (impl == null) { + String n = props.getProperty(prefix + "className"); + try { + impl = LanguageCache.find(n, loader()); + env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); + } catch (Exception ex) { + throw new IllegalStateException("Cannot initialize " + getShortName() + " language with implementation " + n, ex); + } + } + return impl; + } + + @Override + public String toString() { + return "[" + getShortName() + " for " + getMimeTypes() + "]"; + } + + TruffleLanguage.Env getEnv() { + if (env == null) { + env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); + } + return env; + } + } // end of Language + + // + // Accessor helper methods + // + + TruffleLanguage<?> findLanguage(Probe probe) { + Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe); + for (Map.Entry<String, Language> entrySet : langs.entrySet()) { + Language languageDescription = entrySet.getValue(); + if (languageClazz.isInstance(languageDescription.impl)) { + return languageDescription.impl; + } + } + throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); + } + + Env findEnv(Class<? extends TruffleLanguage> languageClazz) { + for (Map.Entry<String, Language> entrySet : langs.entrySet()) { + Language languageDescription = entrySet.getValue(); + if (languageClazz.isInstance(languageDescription.impl)) { + return languageDescription.getEnv(); + } + } + throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); + } + + static DebugSupportProvider findDebuggerSupport(TruffleLanguage<?> l) { + return SPI.getDebugSupport(l); + } + + private static class SPIAccessor extends Accessor { + @Override + public Object importSymbol(Object vmObj, TruffleLanguage<?> ownLang, String globalName) { + TruffleVM vm = (TruffleVM) vmObj; + Object g = vm.globals.get(globalName); + if (g != null) { + return g; + } + Set<Language> uniqueLang = new LinkedHashSet<>(vm.langs.values()); + for (Language dl : uniqueLang) { + TruffleLanguage<?> l = dl.getImpl(); + if (l == ownLang) { + continue; + } + Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, true); + if (obj != null) { + return obj; + } + } + for (Language dl : uniqueLang) { + TruffleLanguage<?> l = dl.getImpl(); + if (l == ownLang) { + continue; + } + Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, false); + if (obj != null) { + return obj; + } + } + return null; + } + + @Override + public Env attachEnv(Object obj, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { + TruffleVM vm = (TruffleVM) obj; + return super.attachEnv(vm, language, stdOut, stdErr, stdIn); + } + + @Override + public Object eval(TruffleLanguage<?> l, Source s) throws IOException { + return super.eval(l, s); + } + + @Override + public Object findExportedSymbol(TruffleLanguage.Env env, String globalName, boolean onlyExplicit) { + return super.findExportedSymbol(env, globalName, onlyExplicit); + } + + @Override + protected Object languageGlobal(TruffleLanguage.Env env) { + return super.languageGlobal(env); + } + + @Override + protected CallTarget createCallTarget(TruffleLanguage<?> lang, Object obj, Object[] args) throws IOException { + return super.createCallTarget(lang, obj, args); + } + + @Override + public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { + return super.getToolSupport(l); + } + + @Override + public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { + return super.getDebugSupport(l); + } + + @Override + protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { + return super.findLanguage(probe); + } + + @Override + protected Env findLanguage(Object obj, Class<? extends TruffleLanguage> languageClass) { + TruffleVM vm = (TruffleVM) obj; + return vm.findEnv(languageClass); + } + + @Override + protected Closeable executionStart(Object obj, Debugger[] fillIn, Source s) { + TruffleVM vm = (TruffleVM) obj; + return super.executionStart(vm, fillIn, s); + } + + @Override + protected void dispatchEvent(Object obj, Object event) { + TruffleVM vm = (TruffleVM) obj; + vm.dispatch(event); + } + } // end of SPIAccessor +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/package-info.java Thu Sep 03 16:38:45 2015 +0200 @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* + @ApiInfo( + group="Stable" + ) + */ + +/** + * Central place to create and control {@link com.oracle.truffle.api.vm.TruffleVM Truffle Virtual + * Machine} and all languages hosted in it. + */ +package com.oracle.truffle.api.vm; +
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Thu Sep 03 16:38:45 2015 +0200 @@ -32,8 +32,6 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.*; -import com.oracle.truffle.api.vm.TruffleVM.Language; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; @@ -41,14 +39,16 @@ /** * An entry point for everyone who wants to implement a Truffle based language. By providing an * 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, multitenant hosting, debugging, etc.) will be made available to them. + * language becomes accessible to users of the {@link com.oracle.truffle.api.vm.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, multitenant hosting, debugging, etc.) will be made + * available to them. * * @param <C> internal state of the language associated with every thread that is executing program * {@link #parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...) * parsed} by the language */ +@SuppressWarnings("javadoc") public abstract class TruffleLanguage<C> { /** * Constructor to be called by subclasses. @@ -57,18 +57,19 @@ } /** - * 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 + * The annotation to use to register your language to the + * {@link com.oracle.truffle.api.vm.TruffleVM Truffle} system. By annotating your implementation + * of {@link TruffleLanguage} by this annotation you are just a * <em>one JAR drop to the class path</em> away from your users. Once they include your JAR in - * their application, your language will be available to the {@link TruffleVM Truffle virtual - * machine}. + * their application, your language will be available to the + * {@link com.oracle.truffle.api.vm.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. + * {@link com.oracle.truffle.api.vm.TruffleVM.Language#getName()} getter. * * @return identifier of your language */ @@ -76,7 +77,7 @@ /** * Unique string identifying the language version. This name will be exposed to users via - * the {@link Language#getVersion()} getter. + * the {@link com.oracle.truffle.api.vm.TruffleVM.Language#getVersion()} getter. * * @return version of your language */ @@ -84,8 +85,9 @@ /** * List of MIME types associated with your language. Users will use them (directly or - * indirectly) when {@link TruffleVM#eval(com.oracle.truffle.api.source.Source) executing} - * their code snippets or their {@link Source files}. + * indirectly) when + * {@link com.oracle.truffle.api.vm.TruffleVM#eval(com.oracle.truffle.api.source.Source) + * executing} their code snippets or their {@link Source files}. * * @return array of MIME types assigned to your language files */ @@ -94,11 +96,12 @@ /** * Creates internal representation of the executing context suitable for given environment. Each - * time the {@link TruffleLanguage language} is used by a new {@link TruffleVM} or in a new - * thread, the system calls this method to let the {@link TruffleLanguage language} prepare for - * <em>execution</em>. The returned execution context is completely language specific; it is - * however expected it will contain reference to here-in provided <code>env</code> and adjust - * itself according to parameters provided by the <code>env</code> object. + * time the {@link TruffleLanguage language} is used by a new + * {@link com.oracle.truffle.api.vm.TruffleVM} or in a new thread, the system calls this method + * to let the {@link TruffleLanguage language} prepare for <em>execution</em>. The returned + * execution context is completely language specific; it is however expected it will contain + * reference to here-in provided <code>env</code> and adjust itself according to parameters + * provided by the <code>env</code> object. * * @param env the environment the language is supposed to operate in * @return internal data of the language in given environment @@ -122,7 +125,7 @@ * just parsed <code>code</code> * @throws IOException thrown when I/O or parsing goes wrong. Here-in thrown exception is * propagate to the user who called one of <code>eval</code> methods of - * {@link TruffleVM} + * {@link com.oracle.truffle.api.vm.TruffleVM} */ protected abstract CallTarget parse(Source code, Node context, String... argumentNames) throws IOException; @@ -233,14 +236,14 @@ * them. */ public static final class Env { - private final TruffleVM vm; + private final Object vm; private final TruffleLanguage<?> lang; private final LangCtx<?> langCtx; private final Reader in; private final Writer err; private final Writer out; - Env(TruffleVM vm, TruffleLanguage<?> lang, Writer out, Writer err, Reader in) { + Env(Object vm, TruffleLanguage<?> lang, Writer out, Writer err, Reader in) { this.vm = vm; this.in = in; this.err = err; @@ -263,7 +266,8 @@ } /** - * Input associated with this {@link TruffleVM}. + * Input associated with {@link com.oracle.truffle.api.vm.TruffleVM} this language is being + * executed in. * * @return reader, never <code>null</code> */ @@ -272,7 +276,8 @@ } /** - * Standard output writer for this {@link TruffleVM}. + * Standard output writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is + * being executed in. * * @return writer, never <code>null</code> */ @@ -281,7 +286,8 @@ } /** - * Standard error writer for this {@link TruffleVM}. + * Standard error writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is + * being executed in. * * @return writer, never <code>null</code> */ @@ -295,13 +301,13 @@ @SuppressWarnings("rawtypes") private static final class AccessAPI extends Accessor { @Override - protected Env attachEnv(TruffleVM vm, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { + protected Env attachEnv(Object vm, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { Env env = new Env(vm, language, stdOut, stdErr, stdIn); return env; } @Override - public Object importSymbol(TruffleVM vm, TruffleLanguage<?> queryingLang, String globalName) { + protected Object importSymbol(Object vm, TruffleLanguage<?> queryingLang, String globalName) { return super.importSymbol(vm, queryingLang, globalName); } @@ -330,7 +336,7 @@ } @Override - protected Env findLanguage(TruffleVM vm, Class<? extends TruffleLanguage> languageClass) { + protected Env findLanguage(Object vm, Class<? extends TruffleLanguage> languageClass) { return super.findLanguage(vm, languageClass); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Thu Sep 03 16:38:45 2015 +0200 @@ -26,16 +26,16 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.TruffleVM; import java.io.IOException; /** - * Breakpoint in a {@link TruffleVM} with {@link com.oracle.truffle.api.debug debugging turned on}. - * You can ask + * Breakpoint in a {@link com.oracle.truffle.api.vm.TruffleVM} with + * {@link com.oracle.truffle.api.debug debugging turned on}. You can ask * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean)} or * {@link Debugger#setTagBreakpoint(int, com.oracle.truffle.api.instrument.SyntaxTag, boolean)} to * create an instance of {@link Breakpoint}. */ +@SuppressWarnings("javadoc") public abstract class Breakpoint { /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Thu Sep 03 16:38:45 2015 +0200 @@ -34,13 +34,14 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.TruffleVM; /** - * Represents debugging related state of a {@link TruffleVM}. Instance of this class is delivered - * via {@link SuspendedEvent#getDebugger()} and {@link ExecutionEvent#getDebugger()} events, once - * {@link com.oracle.truffle.api.debug debugging is turned on}. + * Represents debugging related state of a {@link com.oracle.truffle.api.vm.TruffleVM}. Instance of + * this class is delivered via {@link SuspendedEvent#getDebugger()} and + * {@link ExecutionEvent#getDebugger()} events, once {@link com.oracle.truffle.api.debug debugging + * is turned on}. */ +@SuppressWarnings("javadoc") public final class Debugger { private static final boolean TRACE = false; @@ -57,7 +58,7 @@ } } - private final TruffleVM vm; + private final Object vm; private Source lastSource; interface BreakpointCallback { @@ -91,7 +92,7 @@ */ private DebugExecutionContext debugContext; - Debugger(TruffleVM vm) { + Debugger(Object vm) { this.vm = vm; Source.setFileCaching(true); @@ -121,7 +122,7 @@ this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); } - TruffleVM vm() { + Object vm() { return vm; } @@ -794,7 +795,7 @@ @SuppressWarnings("rawtypes") private static final class AccessorDebug extends Accessor { @Override - protected Closeable executionStart(TruffleVM vm, Debugger[] fillIn, Source s) { + protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) { final Debugger d; if (fillIn[0] == null) { d = fillIn[0] = new Debugger(vm); @@ -816,7 +817,7 @@ } @Override - protected TruffleLanguage.Env findLanguage(TruffleVM vm, Class<? extends TruffleLanguage> languageClass) { + protected TruffleLanguage.Env findLanguage(Object vm, Class<? extends TruffleLanguage> languageClass) { return super.findLanguage(vm, languageClass); } @@ -831,7 +832,7 @@ } @Override - protected void dispatchEvent(TruffleVM vm, Object event) { + protected void dispatchEvent(Object vm, Object event) { super.dispatchEvent(vm, event); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java Thu Sep 03 16:38:45 2015 +0200 @@ -24,12 +24,9 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.vm.TruffleVM; -import com.oracle.truffle.api.vm.TruffleVM.Builder; - /** - * This event is delivered to all {@link Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) + * This event is delivered to all + * {@link com.oracle.truffle.api.vm.TruffleVM.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) * registered event handlers} when an execution is about to be started. The event is the intended * place to initialize debugger - e.g. set * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean) @@ -37,9 +34,10 @@ * occurrence}. Methods in this event can only be used while the handlers process the event. Then * the state of the event becomes invalid and subsequent calls to the event methods yield * {@link IllegalStateException}. One can however obtain reference to {@link Debugger} instance and - * keep it to further manipulate with debugging capabilities of the {@link TruffleVM} when it is - * running. + * keep it to further manipulate with debugging capabilities of the + * {@link com.oracle.truffle.api.vm.TruffleVM} when it is running. */ +@SuppressWarnings("javadoc") public final class ExecutionEvent { private final Debugger debugger; @@ -50,10 +48,10 @@ /** * Debugger associated with the execution. This debugger remains valid after the event is * processed, it is possible and suggested to keep a reference to it and use it any time later - * when evaluating sources in the {@link TruffleVM}. + * when evaluating sources in the {@link com.oracle.truffle.api.vm.TruffleVM}. * * @return instance of debugger associated with the just starting execution and any subsequent - * ones in the same {@link TruffleVM}. + * ones in the same {@link com.oracle.truffle.api.vm.TruffleVM}. */ public Debugger getDebugger() { return debugger; @@ -82,8 +80,9 @@ * <li>User breakpoints are disabled.</li> * <li>Execution will continue until either: * <ol> - * <li>execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT - * STATMENT}, <strong>or:</strong></li> + * <li>execution arrives at a node with the tag + * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATMENT}, + * <strong>or:</strong></li> * <li>execution completes.</li> * </ol> * <li>StepInto mode persists only through one resumption (i.e. {@code stepIntoCount} steps),
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java Thu Sep 03 16:38:45 2015 +0200 @@ -31,15 +31,14 @@ import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.vm.TruffleVM; -import com.oracle.truffle.api.vm.TruffleVM.Builder; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** - * This event is delivered to all {@link Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) + * This event is delivered to all + * {@link com.oracle.truffle.api.vm.TruffleVM.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) * registered event handlers} when an execution is suspended on a * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean) * breakpoint} or during {@link #prepareStepInto(int) stepping}. Methods in this event can only be @@ -47,6 +46,7 @@ * subsequent calls to the event methods yield {@link IllegalStateException}. One can call * {@link #getDebugger()} and keep reference to it for as long as necessary. */ +@SuppressWarnings("javadoc") public final class SuspendedEvent { private final List<String> recentWarnings; private final MaterializedFrame mFrame; @@ -83,10 +83,10 @@ /** * Debugger associated with the just suspended execution. This debugger remains valid after the * event is processed, it is possible and suggested to keep a reference to it and use it any - * time later when evaluating sources in the {@link TruffleVM}. + * time later when evaluating sources in the {@link com.oracle.truffle.api.vm.TruffleVM}. * * @return instance of debugger associated with the just suspended execution and any subsequent - * ones in the same {@link TruffleVM}. + * ones in the same {@link com.oracle.truffle.api.vm.TruffleVM}. */ public Debugger getDebugger() { return debugger; @@ -105,7 +105,8 @@ } /** - * Gets the stack frames from the currently halted {@link TruffleVM} execution. + * Gets the stack frames from the currently halted {@link com.oracle.truffle.api.vm.TruffleVM} + * execution. * * @return list of stack frames */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Thu Sep 03 16:29:30 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Thu Sep 03 16:38:45 2015 +0200 @@ -34,7 +34,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.*; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -48,7 +47,7 @@ private static Accessor NODES; private static Accessor INSTRUMENT; private static Accessor DEBUG; - private static final ThreadLocal<TruffleVM> CURRENT_VM = new ThreadLocal<>(); + private static final ThreadLocal<Object> CURRENT_VM = new ThreadLocal<>(); static { TruffleLanguage<?> lng = new TruffleLanguage<Object>() { @@ -127,7 +126,7 @@ } } - protected Env attachEnv(TruffleVM vm, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { + protected Env attachEnv(Object vm, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { return API.attachEnv(vm, language, stdOut, stdErr, stdIn); } @@ -135,7 +134,7 @@ return API.eval(l, s); } - protected Object importSymbol(TruffleVM vm, TruffleLanguage<?> queryingLang, String globalName) { + protected Object importSymbol(Object vm, TruffleLanguage<?> queryingLang, String globalName) { return SPI.importSymbol(vm, queryingLang, globalName); } @@ -179,8 +178,8 @@ return INSTRUMENT.findLanguage(probe); } - protected Env findLanguage(TruffleVM known, Class<? extends TruffleLanguage> languageClass) { - TruffleVM vm; + protected Env findLanguage(Object known, Class<? extends TruffleLanguage> languageClass) { + Object vm; if (known == null) { vm = CURRENT_VM.get(); if (vm == null) { @@ -192,12 +191,12 @@ return SPI.findLanguage(vm, languageClass); } - private static Reference<TruffleVM> previousVM = new WeakReference<>(null); + private static Reference<Object> previousVM = new WeakReference<>(null); private static Assumption oneVM = Truffle.getRuntime().createAssumption(); - protected Closeable executionStart(TruffleVM vm, Debugger[] fillIn, Source s) { + protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) { final Closeable debugClose = DEBUG.executionStart(vm, fillIn, s); - final TruffleVM prev = CURRENT_VM.get(); + final Object prev = CURRENT_VM.get(); if (!(vm == previousVM.get())) { previousVM = new WeakReference<>(vm); oneVM.invalidate(); @@ -215,7 +214,7 @@ return new ContextCloseable(); } - protected void dispatchEvent(TruffleVM vm, Object event) { + protected void dispatchEvent(Object vm, Object event) { SPI.dispatchEvent(vm, event); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/EventConsumer.java Thu Sep 03 16:29:30 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2014, 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.api.vm; - -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; - -/** - * {@link TruffleVM} generates various events and delivers them to - * {@link TruffleVM.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) registered} handlers. - * Each handler is registered for a particular type of event. Examples of events include - * {@link ExecutionEvent} or {@link SuspendedEvent} useful when debugging {@link TruffleLanguage - * Truffle language}s. - * - * @param <Event> type of event to observe and handle - */ -public abstract class EventConsumer<Event> { - final Class<Event> type; - - /** - * Creates new handler for specified event type. - * - * @param eventType type of events to handle - */ - public EventConsumer(Class<Event> eventType) { - this.type = eventType; - } - - /** - * Called by the {@link TruffleVM} when event of requested type appears. - * - * @param event the instance of an event of the request type - */ - protected abstract void on(Event event); -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/LanguageCache.java Thu Sep 03 16:29:30 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014, 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.api.vm; - -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.vm.TruffleVM.Language; -import java.util.HashMap; -import java.util.Map; - -/** - * Ahead-of-time initialization. If the JVM is started with -Dcom.oracle.truffle.aot=true, it - * populates cache with languages found in application classloader. - */ -final class LanguageCache { - private static final boolean PRELOAD; - private static final Map<String, TruffleLanguage<?>> CACHE; - static { - Map<String, TruffleLanguage<?>> map = null; - if (Boolean.getBoolean("com.oracle.truffle.aot")) { // NOI18N - map = new HashMap<>(); - for (Language description : TruffleVM.newVM().build().getLanguages().values()) { - TruffleLanguage<?> language = description.getImpl(); - map.put(language.getClass().getName(), language); - } - } - CACHE = map; - PRELOAD = CACHE != null; - } - - static TruffleLanguage<?> find(String name, ClassLoader loader) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { - if (PRELOAD) { - return CACHE.get(name); - } else { - Class<?> langClazz = Class.forName(name, true, loader); - return (TruffleLanguage<?>) langClazz.getField("INSTANCE").get(null); - } - } -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Thu Sep 03 16:29:30 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,878 +0,0 @@ -/* - * Copyright (c) 2014, 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.api.vm; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLanguage.Registration; -import com.oracle.truffle.api.debug.*; -import com.oracle.truffle.api.impl.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.source.*; -import java.io.*; -import java.net.*; -import java.nio.file.*; -import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.logging.*; - -/** - * <em>Virtual machine</em> for Truffle based languages. Term virtual machine is a bit overloaded, - * so don't think of <em>Java virtual machine</em> here - while we are running and using - * {@link TruffleVM} inside of a <em>JVM</em> there can be multiple instances (some would say - * tenants) of {@link TruffleVM} running next to each other in a single <em>JVM</em> with a complete - * mutual isolation. There is 1:N mapping between <em>JVM</em> and {@link TruffleVM}. - * <p> - * It would not be correct to think of a {@link TruffleVM} as a runtime for a single Truffle - * language (Ruby, Python, R, C, JavaScript, etc.) either. {@link TruffleVM} can host as many of - * Truffle languages as {@link Registration registered on a class path} of your <em>JVM</em> - * application. {@link TruffleVM} orchestrates these languages, manages exchange of objects and - * calls among them. While it may happen that there is just one activated language inside of a - * {@link TruffleVM}, the greatest strength of {@link TruffleVM} is in interoperability between all - * Truffle languages. There is 1:N mapping between {@link TruffleVM} and {@link TruffleLanguage - * Truffle language implementations}. - * <p> - * 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 cooperate. 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)}. - * 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. - * <p> - * The <code>TruffleVM</code> is single-threaded and tries to enforce that. It records the thread it - * has been {@link Builder#build() created} by and checks that all subsequent calls are coming from - * the same thread. There is 1:1 mapping between {@link TruffleVM} and a thread that can tell it - * what to do. - */ -@SuppressWarnings("rawtypes") -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 Executor executor; - private final Map<String, Language> langs; - private final Reader in; - private final Writer err; - private final Writer out; - private final EventConsumer<?>[] handlers; - private final Map<String, Object> globals; - private Debugger debugger; - - /** - * Private & temporary only constructor. - */ - private TruffleVM() { - this.initThread = null; - this.in = null; - this.err = null; - this.out = null; - this.langs = null; - this.handlers = null; - this.globals = null; - this.executor = null; - } - - /** - * Real constructor used from the builder. - */ - private TruffleVM(Executor executor, Map<String, Object> globals, Writer out, Writer err, Reader in, EventConsumer<?>[] handlers) { - this.executor = executor; - this.out = out; - this.err = err; - this.in = in; - this.handlers = handlers; - this.initThread = Thread.currentThread(); - this.globals = new HashMap<>(globals); - this.langs = new HashMap<>(); - Enumeration<URL> 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; - } - for (int cnt = 1;; cnt++) { - String prefix = "language" + cnt + "."; - if (p.getProperty(prefix + "name") == null) { - break; - } - Language l = new Language(prefix, 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; - } - - /** - * 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.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; - private final List<EventConsumer<?>> handlers = new ArrayList<>(); - private final Map<String, Object> globals = new HashMap<>(); - private Executor executor; - - Builder() { - } - - /** - * Changes the default 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 default 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; - } - - /** - * Registers another instance of {@link EventConsumer} into the to be created - * {@link TruffleVM}. - * - * @param handler the handler to register - * @return instance of this builder - */ - public Builder onEvent(EventConsumer<?> handler) { - handler.getClass(); - handlers.add(handler); - return this; - } - - /** - * Adds global named symbol into the configuration of to-be-built {@link TruffleVM}. This - * symbol will be accessible to all languages via {@link Env#importSymbol(java.lang.String)} - * and will take precedence over {@link TruffleLanguage#findExportedSymbol symbols exported - * by languages itself}. Repeated use of <code>globalSymbol</code> is possible; later - * definition of the same name overrides the previous one. - * - * @param name name of the symbol to register - * @param obj value of the object - expected to be primitive wrapper, {@link String} or - * <code>TruffleObject</code> for mutual inter-operability - * @return instance of this builder - * @see TruffleVM#findGlobalSymbol(java.lang.String) - */ - public Builder globalSymbol(String name, Object obj) { - globals.put(name, obj); - return this; - } - - /** - * Provides own executor for running {@link TruffleVM} scripts. By default - * {@link TruffleVM#eval(com.oracle.truffle.api.source.Source)} and - * {@link Symbol#invoke(java.lang.Object, java.lang.Object...)} are executed synchronously - * in the calling thread. Sometimes, however it is more beneficial to run them - * asynchronously - the easiest way to do so is to provide own executor when configuring the - * { {@link #executor(java.util.concurrent.Executor) the builder}. The executor is expected - * to execute all {@link Runnable runnables} passed into its - * {@link Executor#execute(java.lang.Runnable)} method in the order they arrive and in a - * single (yet arbitrary) thread. - * - * @param executor the executor to use for internal execution inside the {@link #build() to - * be created} {@link TruffleVM} - * @return instance of this builder - */ - @SuppressWarnings("hiding") - public Builder executor(Executor executor) { - this.executor = executor; - 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); - } - Executor nonNullExecutor = executor != null ? executor : new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }; - return new TruffleVM(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0])); - } - } - - /** - * Descriptions of languages supported in this Truffle virtual machine. - * - * @return an immutable map with keys being MIME types and values the {@link Language - * descriptions} of associated languages - */ - public Map<String, Language> 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 <code>null</code> - * @throws IOException exception to signal I/O problems or problems with processing the file's - * content - * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} - */ - @Deprecated - 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); - if (file.getName().endsWith(".c")) { - mimeType = "text/x-c"; - } else if (file.getName().endsWith(".sl")) { - mimeType = "application/x-sl"; - } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { - mimeType = "application/x-r"; - } else { - mimeType = Files.probeContentType(file.toPath()); - } - } else { - URL url = location.toURL(); - s = Source.fromURL(url, location.toString()); - URLConnection conn = url.openConnection(); - mimeType = conn.getContentType(); - } - Language l = langs.get(mimeType); - if (l == null) { - throw new IOException("No language for " + location + " with MIME type " + mimeType + " found. Supported types: " + langs.keySet()); - } - return eval(l, s).get(); - } - - /** - * 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 execution, possibly <code>null</code> - * @throws IOException thrown to signal errors while processing the code - * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} - */ - @Deprecated - public Object eval(String mimeType, Reader reader) throws IOException { - checkThread(); - Language l = langs.get(mimeType); - if (l == null) { - throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); - } - return eval(l, Source.fromReader(reader, mimeType)).get(); - } - - /** - * 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 execution, possibly <code>null</code> - * @throws IOException thrown to signal errors while processing the code - * @deprecated use {@link #eval(com.oracle.truffle.api.source.Source)} - */ - @Deprecated - public Object eval(String mimeType, String code) throws IOException { - checkThread(); - Language l = langs.get(mimeType); - if (l == null) { - throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); - } - return eval(l, Source.fromText(code, mimeType)).get(); - } - - /** - * Evaluates provided source. Chooses language registered for a particular - * {@link Source#getMimeType() MIME type} (throws {@link IOException} if there is none). The - * language is then allowed to parse and execute the source. - * - * @param source code snippet to execute - * @return a {@link Symbol} object that holds result of an execution, never <code>null</code> - * @throws IOException thrown to signal errors while processing the code - */ - public Symbol eval(Source source) throws IOException { - String mimeType = source.getMimeType(); - checkThread(); - Language l = langs.get(mimeType); - if (l == null) { - throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); - } - return eval(l, source); - } - - private Symbol eval(final Language l, final Source s) throws IOException { - final Debugger[] fillIn = {debugger}; - final Object[] result = {null, null}; - final CountDownLatch ready = new CountDownLatch(1); - final TruffleLanguage[] lang = {null}; - executor.execute(new Runnable() { - @Override - public void run() { - TruffleLanguage<?> langImpl = l.getImpl(); - lang[0] = langImpl; - TruffleVM.findDebuggerSupport(langImpl); - evalImpl(fillIn, s, result, langImpl, ready); - } - }); - exceptionCheck(result); - return new Symbol(lang[0], result, ready); - } - - private void evalImpl(Debugger[] fillIn, Source s, Object[] result, TruffleLanguage<?> l, CountDownLatch ready) { - try (Closeable d = SPI.executionStart(this, fillIn, s)) { - if (debugger == null) { - debugger = fillIn[0]; - } - result[0] = SPI.eval(l, s); - } catch (IOException ex) { - result[1] = ex; - } finally { - ready.countDown(); - } - } - - /** - * 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. - * <p> - * The names of the symbols are language dependent, but for example the Java language bindings - * follow the specification for method references: - * <ul> - * <li>"java.lang.Exception::new" is a reference to constructor of {@link Exception} - * <li>"java.lang.Integer::valueOf" is a reference to static method in {@link Integer} class - * </ul> - * Once an symbol is obtained, it remembers values for fast access and is ready for being - * invoked. - * - * @param globalName the name of the symbol to find - * @return found symbol or <code>null</code> if it has not been found - */ - public Symbol findGlobalSymbol(final String globalName) { - checkThread(); - final TruffleLanguage<?>[] lang = {null}; - final Object[] obj = {globals.get(globalName), null}; - final CountDownLatch ready = new CountDownLatch(1); - if (obj[0] == null) { - executor.execute(new Runnable() { - @Override - public void run() { - findGlobalSymbolImpl(obj, globalName, lang, ready); - } - }); - try { - ready.await(); - } catch (InterruptedException ex) { - LOG.log(Level.SEVERE, null, ex); - } - } else { - ready.countDown(); - } - return obj[0] == null ? null : new Symbol(lang[0], obj, ready); - } - - private void findGlobalSymbolImpl(Object[] obj, String globalName, TruffleLanguage<?>[] lang, CountDownLatch ready) { - if (obj[0] == null) { - for (Language dl : langs.values()) { - TruffleLanguage<?> l = dl.getImpl(); - obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, true); - if (obj[0] != null) { - lang[0] = l; - break; - } - } - } - if (obj[0] == null) { - for (Language dl : langs.values()) { - TruffleLanguage<?> l = dl.getImpl(); - obj[0] = SPI.findExportedSymbol(dl.getEnv(), globalName, false); - if (obj[0] != null) { - lang[0] = l; - break; - } - } - } - ready.countDown(); - } - - private void checkThread() { - if (initThread != Thread.currentThread()) { - throw new IllegalStateException("TruffleVM created on " + initThread.getName() + " but used on " + Thread.currentThread().getName()); - } - } - - @SuppressWarnings("unchecked") - void dispatch(Object ev) { - Class type = ev.getClass(); - if (type == SuspendedEvent.class) { - dispatchSuspendedEvent((SuspendedEvent) ev); - } - if (type == ExecutionEvent.class) { - dispatchExecutionEvent((ExecutionEvent) ev); - } - dispatch(type, ev); - } - - @SuppressWarnings("unused") - void dispatchSuspendedEvent(SuspendedEvent event) { - } - - @SuppressWarnings("unused") - void dispatchExecutionEvent(ExecutionEvent event) { - } - - @SuppressWarnings("unchecked") - <Event> void dispatch(Class<Event> type, Event event) { - for (EventConsumer handler : handlers) { - if (handler.type == type) { - handler.on(event); - } - } - } - - static void exceptionCheck(Object[] result) throws RuntimeException, IOException { - if (result[1] instanceof IOException) { - throw (IOException) result[1]; - } - if (result[1] instanceof RuntimeException) { - throw (RuntimeException) result[1]; - } - } - - /** - * 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 TruffleLanguage<?> language; - private final Object[] result; - private final CountDownLatch ready; - private CallTarget target; - - Symbol(TruffleLanguage<?> language, Object[] result, CountDownLatch ready) { - this.language = language; - this.result = result; - this.ready = ready; - } - - /** - * Obtains the object represented by this symbol. The <em>raw</em> object can either be a - * wrapper about primitive type (e.g. {@link Number}, {@link String}, {@link Character}, - * {@link Boolean}) or a <em>TruffleObject</em> representing more complex object from a - * language. The method can return <code>null</code>. - * - * @return the object or <code>null</code> - * @throws IOException in case it is not possible to obtain the value of the object - */ - public Object get() throws IOException { - waitForSymbol(); - exceptionCheck(result); - return result[0]; - } - - /** - * 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 <code>invoke</code> method returns. - * - * @param thiz this/self in language that support such concept; use <code>null</code> to let - * the language use default this/self or ignore the value - * @param args arguments to pass when invoking the symbol - * @return symbol wrapper around the value returned by invoking the symbol, never - * <code>null</code> - * @throws IOException signals problem during execution - */ - public Symbol invoke(final Object thiz, final Object... args) throws IOException { - get(); - final Debugger[] fillIn = {debugger}; - final CountDownLatch done = new CountDownLatch(1); - final Object[] res = {null, null}; - executor.execute(new Runnable() { - @Override - public void run() { - invokeImpl(fillIn, thiz, args, res, done); - } - }); - exceptionCheck(res); - return new Symbol(language, res, done); - } - - private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) { - try (final Closeable c = SPI.executionStart(TruffleVM.this, fillIn, null)) { - if (debugger == null) { - debugger = fillIn[0]; - } - List<Object> arr = new ArrayList<>(); - if (thiz == null && language != null) { - Object global = SPI.languageGlobal(SPI.findLanguage(TruffleVM.this, language.getClass())); - if (global != null) { - arr.add(global); - } - } else { - arr.add(thiz); - } - arr.addAll(Arrays.asList(args)); - for (;;) { - try { - if (target == null) { - target = SPI.createCallTarget(language, result[0], arr.toArray()); - } - res[0] = target.call(arr.toArray()); - break; - } catch (SymbolInvoker.ArgumentsMishmashException ex) { - target = null; - } - } - } catch (IOException ex) { - res[1] = ex; - } catch (RuntimeException ex) { - res[1] = ex; - } finally { - done.countDown(); - } - } - - private void waitForSymbol() throws InterruptedIOException { - checkThread(); - try { - ready.await(); - } catch (InterruptedException ex) { - throw (InterruptedIOException) new InterruptedIOException(ex.getMessage()).initCause(ex); - } - } - } - - /** - * 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 descriptors and creates {@link Language} objects to represent them. One - * can obtain a {@link #getName() name} or list of supported {@link #getMimeTypes() MIME types} - * 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; - private TruffleLanguage.Env env; - private final String prefix; - private String shortName; - - Language(String prefix, Properties props) { - this.prefix = prefix; - this.props = props; - } - - /** - * MIME types recognized by the language. - * - * @return returns immutable set of recognized MIME types - */ - public Set<String> getMimeTypes() { - TreeSet<String> ts = new TreeSet<>(); - for (int i = 0;; i++) { - String mt = props.getProperty(prefix + "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(prefix + "name"); - } - - /** - * Name of the language version. - * - * @return string specifying the language version - */ - public String getVersion() { - return props.getProperty(prefix + "version"); - } - - /** - * Human readable string that identifies the language and version. - * - * @return string describing the specific language version - */ - public String getShortName() { - if (shortName == null) { - shortName = getName() + "(" + getVersion() + ")"; - } - return shortName; - } - - TruffleLanguage<?> getImpl() { - if (impl == null) { - String n = props.getProperty(prefix + "className"); - try { - impl = LanguageCache.find(n, loader()); - env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); - } catch (Exception ex) { - throw new IllegalStateException("Cannot initialize " + getShortName() + " language with implementation " + n, ex); - } - } - return impl; - } - - @Override - public String toString() { - return "[" + getShortName() + " for " + getMimeTypes() + "]"; - } - - TruffleLanguage.Env getEnv() { - if (env == null) { - env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); - } - return env; - } - } // end of Language - - // - // Accessor helper methods - // - - TruffleLanguage<?> findLanguage(Probe probe) { - Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe); - for (Map.Entry<String, Language> entrySet : langs.entrySet()) { - Language languageDescription = entrySet.getValue(); - if (languageClazz.isInstance(languageDescription.impl)) { - return languageDescription.impl; - } - } - throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); - } - - Env findEnv(Class<? extends TruffleLanguage> languageClazz) { - for (Map.Entry<String, Language> entrySet : langs.entrySet()) { - Language languageDescription = entrySet.getValue(); - if (languageClazz.isInstance(languageDescription.impl)) { - return languageDescription.getEnv(); - } - } - throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); - } - - static DebugSupportProvider findDebuggerSupport(TruffleLanguage<?> l) { - return SPI.getDebugSupport(l); - } - - private static class SPIAccessor extends Accessor { - @Override - public Object importSymbol(TruffleVM vm, TruffleLanguage<?> ownLang, String globalName) { - Object g = vm.globals.get(globalName); - if (g != null) { - return g; - } - Set<Language> uniqueLang = new LinkedHashSet<>(vm.langs.values()); - for (Language dl : uniqueLang) { - TruffleLanguage<?> l = dl.getImpl(); - if (l == ownLang) { - continue; - } - Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, true); - if (obj != null) { - return obj; - } - } - for (Language dl : uniqueLang) { - TruffleLanguage<?> l = dl.getImpl(); - if (l == ownLang) { - continue; - } - Object obj = SPI.findExportedSymbol(dl.getEnv(), globalName, false); - if (obj != null) { - return obj; - } - } - return null; - } - - @Override - public Env attachEnv(TruffleVM vm, TruffleLanguage<?> language, Writer stdOut, Writer stdErr, Reader stdIn) { - return super.attachEnv(vm, language, stdOut, stdErr, stdIn); - } - - @Override - public Object eval(TruffleLanguage<?> l, Source s) throws IOException { - return super.eval(l, s); - } - - @Override - public Object findExportedSymbol(TruffleLanguage.Env env, String globalName, boolean onlyExplicit) { - return super.findExportedSymbol(env, globalName, onlyExplicit); - } - - @Override - protected Object languageGlobal(TruffleLanguage.Env env) { - return super.languageGlobal(env); - } - - @Override - protected CallTarget createCallTarget(TruffleLanguage<?> lang, Object obj, Object[] args) throws IOException { - return super.createCallTarget(lang, obj, args); - } - - @Override - public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return super.getToolSupport(l); - } - - @Override - public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return super.getDebugSupport(l); - } - - @Override - protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { - return super.findLanguage(probe); - } - - @Override - protected Env findLanguage(TruffleVM vm, Class<? extends TruffleLanguage> languageClass) { - return vm.findEnv(languageClass); - } - - @Override - protected Closeable executionStart(TruffleVM aThis, Debugger[] fillIn, Source s) { - return super.executionStart(aThis, fillIn, s); - } - - @Override - protected void dispatchEvent(TruffleVM vm, Object event) { - vm.dispatch(event); - } - } // end of SPIAccessor -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package-info.java Thu Sep 03 16:29:30 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +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. - */ - -/* - @ApiInfo( - group="Stable" - ) - */ - -/** - * Central place to create and control {@link com.oracle.truffle.api.vm.TruffleVM Truffle Virtual - * Machine} and all languages hosted in it. - */ -package com.oracle.truffle.api.vm; -