# HG changeset patch # User Jaroslav Tulach # Date 1433398085 -7200 # Node ID 2f9e4d984d16c216d717d16cfb476d1757112715 # Parent 67e28e817d325c9c3d094646bd1d692d5d29398c Give languages a chance to do implicit exports. Prefer explicit exports over implicit ones. diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Thu Jun 04 08:08:05 2015 +0200 @@ -58,7 +58,7 @@ } @Override - protected Object findExportedSymbol(String globalName) { + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { return null; } @@ -86,7 +86,7 @@ } @Override - protected Object findExportedSymbol(String globalName) { + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { return null; } @@ -113,7 +113,7 @@ } @Override - protected Object findExportedSymbol(String globalName) { + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { return null; } diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Thu Jun 04 08:08:05 2015 +0200 @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.vm; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; +import java.io.IOException; +import java.io.Reader; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +public class ImplicitExplicitExportTest { + private TruffleVM vm; + + @Before + public void initializeVM() { + vm = TruffleVM.newVM().build(); + assertTrue("Found " + L1 + " language", vm.getLanguages().containsKey(L1)); + assertTrue("Found " + L2 + " language", vm.getLanguages().containsKey(L2)); + assertTrue("Found " + L3 + " language", vm.getLanguages().containsKey(L3)); + } + + @Test + public void explicitExportFound() throws IOException { + // @formatter:off + vm.eval(L1, + "explicit.ahoj=42" + ); + Object ret = vm.eval(L3, + "return=ahoj" + ); + // @formatter:on + assertEquals("42", ret); + } + + @Test + public void implicitExportFound() throws IOException { + // @formatter:off + vm.eval(L1, + "implicit.ahoj=42" + ); + Object ret = vm.eval(L3, + "return=ahoj" + ); + // @formatter:on + assertEquals("42", ret); + } + + @Test + public void explicitExportPreferred2() throws IOException { + // @formatter:off + vm.eval(L1, + "implicit.ahoj=42" + ); + vm.eval(L2, + "explicit.ahoj=43" + ); + Object ret = vm.eval(L3, + "return=ahoj" + ); + // @formatter:on + assertEquals("Explicit import from L2 is used", "43", ret); + assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null)); + } + + @Test + public void explicitExportPreferred1() throws IOException { + // @formatter:off + vm.eval(L1, + "explicit.ahoj=43" + ); + vm.eval(L2, + "implicit.ahoj=42" + ); + Object ret = vm.eval(L3, + "return=ahoj" + ); + // @formatter:on + assertEquals("Explicit import from L2 is used", "43", ret); + assertEquals("Global symbol is also 43", "43", vm.findGlobalSymbol("ahoj").invoke(null)); + } + + private abstract static class AbstractExportImportLanguage extends TruffleLanguage { + protected AbstractExportImportLanguage(Env env) { + super(env); + } + + private final Map explicit = new HashMap<>(); + private final Map implicit = new HashMap<>(); + + @Override + protected Object eval(Source code) throws IOException { + Properties p = new Properties(); + try (Reader r = code.getReader()) { + p.load(r); + } + Enumeration en = p.keys(); + while (en.hasMoreElements()) { + Object n = en.nextElement(); + if (n instanceof String) { + String k = (String) n; + if (k.startsWith("explicit.")) { + explicit.put(k.substring(9), p.getProperty(k)); + } + if (k.startsWith("implicit.")) { + implicit.put(k.substring(9), p.getProperty(k)); + } + if (k.equals("return")) { + return env().importSymbol(p.getProperty(k)); + } + } + } + return null; + } + + @Override + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { + if (explicit.containsKey(globalName)) { + return explicit.get(globalName); + } + if (!onlyExplicit && implicit.containsKey(globalName)) { + return implicit.get(globalName); + } + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + } + + private static final String L1 = "application/x-test-import-export-1"; + private static final String L2 = "application/x-test-import-export-2"; + private static final String L3 = "application/x-test-import-export-3"; + + @TruffleLanguage.Registration(mimeType = L1, name = "ImportExport1") + public static final class ExportImportLanguage1 extends AbstractExportImportLanguage { + public ExportImportLanguage1(Env env) { + super(env); + } + } + + @TruffleLanguage.Registration(mimeType = L2, name = "ImportExport2") + public static final class ExportImportLanguage2 extends AbstractExportImportLanguage { + public ExportImportLanguage2(Env env) { + super(env); + } + } + + @TruffleLanguage.Registration(mimeType = L3, name = "ImportExport3") + public static final class ExportImportLanguage3 extends AbstractExportImportLanguage { + public ExportImportLanguage3(Env env) { + super(env); + } + } + +} diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Thu Jun 04 08:08:05 2015 +0200 @@ -96,14 +96,25 @@ * somebody asks for it (by calling this method). *

