8068488: Facilitate extension of the javac parser -- missing modifier
Reviewed-by: jjg
--- 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);
+ }
+}