diff --git a/src/share/classes/com/sun/source/tree/LambdaExpressionTree.java b/src/share/classes/com/sun/source/tree/LambdaExpressionTree.java new file mode 100644 index 0000000000000000000000000000000000000000..61ae45aef8722f0b510e002b6927739699fad431 --- /dev/null +++ b/src/share/classes/com/sun/source/tree/LambdaExpressionTree.java @@ -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: + *
+ *   ()->{}
+ *   (List ls)->ls.size()
+ *   (x,y)-> { return x + y; }
+ * 
+ */ +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 getParameters(); + Tree getBody(); + BodyKind getBodyKind(); +} diff --git a/src/share/classes/com/sun/source/tree/MemberReferenceTree.java b/src/share/classes/com/sun/source/tree/MemberReferenceTree.java new file mode 100644 index 0000000000000000000000000000000000000000..68efd16dc36ca801d45c8bedd29b37e64fc2a11f --- /dev/null +++ b/src/share/classes/com/sun/source/tree/MemberReferenceTree.java @@ -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: + *
+ *   expression # [ identifier | new ]
+ * 
+ * + * @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 getTypeArguments(); +} diff --git a/src/share/classes/com/sun/source/tree/Tree.java b/src/share/classes/com/sun/source/tree/Tree.java index 5e26c18936c8893d790802cfd85f6b1346532e91..a558e9e59993a0606832948591100b86a97a776e 100644 --- a/src/share/classes/com/sun/source/tree/Tree.java +++ b/src/share/classes/com/sun/source/tree/Tree.java @@ -131,6 +131,11 @@ public interface Tree { */ MEMBER_SELECT(MemberSelectTree.class), + /** + * Used for instances of {@link MemberReferenceTree}. + */ + MEMBER_REFERENCE(MemberReferenceTree.class), + /** * Used for instances of {@link ForLoopTree}. */ @@ -186,6 +191,11 @@ public interface Tree { */ NEW_CLASS(NewClassTree.class), + /** + * Used for instances of {@link LambdaExpressionTree}. + */ + LAMBDA_EXPRESSION(LambdaExpressionTree.class), + /** * Used for instances of {@link ParenthesizedTree}. */ diff --git a/src/share/classes/com/sun/source/tree/TreeVisitor.java b/src/share/classes/com/sun/source/tree/TreeVisitor.java index ffaf0c3e5637f574f19449cde4a1f0d510b91523..dd3cfcbf699b61404e991829101b83a2669ecb0e 100644 --- a/src/share/classes/com/sun/source/tree/TreeVisitor.java +++ b/src/share/classes/com/sun/source/tree/TreeVisitor.java @@ -85,9 +85,11 @@ public interface TreeVisitor { 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); diff --git a/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java index fc406b457d77a0946ef40d048378a4b107dd61a9..04e69161b4e94b0ad17a684818677ff4e15b838e 100644 --- a/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java +++ b/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java @@ -172,6 +172,10 @@ public class SimpleTreeVisitor implements TreeVisitor { 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 @@ public class SimpleTreeVisitor implements TreeVisitor { 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); } diff --git a/src/share/classes/com/sun/source/util/TreeScanner.java b/src/share/classes/com/sun/source/util/TreeScanner.java index e87189dcc3c7cb1fedc7ac36f0fd2aa57c5bd38c..3619f31358e94ba2131a083faca1228c82fefdcc 100644 --- a/src/share/classes/com/sun/source/util/TreeScanner.java +++ b/src/share/classes/com/sun/source/util/TreeScanner.java @@ -285,6 +285,12 @@ public class TreeScanner implements TreeVisitor { 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 @@ public class TreeScanner implements TreeVisitor { 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; } diff --git a/src/share/classes/com/sun/tools/javac/code/Source.java b/src/share/classes/com/sun/tools/javac/code/Source.java index 2a0948cec9c5b120f60ddabab74cd46359a22106..d4695989a40c0b79fcf12f1d825cea5746d6c116 100644 --- a/src/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/share/classes/com/sun/tools/javac/code/Source.java @@ -194,6 +194,12 @@ public enum Source { 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: diff --git a/src/share/classes/com/sun/tools/javac/comp/Attr.java b/src/share/classes/com/sun/tools/javac/comp/Attr.java index ebecb033566eaa9238a2e723d9764834acab0286..deb9484bc504478f595790ad751ba69904f857f7 100644 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1975,6 +1975,16 @@ public class Attr extends JCTree.Visitor { 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); diff --git a/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index f9ce7a251defc9881f672671c92d5dd8bd07ae90..f0945389e70860dbecc780b6bcd9f4e4646a6448 100644 --- a/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -637,6 +637,10 @@ public class JavaTokenizer { lexError(pos, "unclosed.str.lit"); } break loop; + case '#': + reader.scanChar(); + tk = TokenKind.HASH; + break loop; default: if (isSpecial(reader.ch)) { scanOperator(); diff --git a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java index f2f49ae3bca198616d7173ee09ee89fb85510742..8903acb5d3a19af9533f85ef4eeb805c7de477d6 100644 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -27,6 +27,8 @@ package com.sun.tools.javac.parser; 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 @@ public class JavacParser implements Parser { 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() : null; this.keepLineMap = keepLineMap; @@ -166,6 +172,14 @@ public class JavacParser implements Parser { */ 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { * | [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 @@ public class JavacParser implements Parser { 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 args = new ListBuffer(); - 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 = 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; - t = toP(F.at(token.pos).Select(t, ident())); - t = typeArgumentsOpt(t); + ListBuffer args = new ListBuffer(); + 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))); } - t = bracketsOpt(toP(t)); - } else if ((mode & EXPR) != 0) { + } else if ((mode & TYPE) != 0 && + (token.kind == IDENTIFIER || token.kind == ELLIPSIS)) { + //explicit lambda (w/ non-generic type) mode = EXPR; - JCExpression e = term2Rest(t1, TreeInfo.shiftPrec); - t = F.at(pos1).Binary(op, t, e); - t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); + 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 { - accept(GT); + t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); } } - 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 ((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; - 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)); + if (token.kind == RBRACKET) { 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: + 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; - t = to(F.at(pos).Select(t, names._super)); - t = superSuffix(typeArgs, t); + t = arguments(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#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 args = new ListBuffer(); + 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 @@ public class JavacParser implements Parser { default: return illegal(); } + return term3Rest(t, typeArgs); + } + + JCExpression term3NoParams() { + try { + mode |= NOPARAMS; + return term3(); + } finally { + mode &= ~NOPARAMS; + } + } + + JCExpression term3Rest(JCExpression t, List typeArgs) { if (typeArgs != null) illegal(); while (true) { int pos1 = token.pos; @@ -1149,6 +1279,11 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { return toP(t); } + JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) { + ListBuffer params = new ListBuffer(); + 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 params = explicitParams ? + formalParameters() : + implicitParameters(hasParens); + + return lambdaExpressionOrStatementRest(params, pos); + } + + JCExpression lambdaExpressionOrStatementRest(List 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 args, int pos, int pos2) { + JCBlock block = block(pos2, 0); + return toP(F.at(pos).Lambda(args, block)); + } + + JCExpression lambdaExpression(List args, int pos) { + JCTree expr = parseExpression(); + return toP(F.at(pos).Lambda(args, expr)); + } + /** SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments] */ JCExpression superSuffix(List 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 @@ public class JavacParser implements Parser { 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 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 typeArgs) { @@ -2779,6 +2991,24 @@ public class JavacParser implements Parser { return params.toList(); } + List implicitParameters(boolean hasParens) { + if (hasParens) { + accept(LPAREN); + } + ListBuffer params = new ListBuffer(); + 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 diff --git a/src/share/classes/com/sun/tools/javac/parser/Lexer.java b/src/share/classes/com/sun/tools/javac/parser/Lexer.java index 57d7a985e631dd51c71e462e321bebc95f9ab2f5..ed428f8380346f2698ced9e951a187c41564d410 100644 --- a/src/share/classes/com/sun/tools/javac/parser/Lexer.java +++ b/src/share/classes/com/sun/tools/javac/parser/Lexer.java @@ -49,6 +49,11 @@ public interface Lexer { */ Token token(); + /** + * Return token with given lookahead. + */ + Token token(int lookahead); + /** * Return the last character position of the previous token. */ diff --git a/src/share/classes/com/sun/tools/javac/parser/Scanner.java b/src/share/classes/com/sun/tools/javac/parser/Scanner.java index 41f50cb011024f666f0124718460eb8a460004c8..93b2e8adecf37f84e659721ec534267e57ef194a 100644 --- a/src/share/classes/com/sun/tools/javac/parser/Scanner.java +++ b/src/share/classes/com/sun/tools/javac/parser/Scanner.java @@ -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 @@ public class Scanner implements Lexer { */ private Token prevToken; + /** Buffer of saved tokens (used during lookahead) + */ + private List savedTokens = new ArrayList(); + private JavaTokenizer tokenizer; /** * Create a scanner from the input array. This method might @@ -80,8 +85,23 @@ public class Scanner implements Lexer { } 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; @@ -89,7 +109,11 @@ public class Scanner implements Lexer { public void nextToken() { prevToken = token; - token = tokenizer.readToken(); + if (!savedTokens.isEmpty()) { + token = savedTokens.remove(0); + } else { + token = tokenizer.readToken(); + } } public Token split() { diff --git a/src/share/classes/com/sun/tools/javac/parser/Tokens.java b/src/share/classes/com/sun/tools/javac/parser/Tokens.java index db2e1ec3c63d36d58ffb74c78d51fc4f83da16b5..402e6db0eca59294746547dcc813ed03f5818db4 100644 --- a/src/share/classes/com/sun/tools/javac/parser/Tokens.java +++ b/src/share/classes/com/sun/tools/javac/parser/Tokens.java @@ -176,6 +176,8 @@ public class Tokens { TRUE("true", Tag.NAMED), FALSE("false", Tag.NAMED), NULL("null", Tag.NAMED), + ARROW("->"), + HASH("#"), LPAREN("("), RPAREN(")"), LBRACE("{"), diff --git a/src/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/share/classes/com/sun/tools/javac/resources/compiler.properties index 75a2aaf1a3312e16672fa61d6d1d6dbb6f9aafa5..14f1d6052bda7620810ee486b34f213af1e52400 100644 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1945,6 +1945,16 @@ compiler.err.string.switch.not.supported.in.source=\ 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) diff --git a/src/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/share/classes/com/sun/tools/javac/tree/JCTree.java index 9fd4055e4eb4e1b639383be35c7de29db0ff6744..59fc37e158aa96fc0d083d42a90e8c771baafbd5 100644 --- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -41,6 +41,8 @@ import com.sun.tools.javac.code.Scope.*; 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 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { */ NEWARRAY, + /** Lambda expression, of type Lambda. + */ + LAMBDA, + /** Parenthesized subexpressions, of type Parens. */ PARENS, @@ -222,6 +228,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { */ SELECT, + /** Member references, of type Reference. + */ + REFERENCE, + /** Simple identifiers, of type Ident. */ IDENT, @@ -1486,6 +1496,56 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } } + /** + * A lambda expression. + */ + public static class JCLambda extends JCExpression implements LambdaExpressionTree { + + public List params; + public JCTree body; + public Type targetType; + public boolean canCompleteNormally = true; + public List inferredThrownTypes; + + public JCLambda(List 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 accept(TreeVisitor v, D d) { + return v.visitLambdaExpression(this, d); + } + public Kind getKind() { + return Kind.LAMBDA_EXPRESSION; + } + public JCTree getBody() { + return body; + } + public java.util.List 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 ( ... ) */ @@ -1746,6 +1806,46 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } } + /** + * Selects a member expression. + */ + public static class JCMemberReference extends JCExpression implements MemberReferenceTree { + public ReferenceMode mode; + public Name name; + public JCExpression expr; + public List typeargs; + public Type targetType; + public Symbol sym; + + protected JCMemberReference(ReferenceMode mode, Name name, JCExpression expr, List 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 getTypeArguments() { return typeargs; } + + @Override + public R accept(TreeVisitor v, D d) { + return v.visitMemberReference(this, d); + } + @Override + public Tag getTag() { + return REFERENCE; + } + } + /** * An identifier * @param idname the name @@ -2271,6 +2371,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { 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 abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { 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); } diff --git a/src/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/share/classes/com/sun/tools/javac/tree/Pretty.java index e781fd130e6d37fa8ef2b5148a44026ee7ff7e81..824fa599ff7ea0634a41ca4c89880135e606f165 100644 --- a/src/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -28,6 +28,8 @@ package com.sun.tools.javac.tree; 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 class Pretty extends JCTree.Visitor { } } + 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 class Pretty extends JCTree.Visitor { } } + 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); diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java index 158abc4f161d41d9d5f664ff2a32f816e47bebe7..f8efaa320189edf2ed48b6b679ff04957462596d 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -271,6 +271,13 @@ public class TreeCopier

implements TreeVisitor { return M.at(t.pos).NewClass(encl, typeargs, clazz, args, def); } + public JCTree visitLambdaExpression(LambdaExpressionTree node, P p) { + JCLambda t = (JCLambda) node; + List 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 @@ public class TreeCopier

implements TreeVisitor { 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 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(); diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java index d47d7a79ff1235ed77cecc9cd326f7eb7b7f2462..00eef07509f93d71cc8fac379f7024d6d6d95266 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -227,6 +227,34 @@ public class TreeInfo { } } + /** + * 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)) diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 7f673023def828ff705d609be0cfc7dc988ab257..6191f4b88757367825bb433581475b06fb1dbae1 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -351,6 +351,14 @@ public class TreeMaker implements JCTree.Factory { return tree; } + public JCLambda Lambda(List 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 @@ public class TreeMaker implements JCTree.Factory { return tree; } + public JCMemberReference Reference(JCMemberReference.ReferenceMode mode, Name name, + JCExpression expr, List 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; diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java index 19619665d768ea6c59af014284db2782ad8c8df2..9a7c321f368bf8366df8492421df42294cdf2e6b 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -212,6 +212,11 @@ public class TreeScanner extends Visitor { 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 @@ public class TreeScanner extends Visitor { scan(tree.selected); } + public void visitReference(JCMemberReference tree) { + scan(tree.expr); + scan(tree.typeargs); + } + public void visitIdent(JCIdent tree) { } diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java index f024a287bd9f1ae90333d1e658f902e54c080be5..2bc9c7a1290592fe2155fc6b8e7fd82aae3618ad 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java @@ -282,6 +282,12 @@ public class TreeTranslator extends JCTree.Visitor { 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 @@ public class TreeTranslator extends JCTree.Visitor { result = tree; } + public void visitReference(JCMemberReference tree) { + tree.expr = translate(tree.expr); + result = tree; + } + public void visitIdent(JCIdent tree) { result = tree; } diff --git a/test/tools/javac/diags/examples/CatchWithoutTry.java b/test/tools/javac/diags/examples/CatchWithoutTry.java index 2fa707006369795b4c9fd5141566573080fe43eb..51eebedb014d63163ca2c50b23b58789fa6960cf 100644 --- a/test/tools/javac/diags/examples/CatchWithoutTry.java +++ b/test/tools/javac/diags/examples/CatchWithoutTry.java @@ -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() { diff --git a/test/tools/javac/diags/examples/IllegalChar.java b/test/tools/javac/diags/examples/IllegalChar.java index 003a7b0ece15cbab9b9aabb9dc0652f53eb43ad6..28bf82700e02d9b09ce9f692896b1b46b3906047 100644 --- a/test/tools/javac/diags/examples/IllegalChar.java +++ b/test/tools/javac/diags/examples/IllegalChar.java @@ -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 = `; } diff --git a/test/tools/javac/diags/examples/LambdaNotSupported.java b/test/tools/javac/diags/examples/LambdaNotSupported.java new file mode 100644 index 0000000000000000000000000000000000000000..ea57fe4740d2020fe1364284f2cab620092339e7 --- /dev/null +++ b/test/tools/javac/diags/examples/LambdaNotSupported.java @@ -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 = ()->{}; +} diff --git a/test/tools/javac/diags/examples/MethodReferencesNotSupported.java b/test/tools/javac/diags/examples/MethodReferencesNotSupported.java new file mode 100644 index 0000000000000000000000000000000000000000..df3198008b03f4b7cd21016f944f212a801a55f0 --- /dev/null +++ b/test/tools/javac/diags/examples/MethodReferencesNotSupported.java @@ -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; +} diff --git a/test/tools/javac/diags/examples/NotAStatement.java b/test/tools/javac/diags/examples/NotAStatement.java new file mode 100644 index 0000000000000000000000000000000000000000..39af85a83350c11ae8006206852a46e47630a4b5 --- /dev/null +++ b/test/tools/javac/diags/examples/NotAStatement.java @@ -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; + } +} diff --git a/test/tools/javac/generics/rare/6665356/T6665356.out b/test/tools/javac/generics/rare/6665356/T6665356.out index 62a62e639256f5836fbc6c8946f55461da3f8dea..4994352df4bb632f8e293bf5933b3997dbefe8f8 100644 --- a/test/tools/javac/generics/rare/6665356/T6665356.out +++ b/test/tools/javac/generics/rare/6665356/T6665356.out @@ -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 diff --git a/test/tools/javac/lambda/LambdaParserTest.java b/test/tools/javac/lambda/LambdaParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b686013aa5b05af06afd5df1f73fc16d72759659 --- /dev/null +++ b/test/tools/javac/lambda/LambdaParserTest.java @@ -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"), + EXPLICIT_GENERIC3("A"); + + 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 { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +} diff --git a/test/tools/javac/lambda/MethodReferenceParserTest.java b/test/tools/javac/lambda/MethodReferenceParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0455820063b865f9a216ffd508b57cd49c53d108 --- /dev/null +++ b/test/tools/javac/lambda/MethodReferenceParserTest.java @@ -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(""), + TWO(""); + + 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"), + UNBOUND_GENERIC2("A"), + UNBOUND_GENERIC3("A"); + + 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 { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +} diff --git a/test/tools/javac/quid/T6999438.out b/test/tools/javac/quid/T6999438.out index cd6266895d196d2b412862cdcce1aef4aeab6fce..04fdcfae97ff6a5ef12f0bfdf4f085615dd71f4c 100644 --- a/test/tools/javac/quid/T6999438.out +++ b/test/tools/javac/quid/T6999438.out @@ -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: ';'