1 /*
   2  * Copyright (c) 2014, 2017, 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 import static com.sun.tools.javac.parser.Tokens.TokenKind.IDENTIFIER;
  63 /**
  64  * This is a subclass of JavacParser which overrides one method with a modified
  65  * verson of that method designed to allow parsing of one "snippet" of Java
  66  * code without the surrounding context of class, method, etc.
  67  * Accepts an expression, a statement, an import, or the declaration of a
  68  * method, variable, or type (class, interface, ...).
  69  */
  70 class ReplParser extends JavacParser {
  71 
  72     // force starting in expression mode
  73     private final boolean forceExpression;
  74 
  75     public ReplParser(ParserFactory fac,
  76             com.sun.tools.javac.parser.Lexer S,
  77             boolean keepDocComments,
  78             boolean keepLineMap,
  79             boolean keepEndPositions,
  80             boolean forceExpression) {
  81         super(fac, S, keepDocComments, keepLineMap, keepEndPositions);
  82         this.forceExpression = forceExpression;
  83     }
  84 
  85     /**
  86      * As faithful a clone of the overridden method as possible while still
  87      * achieving the goal of allowing the parse of a stand-alone snippet.
  88      * As a result, some variables are assigned and never used, tests are
  89      * always true, loops don't, etc.  This is to allow easy transition as the
  90      * underlying method changes.
  91      * @return a snippet wrapped in a compilation unit
  92      */
  93     @Override
  94     public JCCompilationUnit parseCompilationUnit() {
  95         Token firstToken = token;
  96         JCModifiers mods = null;
  97         boolean seenImport = false;
  98         boolean seenPackage = false;
  99         ListBuffer<JCTree> defs = new ListBuffer<>();
 100         if (token.kind == MONKEYS_AT) {
 101             mods = modifiersOpt();
 102         }
 103 
 104         boolean firstTypeDecl = true;
 105         while (token.kind != EOF) {
 106             if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) {
 107                 // error recovery
 108                 skip(true, false, false, false);
 109                 if (token.kind == EOF) {
 110                     break;
 111                 }
 112             }
 113             if (mods == null && token.kind == IMPORT) {
 114                 seenImport = true;
 115                 defs.append(importDeclaration());
 116             } else {
 117                 Comment docComment = token.comment(CommentStyle.JAVADOC);
 118                 if (firstTypeDecl && !seenImport && !seenPackage) {
 119                     docComment = firstToken.comment(CommentStyle.JAVADOC);
 120                 }
 121                 List<? extends JCTree> udefs = replUnit(mods, docComment);
 122                // if (def instanceof JCExpressionStatement)
 123                 //     def = ((JCExpressionStatement)def).expr;
 124                 for (JCTree def : udefs) {
 125                     defs.append(def);
 126                 }
 127                 mods = null;
 128                 firstTypeDecl = false;
 129             }
 130             break;  // Remove to process more than one snippet
 131         }
 132         List<JCTree> rdefs = defs.toList();
 133         class ReplUnit extends JCCompilationUnit {
 134 
 135             public ReplUnit(List<JCTree> defs) {
 136                 super(defs);
 137             }
 138         }
 139         JCCompilationUnit toplevel = new ReplUnit(rdefs);
 140         if (rdefs.isEmpty()) {
 141             storeEnd(toplevel, S.prevToken().endPos);
 142         }
 143         toplevel.lineMap = S.getLineMap();
 144         this.endPosTable.setParser(null); // remove reference to parser
 145         toplevel.endPositions = this.endPosTable;
 146         return toplevel;
 147     }
 148 
 149     @SuppressWarnings("fallthrough")
 150      List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) {
 151         switch (token.kind) {
 152             case EOF:
 153                 return List.nil();
 154             case RBRACE:
 155             case CASE:
 156             case DEFAULT:
 157                 // These are illegal, fall through to handle as illegal statement
 158             case LBRACE:
 159             case IF:
 160             case FOR:
 161             case WHILE:
 162             case DO:
 163             case TRY:
 164             case RETURN:
 165             case THROW:
 166             case BREAK:
 167             case CONTINUE:
 168             case SEMI:
 169             case ELSE:
 170             case FINALLY:
 171             case CATCH:
 172             case ASSERT:
 173                 return List.<JCTree>of(parseStatement());
 174             case SYNCHRONIZED:
 175                 if (peekToken(LPAREN)) {
 176                     return List.<JCTree>of(parseStatement());
 177                 }
 178                 //fall-through
 179             default:
 180                 JCModifiers mods = modifiersOpt(pmods);
 181                 if (token.kind == CLASS
 182                         || token.kind == IDENTIFIER && token.name() == names.record
 183                         || token.kind == INTERFACE
 184                         || token.kind == ENUM) {
 185                     return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
 186                 } else {
 187                     int pos = token.pos;
 188                     List<JCTypeParameter> typarams = typeParametersOpt();
 189                     // if there are type parameters but no modifiers, save the start
 190                     // position of the method in the modifiers.
 191                     if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
 192                         mods.pos = pos;
 193                         storeEnd(mods, pos);
 194                     }
 195                     List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
 196 
 197                     if (annosAfterParams.nonEmpty()) {
 198                         checkSourceLevel(annosAfterParams.head.pos, Feature.ANNOTATIONS_AFTER_TYPE_PARAMS);
 199                         mods.annotations = mods.annotations.appendList(annosAfterParams);
 200                         if (mods.pos == Position.NOPOS) {
 201                             mods.pos = mods.annotations.head.pos;
 202                         }
 203                     }
 204 
 205                     Token prevToken = token;
 206                     pos = token.pos;
 207                     JCExpression t;
 208                     boolean isVoid = token.kind == VOID;
 209                     if (isVoid) {
 210                         t = to(F.at(pos).TypeIdent(TypeTag.VOID));
 211                         nextToken();
 212                     } else {
 213                         // return type of method, declared type of variable, or an expression
 214                         // unless expression is being forced
 215                         t = term(forceExpression
 216                                 ? EXPR
 217                                 : EXPR | TYPE);
 218                     }
 219                     if (token.kind == COLON && t.hasTag(IDENT)) {
 220                         // labelled statement
 221                         nextToken();
 222                         JCStatement stat = parseStatement();
 223                         return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat));
 224                     } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) {
 225                         // we have "Type Ident", so we can assume it is variable or method declaration
 226                         pos = token.pos;
 227                         Name name = ident();
 228                         if (token.kind == LPAREN) {
 229                         // method declaration
 230                             //mods.flags |= Flags.STATIC;
 231                             return List.of(methodDeclaratorRest(
 232                                     pos, mods, t, name, typarams,
 233                                     false, isVoid, false, dc));
 234                         } else if (!isVoid && typarams.isEmpty()) {
 235                         // variable declaration
 236                             //mods.flags |= Flags.STATIC;
 237                             List<JCTree> defs
 238                                     = variableDeclaratorsRest(pos, mods, t, name, false, dc,
 239                                             new ListBuffer<JCTree>(), true).toList();
 240                             accept(SEMI);
 241                             storeEnd(defs.last(), S.prevToken().endPos);
 242                             return defs;
 243                         } else {
 244                             // malformed declaration, return error
 245                             pos = token.pos;
 246                             List<JCTree> err = isVoid
 247                                     ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams,
 248                                                             List.nil(), List.nil(), null, null)))
 249                                     : null;
 250                             return List.<JCTree>of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
 251                         }
 252                     } else if (!typarams.isEmpty()) {
 253                         // type parameters on non-variable non-method -- error
 254                         return List.<JCTree>of(syntaxError(token.pos, Errors.IllegalStartOfType));
 255                     } else {
 256                         // expression-statement or expression to evaluate
 257                         JCExpressionStatement expr = toP(F.at(pos).Exec(t));
 258                         return List.<JCTree>of(expr);
 259                     }
 260 
 261                 }
 262         }
 263     }
 264 }