8068488: Facilitate extension of the javac parser -- missing modifier
authorrfield
Fri, 16 Jan 2015 20:03:30 -0800
changeset 28587 ce5606145ea3
parent 28462 8327024a9955
child 28588 16eda7aedf89
8068488: Facilitate extension of the javac parser -- missing modifier Reviewed-by: jjg
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/test/tools/javac/parser/extend/JavacExtensionTest.java
langtools/test/tools/javac/parser/extend/TrialParser.java
langtools/test/tools/javac/parser/extend/TrialParserFactory.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Jul 05 20:15:13 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Jan 16 20:03:30 2015 -0800
@@ -85,7 +85,7 @@
     private Names names;
 
     /** End position mappings container */
-    private final AbstractEndPosTable endPosTable;
+    protected final AbstractEndPosTable endPosTable;
 
     // Because of javac's limited lookahead, some contexts are ambiguous in
     // the presence of type annotations even though they are not ambiguous
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/parser/extend/JavacExtensionTest.java	Fri Jan 16 20:03:30 2015 -0800
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.Context;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.ToolProvider;
+import javax.tools.StandardJavaFileManager;
+import com.sun.source.tree.ImportTree;
+import java.util.Collections;
+import java.io.PrintWriter;
+import com.sun.source.tree.VariableTree;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+/*
+ * @test
+ * @bug 8067384 8068488
+ * @summary Verify that JavacParser can be extended
+ */
+public class JavacExtensionTest {
+
+    public static void main(String[] args) throws Exception {
+        PrintWriter pw = new PrintWriter("trialSource.java", "UTF-8");
+        pw.println("int x = 9;");
+        pw.close();
+        List<? extends Tree> defs = parse("trialSource.java");
+        if (defs.size() != 1) {
+            throw new AssertionError("Expected only one def, got: " + defs.size());
+        }
+        Tree tree = defs.get(0);
+        if (tree instanceof VariableTree) {
+            System.out.println("Passes!  --- " + tree);
+        } else {
+            throw new AssertionError("Expected VariableTree, got: " + tree);
+        }
+    }
+
+    static List<? extends Tree> parse(String srcfile) throws Exception {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+        Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(srcfile);
+        String classPath = System.getProperty("java.class.path");
+        List<String> options = Arrays.asList("-classpath", classPath);
+        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+        Context context = new Context();
+        JavacTaskImpl task = (JavacTaskImpl) ((JavacTool) compiler).getTask(null, null,
+                diagnostics, options, null, fileObjects, context);
+        TrialParserFactory.instance(context);
+        Iterable<? extends CompilationUnitTree> asts = task.parse();
+        Iterator<? extends CompilationUnitTree> it = asts.iterator();
+        if (it.hasNext()) {
+            CompilationUnitTree cut = it.next();
+            return cut.getTypeDecls();
+        } else {
+            throw new AssertionError("Expected compilation unit");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/parser/extend/TrialParser.java	Fri Jan 16 20:03:30 2015 -0800
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.parser.Tokens.Comment;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
+import com.sun.tools.javac.parser.Tokens.Token;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.PACKAGE;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
+import static com.sun.tools.javac.tree.JCTree.Tag.IDENT;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
+
+/**
+ *
+ * @author Robert Field
+ */
+class TrialParser extends JavacParser {
+
+    public TrialParser(ParserFactory fac,
+            com.sun.tools.javac.parser.Lexer S,
+            boolean keepDocComments,
+            boolean keepLineMap,
+            boolean keepEndPositions) {
+        super(fac, S, keepDocComments, keepLineMap, keepEndPositions);
+    }
+
+    @Override
+    public JCCompilationUnit parseCompilationUnit() {
+        Token firstToken = token;
+        JCModifiers mods = null;
+        boolean seenImport = false;
+        boolean seenPackage = false;
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        if (token.kind == MONKEYS_AT) {
+            mods = modifiersOpt();
+        }
+
+        if (token.kind == PACKAGE) {
+            int packagePos = token.pos;
+            List<JCAnnotation> annotations = List.nil();
+            seenPackage = true;
+            if (mods != null) {
+                checkNoMods(mods.flags);
+                annotations = mods.annotations;
+                mods = null;
+            }
+            nextToken();
+            JCExpression pid = qualident(false);
+            accept(SEMI);
+            JCPackageDecl pd = F.at(packagePos).PackageDecl(annotations, pid);
+            attach(pd, firstToken.comment(CommentStyle.JAVADOC));
+            storeEnd(pd, token.pos);
+            defs.append(pd);
+        }
+
+        boolean firstTypeDecl = true;
+        while (token.kind != EOF) {
+            if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) {
+                // error recovery
+                skip(true, false, false, false);
+                if (token.kind == EOF) {
+                    break;
+                }
+            }
+            if (mods == null && token.kind == IMPORT) {
+                seenImport = true;
+                defs.append(importDeclaration());
+                break;
+            } else {
+                Comment docComment = token.comment(CommentStyle.JAVADOC);
+                if (firstTypeDecl && !seenImport && !seenPackage) {
+                    docComment = firstToken.comment(CommentStyle.JAVADOC);
+                }
+                List<? extends JCTree> udefs = aUnit(mods, docComment);
+                for (JCTree def : udefs) {
+                    defs.append(def);
+                }
+                mods = null;
+                firstTypeDecl = false;
+                break;
+            }
+        }
+        List<JCTree> rdefs = defs.toList();
+        class TrialUnit extends JCCompilationUnit {
+
+            public TrialUnit(List<JCTree> defs) {
+                super(defs);
+            }
+        }
+        JCCompilationUnit toplevel = new TrialUnit(rdefs);
+        if (rdefs.isEmpty()) {
+            storeEnd(toplevel, S.prevToken().endPos);
+        }
+        toplevel.lineMap = S.getLineMap();
+        this.endPosTable.setParser(null); // remove reference to parser
+        toplevel.endPositions = this.endPosTable;
+        return toplevel;
+    }
+
+    List<? extends JCTree> aUnit(JCModifiers pmods, Comment dc) {
+        switch (token.kind) {
+            case EOF:
+                return List.nil();
+            case RBRACE:
+            case CASE:
+            case DEFAULT:
+                // These are illegal, fall through to handle as illegal statement
+            case LBRACE:
+            case IF:
+            case FOR:
+            case WHILE:
+            case DO:
+            case TRY:
+            case SWITCH:
+            case SYNCHRONIZED:
+            case RETURN:
+            case THROW:
+            case BREAK:
+            case CONTINUE:
+            case SEMI:
+            case ELSE:
+            case FINALLY:
+            case CATCH:
+            case ASSERT:
+                return List.<JCTree>of(parseStatement());
+            default:
+                JCModifiers mods = modifiersOpt(pmods);
+                if (token.kind == CLASS
+                        || token.kind == INTERFACE
+                        || token.kind == ENUM) {
+                    return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc));
+                } else {
+                    int pos = token.pos;
+                    List<JCTypeParameter> typarams = typeParametersOpt();
+                // if there are type parameters but no modifiers, save the start
+                    // position of the method in the modifiers.
+                    if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
+                        mods.pos = pos;
+                        storeEnd(mods, pos);
+                    }
+                    List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
+
+                    if (annosAfterParams.nonEmpty()) {
+                        checkAnnotationsAfterTypeParams(annosAfterParams.head.pos);
+                        mods.annotations = mods.annotations.appendList(annosAfterParams);
+                        if (mods.pos == Position.NOPOS) {
+                            mods.pos = mods.annotations.head.pos;
+                        }
+                    }
+
+                    Token prevToken = token;
+                    pos = token.pos;
+                    JCExpression t;
+                    boolean isVoid = token.kind == VOID;
+                    if (isVoid) {
+                        t = to(F.at(pos).TypeIdent(TypeTag.VOID));
+                        nextToken();
+                    } else {
+                        // return type of method, declared type of variable, or an expression
+                        t = term(EXPR | TYPE);
+                    }
+                    if (token.kind == COLON && t.hasTag(IDENT)) {
+                        // labelled statement
+                        nextToken();
+                        JCStatement stat = parseStatement();
+                        return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat));
+                    } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) {
+                        // we have "Type Ident", so we can assume it is variable or method declaration
+                        pos = token.pos;
+                        Name name = ident();
+                        if (token.kind == LPAREN) {
+                        // method declaration
+                            //mods.flags |= Flags.STATIC;
+                            return List.of(methodDeclaratorRest(
+                                    pos, mods, t, name, typarams,
+                                    false, isVoid, dc));
+                        } else if (!isVoid && typarams.isEmpty()) {
+                        // variable declaration
+                            //mods.flags |= Flags.STATIC;
+                            List<JCTree> defs
+                                    = variableDeclaratorsRest(pos, mods, t, name, false, dc,
+                                            new ListBuffer<JCTree>()).toList();
+                            accept(SEMI);
+                            storeEnd(defs.last(), S.prevToken().endPos);
+                            return defs;
+                        } else {
+                            // malformed declaration, return error
+                            pos = token.pos;
+                            List<JCTree> err = isVoid
+                                    ? List.<JCTree>of(toP(F.at(pos).MethodDef(mods, name, t, typarams,
+                                                            List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null)))
+                                    : null;
+                            return List.<JCTree>of(syntaxError(token.pos, err, "expected", LPAREN));
+                        }
+                    } else if (!typarams.isEmpty()) {
+                        // type parameters on non-variable non-method -- error
+                        return List.<JCTree>of(syntaxError(token.pos, "illegal.start.of.type"));
+                    } else {
+                        // expression-statement or expression to evaluate
+                        accept(SEMI);
+                        JCExpressionStatement expr = toP(F.at(pos).Exec(t));
+                        return List.<JCTree>of(expr);
+                    }
+
+                }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/parser/extend/TrialParserFactory.java	Fri Jan 16 20:03:30 2015 -0800
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.util.Context;
+
+/**
+ *
+ * @author Robert Field
+ */
+class TrialParserFactory extends ParserFactory {
+
+    public static ParserFactory instance(Context context) {
+        ParserFactory instance = context.get(parserFactoryKey);
+        if (instance == null) {
+            instance = new TrialParserFactory(context);
+        }
+        return instance;
+    }
+
+    private final ScannerFactory scannerFactory;
+
+    protected TrialParserFactory(Context context) {
+        super(context);
+        this.scannerFactory = ScannerFactory.instance(context);
+    }
+
+    @Override
+    public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) {
+        com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments);
+        return new TrialParser(this, lexer, keepDocComments, keepLineMap, keepEndPos);
+    }
+}