Mercurial > hg > openjdk > jdk9 > langtools
changeset 1145:2584f5358cba
Merge
author | lana |
---|---|
date | Tue, 06 Dec 2011 20:26:09 -0800 |
parents | ec2c0973cc31 (current diff) 3343b22e2761 (diff) |
children | 1cbe86c11ba6 |
files | |
diffstat | 30 files changed, 1370 insertions(+), 112 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/source/tree/LambdaExpressionTree.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011, 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 com.sun.source.tree; + +import java.util.List; + +/** + * A tree node for a lambda expression. + * + * For example: + * <pre> + * ()->{} + * (List<String> ls)->ls.size() + * (x,y)-> { return x + y; } + * </pre> + */ +public interface LambdaExpressionTree extends ExpressionTree { + + /** + * Lambda expressions come in two forms: (i) expression lambdas, whose body + * is an expression, and (ii) statement lambdas, whose body is a block + */ + public enum BodyKind { + /** enum constant for expression lambdas */ + EXPRESSION, + /** enum constant for statement lambdas */ + STATEMENT; + } + + List<? extends VariableTree> getParameters(); + Tree getBody(); + BodyKind getBodyKind(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/source/tree/MemberReferenceTree.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011, 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 com.sun.source.tree; + +import java.util.List; + +import javax.lang.model.element.Name; + +/** + * A tree node for a member reference expression. + * + * For example: + * <pre> + * <em>expression</em> # <em>[ identifier | new ]</em> + * </pre> + * + * @see JSR 292 + */ +public interface MemberReferenceTree extends ExpressionTree { + + /** + * There are two kinds of member references: (i) method references and + * (ii) constructor references + */ + public enum ReferenceMode { + /** enum constant for method references */ + INVOKE, + /** enum constant for constructor references */ + NEW + } + ReferenceMode getMode(); + ExpressionTree getQualifierExpression(); + Name getName(); + List<? extends ExpressionTree> getTypeArguments(); +}
--- a/src/share/classes/com/sun/source/tree/Tree.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/source/tree/Tree.java Tue Dec 06 20:26:09 2011 -0800 @@ -132,6 +132,11 @@ MEMBER_SELECT(MemberSelectTree.class), /** + * Used for instances of {@link MemberReferenceTree}. + */ + MEMBER_REFERENCE(MemberReferenceTree.class), + + /** * Used for instances of {@link ForLoopTree}. */ FOR_LOOP(ForLoopTree.class), @@ -187,6 +192,11 @@ NEW_CLASS(NewClassTree.class), /** + * Used for instances of {@link LambdaExpressionTree}. + */ + LAMBDA_EXPRESSION(LambdaExpressionTree.class), + + /** * Used for instances of {@link ParenthesizedTree}. */ PARENTHESIZED(ParenthesizedTree.class),
--- a/src/share/classes/com/sun/source/tree/TreeVisitor.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/source/tree/TreeVisitor.java Tue Dec 06 20:26:09 2011 -0800 @@ -85,9 +85,11 @@ R visitModifiers(ModifiersTree node, P p); R visitNewArray(NewArrayTree node, P p); R visitNewClass(NewClassTree node, P p); + R visitLambdaExpression(LambdaExpressionTree node, P p); R visitParenthesized(ParenthesizedTree node, P p); R visitReturn(ReturnTree node, P p); R visitMemberSelect(MemberSelectTree node, P p); + R visitMemberReference(MemberReferenceTree node, P p); R visitEmptyStatement(EmptyStatementTree node, P p); R visitSwitch(SwitchTree node, P p); R visitSynchronized(SynchronizedTree node, P p);
--- a/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java Tue Dec 06 20:26:09 2011 -0800 @@ -172,6 +172,10 @@ return defaultAction(node, p); } + public R visitLambdaExpression(LambdaExpressionTree node, P p) { + return defaultAction(node, p); + } + public R visitParenthesized(ParenthesizedTree node, P p) { return defaultAction(node, p); } @@ -208,6 +212,10 @@ return defaultAction(node, p); } + public R visitMemberReference(MemberReferenceTree node, P p) { + return defaultAction(node, p); + } + public R visitIdentifier(IdentifierTree node, P p) { return defaultAction(node, p); }
--- a/src/share/classes/com/sun/source/util/TreeScanner.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/source/util/TreeScanner.java Tue Dec 06 20:26:09 2011 -0800 @@ -285,6 +285,12 @@ return r; } + public R visitLambdaExpression(LambdaExpressionTree node, P p) { + R r = scan(node.getParameters(), p); + r = scanAndReduce(node.getBody(), p, r); + return r; + } + public R visitParenthesized(ParenthesizedTree node, P p) { return scan(node.getExpression(), p); } @@ -333,6 +339,12 @@ return scan(node.getExpression(), p); } + public R visitMemberReference(MemberReferenceTree node, P p) { + R r = scan(node.getQualifierExpression(), p); + r = scanAndReduce(node.getTypeArguments(), p, r); + return r; + } + public R visitIdentifier(IdentifierTree node, P p) { return null; }
--- a/src/share/classes/com/sun/tools/javac/code/Source.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/code/Source.java Tue Dec 06 20:26:09 2011 -0800 @@ -194,6 +194,12 @@ public boolean allowObjectToPrimitiveCast() { return compareTo(JDK1_7) >= 0; } + public boolean allowLambda() { + return compareTo(JDK1_8) >= 0; + } + public boolean allowMethodReferences() { + return compareTo(JDK1_8) >= 0; + } public static SourceVersion toSourceVersion(Source source) { switch(source) { case JDK1_2:
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue Dec 06 20:26:09 2011 -0800 @@ -1975,6 +1975,16 @@ result = check(tree, owntype, VAL, pkind, pt); } + @Override + public void visitLambda(JCLambda that) { + throw new UnsupportedOperationException("Lambda expression not supported yet"); + } + + @Override + public void visitReference(JCMemberReference that) { + throw new UnsupportedOperationException("Member references not supported yet"); + } + public void visitParens(JCParens tree) { Type owntype = attribTree(tree.expr, env, pkind, pt); result = check(tree, owntype, pkind, pkind, pt);
--- a/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java Tue Dec 06 20:26:09 2011 -0800 @@ -637,6 +637,10 @@ lexError(pos, "unclosed.str.lit"); } break loop; + case '#': + reader.scanChar(); + tk = TokenKind.HASH; + break loop; default: if (isSpecial(reader.ch)) { scanOperator();
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Tue Dec 06 20:26:09 2011 -0800 @@ -27,6 +27,8 @@ import java.util.*; +import com.sun.source.tree.MemberReferenceTree.ReferenceMode; + import com.sun.tools.javac.code.*; import com.sun.tools.javac.parser.Tokens.*; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; @@ -110,6 +112,10 @@ this.allowDiamond = source.allowDiamond(); this.allowMulticatch = source.allowMulticatch(); this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true); + this.allowLambda = source.allowLambda() && + fac.options.isSet("allowLambda"); + this.allowMethodReferences = source.allowMethodReferences() && + fac.options.isSet("allowMethodReferences"); this.keepDocComments = keepDocComments; docComments = keepDocComments ? new HashMap<JCTree,String>() : null; this.keepLineMap = keepLineMap; @@ -166,6 +172,14 @@ */ boolean allowStringFolding; + /** Switch: should we recognize lambda expressions? + */ + boolean allowLambda; + + /** Switch: should we allow method/constructor references? + */ + boolean allowMethodReferences; + /** Switch: should we keep docComments? */ boolean keepDocComments; @@ -203,6 +217,30 @@ token = S.token(); } + protected boolean peekToken(TokenKind tk) { + return S.token(1).kind == tk; + } + + protected boolean peekToken(TokenKind tk1, TokenKind tk2) { + return S.token(1).kind == tk1 && + S.token(2).kind == tk2; + } + + protected boolean peekToken(TokenKind tk1, TokenKind tk2, TokenKind tk3) { + return S.token(1).kind == tk1 && + S.token(2).kind == tk2 && + S.token(3).kind == tk3; + } + + protected boolean peekToken(TokenKind... kinds) { + for (int lookahead = 0 ; lookahead < kinds.length ; lookahead++) { + if (S.token(lookahead + 1).kind != kinds[lookahead]) { + return false; + } + } + return true; + } + /* ---------- error recovery -------------- */ private JCErroneous errorTree; @@ -749,7 +787,7 @@ top++; topOp = token; nextToken(); - odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3(); + odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3NoParams(); while (top > 0 && prec(topOp.kind) >= prec(token.kind)) { odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1], odStack[top]); @@ -849,7 +887,10 @@ * | [TypeArguments] THIS [Arguments] * | [TypeArguments] SUPER SuperSuffix * | NEW [TypeArguments] Creator + * | "(" Arguments ")" "->" ( Expression | Block ) + * | Ident "->" ( Expression | Block ) * | Ident { "." Ident } + * | Expression3 MemberReferenceSuffix * [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" ) * | Arguments * | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator ) @@ -890,60 +931,87 @@ mode = EXPR; t = literal(names.hyphen, pos); } else { - t = term3(); + t = term3NoParams(); return F.at(pos).Unary(unoptag(tk), t); } } else return illegal(); break; case LPAREN: if (typeArgs == null && (mode & EXPR) != 0) { - nextToken(); - mode = EXPR | TYPE | NOPARAMS; - t = term3(); - if ((mode & TYPE) != 0 && token.kind == LT) { - // Could be a cast to a parameterized type - JCTree.Tag op = JCTree.Tag.LT; - int pos1 = token.pos; + if (peekToken(FINAL) || + peekToken(RPAREN) || + peekToken(IDENTIFIER, COMMA) || + peekToken(IDENTIFIER, RPAREN, ARROW)) { + //implicit n-ary lambda + t = lambdaExpressionOrStatement(true, peekToken(FINAL), pos); + break; + } else { nextToken(); - mode &= (EXPR | TYPE); - mode |= TYPEARG; - JCExpression t1 = term3(); - if ((mode & TYPE) != 0 && - (token.kind == COMMA || token.kind == GT)) { - mode = TYPE; - ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); - args.append(t1); - while (token.kind == COMMA) { + mode = EXPR | TYPE; + t = term3NoParams(); + if ((mode & TYPE) != 0 && token.kind == LT) { + // Could be a cast to a parameterized type + JCTree.Tag op = JCTree.Tag.LT; + int pos1 = token.pos; + nextToken(); + mode &= (EXPR | TYPE); + mode |= TYPEARG; + JCExpression t1 = term3(); + if ((mode & TYPE) != 0 && + (token.kind == COMMA || token.kind == GT)) { + mode = TYPE; + ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); + args.append(t1); + while (token.kind == COMMA) { + nextToken(); + args.append(typeArgument()); + } + accept(GT); + t = toP(F.at(pos1).TypeApply(t, args.toList())); + checkGenerics(); + mode = EXPR | TYPE; //could be a lambda or a method ref or a cast to a type + t = term3Rest(t, typeArgs); + if (token.kind == IDENTIFIER || token.kind == ELLIPSIS) { + //explicit lambda (w/ generic type) + mode = EXPR; + JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER); + if (token.kind == ELLIPSIS) { + mods.flags = Flags.VARARGS; + t = to(F.at(token.pos).TypeArray(t)); + nextToken(); + } + t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos); + break; + } + } else { + Assert.check((mode & EXPR) != 0); + mode = EXPR; + JCExpression e = term2Rest(t1, TreeInfo.shiftPrec); + t = F.at(pos1).Binary(op, t, e); + t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); + } + } else if ((mode & TYPE) != 0 && + (token.kind == IDENTIFIER || token.kind == ELLIPSIS)) { + //explicit lambda (w/ non-generic type) + mode = EXPR; + JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER); + if (token.kind == ELLIPSIS) { + mods.flags = Flags.VARARGS; + t = to(F.at(token.pos).TypeArray(t)); nextToken(); - args.append(typeArgument()); } - accept(GT); - t = toP(F.at(pos1).TypeApply(t, args.toList())); - checkGenerics(); - while (token.kind == DOT) { - nextToken(); - mode = TYPE; - t = toP(F.at(token.pos).Select(t, ident())); - t = typeArgumentsOpt(t); - } - t = bracketsOpt(toP(t)); - } else if ((mode & EXPR) != 0) { - mode = EXPR; - JCExpression e = term2Rest(t1, TreeInfo.shiftPrec); - t = F.at(pos1).Binary(op, t, e); + t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos); + break; + } else { t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); - } else { - accept(GT); } } - else { - t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); - } + accept(RPAREN); lastmode = mode; mode = EXPR; if ((lastmode & EXPR) == 0) { - JCExpression t1 = term3(); + JCExpression t1 = term3NoParams(); return F.at(pos).TypeCast(t, t1); } else if ((lastmode & TYPE) != 0) { switch (token.kind) { @@ -953,14 +1021,16 @@ case INTLITERAL: case LONGLITERAL: case FLOATLITERAL: case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL: case TRUE: case FALSE: case NULL: - case NEW: case IDENTIFIER: case ASSERT: case ENUM: + case NEW: case IDENTIFIER: case ASSERT: case ENUM: case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID: - JCExpression t1 = term3(); + JCExpression t1 = term3NoParams(); return F.at(pos).TypeCast(t, t1); } } - } else return illegal(); + } else { + return illegal(); + } t = toP(F.at(pos).Parens(t)); break; case THIS: @@ -1003,75 +1073,122 @@ break; case IDENTIFIER: case ASSERT: case ENUM: if (typeArgs != null) return illegal(); - t = toP(F.at(token.pos).Ident(ident())); - loop: while (true) { - pos = token.pos; - switch (token.kind) { - case LBRACKET: - nextToken(); - if (token.kind == RBRACKET) { + if ((mode & EXPR) != 0 && peekToken(ARROW)) { + t = lambdaExpressionOrStatement(false, false, pos); + } else { + t = toP(F.at(token.pos).Ident(ident())); + loop: while (true) { + pos = token.pos; + switch (token.kind) { + case LBRACKET: nextToken(); - t = bracketsOpt(t); - t = toP(F.at(pos).TypeArray(t)); - t = bracketsSuffix(t); - } else { + if (token.kind == RBRACKET) { + nextToken(); + t = bracketsOpt(t); + t = toP(F.at(pos).TypeArray(t)); + t = bracketsSuffix(t); + } else { + if ((mode & EXPR) != 0) { + mode = EXPR; + JCExpression t1 = term(); + t = to(F.at(pos).Indexed(t, t1)); + } + accept(RBRACKET); + } + break loop; + case LPAREN: if ((mode & EXPR) != 0) { mode = EXPR; - JCExpression t1 = term(); - t = to(F.at(pos).Indexed(t, t1)); + t = arguments(typeArgs, t); + typeArgs = null; } - accept(RBRACKET); - } - break loop; - case LPAREN: - if ((mode & EXPR) != 0) { - mode = EXPR; - t = arguments(typeArgs, t); - typeArgs = null; - } - break loop; - case DOT: - nextToken(); - int oldmode = mode; - mode &= ~NOPARAMS; - typeArgs = typeArgumentsOpt(EXPR); - mode = oldmode; - if ((mode & EXPR) != 0) { - switch (token.kind) { - case CLASS: - if (typeArgs != null) return illegal(); - mode = EXPR; - t = to(F.at(pos).Select(t, names._class)); - nextToken(); - break loop; - case THIS: - if (typeArgs != null) return illegal(); - mode = EXPR; - t = to(F.at(pos).Select(t, names._this)); - nextToken(); - break loop; - case SUPER: - mode = EXPR; - t = to(F.at(pos).Select(t, names._super)); - t = superSuffix(typeArgs, t); - typeArgs = null; - break loop; - case NEW: - if (typeArgs != null) return illegal(); - mode = EXPR; + break loop; + case DOT: + nextToken(); + int oldmode = mode; + mode &= ~NOPARAMS; + typeArgs = typeArgumentsOpt(EXPR); + mode = oldmode; + if ((mode & EXPR) != 0) { + switch (token.kind) { + case CLASS: + if (typeArgs != null) return illegal(); + mode = EXPR; + t = to(F.at(pos).Select(t, names._class)); + nextToken(); + break loop; + case THIS: + if (typeArgs != null) return illegal(); + mode = EXPR; + t = to(F.at(pos).Select(t, names._this)); + nextToken(); + break loop; + case SUPER: + mode = EXPR; + t = to(F.at(pos).Select(t, names._super)); + t = superSuffix(typeArgs, t); + typeArgs = null; + break loop; + case NEW: + if (typeArgs != null) return illegal(); + mode = EXPR; + int pos1 = token.pos; + nextToken(); + if (token.kind == LT) typeArgs = typeArguments(false); + t = innerCreator(pos1, typeArgs, t); + typeArgs = null; + break loop; + } + } + // typeArgs saved for next loop iteration. + t = toP(F.at(pos).Select(t, ident())); + break; + case LT: + if ((mode & (TYPE | NOPARAMS)) == 0) { + //could be an unbound method reference whose qualifier + //is a generic type i.e. A<S>#m + mode = EXPR | TYPE; + JCTree.Tag op = JCTree.Tag.LT; int pos1 = token.pos; nextToken(); - if (token.kind == LT) typeArgs = typeArguments(false); - t = innerCreator(pos1, typeArgs, t); - typeArgs = null; - break loop; + mode |= EXPR | TYPE | TYPEARG; + JCExpression t1 = term3(); + if ((mode & TYPE) != 0 && + (token.kind == COMMA || token.kind == GT)) { + mode = TYPE; + ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); + args.append(t1); + while (token.kind == COMMA) { + nextToken(); + args.append(typeArgument()); + } + accept(GT); + t = toP(F.at(pos1).TypeApply(t, args.toList())); + checkGenerics(); + while (token.kind == DOT) { + nextToken(); + mode = TYPE; + t = toP(F.at(token.pos).Select(t, ident())); + t = typeArgumentsOpt(t); + } + if (token.kind != HASH) { + //method reference expected here + t = illegal(); + } + mode = EXPR; + break; + } else if ((mode & EXPR) != 0) { + //rollback - it was a binary expression + mode = EXPR; + JCExpression e = term2Rest(t1, TreeInfo.shiftPrec); + t = F.at(pos1).Binary(op, t, e); + t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); + } } + break loop; + default: + break loop; } - // typeArgs saved for next loop iteration. - t = toP(F.at(pos).Select(t, ident())); - break; - default: - break loop; } } if (typeArgs != null) illegal(); @@ -1105,6 +1222,19 @@ default: return illegal(); } + return term3Rest(t, typeArgs); + } + + JCExpression term3NoParams() { + try { + mode |= NOPARAMS; + return term3(); + } finally { + mode &= ~NOPARAMS; + } + } + + JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) { if (typeArgs != null) illegal(); while (true) { int pos1 = token.pos; @@ -1149,6 +1279,11 @@ t = argumentsOpt(typeArgs, typeArgumentsOpt(t)); typeArgs = null; } + } else if ((mode & EXPR) != 0 && token.kind == HASH) { + mode = EXPR; + if (typeArgs != null) return illegal(); + accept(HASH); + t = memberReferenceSuffix(pos1, t); } else { break; } @@ -1162,12 +1297,59 @@ return toP(t); } + JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) { + ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); + params.append(firstParam); + JCVariableDecl lastParam = firstParam; + while ((lastParam.mods.flags & Flags.VARARGS) == 0 && token.kind == COMMA) { + nextToken(); + params.append(lastParam = formalParameter()); + } + accept(RPAREN); + return lambdaExpressionOrStatementRest(params.toList(), pos); + } + + JCExpression lambdaExpressionOrStatement(boolean hasParens, boolean explicitParams, int pos) { + List<JCVariableDecl> params = explicitParams ? + formalParameters() : + implicitParameters(hasParens); + + return lambdaExpressionOrStatementRest(params, pos); + } + + JCExpression lambdaExpressionOrStatementRest(List<JCVariableDecl> args, int pos) { + if (token.kind != ARROW) { + //better error recovery + return F.at(pos).Erroneous(args); + } + + checkLambda(); + accept(ARROW); + + return token.kind == LBRACE ? + lambdaStatement(args, pos, pos) : + lambdaExpression(args, pos); + } + + JCExpression lambdaStatement(List<JCVariableDecl> args, int pos, int pos2) { + JCBlock block = block(pos2, 0); + return toP(F.at(pos).Lambda(args, block)); + } + + JCExpression lambdaExpression(List<JCVariableDecl> args, int pos) { + JCTree expr = parseExpression(); + return toP(F.at(pos).Lambda(args, expr)); + } + /** SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments] */ JCExpression superSuffix(List<JCExpression> typeArgs, JCExpression t) { nextToken(); if (token.kind == LPAREN || typeArgs != null) { t = arguments(typeArgs, t); + } else if (token.kind == HASH) { + if (typeArgs != null) return illegal(); + t = memberReferenceSuffix(t); } else { int pos = token.pos; accept(DOT); @@ -1377,6 +1559,36 @@ return t; } + /** + * MemberReferenceSuffix = "#" [TypeArguments] Ident + * | "#" [TypeArguments] "new" + */ + JCExpression memberReferenceSuffix(JCExpression t) { + int pos1 = token.pos; + accept(HASH); + return memberReferenceSuffix(pos1, t); + } + + JCExpression memberReferenceSuffix(int pos1, JCExpression t) { + checkMethodReferences(); + mode = EXPR; + List<JCExpression> typeArgs = null; + if (token.kind == LT) { + typeArgs = typeArguments(false); + } + Name refName = null; + ReferenceMode refMode = null; + if (token.kind == NEW) { + refMode = ReferenceMode.NEW; + refName = names.init; + nextToken(); + } else { + refMode = ReferenceMode.INVOKE; + refName = ident(); + } + return toP(F.at(t.getStartPosition()).Reference(refMode, refName, t, typeArgs)); + } + /** Creator = Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest ) */ JCExpression creator(int newpos, List<JCExpression> typeArgs) { @@ -2779,6 +2991,24 @@ return params.toList(); } + List<JCVariableDecl> implicitParameters(boolean hasParens) { + if (hasParens) { + accept(LPAREN); + } + ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); + if (token.kind != RPAREN && token.kind != ARROW) { + params.append(implicitParameter()); + while (token.kind == COMMA) { + nextToken(); + params.append(implicitParameter()); + } + } + if (hasParens) { + accept(RPAREN); + } + return params.toList(); + } + JCModifiers optFinal(long flags) { JCModifiers mods = modifiersOpt(); checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED)); @@ -2801,6 +3031,11 @@ return variableDeclaratorId(mods, type); } + protected JCVariableDecl implicitParameter() { + JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER); + return variableDeclaratorId(mods, null); + } + /* ---------- auxiliary methods -------------- */ void error(int pos, String key, Object ... args) { @@ -3024,6 +3259,18 @@ allowTWR = true; } } + void checkLambda() { + if (!allowLambda) { + log.error(token.pos, "lambda.not.supported.in.source", source.name); + allowLambda = true; + } + } + void checkMethodReferences() { + if (!allowMethodReferences) { + log.error(token.pos, "method.references.not.supported.in.source", source.name); + allowMethodReferences = true; + } + } /* * a functional source tree and end position mappings
--- a/src/share/classes/com/sun/tools/javac/parser/Lexer.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/parser/Lexer.java Tue Dec 06 20:26:09 2011 -0800 @@ -50,6 +50,11 @@ Token token(); /** + * Return token with given lookahead. + */ + Token token(int lookahead); + + /** * Return the last character position of the previous token. */ Token prevToken();
--- a/src/share/classes/com/sun/tools/javac/parser/Scanner.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/parser/Scanner.java Tue Dec 06 20:26:09 2011 -0800 @@ -26,8 +26,9 @@ package com.sun.tools.javac.parser; import java.nio.*; +import java.util.List; +import java.util.ArrayList; -import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.Position.LineMap; import com.sun.tools.javac.parser.JavaTokenizer.*; @@ -53,6 +54,10 @@ */ private Token prevToken; + /** Buffer of saved tokens (used during lookahead) + */ + private List<Token> savedTokens = new ArrayList<Token>(); + private JavaTokenizer tokenizer; /** * Create a scanner from the input array. This method might @@ -80,16 +85,35 @@ } public Token token() { - return token; + return token(0); } + public Token token(int lookahead) { + if (lookahead == 0) { + return token; + } else { + ensureLookahead(lookahead); + return savedTokens.get(lookahead - 1); + } + } + //where + private void ensureLookahead(int lookahead) { + for (int i = savedTokens.size() ; i < lookahead ; i ++) { + savedTokens.add(tokenizer.readToken()); + } + } + public Token prevToken() { return prevToken; } public void nextToken() { prevToken = token; - token = tokenizer.readToken(); + if (!savedTokens.isEmpty()) { + token = savedTokens.remove(0); + } else { + token = tokenizer.readToken(); + } } public Token split() {
--- a/src/share/classes/com/sun/tools/javac/parser/Tokens.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/parser/Tokens.java Tue Dec 06 20:26:09 2011 -0800 @@ -176,6 +176,8 @@ TRUE("true", Tag.NAMED), FALSE("false", Tag.NAMED), NULL("null", Tag.NAMED), + ARROW("->"), + HASH("#"), LPAREN("("), RPAREN(")"), LBRACE("{"),
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Dec 06 20:26:09 2011 -0800 @@ -1945,6 +1945,16 @@ strings in switch are not supported in -source {0}\n\ (use -source 7 or higher to enable strings in switch) +# 0: string +compiler.err.lambda.not.supported.in.source=\ + lambda expressions are not supported in -source {0}\n\ + (use -source 8 or higher to enable lambda expressions) + +# 0: string +compiler.err.method.references.not.supported.in.source=\ + method references are not supported in -source {0}\n\ + (use -source 8 or higher to enable method references) + ######################################## # Diagnostics for verbose resolution # used by Resolve (debug only)
--- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java Tue Dec 06 20:26:09 2011 -0800 @@ -41,6 +41,8 @@ import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.parser.EndPosTable; import com.sun.source.tree.*; +import com.sun.source.tree.LambdaExpressionTree.BodyKind; +import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import static com.sun.tools.javac.code.BoundKind.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; @@ -198,6 +200,10 @@ */ NEWARRAY, + /** Lambda expression, of type Lambda. + */ + LAMBDA, + /** Parenthesized subexpressions, of type Parens. */ PARENS, @@ -222,6 +228,10 @@ */ SELECT, + /** Member references, of type Reference. + */ + REFERENCE, + /** Simple identifiers, of type Ident. */ IDENT, @@ -1487,6 +1497,56 @@ } /** + * A lambda expression. + */ + public static class JCLambda extends JCExpression implements LambdaExpressionTree { + + public List<JCVariableDecl> params; + public JCTree body; + public Type targetType; + public boolean canCompleteNormally = true; + public List<Type> inferredThrownTypes; + + public JCLambda(List<JCVariableDecl> params, + JCTree body) { + this.params = params; + this.body = body; + } + @Override + public Tag getTag() { + return LAMBDA; + } + @Override + public void accept(Visitor v) { + v.visitLambda(this); + } + @Override + public <R, D> R accept(TreeVisitor<R, D> v, D d) { + return v.visitLambdaExpression(this, d); + } + public Kind getKind() { + return Kind.LAMBDA_EXPRESSION; + } + public JCTree getBody() { + return body; + } + public java.util.List<? extends VariableTree> getParameters() { + return params; + } + @Override + public JCLambda setType(Type type) { + super.setType(type); + return this; + } + @Override + public BodyKind getBodyKind() { + return body.hasTag(BLOCK) ? + BodyKind.STATEMENT : + BodyKind.EXPRESSION; + } + } + + /** * A parenthesized subexpression ( ... ) */ public static class JCParens extends JCExpression implements ParenthesizedTree { @@ -1747,6 +1807,46 @@ } /** + * Selects a member expression. + */ + public static class JCMemberReference extends JCExpression implements MemberReferenceTree { + public ReferenceMode mode; + public Name name; + public JCExpression expr; + public List<JCExpression> typeargs; + public Type targetType; + public Symbol sym; + + protected JCMemberReference(ReferenceMode mode, Name name, JCExpression expr, List<JCExpression> typeargs) { + this.mode = mode; + this.name = name; + this.expr = expr; + this.typeargs = typeargs; + } + @Override + public void accept(Visitor v) { v.visitReference(this); } + + public Kind getKind() { return Kind.MEMBER_REFERENCE; } + @Override + public ReferenceMode getMode() { return mode; } + @Override + public JCExpression getQualifierExpression() { return expr; } + @Override + public Name getName() { return name; } + @Override + public List<JCExpression> getTypeArguments() { return typeargs; } + + @Override + public <R,D> R accept(TreeVisitor<R,D> v, D d) { + return v.visitMemberReference(this, d); + } + @Override + public Tag getTag() { + return REFERENCE; + } + } + + /** * An identifier * @param idname the name * @param sym the symbol @@ -2271,6 +2371,7 @@ public void visitApply(JCMethodInvocation that) { visitTree(that); } public void visitNewClass(JCNewClass that) { visitTree(that); } public void visitNewArray(JCNewArray that) { visitTree(that); } + public void visitLambda(JCLambda that) { visitTree(that); } public void visitParens(JCParens that) { visitTree(that); } public void visitAssign(JCAssign that) { visitTree(that); } public void visitAssignop(JCAssignOp that) { visitTree(that); } @@ -2280,6 +2381,7 @@ public void visitTypeTest(JCInstanceOf that) { visitTree(that); } public void visitIndexed(JCArrayAccess that) { visitTree(that); } public void visitSelect(JCFieldAccess that) { visitTree(that); } + public void visitReference(JCMemberReference that) { visitTree(that); } public void visitIdent(JCIdent that) { visitTree(that); } public void visitLiteral(JCLiteral that) { visitTree(that); } public void visitTypeIdent(JCPrimitiveTypeTree that) { visitTree(that); }
--- a/src/share/classes/com/sun/tools/javac/tree/Pretty.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/Pretty.java Tue Dec 06 20:26:09 2011 -0800 @@ -28,6 +28,8 @@ import java.io.*; import java.util.*; +import com.sun.source.tree.MemberReferenceTree.ReferenceMode; + import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.List; import com.sun.tools.javac.code.*; @@ -907,6 +909,17 @@ } } + public void visitLambda(JCLambda tree) { + try { + print("("); + printExprs(tree.params); + print(")->"); + printExpr(tree.body); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitParens(JCParens tree) { try { print("("); @@ -1052,6 +1065,21 @@ } } + public void visitReference(JCMemberReference tree) { + try { + printExpr(tree.expr); + print("#"); + if (tree.typeargs != null) { + print("<"); + printExprs(tree.typeargs); + print(">"); + } + print(tree.getMode() == ReferenceMode.INVOKE ? tree.name : "new"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitIdent(JCIdent tree) { try { print(tree.name);
--- a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java Tue Dec 06 20:26:09 2011 -0800 @@ -271,6 +271,13 @@ return M.at(t.pos).NewClass(encl, typeargs, clazz, args, def); } + public JCTree visitLambdaExpression(LambdaExpressionTree node, P p) { + JCLambda t = (JCLambda) node; + List<JCVariableDecl> params = copy(t.params, p); + JCTree body = copy(t.body, p); + return M.at(t.pos).Lambda(params, body); + } + public JCTree visitParenthesized(ParenthesizedTree node, P p) { JCParens t = (JCParens) node; JCExpression expr = copy(t.expr, p); @@ -289,6 +296,13 @@ return M.at(t.pos).Select(selected, t.name); } + public JCTree visitMemberReference(MemberReferenceTree node, P p) { + JCMemberReference t = (JCMemberReference) node; + JCExpression expr = copy(t.expr, p); + List<JCExpression> typeargs = copy(t.typeargs, p); + return M.at(t.pos).Reference(t.mode, t.name, expr, typeargs); + } + public JCTree visitEmptyStatement(EmptyStatementTree node, P p) { JCSkip t = (JCSkip) node; return M.at(t.pos).Skip();
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java Tue Dec 06 20:26:09 2011 -0800 @@ -227,6 +227,34 @@ } } + /** + * Return true if the AST corresponds to a static select of the kind A.B + */ + public static boolean isStaticSelector(JCTree base, Names names) { + if (base == null) + return false; + switch (base.getTag()) { + case IDENT: + JCIdent id = (JCIdent)base; + return id.name != names._this && + id.name != names._super && + isStaticSym(base); + case SELECT: + return isStaticSym(base) && + isStaticSelector(((JCFieldAccess)base).selected, names); + case TYPEAPPLY: + return true; + default: + return false; + } + } + //where + private static boolean isStaticSym(JCTree tree) { + Symbol sym = symbol(tree); + return (sym.kind == Kinds.TYP || + sym.kind == Kinds.PCK); + } + /** Return true if a tree represents the null literal. */ public static boolean isNull(JCTree tree) { if (!tree.hasTag(LITERAL))
--- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java Tue Dec 06 20:26:09 2011 -0800 @@ -351,6 +351,14 @@ return tree; } + public JCLambda Lambda(List<JCVariableDecl> params, + JCTree body) + { + JCLambda tree = new JCLambda(params, body); + tree.pos = pos; + return tree; + } + public JCParens Parens(JCExpression expr) { JCParens tree = new JCParens(expr); tree.pos = pos; @@ -405,6 +413,13 @@ return tree; } + public JCMemberReference Reference(JCMemberReference.ReferenceMode mode, Name name, + JCExpression expr, List<JCExpression> typeargs) { + JCMemberReference tree = new JCMemberReference(mode, name, expr, typeargs); + tree.pos = pos; + return tree; + } + public JCIdent Ident(Name name) { JCIdent tree = new JCIdent(name, null); tree.pos = pos;
--- a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java Tue Dec 06 20:26:09 2011 -0800 @@ -212,6 +212,11 @@ scan(tree.elems); } + public void visitLambda(JCLambda tree) { + scan(tree.body); + scan(tree.params); + } + public void visitParens(JCParens tree) { scan(tree.expr); } @@ -254,6 +259,11 @@ scan(tree.selected); } + public void visitReference(JCMemberReference tree) { + scan(tree.expr); + scan(tree.typeargs); + } + public void visitIdent(JCIdent tree) { }
--- a/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Thu Dec 01 10:33:34 2011 -0800 +++ b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Tue Dec 06 20:26:09 2011 -0800 @@ -282,6 +282,12 @@ result = tree; } + public void visitLambda(JCLambda tree) { + tree.params = translate(tree.params); + tree.body = translate(tree.body); + result = tree; + } + public void visitNewArray(JCNewArray tree) { tree.elemtype = translate(tree.elemtype); tree.dims = translate(tree.dims); @@ -340,6 +346,11 @@ result = tree; } + public void visitReference(JCMemberReference tree) { + tree.expr = translate(tree.expr); + result = tree; + } + public void visitIdent(JCIdent tree) { result = tree; }
--- a/test/tools/javac/diags/examples/CatchWithoutTry.java Thu Dec 01 10:33:34 2011 -0800 +++ b/test/tools/javac/diags/examples/CatchWithoutTry.java Tue Dec 06 20:26:09 2011 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 @@ -23,7 +23,6 @@ // key: compiler.err.catch.without.try // key: compiler.err.expected -// key: compiler.err.not.stmt class CatchWithoutTry { void m() {
--- a/test/tools/javac/diags/examples/IllegalChar.java Thu Dec 01 10:33:34 2011 -0800 +++ b/test/tools/javac/diags/examples/IllegalChar.java Tue Dec 06 20:26:09 2011 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 @@ -24,5 +24,5 @@ // key: compiler.err.illegal.char class IllegalChar { - int i = #; + int i = `; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/diags/examples/LambdaNotSupported.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +// key: compiler.err.lambda.not.supported.in.source +// options: -source 7 -Xlint:-options + +class LambdaNotSupported { + S s = ()->{}; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/diags/examples/MethodReferencesNotSupported.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +// key: compiler.err.method.references.not.supported.in.source +// options: -source 7 -Xlint:-options + +class MethodReferencesNotSupported { + S s = A#foo; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/diags/examples/NotAStatement.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010, 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. + * + * 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. + */ + +// key: compiler.err.not.stmt + +class NotAStatement { + void m() { + x + 1; + } +}
--- a/test/tools/javac/generics/rare/6665356/T6665356.out Thu Dec 01 10:33:34 2011 -0800 +++ b/test/tools/javac/generics/rare/6665356/T6665356.out Tue Dec 06 20:26:09 2011 -0800 @@ -1,5 +1,5 @@ T6665356.java:17:37: compiler.err.improperly.formed.type.param.missing T6665356.java:18:40: compiler.err.improperly.formed.type.inner.raw.param -T6665356.java:26:23: compiler.err.improperly.formed.type.param.missing +T6665356.java:26:22: compiler.err.improperly.formed.type.param.missing T6665356.java:27:25: compiler.err.improperly.formed.type.inner.raw.param 4 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/LambdaParserTest.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +/* + * @test + * @bug 7115050 + * @summary Add parser support for lambda expressions + */ + +import com.sun.source.util.JavacTask; +import java.net.URI; +import java.util.Arrays; +import javax.tools.Diagnostic; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +public class LambdaParserTest { + + static int checkCount = 0; + + enum LambdaKind { + NILARY_EXPR("()->x"), + NILARY_STMT("()->{ return x; }"), + ONEARY_SHORT_EXPR("x->x"), + ONEARY_SHORT_STMT("x->{ return x; }"), + ONEARY_EXPR("(#M1 #T1 x)->x"), + ONEARY_STMT("(#M1 #T1 x)->{ return x; }"), + TWOARY_EXPR("(#M1 #T1 x, #M2 #T2 y)->x"), + TWOARY_STMT("(#M1 #T1 x, #M2 #T2 y)->{ return x; }"); + + String lambdaTemplate; + + LambdaKind(String lambdaTemplate) { + this.lambdaTemplate = lambdaTemplate; + } + + String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2, + ModifierKind mk1, ModifierKind mk2) { + return lambdaTemplate.replaceAll("#M1", mk1.modifier) + .replaceAll("#M2", mk2.modifier) + .replaceAll("#T1", pk1.parameterType) + .replaceAll("#T2", pk2.parameterType); + } + + int arity() { + switch (this) { + case NILARY_EXPR: + case NILARY_STMT: return 0; + case ONEARY_SHORT_EXPR: + case ONEARY_SHORT_STMT: + case ONEARY_EXPR: + case ONEARY_STMT: return 1; + case TWOARY_EXPR: + case TWOARY_STMT: return 2; + default: throw new AssertionError("Invalid lambda kind " + this); + } + } + + boolean isShort() { + return this == ONEARY_SHORT_EXPR || + this == ONEARY_SHORT_STMT; + } + } + + enum LambdaParameterKind { + IMPLICIT(""), + EXPLIICT_SIMPLE("A"), + EXPLICIT_VARARGS("A..."), + EXPLICIT_GENERIC1("A<X>"), + EXPLICIT_GENERIC3("A<? extends X, ? super Y>"); + + String parameterType; + + LambdaParameterKind(String parameterType) { + this.parameterType = parameterType; + } + + boolean explicit() { + return this != IMPLICIT; + } + } + + enum ModifierKind { + NONE(""), + FINAL("final"), + PUBLIC("public"); + + String modifier; + + ModifierKind(String modifier) { + this.modifier = modifier; + } + + boolean compatibleWith(LambdaParameterKind pk) { + switch (this) { + case PUBLIC: return false; + case FINAL: return pk != LambdaParameterKind.IMPLICIT; + case NONE: return true; + default: throw new AssertionError("Invalid modifier kind " + this); + } + } + } + + enum ExprKind { + NONE("#L#S"), + SINGLE_PAREN1("(#L#S)"), + SINGLE_PAREN2("(#L)#S"), + DOUBLE_PAREN1("((#L#S))"), + DOUBLE_PAREN2("((#L)#S)"), + DOUBLE_PAREN3("((#L))#S"); + + String expressionTemplate; + + ExprKind(String expressionTemplate) { + this.expressionTemplate = expressionTemplate; + } + + String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2, + ModifierKind mk1, ModifierKind mk2, LambdaKind lk, SubExprKind sk) { + return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2)) + .replaceAll("#S", sk.subExpression); + } + } + + enum SubExprKind { + NONE(""), + SELECT_FIELD(".f"), + SELECT_METHOD(".f()"), + SELECT_NEW(".new Foo()"), + POSTINC("++"), + POSTDEC("--"); + + String subExpression; + + SubExprKind(String subExpression) { + this.subExpression = subExpression; + } + } + + public static void main(String... args) throws Exception { + + //create default shared JavaCompiler - reused across multiple compilations + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); + + for (LambdaKind lk : LambdaKind.values()) { + for (LambdaParameterKind pk1 : LambdaParameterKind.values()) { + if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) continue; + for (LambdaParameterKind pk2 : LambdaParameterKind.values()) { + if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) continue; + for (ModifierKind mk1 : ModifierKind.values()) { + if (mk1 != ModifierKind.NONE && lk.isShort()) continue; + if (lk.arity() < 1 && mk1 != ModifierKind.NONE) continue; + for (ModifierKind mk2 : ModifierKind.values()) { + if (lk.arity() < 2 && mk2 != ModifierKind.NONE) continue; + for (SubExprKind sk : SubExprKind.values()) { + for (ExprKind ek : ExprKind.values()) { + new LambdaParserTest(pk1, pk2, mk1, mk2, lk, sk, ek) + .run(comp, fm); + } + } + } + } + } + } + } + System.out.println("Total check executed: " + checkCount); + } + + LambdaParameterKind pk1; + LambdaParameterKind pk2; + ModifierKind mk1; + ModifierKind mk2; + LambdaKind lk; + SubExprKind sk; + ExprKind ek; + JavaSource source; + DiagnosticChecker diagChecker; + + LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1, + ModifierKind mk2, LambdaKind lk, SubExprKind sk, ExprKind ek) { + this.pk1 = pk1; + this.pk2 = pk2; + this.mk1 = mk1; + this.mk2 = mk2; + this.lk = lk; + this.sk = sk; + this.ek = ek; + this.source = new JavaSource(); + this.diagChecker = new DiagnosticChecker(); + } + + class JavaSource extends SimpleJavaFileObject { + + String template = "class Test {\n" + + " SAM s = #E;\n" + + "}"; + + String source; + + public JavaSource() { + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); + source = template.replaceAll("#E", ek.expressionString(pk1, pk2, mk1, mk2, lk, sk)); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return source; + } + } + + void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { + JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, + Arrays.asList("-XDallowLambda"), null, Arrays.asList(source)); + try { + ct.parse(); + } catch (Throwable ex) { + throw new AssertionError("Error thron when parsing the following source:\n" + source.getCharContent(true)); + } + check(); + } + + void check() { + checkCount++; + + boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) || + (lk.arity() > 1 && !mk2.compatibleWith(pk2)); + + if (lk.arity() == 2 && + (pk1.explicit() != pk2.explicit() || + pk1 == LambdaParameterKind.EXPLICIT_VARARGS)) { + errorExpected = true; + } + + if (errorExpected != diagChecker.errorFound) { + throw new Error("invalid diagnostics for source:\n" + + source.getCharContent(true) + + "\nFound error: " + diagChecker.errorFound + + "\nExpected error: " + errorExpected); + } + } + + static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { + + boolean errorFound; + + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/MethodReferenceParserTest.java Tue Dec 06 20:26:09 2011 -0800 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +/* + * @test + * @bug 7115052 + * @summary Add parser support for method references + */ + +import com.sun.source.util.JavacTask; +import java.net.URI; +import java.util.Arrays; +import javax.tools.Diagnostic; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +public class MethodReferenceParserTest { + + static int checkCount = 0; + + enum ReferenceKind { + METHOD_REF("#Q##Gm"), + CONSTRUCTOR_REF("#Q##Gnew"), + ERR_SUPER("#Q##Gsuper"), + ERR_METH0("#Q##Gm()"), + ERR_METH1("#Q##Gm(X)"), + ERR_CONSTR0("#Q##Gnew()"), + ERR_CONSTR1("#Q##Gnew(X)"); + + String referenceTemplate; + + ReferenceKind(String referenceTemplate) { + this.referenceTemplate = referenceTemplate; + } + + String getReferenceString(QualifierKind qk, GenericKind gk) { + return referenceTemplate + .replaceAll("#Q", qk.qualifier) + .replaceAll("#G", gk.typeParameters); + } + + boolean erroneous() { + switch (this) { + case ERR_SUPER: + case ERR_METH0: + case ERR_METH1: + case ERR_CONSTR0: + case ERR_CONSTR1: + return true; + default: return false; + } + } + } + + enum GenericKind { + NONE(""), + ONE("<X>"), + TWO("<X,Y>"); + + String typeParameters; + + GenericKind(String typeParameters) { + this.typeParameters = typeParameters; + } + } + + enum QualifierKind { + THIS("this"), + SUPER("super"), + NEW("new Foo()"), + METHOD("m()"), + FIELD("a.f"), + UBOUND_SIMPLE("A"), + UNBOUND_GENERIC1("A<X>"), + UNBOUND_GENERIC2("A<X, Y>"), + UNBOUND_GENERIC3("A<? extends X, ? super Y>"); + + String qualifier; + + QualifierKind(String qualifier) { + this.qualifier = qualifier; + } + } + + enum ExprKind { + NONE("#R#S"), + SINGLE_PAREN1("(#R#S)"), + SINGLE_PAREN2("(#R)#S"), + DOUBLE_PAREN1("((#R#S))"), + DOUBLE_PAREN2("((#R)#S)"), + DOUBLE_PAREN3("((#R))#S"); + + String expressionTemplate; + + ExprKind(String expressionTemplate) { + this.expressionTemplate = expressionTemplate; + } + + String expressionString(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk) { + return expressionTemplate + .replaceAll("#R", rk.getReferenceString(qk, gk)) + .replaceAll("#S", sk.subExpression); + } + } + + enum SubExprKind { + NONE(""), + SELECT_FIELD(".f"), + SELECT_METHOD(".f()"), + SELECT_NEW(".new Foo()"), + POSTINC("++"), + POSTDEC("--"); + + String subExpression; + + SubExprKind(String subExpression) { + this.subExpression = subExpression; + } + } + + public static void main(String... args) throws Exception { + + //create default shared JavaCompiler - reused across multiple compilations + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); + + for (ReferenceKind rk : ReferenceKind.values()) { + for (QualifierKind qk : QualifierKind.values()) { + for (GenericKind gk : GenericKind.values()) { + for (SubExprKind sk : SubExprKind.values()) { + for (ExprKind ek : ExprKind.values()) { + new MethodReferenceParserTest(rk, qk, gk, sk, ek).run(comp, fm); + } + } + } + } + } + System.out.println("Total check executed: " + checkCount); + } + + ReferenceKind rk; + QualifierKind qk; + GenericKind gk; + SubExprKind sk; + ExprKind ek; + JavaSource source; + DiagnosticChecker diagChecker; + + MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek) { + this.rk = rk; + this.qk = qk; + this.gk = gk; + this.sk = sk; + this.ek = ek; + this.source = new JavaSource(); + this.diagChecker = new DiagnosticChecker(); + } + + class JavaSource extends SimpleJavaFileObject { + + String template = "class Test {\n" + + " SAM s = #E;\n" + + "}"; + + String source; + + public JavaSource() { + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); + source = template.replaceAll("#E", ek.expressionString(rk, qk, gk, sk)); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return source; + } + } + + void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { + JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, + Arrays.asList("-XDallowMethodReferences"), null, Arrays.asList(source)); + try { + ct.parse(); + } catch (Throwable ex) { + throw new AssertionError("Error thrown when parsing the following source:\n" + source.getCharContent(true)); + } + check(); + } + + void check() { + checkCount++; + + if (diagChecker.errorFound != rk.erroneous()) { + throw new Error("invalid diagnostics for source:\n" + + source.getCharContent(true) + + "\nFound error: " + diagChecker.errorFound + + "\nExpected error: " + rk.erroneous()); + } + } + + static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { + + boolean errorFound; + + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +}
--- a/test/tools/javac/quid/T6999438.out Thu Dec 01 10:33:34 2011 -0800 +++ b/test/tools/javac/quid/T6999438.out Tue Dec 06 20:26:09 2011 -0800 @@ -1,4 +1,4 @@ -T6999438.java:8:9: compiler.err.illegal.char: 35 +T6999438.java:8:8: compiler.err.expected: token.identifier T6999438.java:8:10: compiler.err.illegal.start.of.type T6999438.java:8:25: compiler.err.expected: token.identifier T6999438.java:8:26: compiler.err.expected: ';'