/* * Copyright (c) 2014, 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. 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 jdk.jshell; 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; /** * This is a subclass of JavacParser which overrides one method with a modified * verson of that method designed to allow parsing of one "snippet" of Java * code without the surrounding context of class, method, etc. * Accepts an expression, a statement, an import, or the declaration of a * method, variable, or type (class, interface, ...). */ class ReplParser extends JavacParser { public ReplParser(ParserFactory fac, com.sun.tools.javac.parser.Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) { super(fac, S, keepDocComments, keepLineMap, keepEndPositions); } /** * As faithful a clone of the overridden method as possible while still * achieving the goal of allowing the parse of a stand-alone snippet. * As a result, some variables are assigned and never used, tests are * always true, loops don't, etc. This is to allow easy transition as the * underlying method changes. * @return a snippet wrapped in a compilation unit */ @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(); } 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()); } else { Comment docComment = token.comment(CommentStyle.JAVADOC); if (firstTypeDecl && !seenImport && !seenPackage) { docComment = firstToken.comment(CommentStyle.JAVADOC); } List<? extends JCTree> udefs = replUnit(mods, docComment); // if (def instanceof JCExpressionStatement) // def = ((JCExpressionStatement)def).expr; for (JCTree def : udefs) { defs.append(def); } mods = null; firstTypeDecl = false; } break; // Remove to process more than one snippet } List<JCTree> rdefs = defs.toList(); class ReplUnit extends JCCompilationUnit { public ReplUnit(List<JCTree> defs) { super(defs); } } JCCompilationUnit toplevel = new ReplUnit(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; } @SuppressWarnings("fallthrough") List<? extends JCTree> replUnit(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 RETURN: case THROW: case BREAK: case CONTINUE: case SEMI: case ELSE: case FINALLY: case CATCH: case ASSERT: return List.<JCTree>of(parseStatement()); case SYNCHRONIZED: if (peekToken(LPAREN)) { return List.<JCTree>of(parseStatement()); } //fall-through 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 JCExpressionStatement expr = toP(F.at(pos).Exec(t)); return List.<JCTree>of(expr); } } } } }