* The exported object can either be TruffleObject (e.g. a native object from the - * other language) to support interoperability between languages or one of Java primitive - * wrappers ( {@link Integer}, {@link Double}, {@link Short}, etc.). + * other language) to support inter-operability between languages, {@link String} or one of Java + * primitive wrappers ( {@link Integer}, {@link Double}, {@link Short}, {@link Boolean}, etc.). + *

+ * The way a symbol becomes exported is language dependant. In general it is preferred + * to make the export explicit - e.g. call some function or method to register an object under + * specific name. Some languages may however decide to support implicit export of symbols (for + * example from global scope, if they have one). However explicit exports should always be + * preferred. Implicitly exported object of some name should only be used when there is no + * explicit export under such globalName. To ensure so the infrastructure first + * asks all known languages for onlyExplicit symbols and only when none is found, + * it does one more round with onlyExplicit set to false. * * @param globalName the name of the global symbol to find + * @param onlyExplicit should the language seek for implicitly exported object or only consider + * the explicitly exported ones? * @return an exported object or null, if the symbol does not represent anything * meaningful in this language */ - protected abstract Object findExportedSymbol(String globalName); + protected abstract Object findExportedSymbol(String globalName, boolean onlyExplicit); /** * Returns global object for the language. @@ -210,8 +221,8 @@ } @Override - protected Object findExportedSymbol(TruffleLanguage l, String globalName) { - return l.findExportedSymbol(globalName); + protected Object findExportedSymbol(TruffleLanguage l, String globalName, boolean onlyExplicit) { + return l.findExportedSymbol(globalName, onlyExplicit); } @Override diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Thu Jun 04 08:08:05 2015 +0200 @@ -46,7 +46,7 @@ } @Override - protected Object findExportedSymbol(String globalName) { + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { return null; } @@ -89,8 +89,8 @@ return SPI.importSymbol(vm, queryingLang, globalName); } - protected Object findExportedSymbol(TruffleLanguage l, String globalName) { - return API.findExportedSymbol(l, globalName); + protected Object findExportedSymbol(TruffleLanguage l, String globalName, boolean onlyExplicit) { + return API.findExportedSymbol(l, globalName, onlyExplicit); } protected Object languageGlobal(TruffleLanguage l) { diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Thu Jun 04 08:08:05 2015 +0200 @@ -104,9 +104,15 @@ LOG.log(Level.CONFIG, "Cannot process " + u + " as language definition", ex); continue; } - Language l = new Language(p); - for (String mimeType : l.getMimeTypes()) { - langs.put(mimeType, l); + 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); + } } } } @@ -327,12 +333,22 @@ Object global = null; for (Language dl : langs.values()) { TruffleLanguage l = dl.getImpl(); - obj = SPI.findExportedSymbol(l, globalName); + obj = SPI.findExportedSymbol(l, globalName, true); if (obj != null) { global = SPI.languageGlobal(l); break; } } + if (obj == null) { + for (Language dl : langs.values()) { + TruffleLanguage l = dl.getImpl(); + obj = SPI.findExportedSymbol(l, globalName, false); + if (obj != null) { + global = SPI.languageGlobal(l); + break; + } + } + } return obj == null ? null : new Symbol(obj, global); } @@ -399,8 +415,10 @@ public final class Language { private final Properties props; private TruffleLanguage impl; + private final String prefix; - Language(Properties props) { + Language(String prefix, Properties props) { + this.prefix = prefix; this.props = props; } @@ -412,7 +430,7 @@ public Set getMimeTypes() { TreeSet ts = new TreeSet<>(); for (int i = 0;; i++) { - String mt = props.getProperty("mimeType." + i); + String mt = props.getProperty(prefix + "mimeType." + i); if (mt == null) { break; } @@ -427,12 +445,12 @@ * @return string giving the language a name */ public String getName() { - return props.getProperty("name"); + return props.getProperty(prefix + "name"); } TruffleLanguage getImpl() { if (impl == null) { - String n = props.getProperty("className"); + String n = props.getProperty(prefix + "className"); try { Class langClazz = Class.forName(n, true, loader()); Constructor constructor = langClazz.getConstructor(Env.class); @@ -459,7 +477,17 @@ if (l == ownLang) { continue; } - Object obj = SPI.findExportedSymbol(l, globalName); + Object obj = SPI.findExportedSymbol(l, globalName, true); + if (obj != null) { + return obj; + } + } + for (Language dl : uniqueLang) { + TruffleLanguage l = dl.getImpl(); + if (l == ownLang) { + continue; + } + Object obj = SPI.findExportedSymbol(l, globalName, false); if (obj != null) { return obj; } @@ -478,8 +506,8 @@ } @Override - public Object findExportedSymbol(TruffleLanguage l, String globalName) { - return super.findExportedSymbol(l, globalName); + public Object findExportedSymbol(TruffleLanguage l, String globalName, boolean onlyExplicit) { + return super.findExportedSymbol(l, globalName, onlyExplicit); } @Override diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java Thu Jun 04 08:08:05 2015 +0200 @@ -37,33 +37,50 @@ @SupportedAnnotationTypes("com.oracle.truffle.api.*") public final class LanguageRegistrationProcessor extends AbstractProcessor { + private final List registrations = new ArrayList<>(); + @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } - private void createProviderFile(TypeElement language, Registration annotation) { + private void generateFile(List languages) { String filename = "META-INF/truffle/language"; - try { - FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, language); - Properties p = new Properties(); - String className = processingEnv.getElementUtils().getBinaryName(language).toString(); - p.setProperty("name", annotation.name()); - p.setProperty("className", className); + Properties p = new Properties(); + int cnt = 0; + for (TypeElement l : languages) { + Registration annotation = l.getAnnotation(Registration.class); + if (annotation == null) { + continue; + } + String prefix = "language" + ++cnt + "."; + String className = processingEnv.getElementUtils().getBinaryName(l).toString(); + p.setProperty(prefix + "name", annotation.name()); + p.setProperty(prefix + "className", className); String[] mimes = annotation.mimeType(); for (int i = 0; i < mimes.length; i++) { - p.setProperty("mimeType." + i, mimes[i]); + p.setProperty(prefix + "mimeType." + i, mimes[i]); } - try (OutputStream os = file.openOutputStream()) { - p.store(os, "Generated by " + LanguageRegistrationProcessor.class.getName()); + } + if (cnt > 0) { + try { + FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, languages.toArray(new Element[0])); + try (OutputStream os = file.openOutputStream()) { + p.store(os, "Generated by " + LanguageRegistrationProcessor.class.getName()); + } + } catch (IOException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), languages.get(0)); } - } catch (IOException e) { - processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), language); } } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + generateFile(registrations); + registrations.clear(); + return true; + } for (Element e : roundEnv.getElementsAnnotatedWith(Registration.class)) { Registration annotation = e.getAnnotation(Registration.class); if (annotation != null && e.getKind() == ElementKind.CLASS) { @@ -103,7 +120,7 @@ continue; } assertNoErrorExpected(e); - createProviderFile((TypeElement) e, annotation); + registrations.add((TypeElement) e); } } diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/SymbolInvokerImpl.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/SymbolInvokerImpl.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/SymbolInvokerImpl.java Thu Jun 04 08:08:05 2015 +0200 @@ -40,6 +40,15 @@ @Override protected Object invoke(Object symbol, Object... arr) throws IOException { + if (symbol instanceof String) { + return symbol; + } + if (symbol instanceof Number) { + return symbol; + } + if (symbol instanceof Boolean) { + return symbol; + } ForeignObjectAccessNode callMain = ForeignObjectAccessNode.getAccess(Execute.create(Receiver.create(), arr.length)); CallTarget callMainTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(callMain, (TruffleObject) symbol, arr)); VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(arr, UNUSED_FRAMEDESCRIPTOR); diff -r 67e28e817d32 -r 2f9e4d984d16 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Thu Jun 04 01:00:44 2015 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Thu Jun 04 08:08:05 2015 +0200 @@ -368,7 +368,7 @@ } @Override - protected Object findExportedSymbol(String globalName) { + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { for (SLFunction f : context.getFunctionRegistry().getFunctions()) { if (globalName.equals(f.getName())) { return f; diff -r 67e28e817d32 -r 2f9e4d984d16 mx/suite.py --- a/mx/suite.py Thu Jun 04 01:00:44 2015 +0200 +++ b/mx/suite.py Thu Jun 04 08:08:05 2015 +0200 @@ -1085,6 +1085,7 @@ ], "checkstyle" : "com.oracle.graal.graph", "javaCompliance" : "1.7", + "annotationProcessors" : ["com.oracle.truffle.dsl.processor"], "workingSets" : "API,Truffle,Test", "jacoco" : "exclude", },