# HG changeset patch # User Jaroslav Tulach # Date 1432549613 -7200 # Node ID bb51b9a142b3f07d0951f48b7e94e7c96b06804a # Parent c2b006c5e15fe05e868a3cff68c7295551aca882 Enforcing public, one parameter constructor for each TruffleLanguage by annotation processor and required call to super. diff -r c2b006c5e15f -r bb51b9a142b3 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Mon May 25 12:26:53 2015 +0200 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.dsl.test.processor; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.dsl.ExpectError; +import com.oracle.truffle.api.source.Source; +import java.io.IOException; + +public class LanguageRegistrationTest { + + @ExpectError("Registered language class must be public") + @TruffleLanguage.Registration(name = "myLang", mimeType = "text/x-my") + private static final class MyLang { + } + + @ExpectError("Registered language inner-class must be static") + @TruffleLanguage.Registration(name = "myLangNonStatic", mimeType = "text/x-my") + public final class MyLangNonStatic { + } + + @ExpectError("Registered language class must subclass TruffleLanguage") + @TruffleLanguage.Registration(name = "myLang", mimeType = "text/x-my") + public static final class MyLangNoSubclass { + } + + @ExpectError("Language must have a public constructor accepting TruffleLanguage.Env as parameter") + @TruffleLanguage.Registration(name = "myLangNoCnstr", mimeType = "text/x-my") + public static final class MyLangWrongConstr extends TruffleLanguage { + private MyLangWrongConstr() { + super(null); + } + + @Override + protected Object eval(Source code) throws IOException { + return null; + } + + @Override + protected Object findExportedSymbol(String globalName) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + } + + @ExpectError("Language must have a public constructor accepting TruffleLanguage.Env as parameter") + @TruffleLanguage.Registration(name = "myLangNoCnstr", mimeType = "text/x-my") + public static final class MyLangNoConstr extends TruffleLanguage { + public MyLangNoConstr() { + super(null); + } + + @Override + protected Object eval(Source code) throws IOException { + return null; + } + + @Override + protected Object findExportedSymbol(String globalName) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + } + + @TruffleLanguage.Registration(name = "myLangGood", mimeType = "text/x-my") + public static final class MyLangGood extends TruffleLanguage { + public MyLangGood(TruffleLanguage.Env env) { + super(env); + } + + @Override + protected Object eval(Source code) throws IOException { + return null; + } + + @Override + protected Object findExportedSymbol(String globalName) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + } +} diff -r c2b006c5e15f -r bb51b9a142b3 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 Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Mon May 25 12:26:53 2015 +0200 @@ -33,6 +33,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Constructor; /** * An entry point for everyone who wants to implement a Truffle based language. By providing @@ -42,7 +43,17 @@ * language support, multi tennat hosting, debugging, etc.) will be made available to them. */ public abstract class TruffleLanguage { - private Env env; + private final Env env; + + /** + * Constructor to be called by subclasses. + * + * @param env language environment that will be available via {@link #env()} method to + * subclasses. + */ + protected TruffleLanguage(Env env) { + this.env = env; + } /** * The annotation to use to register your language to the {@link TruffleVM Truffle} system. By @@ -72,11 +83,6 @@ String[] mimeType(); } - @SuppressWarnings("all") - void attachEnv(Env env) { - this.env = env; - } - protected final Env env() { if (this.env == null) { throw new NullPointerException("Accessing env before initialization is finished"); @@ -130,9 +136,13 @@ private final TruffleVM vm; private final TruffleLanguage lang; - Env(TruffleVM vm, TruffleLanguage lang) { + Env(TruffleVM vm, Constructor langConstructor) { this.vm = vm; - this.lang = lang; + try { + this.lang = (TruffleLanguage) langConstructor.newInstance(this); + } catch (Exception ex) { + throw new IllegalStateException("Cannot construct language " + langConstructor.getClass().getName(), ex); + } } /** @@ -154,10 +164,9 @@ private static final class AccessAPI extends Accessor { @Override - protected Env attachEnv(TruffleVM vm, TruffleLanguage l) { - Env env = new Env(vm, l); - l.attachEnv(env); - return env; + protected TruffleLanguage attachEnv(TruffleVM vm, Constructor langClazz) { + Env env = new Env(vm, langClazz); + return env.lang; } @Override diff -r c2b006c5e15f -r bb51b9a142b3 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 Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Mon May 25 12:26:53 2015 +0200 @@ -25,10 +25,10 @@ package com.oracle.truffle.api.impl; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.vm.TruffleVM; import com.oracle.truffle.api.source.Source; import java.io.IOException; +import java.lang.reflect.Constructor; import java.util.ServiceLoader; /** @@ -38,7 +38,7 @@ private static Accessor API; private static Accessor SPI; static { - TruffleLanguage lng = new TruffleLanguage() { + TruffleLanguage lng = new TruffleLanguage(null) { @Override protected Object eval(Source code) throws IOException { return null; @@ -76,8 +76,8 @@ } } - protected Env attachEnv(TruffleVM vm, TruffleLanguage l) { - return API.attachEnv(vm, l); + protected TruffleLanguage attachEnv(TruffleVM vm, Constructor langClazz) { + return API.attachEnv(vm, langClazz); } protected Object eval(TruffleLanguage l, Source s) throws IOException { diff -r c2b006c5e15f -r bb51b9a142b3 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 Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Mon May 25 12:26:53 2015 +0200 @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.lang.reflect.Constructor; import java.net.URI; import java.net.URL; import java.net.URLConnection; @@ -333,9 +334,9 @@ if (impl == null) { String n = props.getProperty("className"); try { - TruffleLanguage lang = (TruffleLanguage) Class.forName(n, true, loader()).newInstance(); - SPI.attachEnv(TruffleVM.this, lang); - impl = lang; + Class langClazz = Class.forName(n, true, loader()); + Constructor constructor = langClazz.getConstructor(Env.class); + impl = SPI.attachEnv(TruffleVM.this, constructor); } catch (Exception ex) { throw new IllegalStateException("Cannot initialize " + getName() + " language with implementation " + n, ex); } @@ -367,8 +368,8 @@ } @Override - public Env attachEnv(TruffleVM vm, TruffleLanguage l) { - return super.attachEnv(vm, l); + public TruffleLanguage attachEnv(TruffleVM vm, Constructor langClazz) { + return super.attachEnv(vm, langClazz); } @Override diff -r c2b006c5e15f -r bb51b9a142b3 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 Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java Mon May 25 12:26:53 2015 +0200 @@ -22,19 +22,27 @@ */ package com.oracle.truffle.dsl.processor; +import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.dsl.ExpectError; import java.io.IOException; import java.io.OutputStream; +import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic.Kind; import javax.tools.FileObject; import javax.tools.StandardLocation; @@ -72,12 +80,78 @@ Registration annotation = e.getAnnotation(Registration.class); if (annotation != null && e.getKind() == ElementKind.CLASS) { if (!e.getModifiers().contains(Modifier.PUBLIC)) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Registered language class must be public", e); + emitError("Registered language class must be public", e); + continue; + } + if (e.getEnclosingElement().getKind() != ElementKind.PACKAGE && !e.getModifiers().contains(Modifier.STATIC)) { + emitError("Registered language inner-class must be static", e); + continue; + } + TypeMirror truffleLang = processingEnv.getElementUtils().getTypeElement(TruffleLanguage.class.getName()).asType(); + if (!processingEnv.getTypeUtils().isAssignable(e.asType(), truffleLang)) { + emitError("Registered language class must subclass TruffleLanguage", e); + continue; } + boolean found = false; + for (Element mem : e.getEnclosedElements()) { + if (mem.getKind() != ElementKind.CONSTRUCTOR) { + continue; + } + ExecutableElement ee = (ExecutableElement) mem; + if (ee.getParameters().size() != 1) { + continue; + } + if (!ee.getModifiers().contains(Modifier.PUBLIC)) { + continue; + } + TypeMirror env = processingEnv.getElementUtils().getTypeElement(TruffleLanguage.Env.class.getCanonicalName()).asType(); + if (processingEnv.getTypeUtils().isSameType(ee.getParameters().get(0).asType(), env)) { + found = true; + break; + } + } + if (!found) { + emitError("Language must have a public constructor accepting TruffleLanguage.Env as parameter", e); + continue; + } + assertNoErrorExpected(e); createProviderFile((TypeElement) e, annotation); } } return true; } + + void assertNoErrorExpected(Element e) { + TypeElement eee = processingEnv.getElementUtils().getTypeElement(ExpectError.class.getName()); + for (AnnotationMirror am : e.getAnnotationMirrors()) { + if (am.getAnnotationType().asElement().equals(eee)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Expected an error, but none found!", e); + } + } + } + + void emitError(String msg, Element e) { + TypeElement eee = processingEnv.getElementUtils().getTypeElement(ExpectError.class.getName()); + for (AnnotationMirror am : e.getAnnotationMirrors()) { + if (am.getAnnotationType().asElement().equals(eee)) { + Map vals = am.getElementValues(); + if (vals.size() == 1) { + AnnotationValue av = vals.values().iterator().next(); + if (av.getValue() instanceof List) { + List arr = (List) av.getValue(); + for (Object o : arr) { + if (o instanceof AnnotationValue) { + AnnotationValue ov = (AnnotationValue) o; + if (msg.equals(ov.getValue())) { + return; + } + } + } + } + } + } + } + processingEnv.getMessager().printMessage(Kind.ERROR, msg, e); + } } diff -r c2b006c5e15f -r bb51b9a142b3 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java Mon May 25 12:26:53 2015 +0200 @@ -128,7 +128,7 @@ void assertNoErrorExpected(Element e) { TypeElement eee = processingEnv.getElementUtils().getTypeElement(ExpectError.class.getName()); for (AnnotationMirror am : e.getAnnotationMirrors()) { - if (am.getAnnotationType().asElement() == eee) { + if (am.getAnnotationType().asElement().equals(eee)) { processingEnv.getMessager().printMessage(Kind.ERROR, "Expected an error, but none found!", e); } } @@ -137,7 +137,7 @@ void emitError(String msg, Element e) { TypeElement eee = processingEnv.getElementUtils().getTypeElement(ExpectError.class.getName()); for (AnnotationMirror am : e.getAnnotationMirrors()) { - if (am.getAnnotationType().asElement() == eee) { + if (am.getAnnotationType().asElement().equals(eee)) { Map vals = am.getElementValues(); if (vals.size() == 1) { AnnotationValue av = vals.values().iterator().next(); diff -r c2b006c5e15f -r bb51b9a142b3 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 Mon May 25 10:36:30 2015 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Mon May 25 12:26:53 2015 +0200 @@ -134,7 +134,8 @@ public class SLMain extends TruffleLanguage { private final SLContext context; - public SLMain() { + public SLMain(Env env) { + super(env); this.context = SLContextFactory.create(new BufferedReader(new InputStreamReader(System.in)), new PrintWriter(System.out)); }