src/jdk.compiler/share/classes/com/sun/tools/javac/api/TreeBuilderImpl.java
author jlahoda
Tue, 02 Apr 2019 16:51:25 +0200
branchjlahoda-tree-builder
changeset 57300 c79e191854e4
parent 57298 72d5f7163f12
permissions -rw-r--r--
Adding support for modifiers.

/*
 * 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.nio.CharBuffer;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.lang.model.element.Modifier;
import javax.tools.JavaFileObject;

import com.sun.source.doctree.DocTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.TreeBuilder;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.tree.JCTree;
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.JCMethodDecl;
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.main.JavaCompiler;
import com.sun.tools.javac.parser.Parser;
import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.Log.DiagnosticHandler;
import com.sun.tools.javac.util.Names;

/**
 * Implementation for TreeBuilder.
 * Note: this is only a crude experiment.
 */
public class TreeBuilderImpl implements TreeBuilder {

    private final JavaCompiler compiler;
    private final ParserFactory parserFactory;
    private final TreeMaker make;
    private final Names names;

    public TreeBuilderImpl(JavaCompiler compiler, ParserFactory parserFactory, TreeMaker make, Names names) {
        this.compiler = compiler;
        this.parserFactory = parserFactory;
        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<Type> sup) {
            result.extending = visitType(sup); //TODO: check extending not filled!
            return this;
        }

        @Override
        public Class superinterface(Consumer<Type> sup) {
            result.implementing = result.implementing.append(visitType(sup));
            return this;
        }

