test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java

Tue, 14 May 2013 11:11:09 -0700

author
rfield
date
Tue, 14 May 2013 11:11:09 -0700
changeset 1752
c09b7234cded
parent 1678
c635a966ce84
child 2047
5f915a0c9615
permissions
-rw-r--r--

8012556: Implement lambda methods on interfaces as static
8006140: Javac NPE compiling Lambda expression on initialization expression of static field in interface
Summary: Lambdas occurring in static contexts or those not needing instance information should be generated into static methods. This has long been the case for classes. However, as a work-around to the lack of support for statics on interfaces, interface lambda methods have been generated into default methods. For lambdas in interface static contexts (fields and static methods) this causes an NPE in javac because there is no 'this'. MethodHandles now support static methods on interfaces. This changeset allows lambda methods to be generated as static interface methods. An existing bug in Hotspot (8013875) is exposed in a test when the "-esa" flag is used. This test and another test that already exposed this bug have been marked with @ignore.
Reviewed-by: mcimadamore

mcimadamore@1436 1 /*
mcimadamore@1436 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
mcimadamore@1436 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
mcimadamore@1436 4 *
mcimadamore@1436 5 * This code is free software; you can redistribute it and/or modify it
mcimadamore@1436 6 * under the terms of the GNU General Public License version 2 only, as
mcimadamore@1436 7 * published by the Free Software Foundation.
mcimadamore@1436 8 *
mcimadamore@1436 9 * This code is distributed in the hope that it will be useful, but WITHOUT
mcimadamore@1436 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
mcimadamore@1436 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
mcimadamore@1436 12 * version 2 for more details (a copy is included in the LICENSE file that
mcimadamore@1436 13 * accompanied this code).
mcimadamore@1436 14 *
mcimadamore@1436 15 * You should have received a copy of the GNU General Public License version
mcimadamore@1436 16 * 2 along with this work; if not, write to the Free Software Foundation,
mcimadamore@1436 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
mcimadamore@1436 18 *
mcimadamore@1436 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
mcimadamore@1436 20 * or visit www.oracle.com if you need additional information or have any
mcimadamore@1436 21 * questions.
mcimadamore@1436 22 */
mcimadamore@1436 23
mcimadamore@1436 24 /*
mcimadamore@1436 25 * @test
mcimadamore@1436 26 * @bug 8002099
mcimadamore@1436 27 * @summary Add support for intersection types in cast expression
mcimadamore@1436 28 */
mcimadamore@1436 29
mcimadamore@1436 30 import com.sun.source.util.JavacTask;
mcimadamore@1436 31 import com.sun.tools.javac.util.ListBuffer;
mcimadamore@1436 32 import java.net.URI;
mcimadamore@1678 33 import java.util.ArrayList;
mcimadamore@1436 34 import java.util.Arrays;
mcimadamore@1678 35 import java.util.List;
mcimadamore@1436 36 import javax.tools.Diagnostic;
mcimadamore@1436 37 import javax.tools.JavaCompiler;
mcimadamore@1436 38 import javax.tools.JavaFileObject;
mcimadamore@1436 39 import javax.tools.SimpleJavaFileObject;
mcimadamore@1436 40 import javax.tools.StandardJavaFileManager;
mcimadamore@1436 41 import javax.tools.ToolProvider;
mcimadamore@1436 42
mcimadamore@1436 43 public class IntersectionTargetTypeTest {
mcimadamore@1436 44
mcimadamore@1436 45 static int checkCount = 0;
mcimadamore@1436 46
mcimadamore@1436 47 enum BoundKind {
mcimadamore@1436 48 INTF,
mcimadamore@1678 49 CLASS;
mcimadamore@1436 50 }
mcimadamore@1436 51
mcimadamore@1436 52 enum MethodKind {
mcimadamore@1678 53 NONE(false),
mcimadamore@1678 54 ABSTRACT_M(true),
mcimadamore@1678 55 DEFAULT_M(false),
mcimadamore@1678 56 ABSTRACT_G(true),
mcimadamore@1678 57 DEFAULT_G(false);
mcimadamore@1678 58
mcimadamore@1678 59 boolean isAbstract;
mcimadamore@1678 60
mcimadamore@1678 61 MethodKind(boolean isAbstract) {
mcimadamore@1678 62 this.isAbstract = isAbstract;
mcimadamore@1678 63 }
mcimadamore@1436 64 }
mcimadamore@1436 65
mcimadamore@1436 66 enum TypeKind {
mcimadamore@1678 67 A("interface A { }\n", "A", BoundKind.INTF, MethodKind.NONE),
mcimadamore@1678 68 B("interface B { default void m() { } }\n", "B", BoundKind.INTF, MethodKind.DEFAULT_M),
mcimadamore@1678 69 C("interface C { void m(); }\n", "C", BoundKind.INTF, MethodKind.ABSTRACT_M),
mcimadamore@1678 70 D("interface D extends B { }\n", "D", BoundKind.INTF, MethodKind.DEFAULT_M),
mcimadamore@1678 71 E("interface E extends C { }\n", "E", BoundKind.INTF, MethodKind.ABSTRACT_M),
mcimadamore@1678 72 F("interface F extends C { void g(); }\n", "F", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.ABSTRACT_M),
mcimadamore@1678 73 G("interface G extends B { void g(); }\n", "G", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.DEFAULT_M),
mcimadamore@1678 74 H("interface H extends A { void g(); }\n", "H", BoundKind.INTF, MethodKind.ABSTRACT_G),
mcimadamore@1436 75 OBJECT("", "Object", BoundKind.CLASS),
mcimadamore@1436 76 STRING("", "String", BoundKind.CLASS);
mcimadamore@1436 77
mcimadamore@1436 78 String declStr;
mcimadamore@1436 79 String typeStr;
mcimadamore@1436 80 BoundKind boundKind;
mcimadamore@1678 81 MethodKind[] methodKinds;
mcimadamore@1436 82
mcimadamore@1678 83 private TypeKind(String declStr, String typeStr, BoundKind boundKind, MethodKind... methodKinds) {
mcimadamore@1436 84 this.declStr = declStr;
mcimadamore@1436 85 this.typeStr = typeStr;
mcimadamore@1436 86 this.boundKind = boundKind;
mcimadamore@1678 87 this.methodKinds = methodKinds;
mcimadamore@1436 88 }
mcimadamore@1436 89
mcimadamore@1436 90 boolean compatibleSupertype(TypeKind tk) {
mcimadamore@1436 91 if (tk == this) return true;
mcimadamore@1436 92 switch (tk) {
mcimadamore@1436 93 case B:
mcimadamore@1436 94 return this != C && this != E && this != F;
mcimadamore@1436 95 case C:
mcimadamore@1436 96 return this != B && this != C && this != D && this != G;
mcimadamore@1436 97 case D: return compatibleSupertype(B);
mcimadamore@1436 98 case E:
mcimadamore@1436 99 case F: return compatibleSupertype(C);
mcimadamore@1436 100 case G: return compatibleSupertype(B);
mcimadamore@1436 101 case H: return compatibleSupertype(A);
mcimadamore@1436 102 default:
mcimadamore@1436 103 return true;
mcimadamore@1436 104 }
mcimadamore@1436 105 }
mcimadamore@1436 106 }
mcimadamore@1436 107
mcimadamore@1436 108 enum CastKind {
mcimadamore@1436 109 ONE_ARY("(#B0)", 1),
mcimadamore@1436 110 TWO_ARY("(#B0 & #B1)", 2),
mcimadamore@1436 111 THREE_ARY("(#B0 & #B1 & #B2)", 3);
mcimadamore@1436 112
mcimadamore@1436 113 String castTemplate;
mcimadamore@1436 114 int nbounds;
mcimadamore@1436 115
mcimadamore@1436 116 CastKind(String castTemplate, int nbounds) {
mcimadamore@1436 117 this.castTemplate = castTemplate;
mcimadamore@1436 118 this.nbounds = nbounds;
mcimadamore@1436 119 }
mcimadamore@1436 120 }
mcimadamore@1436 121
mcimadamore@1436 122 enum ExpressionKind {
mcimadamore@1436 123 LAMBDA("()->{}", true),
mcimadamore@1436 124 MREF("this::m", true),
mcimadamore@1436 125 //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
mcimadamore@1436 126 //COND_MREF("(true ? this::m : this::m)", true),
mcimadamore@1436 127 STANDALONE("null", false);
mcimadamore@1436 128
mcimadamore@1436 129 String exprString;
mcimadamore@1436 130 boolean isFunctional;
mcimadamore@1436 131
mcimadamore@1436 132 private ExpressionKind(String exprString, boolean isFunctional) {
mcimadamore@1436 133 this.exprString = exprString;
mcimadamore@1436 134 this.isFunctional = isFunctional;
mcimadamore@1436 135 }
mcimadamore@1436 136 }
mcimadamore@1436 137
mcimadamore@1436 138 static class CastInfo {
mcimadamore@1436 139 CastKind kind;
mcimadamore@1436 140 TypeKind[] types;
mcimadamore@1436 141
mcimadamore@1436 142 CastInfo(CastKind kind, TypeKind... types) {
mcimadamore@1436 143 this.kind = kind;
mcimadamore@1436 144 this.types = types;
mcimadamore@1436 145 }
mcimadamore@1436 146
mcimadamore@1436 147 String getCast() {
mcimadamore@1436 148 String temp = kind.castTemplate;
mcimadamore@1436 149 for (int i = 0; i < kind.nbounds ; i++) {
mcimadamore@1436 150 temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
mcimadamore@1436 151 }
mcimadamore@1436 152 return temp;
mcimadamore@1436 153 }
mcimadamore@1436 154
mcimadamore@1436 155 boolean wellFormed() {
mcimadamore@1436 156 //check for duplicate types
mcimadamore@1436 157 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 158 for (int j = 0 ; j < types.length ; j++) {
mcimadamore@1436 159 if (i != j && types[i] == types[j]) {
mcimadamore@1436 160 return false;
mcimadamore@1436 161 }
mcimadamore@1436 162 }
mcimadamore@1436 163 }
mcimadamore@1436 164 //check that classes only appear as first bound
mcimadamore@1436 165 boolean classOk = true;
mcimadamore@1436 166 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 167 if (types[i].boundKind == BoundKind.CLASS &&
mcimadamore@1436 168 !classOk) {
mcimadamore@1436 169 return false;
mcimadamore@1436 170 }
mcimadamore@1436 171 classOk = false;
mcimadamore@1436 172 }
mcimadamore@1436 173 //check that supertypes are mutually compatible
mcimadamore@1436 174 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 175 for (int j = 0 ; j < types.length ; j++) {
mcimadamore@1436 176 if (!types[i].compatibleSupertype(types[j]) && i != j) {
mcimadamore@1436 177 return false;
mcimadamore@1436 178 }
mcimadamore@1436 179 }
mcimadamore@1436 180 }
mcimadamore@1436 181 return true;
mcimadamore@1436 182 }
mcimadamore@1436 183 }
mcimadamore@1436 184
mcimadamore@1436 185 public static void main(String... args) throws Exception {
mcimadamore@1436 186 //create default shared JavaCompiler - reused across multiple compilations
mcimadamore@1436 187 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
mcimadamore@1436 188 StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
mcimadamore@1436 189
mcimadamore@1436 190 for (CastInfo cInfo : allCastInfo()) {
mcimadamore@1436 191 for (ExpressionKind ek : ExpressionKind.values()) {
mcimadamore@1436 192 new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm);
mcimadamore@1436 193 }
mcimadamore@1436 194 }
mcimadamore@1436 195 System.out.println("Total check executed: " + checkCount);
mcimadamore@1436 196 }
mcimadamore@1436 197
mcimadamore@1436 198 static List<CastInfo> allCastInfo() {
mcimadamore@1436 199 ListBuffer<CastInfo> buf = ListBuffer.lb();
mcimadamore@1436 200 for (CastKind kind : CastKind.values()) {
mcimadamore@1436 201 for (TypeKind b1 : TypeKind.values()) {
mcimadamore@1436 202 if (kind.nbounds == 1) {
mcimadamore@1436 203 buf.append(new CastInfo(kind, b1));
mcimadamore@1436 204 continue;
mcimadamore@1436 205 } else {
mcimadamore@1436 206 for (TypeKind b2 : TypeKind.values()) {
mcimadamore@1436 207 if (kind.nbounds == 2) {
mcimadamore@1436 208 buf.append(new CastInfo(kind, b1, b2));
mcimadamore@1436 209 continue;
mcimadamore@1436 210 } else {
mcimadamore@1436 211 for (TypeKind b3 : TypeKind.values()) {
mcimadamore@1436 212 buf.append(new CastInfo(kind, b1, b2, b3));
mcimadamore@1436 213 }
mcimadamore@1436 214 }
mcimadamore@1436 215 }
mcimadamore@1436 216 }
mcimadamore@1436 217 }
mcimadamore@1436 218 }
mcimadamore@1436 219 return buf.toList();
mcimadamore@1436 220 }
mcimadamore@1436 221
mcimadamore@1436 222 CastInfo cInfo;
mcimadamore@1436 223 ExpressionKind ek;
mcimadamore@1436 224 JavaSource source;
mcimadamore@1436 225 DiagnosticChecker diagChecker;
mcimadamore@1436 226
mcimadamore@1436 227 IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
mcimadamore@1436 228 this.cInfo = cInfo;
mcimadamore@1436 229 this.ek = ek;
mcimadamore@1436 230 this.source = new JavaSource();
mcimadamore@1436 231 this.diagChecker = new DiagnosticChecker();
mcimadamore@1436 232 }
mcimadamore@1436 233
mcimadamore@1436 234 class JavaSource extends SimpleJavaFileObject {
mcimadamore@1436 235
mcimadamore@1436 236 String bodyTemplate = "class Test {\n" +
mcimadamore@1436 237 " void m() { }\n" +
mcimadamore@1436 238 " void test() {\n" +
mcimadamore@1436 239 " Object o = #C#E;\n" +
mcimadamore@1436 240 " } }";
mcimadamore@1436 241
mcimadamore@1436 242 String source = "";
mcimadamore@1436 243
mcimadamore@1436 244 public JavaSource() {
mcimadamore@1436 245 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
mcimadamore@1436 246 for (TypeKind tk : TypeKind.values()) {
mcimadamore@1436 247 source += tk.declStr;
mcimadamore@1436 248 }
mcimadamore@1436 249 source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
mcimadamore@1436 250 }
mcimadamore@1436 251
mcimadamore@1436 252 @Override
mcimadamore@1436 253 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
mcimadamore@1436 254 return source;
mcimadamore@1436 255 }
mcimadamore@1436 256 }
mcimadamore@1436 257
mcimadamore@1436 258 void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
mcimadamore@1436 259 JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
mcimadamore@1511 260 null, null, Arrays.asList(source));
mcimadamore@1436 261 try {
mcimadamore@1436 262 ct.analyze();
mcimadamore@1436 263 } catch (Throwable ex) {
mcimadamore@1436 264 throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
mcimadamore@1436 265 }
mcimadamore@1436 266 check();
mcimadamore@1436 267 }
mcimadamore@1436 268
mcimadamore@1436 269 void check() {
mcimadamore@1436 270 checkCount++;
mcimadamore@1436 271
mcimadamore@1436 272 boolean errorExpected = !cInfo.wellFormed();
mcimadamore@1436 273
mcimadamore@1436 274 if (ek.isFunctional) {
mcimadamore@1678 275 List<MethodKind> mks = new ArrayList<>();
mcimadamore@1678 276 for (TypeKind tk : cInfo.types) {
mcimadamore@1678 277 if (tk.boundKind == BoundKind.CLASS) {
mcimadamore@1678 278 errorExpected = true;
mcimadamore@1678 279 break;
mcimadamore@1678 280 } else {
mcimadamore@1678 281 mks = mergeMethods(mks, Arrays.asList(tk.methodKinds));
mcimadamore@1436 282 }
mcimadamore@1436 283 }
mcimadamore@1678 284 int abstractCount = 0;
mcimadamore@1678 285 for (MethodKind mk : mks) {
mcimadamore@1678 286 if (mk.isAbstract) {
mcimadamore@1678 287 abstractCount++;
mcimadamore@1678 288 }
mcimadamore@1678 289 }
mcimadamore@1678 290 errorExpected |= abstractCount != 1;
mcimadamore@1436 291 }
mcimadamore@1436 292
mcimadamore@1436 293 if (errorExpected != diagChecker.errorFound) {
mcimadamore@1436 294 throw new Error("invalid diagnostics for source:\n" +
mcimadamore@1436 295 source.getCharContent(true) +
mcimadamore@1436 296 "\nFound error: " + diagChecker.errorFound +
mcimadamore@1436 297 "\nExpected error: " + errorExpected);
mcimadamore@1436 298 }
mcimadamore@1436 299 }
mcimadamore@1436 300
mcimadamore@1678 301 List<MethodKind> mergeMethods(List<MethodKind> l1, List<MethodKind> l2) {
mcimadamore@1678 302 List<MethodKind> mergedMethods = new ArrayList<>(l1);
mcimadamore@1678 303 for (MethodKind mk2 : l2) {
mcimadamore@1678 304 boolean add = !mergedMethods.contains(mk2);
mcimadamore@1678 305 switch (mk2) {
mcimadamore@1678 306 case ABSTRACT_G:
mcimadamore@1678 307 add = add && !mergedMethods.contains(MethodKind.DEFAULT_G);
mcimadamore@1678 308 break;
mcimadamore@1678 309 case ABSTRACT_M:
mcimadamore@1678 310 add = add && !mergedMethods.contains(MethodKind.DEFAULT_M);
mcimadamore@1678 311 break;
mcimadamore@1678 312 case DEFAULT_G:
mcimadamore@1678 313 mergedMethods.remove(MethodKind.ABSTRACT_G);
mcimadamore@1678 314 case DEFAULT_M:
mcimadamore@1678 315 mergedMethods.remove(MethodKind.ABSTRACT_M);
mcimadamore@1678 316 case NONE:
mcimadamore@1678 317 add = false;
mcimadamore@1678 318 break;
mcimadamore@1678 319 }
mcimadamore@1678 320 if (add) {
mcimadamore@1678 321 mergedMethods.add(mk2);
mcimadamore@1678 322 }
mcimadamore@1678 323 }
mcimadamore@1678 324 return mergedMethods;
mcimadamore@1678 325 }
mcimadamore@1678 326
mcimadamore@1436 327 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
mcimadamore@1436 328
mcimadamore@1436 329 boolean errorFound;
mcimadamore@1436 330
mcimadamore@1436 331 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
mcimadamore@1436 332 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
mcimadamore@1436 333 errorFound = true;
mcimadamore@1436 334 }
mcimadamore@1436 335 }
mcimadamore@1436 336 }
mcimadamore@1436 337 }

mercurial