| /* | |
| * Copyright (c) 2014, 2023, 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; | |
| 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.Tokens.Comment; | |
| import com.sun.tools.javac.parser.Tokens.Token; | |
| 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.SWITCH; | |
| 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; | |
| private final Source source; | |
| public ReplParser(ReplParserFactory 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; | |
| this.source = fac.source; | |
| } | |
| /** | |
| * 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.docComment(); | |
| if (firstTypeDecl && !seenImport && !seenPackage) { | |
| docComment = firstToken.docComment(); | |
| } | |
| 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 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 | |
| case SWITCH: | |
| if (token.kind == SWITCH && !Feature.SWITCH_EXPRESSION.allowedInSource(source)) { | |
| return List.<JCTree>of(parseStatement()); | |
| } | |
| //fall-through | |
| default: | |
| JCModifiers mods = modifiersOpt(pmods); | |
| if (token.kind == CLASS | |
| || isRecordStart() | |
| || token.kind == INTERFACE | |
| || token.kind == ENUM) { | |
| return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(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()) { | |
| 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.test(token.kind)) { | |
| // we have "Type Ident", so we can assume it is variable or method declaration | |
| pos = token.pos; | |
| Name name = identOrUnderscore(); | |
| if (token.kind == LPAREN) { | |
| // method declaration | |
| //mods.flags |= Flags.STATIC; | |
| return List.of(methodDeclaratorRest( | |
| pos, mods, t, name, typarams, | |
| false, isVoid, false, 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); | |
| } | |
| } | |
| } | |
| } | |
| } |