        @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.append(vi.result);
            return this;
        }

        @Override
        public Class method(String name, Consumer<Type> restype, Consumer<Method> method) {
            TypeImpl ti = new TypeImpl();
            restype.accept(ti);
            if (ti.type == null) {
                throw new IllegalStateException("Type not provided!");
            }
            MethodImpl vi = new MethodImpl(ti.type, name);
            method.accept(vi);
            result.defs = result.defs.append(vi.result);
            return this;
        }

        @Override
        public Class _class(String name, Consumer<Class> clazz) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Class modifiers(Consumer<Modifiers> modifiers) {
            modifiers.accept(new ModifiersImpl(result.mods));
            return this;
        }

        @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(Consumer<QualifiedName> className, Consumer<TypeArguments> typeArguments) {
            JCExpression clazz = visitQualifiedName(className);
            TypeArgumentsImpl ta = new TypeArgumentsImpl();
            typeArguments.accept(ta);
            if (ta.types.isEmpty()) {
                type = clazz;
            } else {
                type = make.TypeApply(clazz, ta.types);
            }
        }

        @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.");
        }

        @Override
        public void type(String typeSpec) {
            type = parse(typeSpec, Parser::parseType);
        }

    }

    private final class TypeArgumentsImpl implements TypeArguments {
        private List<JCExpression> types = List.nil();
        @Override
        public TypeArguments type(Consumer<Type> t) {
            TypeImpl type = new TypeImpl();
            t.accept(type);
            types = types.append(type.type);
            return this;
        }
    }

    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) {
            result.init = visitExpression(init);
            return this;
        }

        @Override
        public Variable modifiers(Consumer<Modifiers> modifiers) {
            modifiers.accept(new ModifiersImpl(result.mods));
            return this;
        }

        @Override
        public Variable javadoc(DocTree doc) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Variable javadoc(String doc) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        
    }

    private final class MethodImpl implements Method {

        private final JCMethodDecl result;

        public MethodImpl(JCExpression restype, String name) {
            result = make.MethodDef(make.Modifiers(0), names.fromString(name), restype, List.nil(), List.nil(), List.nil(), null, null);
        }

        @Override
        public Method parameter(Consumer<Type> type, Consumer<Parameter> parameter) {
            ParameterImpl paramImpl = new ParameterImpl(visitType(type));
            parameter.accept(paramImpl);
            result.params = result.params.append(paramImpl.result);
            return this;
        }

        @Override
        public Method body(Consumer<Block> statements) {
            BlockImpl block = new BlockImpl();
            statements.accept(block);
            result.body = make.Block(0, block.statements);
            return this;
        }

        @Override
        public Method body(String body) {
            JCStatement parsedBody = parse(body, Parser::parseStatement);
            if (!parsedBody.hasTag(Tag.BLOCK)) {
                throw new IllegalArgumentException("Block not provided!");
            }
            result.body = (JCBlock) parsedBody;
            return this;
        }

        @Override
        public Method modifiers(Consumer<Modifiers> modifiers) {
            modifiers.accept(new ModifiersImpl(result.mods));
            return this;
        }

        @Override
        public Method javadoc(DocTree doc) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Method javadoc(String doc) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
        
    }

    private final class ParameterImpl implements Parameter {

        private final JCVariableDecl result;

        public ParameterImpl(JCExpression type) {
            //TODO: infer name
            result = make.VarDef(make.Modifiers(0), null, type, null);
        }

        @Override
        public Parameter modifiers(Consumer<Modifiers> modifiers) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public Parameter name(String name) {
            result.name = names.fromString(name); //XXX: check not set yet.
            return this;
        }
        
    }

    private final class BlockImpl extends StatementBaseImpl<Block> implements Block {

        private List<JCStatement> statements = List.nil();

        @Override
        protected Block addStatement(JCStatement stat) {
            statements = statements.append(stat);
            return this;
        }
        
    }

    private final class StatementImpl extends StatementBaseImpl<Void> implements Statement {
        private JCStatement result;

        @Override
        protected Void addStatement(JCStatement stat) {
            if (result != null) {
                throw new IllegalStateException();
            }
            result = stat;
            return null;
        }
    }

    private abstract class StatementBaseImpl<S> implements StatementBase<S> {

        @Override
        public S _if(Consumer<Expression> cond, Consumer<Statement> ifPart) {
            JCExpression expr = visitExpression(cond);
            //TODO: should this automatic wrapping with parenthesized be here?
            expr = make.Parens(expr);
            StatementImpl ifStatement = new StatementImpl();
            ifPart.accept(ifStatement);
            //TODO: check ifPart filled!
            return addStatement(make.If(expr, ifStatement.result, null));
        }

        @Override
        public S _if(Consumer<Expression> cond, Consumer<Statement> ifPart, Consumer<Statement> elsePart) {
            JCExpression expr = visitExpression(cond);
            //TODO: should this automatic wrapping with parenthesized be here?
            expr = make.Parens(expr);
            StatementImpl ifStatement = new StatementImpl();
            ifPart.accept(ifStatement);
            //TODO: check ifPart filled!
            StatementImpl elseStatement = new StatementImpl();
            elsePart.accept(elseStatement);
            return addStatement(make.If(expr, ifStatement.result, elseStatement.result));
        }

        @Override
        public S _return() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public S _return(Consumer<Expression> expr) {
            return addStatement(make.Return(visitExpression(expr)));
        }

        @Override
        public S expr(Consumer<Expression> expr) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public S skip() {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public S statement(String statement) {
            return addStatement(parse(statement, Parser::parseStatement));
        }

        protected abstract S addStatement(JCStatement stat);
    }
    
    private final class ExpressionImpl implements Expression {

        private JCExpression expr;

        @Override
        public void equal_to(Consumer<Expression> lhs, Consumer<Expression> rhs) {
            expr = make.Binary(Tag.EQ,
                               visitExpression(lhs),
                               visitExpression(rhs));
        }

        @Override
        public void minusminus(Consumer<Expression> expr) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public void plus(Consumer<Expression> lhs, Consumer<Expression> rhs) {
            expr = make.Binary(Tag.PLUS,
                               visitExpression(lhs),
                               visitExpression(rhs));
        }

        @Override
        public void cond(Consumer<Expression> cond, Consumer<Expression> truePart, Consumer<Expression> falsePart) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public void select(Consumer<Expression> selected, String name) {
            expr = make.Select(visitExpression(selected), names.fromString(name));
        }

        @Override
        public void ident(String ident) {
            expr = make.Ident(names.fromString(ident)); //XXX
        }

        @Override
        public void literal(Object value) {
            expr = make.Literal(value);
        }

        @Override
        public void expression(String expression) {
            expr = parse(expression, Parser::parseExpression);
        }

    }

    private final class QualifiedNameImpl implements QualifiedName {

        private JCExpression expr;

        @Override
        public void select(Consumer<QualifiedName> selected, String name) {
            expr = make.Select(visitQualifiedName(selected), names.fromString(name));
        }

        @Override
        public void ident(String ident) {
            expr = make.Ident(names.fromString(ident));
        }

        @Override
        public void ident(String... qnames) {
            expr = make.Ident(names.fromString(qnames[0]));
            for (int i = 1; i < qnames.length; i++) {
                expr = make.Select(expr, names.fromString(qnames[i]));
            }
        }

    }

    private final class ModifiersImpl implements Modifiers {

        private final JCModifiers mods;

        public ModifiersImpl(JCModifiers mods) {
            this.mods = mods;
        }

        @Override
        public Modifiers modifier(Modifier modifier) {
            long flag;
            switch (modifier) {
                case PUBLIC: flag = Flags.PUBLIC; break;
                case PROTECTED: flag = Flags.PROTECTED; break;
                case PRIVATE: flag = Flags.PRIVATE; break;
                case ABSTRACT: flag = Flags.ABSTRACT; break;
                case STATIC: flag = Flags.STATIC; break;
                case FINAL: flag = Flags.FINAL; break;
                case TRANSIENT: flag = Flags.TRANSIENT; break;
                case VOLATILE: flag = Flags.VOLATILE; break;
                case SYNCHRONIZED: flag = Flags.SYNCHRONIZED; break;
                case NATIVE: flag = Flags.NATIVE; break;
                case STRICTFP: flag = Flags.STRICTFP; break;
                case DEFAULT: flag = Flags.DEFAULT; break;
                default:
                    throw new IllegalArgumentException("Unknown modifier: " + modifier);
            }
            mods.flags |= flag;
            return this;
        }

        @Override
        public Modifiers annotation(Consumer<Annotation> annotation) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

    }

    private JCExpression visitExpression(Consumer<Expression> c) {
        ExpressionImpl expr = new ExpressionImpl();

        c.accept(expr);

        if (expr.expr == null) {
            throw new IllegalStateException("Expression not provided!");
        }

        return expr.expr;
    }

    private JCExpression visitQualifiedName(Consumer<QualifiedName> c) {
        QualifiedNameImpl expr = new QualifiedNameImpl();

        c.accept(expr);

        if (expr.expr == null) {
            throw new IllegalStateException("Name not provided!");
        }

        return expr.expr;
    }

    private JCExpression visitType(Consumer<Type> c) {
        TypeImpl type = new TypeImpl();

        c.accept(type);

        if (type.type == null) {
            throw new IllegalStateException("Expression not provided!");
        }

        return type.type;
    }

    private <T extends JCTree> T parse(String toParse, Function<Parser, T> runParse) {
        if (toParse == null || toParse.equals(""))
            throw new IllegalArgumentException();
        JavaFileObject prev = compiler.log.useSource(null);
        DiagnosticHandler h = null;
        try {
            h = new DiagnosticHandler() {
                {
                    install(compiler.log);
                }
                @Override
                public void report(JCDiagnostic err) {
                    if (err.getKind() == JCDiagnostic.Kind.ERROR) {
                        throw new IllegalArgumentException("Cannot parse input: " + err.getMessage(null));
                    }
                }
            };
            CharBuffer buf = CharBuffer.wrap((toParse+"\u0000").toCharArray(), 0, toParse.length());
            Parser parser = parserFactory.newParser(buf, false, false, false);
            return runParse.apply(parser);
        } finally {
            compiler.log.popDiagnosticHandler(h);
            compiler.log.useSource(prev);
        }
    }

}