changeset 21481:bb51b9a142b3

Enforcing public, one parameter constructor for each TruffleLanguage by annotation processor and required call to super.
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Mon, 25 May 2015 12:26:53 +0200
parents c2b006c5e15f
children 2fe8729dd813
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java
diffstat 7 files changed, 239 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- /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;
+        }
+    }
+}
--- 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
--- 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 {
--- 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
--- 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<? extends ExecutableElement, ? extends AnnotationValue> 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);
+    }
 }
--- 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<? extends ExecutableElement, ? extends AnnotationValue> vals = am.getElementValues();
                 if (vals.size() == 1) {
                     AnnotationValue av = vals.values().iterator().next();
--- 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));
     }