| /* |
| * Copyright (c) 2014, 2018, 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.Source.Feature; |
| 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 com.sun.tools.javac.resources.CompilerProperties; |
| import com.sun.tools.javac.resources.CompilerProperties.Errors; |
| 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.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.JCStatement; |
| import com.sun.tools.javac.tree.JCTree.JCTypeParameter; |
| 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 { |
| |
| // force starting in expression mode |
| private final boolean forceExpression; |
| |
| public ReplParser(ParserFactory fac, |
| com.sun.tools.javac.parser.Lexer S, |
| boolean keepDocComments, |
| boolean keepLineMap, |
| boolean keepEndPositions, |
| boolean forceExpression) { |
| super(fac, S, keepDocComments, keepLineMap, keepEndPositions); |
| this.forceExpression = forceExpression; |
| } |
| |
| /** |
| * 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()) { |
| checkSourceLevel(annosAfterParams.head.pos, Feature.ANNOTATIONS_AFTER_TYPE_PARAMS); |
| 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 |
| // unless expression is being forced |
| t = term(forceExpression |
| ? EXPR |
| : 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>(), true).toList(); |
| accept(SEMI); |
| storeEnd(defs.last(), S.prevToken().endPos); |
| return defs; |
| } else { |
| // malformed declaration, return error |
| pos = token.pos; |
| List<JCTree> err = isVoid |
| ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams, |
| List.nil(), List.nil(), null, null))) |
| : null; |
| return List.<JCTree>of(syntaxError(token.pos, err, Errors.Expected(LPAREN))); |
| } |
| } else if (!typarams.isEmpty()) { |
| // type parameters on non-variable non-method -- error |
| return List.<JCTree>of(syntaxError(token.pos, Errors.IllegalStartOfType)); |
| } else { |
| // expression-statement or expression to evaluate |
| JCExpressionStatement expr = toP(F.at(pos).Exec(t)); |
| return List.<JCTree>of(expr); |
| } |
| |
| } |
| } |
| } |
| } |