A first very crude experiment on a tree builder for javac.
Contributed-by: maurizio.cimadamore@oracle.com, jan.lahoda@oracle.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeBuilder.java Fri Mar 15 14:21:44 2019 +0100
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2019, 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.sun.source.util;
+
+import java.util.function.Consumer;
+
+import javax.lang.model.element.Modifier;
+
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.CompilationUnitTree;
+
+/**
+ * Note: a crude experiment to explore a builder for javac ASTs.
+ */
+public interface TreeBuilder {
+
+ CompilationUnitTree createCompilationUnitTree(Consumer<CompilationUnit> unit); //TODO: should be in Trees?
+
+ //mixins:
+ interface WithModifiers<T extends WithModifiers<T>> {
+ T modifiers(Consumer<Modifiers> modifiers);
+ }
+
+ interface WithJavadoc<T extends WithJavadoc<T>> {
+ T javadoc(DocTree doc);
+ T javadoc(String doc);
+ }
+
+ //builders:
+ interface CompilationUnit {
+ CompilationUnit _package(String... qname);
+ CompilationUnit _class(String name, Consumer<Class> clazz);
+ }
+
+ interface Class extends WithModifiers<Class>, WithJavadoc<Class> {
+ //setters:
+ Class superclass(Consumer<Expression> sup);
+ Class superinterface(Consumer<Expression> sup);
+ //type parameters?
+
+ //adders:
+ default Class field(String name, Consumer<Type> type) {
+ //TODO: has this overload a meaning
+ return field(name, type, V -> {});
+ }
+
+ Class field(String name, Consumer<Type> type, Consumer<Variable> field);
+ Class method(String name, Consumer<Type> restype, Consumer<Method> method);
+ Class _class(String name, Consumer<Class> clazz);
+
+ //TODO:initializer block
+ }
+
+ interface Variable extends WithModifiers<Variable>, WithJavadoc<Variable> {
+ //setters:
+ Variable init(Consumer<Expression> init);
+ }
+
+ interface Parameter extends WithModifiers<Parameter> {
+ Parameter modifiers(Consumer<Modifiers> modifiers); //TODO: limit to final only?
+
+ Parameter name(String name);
+ }
+
+ interface Method extends WithModifiers<Method>, WithJavadoc<Method> {
+ default Method parameter(Consumer<Type> type) {
+ return parameter(type, P -> {});
+ }
+
+ Method parameter(Consumer<Type> type, Consumer<Parameter> parameter);
+
+ Method body(Consumer<Statements> statements);
+ //throws, default value
+ }
+
+ interface Modifiers {
+ Modifiers modifier(Modifier modifier);
+ Modifiers annotation(Consumer<Annotation> annotation);
+ }
+
+ interface Annotation {
+ //TODO...
+ }
+
+ interface Type {
+ void _class(String simpleName); //TODO: type parameters - declaredtype???
+// void _class(String simpleName, Consumer<TypeArgs> targs);
+ void _int();
+ void _float();
+ void _void();
+ }
+
+// interface TypeArgs { //???
+// TypeArgs type(Consumer<Type> t);
+// TypeArgs _extends(Consumer<Type> t);
+// TypeArgs _super(Consumer<Type> t);
+// TypeArgs unbound();
+// }
+
+ interface Expression {
+ void minusminus(Consumer<Expression> expr);
+ void plus(Consumer<Expression> lhs, Consumer<Expression> rhs);
+ void cond(Consumer<Expression> cond, Consumer<Expression> truePart, Consumer<Expression> falsePart);
+ void ident(String... qnames);
+ }
+
+ interface Statements {
+ Statements _if(Consumer<Expression> cond, Consumer<Statements> ifPart, Consumer<Statements> elsePart);
+ Statements expr(Consumer<Expression> expr);
+ Statements skip();
+ }
+
+ static void test(TreeBuilder builder) {
+ builder.createCompilationUnitTree(
+ U -> U._class("Foo", C -> C.field("x", Type::_int)
+ .method("foo",
+ Type::_void,
+ M -> M.parameter(T -> T._class("Foo"))
+ .parameter(T -> T._float(), P -> P.name("whatever"))
+ .body(B -> B._if(E -> E.minusminus(V -> V.ident("foo", "bar")),
+ Statements::skip,
+ Statements::skip
+ )
+ )
+ )));
+ }
+
+ //annotations?
+ //positions?
+}
--- a/src/jdk.compiler/share/classes/com/sun/source/util/Trees.java Fri Mar 15 14:19:05 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/Trees.java Fri Mar 15 14:21:44 2019 +0100
@@ -255,4 +255,6 @@
* @return The lub of the exception parameter
*/
public abstract TypeMirror getLub(CatchTree tree);
+
+ public abstract TreeBuilder getTreeBuilder();
}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Fri Mar 15 14:19:05 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java Fri Mar 15 14:21:44 2019 +0100
@@ -72,6 +72,7 @@
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SimpleDocTreeVisitor;
+import com.sun.source.util.TreeBuilder;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Scope.NamedImportScope;
@@ -1242,4 +1243,8 @@
jcCompilationUnit.toplevelScope = WriteableScope.create(psym);
return new TreePath(jcCompilationUnit);
}
+
+ public TreeBuilder getTreeBuilder() {
+ return new TreeBuilderImpl(treeMaker, names);
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/TreeBuilderImpl.java Fri Mar 15 14:21:44 2019 +0100
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2019, 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.sun.tools.javac.api;
+
+import java.util.function.Consumer;
+
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.TreeBuilder;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
+
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * Implementation for TreeBuilder.
+ * Note: this is only a crude experiment.
+ */
+public class TreeBuilderImpl implements TreeBuilder {
+
+ private final TreeMaker make;
+ private final Names names;
+
+ public TreeBuilderImpl(TreeMaker make, Names names) {
+ this.make = make;
+ this.names = names;
+ }
+
+ @Override
+ public CompilationUnitTree createCompilationUnitTree(Consumer<CompilationUnit> unit) {
+ CompilationUnitImpl cui = new CompilationUnitImpl();
+ unit.accept(cui);
+ return cui.result;
+ }
+
+ private final class CompilationUnitImpl implements CompilationUnit {
+
+ private final JCCompilationUnit result;
+
+ public CompilationUnitImpl() {
+ this.result = make.TopLevel(List.nil());
+ }
+
+ @Override
+ public CompilationUnit _package(String... qname) {
+ JCExpression qualIdent = make.Ident(names.fromString(qname[0])); //XXX: should check qname.length > 0!
+ for (int i = 1; i < qname.length; i++) {
+ qualIdent = make.Select(qualIdent, names.fromString(qname[i]));
+ }
+ result.defs = result.defs.stream().filter(t -> !t.hasTag(Tag.PACKAGEDEF)).collect(List.collector()) //XXX: what should be the behavior if already filled?
+ .prepend(make.PackageDecl(List.nil(), qualIdent));
+ return this;
+ }
+
+ @Override
+ public CompilationUnit _class(String name, Consumer<Class> clazz) {
+ ClassImpl ci = new ClassImpl(name);
+ clazz.accept(ci);
+ result.defs = result.defs.append(ci.result);
+ return this;
+ }
+
+ }
+
+ private final class ClassImpl implements Class {
+
+ private final JCClassDecl result;
+
+ public ClassImpl(String name) {
+ this.result = make.ClassDef(make.Modifiers(0), names.fromString(name), List.nil(), null, List.nil(), List.nil());
+ }
+
+ @Override
+ public Class superclass(Consumer<Expression> sup) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class superinterface(Consumer<Expression> sup) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class field(String name, Consumer<Type> type, Consumer<Variable> field) {
+ TypeImpl ti = new TypeImpl();
+ type.accept(ti);
+ if (ti.type == null) {
+ throw new IllegalStateException("Type not provided!");
+ }
+ VariableImpl vi = new VariableImpl(ti.type, name);
+ field.accept(vi);
+ result.defs = result.defs.prepend(vi.result);
+ return this;
+ }
+
+ @Override
+ public Class method(String name, Consumer<Type> restype, Consumer<Method> method) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class _class(String name, Consumer<Class> clazz) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class modifiers(Consumer<Modifiers> modifiers) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class javadoc(DocTree doc) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Class javadoc(String doc) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }
+
+ private final class TypeImpl implements Type {
+
+ private JCExpression type;
+
+ @Override
+ public void _class(String simpleName) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void _int() {
+ //XXX: check empty!
+ type = make.TypeIdent(TypeTag.INT);
+ }
+
+ @Override
+ public void _float() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void _void() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }
+
+ private final class VariableImpl implements Variable {
+
+ private final JCVariableDecl result;
+
+ public VariableImpl(JCExpression type, String name) {
+ result = make.VarDef(make.Modifiers(0), names.fromString(name), type, null);
+ }
+
+ @Override
+ public Variable init(Consumer<Expression> init) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Variable modifiers(Consumer<Modifiers> modifiers) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Variable javadoc(DocTree doc) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Variable javadoc(String doc) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/api/ast/ASTBuilder.java Fri Mar 15 14:21:44 2019 +0100
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 9999999
+ * @summary XXX
+ * @modules java.compiler
+ * jdk.compiler
+ */
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TreeBuilder;
+import com.sun.source.util.TreeBuilder.*;
+import com.sun.source.util.Trees;
+import com.sun.source.util.TreeScanner;
+
+public class ASTBuilder {
+
+ public static void main(String[] args) throws Exception {
+ runTest("class Test {" +
+ " int x;" +
+ "}",
+ U -> U._class("Test", C -> C.field("x", Type::_int)));
+ }
+
+ private static void runTest(String expectedCode, Consumer<CompilationUnit> actualBuilder) throws Exception {
+ final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ assert tool != null;
+
+ JavacTask expecteTask = (JavacTask) tool.getTask(null, null, null,
+ null, null, Arrays.asList(new MyFileObject(expectedCode)));
+ String expectedDump = dumpTree(expecteTask.parse().iterator().next());
+
+ JavacTask ct = (JavacTask) tool.getTask(null, null, null,
+ null, null, Arrays.asList(new MyFileObject("")));
+ ct.parse(); //init javac
+ Trees t = Trees.instance(ct);
+
+ TreeBuilder builder = t.getTreeBuilder();
+
+ CompilationUnitTree cut = builder.createCompilationUnitTree(actualBuilder);
+
+ String actualDump = dumpTree(cut);
+
+ if (!actualDump.equals(expectedDump)) {
+ throw new AssertionError("Expected and actual dump differ. Expected: " + expectedDump + "; actual: " + actualDump);
+ }
+ }
+
+ static String dumpTree(Tree t) {
+ StringBuilder result = new StringBuilder();
+ new TreeScanner<Void, Void>() {
+ String sep = "";
+ @Override
+ public Void scan(Tree tree, Void p) {
+ result.append(sep);
+ sep = " ";
+ if (tree == null) {
+ result.append("null");
+ return null;
+ }
+ result.append("(");
+ result.append(tree.getKind().name());
+ super.scan(tree, p);
+ result.append(")");
+ return null;
+ }
+ @Override
+ public Void scan(Iterable<? extends Tree> nodes, Void p) {
+ result.append(sep);
+ sep = " ";
+ if (nodes == null) {
+ result.append("null");
+ return null;
+ }
+ result.append("(");
+ super.scan(nodes, p);
+ result.append(")");
+ return null;
+ }
+ }.scan(t, null);
+ return result.toString();
+ }
+
+ static class MyFileObject extends SimpleJavaFileObject {
+ private String text;
+
+ public MyFileObject(String text) {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ this.text = text;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return text;
+ }
+ }
+}
\ No newline at end of file