1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jshell;
  27 
  28 import com.sun.tools.javac.code.Source.Feature;
  29 import com.sun.tools.javac.code.TypeTag;
  30 import com.sun.tools.javac.parser.JavacParser;
  31 import com.sun.tools.javac.parser.ParserFactory;
  32 import com.sun.tools.javac.parser.Tokens.Comment;
  33 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
  34 import com.sun.tools.javac.parser.Tokens.Token;
  35 import com.sun.tools.javac.resources.CompilerProperties;
  36 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  37 import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS;
  38 import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON;
  39 import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM;
  40 import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF;
  41 import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
  42 import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE;
  43 import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN;
  44 import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
  45 import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI;
  46 import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID;
  47 import com.sun.tools.javac.tree.JCTree;
  48 import com.sun.tools.javac.tree.JCTree.JCAnnotation;
  49 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
  50 import com.sun.tools.javac.tree.JCTree.JCExpression;
  51 import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
  52 import com.sun.tools.javac.tree.JCTree.JCModifiers;
  53 import com.sun.tools.javac.tree.JCTree.JCStatement;
  54 import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
  55 import com.sun.tools.javac.tree.JCTree.Tag;
  56 import static com.sun.tools.javac.tree.JCTree.Tag.IDENT;
  57 import com.sun.tools.javac.util.List;
  58 import com.sun.tools.javac.util.ListBuffer;
  59 import com.sun.tools.javac.util.Name;
  60 import com.sun.tools.javac.util.Position;
  61 
  62 /**
  63  * This is a subclass of JavacParser which overrides one method with a modified
  64  * verson of that method designed to allow parsing of one "snippet" of Java
  65  * code without the surrounding context of class, method, etc.
  66  * Accepts an expression, a statement, an import, or the declaration of a
  67  * method, variable, or type (class, interface, ...).
  68  */
  69 class ReplParser extends JavacParser {
  70 
  71     // force starting in expression mode
  72     private final boolean forceExpression;
  73 
  74     public ReplParser(ParserFactory fac,
  75             com.sun.tools.javac.parser.Lexer S,
  76             boolean keepDocComments,
  77             boolean keepLineMap,
  78             boolean keepEndPositions,
  79             boolean forceExpression) {
  80         super(fac, S, keepDocComments, keepLineMap, keepEndPositions);
  81         this.forceExpression = forceExpression;
  82     }
  83 
  84     /**
  85      * As faithful a clone of the overridden method as possible while still
  86      * achieving the goal of allowing the parse of a stand-alone snippet.
  87      * As a result, some variables are assigned and never used, tests are
  88      * always true, loops don't, etc.  This is to allow easy transition as the
  89      * underlying method changes.
  90      * @return a snippet wrapped in a compilation unit
  91      */
  92     @Override
  93     public JCCompilationUnit parseCompilationUnit() {
  94         Token firstToken = token;
  95         JCModifiers mods = null;
  96         boolean seenImport = false;
  97         boolean seenPackage = false;
  98         ListBuffer<JCTree> defs = new ListBuffer<>();
  99         if (token.kind == MONKEYS_AT) {
 100             mods = modifiersOpt();
 101         }
 102 
 103         boolean firstTypeDecl = true;
 104         while (token.kind != EOF) {
 105             if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) {
 106                 // error recovery
 107                 skip(true, false, false, false);
 108                 if (token.kind == EOF) {
 109                     break;
 110                 }
 111             }
 112             if (mods == null && token.kind == IMPORT) {
 113                 seenImport = true;
 114                 defs.append(importDeclaration());
 115             } else {
 116                 Comment docComment = token.comment(CommentStyle.JAVADOC);
 117                 if (firstTypeDecl && !seenImport && !seenPackage) {
 118                     docComment = firstToken.comment(CommentStyle.JAVADOC);
 119                 }
 120                 List<? extends JCTree> udefs = replUnit(mods, docComment);
 121                // if (def instanceof JCExpressionStatement)
 122                 //     def = ((JCExpressionStatement)def).expr;
 123                 for (JCTree def : udefs) {
 124                     defs.append(def);
 125                 }
 126                 mods = null;
 127                 firstTypeDecl = false;
 128             }
 129             break;  // Remove to process more than one snippet
 130         }
 131         List<JCTree> rdefs = defs.toList();
 132         class ReplUnit extends JCCompilationUnit {
 133 
 134             public ReplUnit(List<JCTree> defs) {
 135                 super(defs);
 136             }
 137         }
 138         JCCompilationUnit toplevel = new ReplUnit(rdefs);
 139         if (rdefs.isEmpty()) {
 140             storeEnd(toplevel, S.prevToken().endPos);
 141         }
 142         toplevel.lineMap = S.getLineMap();
 143         this.endPosTable.setParser(null); // remove reference to parser
 144         toplevel.endPositions = this.endPosTable;
 145         return toplevel;
 146     }
 147 
 148     @SuppressWarnings("fallthrough")
 149      List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) {
 150         switch (token.kind) {
 151             case EOF:
 152                 return List.nil();
 153             case RBRACE:
 154             case CASE:
 155             case DEFAULT:
 156                 // These are illegal, fall through to handle as illegal statement
 157             case LBRACE:
 158             case IF:
 159             case FOR:
 160             case WHILE:
 161             case DO:
 162             case TRY:
 163             case RETURN:
 164             case THROW:
 165             case BREAK:
 166             case CONTINUE:
 167             case SEMI:
 168             case ELSE:
 169             case FINALLY:
 170             case CATCH:
 171             case ASSERT:
 172                 return List.<JCTree>of(parseStatement());
 173             case SYNCHRONIZED:
 174                 if (peekToken(LPAREN)) {
 175                     return List.<JCTree>of(parseStatement());
 176                 }
 177                 //fall-through
 178             default:
 179                 JCModifiers mods = modifiersOpt(pmods);
 180                 if (token.kind == CLASS
 181                         || token.kind == INTERFACE
 182                         || token.kind == ENUM) {
 183                     return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc));
 184                 } else {
 185                     int pos = token.pos;
 186                     List<JCTypeParameter> typarams = typeParametersOpt();
 187                     // if there are type parameters but no modifiers, save the start
 188                     // position of the method in the modifiers.
 189                     if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
 190                         mods.pos = pos;
 191                         storeEnd(mods, pos);
 192                     }
 193                     List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
 194 
 195                     if (annosAfterParams.nonEmpty()) {
 196                         checkSourceLevel(annosAfterParams.head.pos, Feature.ANNOTATIONS_AFTER_TYPE_PARAMS);
 197                         mods.annotations = mods.annotations.appendList(annosAfterParams);
 198                         if (mods.pos == Position.NOPOS) {
 199                             mods.pos = mods.annotations.head.pos;
 200                         }
 201                     }
 202 
 203                     Token prevToken = token;
 204                     pos = token.pos;
 205                     JCExpression t;
 206                     boolean isVoid = token.kind == VOID;
 207                     if (isVoid) {
 208                         t = to(F.at(pos).TypeIdent(TypeTag.VOID));
 209                         nextToken();
 210                     } else {
 211                         // return type of method, declared type of variable, or an expression
 212                         // unless expression is being forced
 213                         t = term(forceExpression
 214                                 ? EXPR
 215                                 : EXPR | TYPE);
 216                     }
 217                     if (token.kind == COLON && t.hasTag(IDENT)) {
 218                         // labelled statement
 219                         nextToken();
 220                         JCStatement stat = parseStatement();
 221                         return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat));
 222                     } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) {
 223                         // we have "Type Ident", so we can assume it is variable or method declaration
 224                         pos = token.pos;
 225                         Name name = ident();
 226                         if (token.kind == LPAREN) {
 227                         // method declaration
 228                             //mods.flags |= Flags.STATIC;
 229                             return List.of(methodDeclaratorRest(
 230                                     pos, mods, t, name, typarams,
 231                                     false, isVoid, dc));
 232                         } else if (!isVoid && typarams.isEmpty()) {
 233                         // variable declaration
 234                             //mods.flags |= Flags.STATIC;
 235                             List<JCTree> defs
 236                                     = variableDeclaratorsRest(pos, mods, t, name, false, dc,
 237                                             new ListBuffer<JCTree>(), true).toList();
 238                             accept(SEMI);
 239                             storeEnd(defs.last(), S.prevToken().endPos);
 240                             return defs;
 241                         } else {
 242                             // malformed declaration, return error
 243                             pos = token.pos;
 244                             List<JCTree> err = isVoid
 245                                     ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams,
 246                                                             List.nil(), List.nil(), null, null)))
 247                                     : null;
 248                             return List.<JCTree>of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
 249                         }
 250                     } else if (!typarams.isEmpty()) {
 251                         // type parameters on non-variable non-method -- error
 252                         return List.<JCTree>of(syntaxError(token.pos, Errors.IllegalStartOfType));
 253                     } else {
 254                         // expression-statement or expression to evaluate
 255                         JCExpressionStatement expr = toP(F.at(pos).Exec(t));
 256                         return List.<JCTree>of(expr);
 257                     }
 258 
 259                 }
 260         }
 261     }
 262 }