1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8139829 27 * @summary Test access to members of user defined class. 28 * @build KullaTesting TestingInputStream ExpectedDiagnostic 29 * @run testng/timeout=600 ClassMembersTest 30 */ 31 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 import javax.tools.Diagnostic; 37 38 import jdk.jshell.SourceCodeAnalysis; 39 import org.testng.annotations.DataProvider; 40 import org.testng.annotations.Test; 41 import jdk.jshell.TypeDeclSnippet; 42 import static jdk.jshell.Snippet.Status.OVERWRITTEN; 43 import static jdk.jshell.Snippet.Status.VALID; 44 45 public class ClassMembersTest extends KullaTesting { 46 47 @Test(dataProvider = "memberTestCase") memberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference)48 public void memberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 49 MemberTestCase testCase = new MemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference); 50 assertEval(testCase.generateSource()); 51 String expectedMessage = testCase.expectedMessage; 52 if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) { 53 assertEval("A a = new A();"); 54 } 55 if (expectedMessage == null) { 56 assertEval(testCase.useCodeChunk()); 57 } else { 58 assertDeclareFail(testCase.useCodeChunk(), expectedMessage); 59 } 60 } 61 parseCode(String input)62 private List<String> parseCode(String input) { 63 List<String> list = new ArrayList<>(); 64 SourceCodeAnalysis codeAnalysis = getAnalysis(); 65 String source = input; 66 while (!source.trim().isEmpty()) { 67 SourceCodeAnalysis.CompletionInfo info = codeAnalysis.analyzeCompletion(source); 68 list.add(info.source()); 69 source = info.remaining(); 70 } 71 return list; 72 } 73 74 @Test(dataProvider = "memberTestCase") extendsMemberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference)75 public void extendsMemberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 76 MemberTestCase testCase = new ExtendsMemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference); 77 String input = testCase.generateSource(); 78 List<String> ss = parseCode(input); 79 assertEval(ss.get(0)); 80 if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) { 81 assertEval(ss.get(1)); 82 assertEval("B b = new B();"); 83 } 84 String expectedMessage = testCase.expectedMessage; 85 if (expectedMessage == null) { 86 assertEval(testCase.useCodeChunk()); 87 } else { 88 assertDeclareFail(testCase.useCodeChunk(), expectedMessage); 89 } 90 } 91 92 @Test interfaceTest()93 public void interfaceTest() { 94 String interfaceSource = 95 "interface A {\n" + 96 " default int defaultMethod() { return 1; }\n" + 97 " static int staticMethod() { return 2; }\n" + 98 " int method();\n" + 99 " class Inner1 {}\n" + 100 " static class Inner2 {}\n" + 101 "}"; 102 assertEval(interfaceSource); 103 assertEval("A.staticMethod();", "2"); 104 String classSource = 105 "class B implements A {\n" + 106 " public int method() { return 3; }\n" + 107 "}"; 108 assertEval(classSource); 109 assertEval("B b = new B();"); 110 assertEval("b.defaultMethod();", "1"); 111 assertDeclareFail("B.staticMethod();", 112 new ExpectedDiagnostic("compiler.err.cant.resolve.location.args", 0, 14, 1, -1, -1, Diagnostic.Kind.ERROR)); 113 assertEval("b.method();", "3"); 114 assertEval("new A.Inner1();"); 115 assertEval("new A.Inner2();"); 116 assertEval("new B.Inner1();"); 117 assertEval("new B.Inner2();"); 118 } 119 120 @Test enumTest()121 public void enumTest() { 122 String enumSource = 123 "enum E {A(\"s\");\n" + 124 " private final String s;\n" + 125 " private E(String s) { this.s = s; }\n" + 126 " public String method() { return s; }\n" + 127 " private String privateMethod() { return s; }\n" + 128 " public static String staticMethod() { return staticPrivateMethod(); }\n" + 129 " private static String staticPrivateMethod() { return \"a\"; }\n" + 130 "}"; 131 assertEval(enumSource); 132 assertEval("E a = E.A;", "A"); 133 assertDeclareFail("a.s;", 134 new ExpectedDiagnostic("compiler.err.report.access", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR)); 135 assertDeclareFail("new E(\"q\");", 136 new ExpectedDiagnostic("compiler.err.enum.cant.be.instantiated", 0, 10, 0, -1, -1, Diagnostic.Kind.ERROR)); 137 assertEval("a.method();", "\"s\""); 138 assertDeclareFail("a.privateMethod();", 139 new ExpectedDiagnostic("compiler.err.report.access", 0, 15, 1, -1, -1, Diagnostic.Kind.ERROR)); 140 assertEval("E.staticMethod();", "\"a\""); 141 assertDeclareFail("a.staticPrivateMethod();", 142 new ExpectedDiagnostic("compiler.err.report.access", 0, 21, 1, -1, -1, Diagnostic.Kind.ERROR)); 143 assertDeclareFail("E.method();", 144 new ExpectedDiagnostic("compiler.err.non-static.cant.be.ref", 0, 8, 1, -1, -1, Diagnostic.Kind.ERROR)); 145 } 146 147 @Test(dataProvider = "retentionPolicyTestCase") annotationTest(RetentionPolicy policy)148 public void annotationTest(RetentionPolicy policy) { 149 assertEval("import java.lang.annotation.*;"); 150 String annotationSource = 151 "@Retention(RetentionPolicy." + policy.toString() + ")\n" + 152 "@interface A {}"; 153 assertEval(annotationSource); 154 String classSource = 155 "@A class C {\n" + 156 " @A C() {}\n" + 157 " @A void f() {}\n" + 158 " @A int f;\n" + 159 " @A class Inner {}\n" + 160 "}"; 161 assertEval(classSource); 162 String isRuntimeVisible = policy == RetentionPolicy.RUNTIME ? "true" : "false"; 163 assertEval("C.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 164 assertEval("C.class.getDeclaredConstructor().getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 165 assertEval("C.class.getDeclaredMethod(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 166 assertEval("C.class.getDeclaredField(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 167 assertEval("C.Inner.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible); 168 } 169 170 @DataProvider(name = "retentionPolicyTestCase") retentionPolicyTestCaseGenerator()171 public Object[][] retentionPolicyTestCaseGenerator() { 172 List<Object[]> list = new ArrayList<>(); 173 for (RetentionPolicy policy : RetentionPolicy.values()) { 174 list.add(new Object[]{policy}); 175 } 176 return list.toArray(new Object[list.size()][]); 177 } 178 179 @DataProvider(name = "memberTestCase") memberTestCaseGenerator()180 public Object[][] memberTestCaseGenerator() { 181 List<Object[]> list = new ArrayList<>(); 182 for (AccessModifier accessModifier : AccessModifier.values()) { 183 for (Static isStaticMember : Static.values()) { 184 for (Static isStaticReference : Static.values()) { 185 for (CodeChunk codeChunk : CodeChunk.values()) { 186 if (codeChunk == CodeChunk.CONSTRUCTOR && isStaticMember == Static.STATIC) { 187 continue; 188 } 189 list.add(new Object[]{ accessModifier, codeChunk, isStaticMember, isStaticReference }); 190 } 191 } 192 } 193 } 194 return list.toArray(new Object[list.size()][]); 195 } 196 197 public static class ExtendsMemberTestCase extends MemberTestCase { 198 ExtendsMemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference)199 public ExtendsMemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) { 200 super(accessModifier, codeChunk, isStaticMember, isStaticReference); 201 } 202 203 @Override getSourceTemplate()204 public String getSourceTemplate() { 205 return super.getSourceTemplate() + "\n" 206 + "class B extends A {}"; 207 } 208 209 @Override errorMessage()210 public String errorMessage() { 211 if (!isAccessible()) { 212 if (codeChunk == CodeChunk.METHOD) { 213 return "compiler.err.cant.resolve.location.args"; 214 } 215 if (codeChunk == CodeChunk.CONSTRUCTOR) { 216 return "compiler.err.cant.resolve.location"; 217 } 218 } 219 return super.errorMessage(); 220 } 221 222 @Override useCodeChunk()223 public String useCodeChunk() { 224 return useCodeChunk("B"); 225 } 226 } 227 228 public static class MemberTestCase { 229 public final AccessModifier accessModifier; 230 public final CodeChunk codeChunk; 231 public final Static isStaticMember; 232 public final Static isStaticReference; 233 public final String expectedMessage; 234 MemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference)235 public MemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, 236 Static isStaticReference) { 237 this.accessModifier = accessModifier; 238 this.codeChunk = codeChunk; 239 this.isStaticMember = isStaticMember; 240 this.isStaticReference = isStaticReference; 241 this.expectedMessage = errorMessage(); 242 } 243 getSourceTemplate()244 public String getSourceTemplate() { 245 return "class A {\n" + 246 " #MEMBER#\n" + 247 "}"; 248 } 249 isAccessible()250 public boolean isAccessible() { 251 return accessModifier != AccessModifier.PRIVATE; 252 } 253 errorMessage()254 public String errorMessage() { 255 if (!isAccessible()) { 256 return "compiler.err.report.access"; 257 } 258 if (codeChunk == CodeChunk.INNER_INTERFACE) { 259 return "compiler.err.abstract.cant.be.instantiated"; 260 } 261 if (isStaticMember == Static.STATIC) { 262 if (isStaticReference == Static.NO && codeChunk == CodeChunk.INNER_CLASS) { 263 return "compiler.err.qualified.new.of.static.class"; 264 } 265 return null; 266 } 267 if (isStaticReference == Static.STATIC) { 268 if (codeChunk == CodeChunk.CONSTRUCTOR) { 269 return null; 270 } 271 if (codeChunk == CodeChunk.INNER_CLASS) { 272 return "compiler.err.encl.class.required"; 273 } 274 return "compiler.err.non-static.cant.be.ref"; 275 } 276 return null; 277 } 278 generateSource()279 public String generateSource() { 280 return getSourceTemplate().replace("#MEMBER#", codeChunk.generateSource(accessModifier, isStaticMember)); 281 } 282 useCodeChunk(String className)283 protected String useCodeChunk(String className) { 284 String name = className.toLowerCase(); 285 switch (codeChunk) { 286 case CONSTRUCTOR: 287 return String.format("new %s();", className); 288 case METHOD: 289 if (isStaticReference == Static.STATIC) { 290 return String.format("%s.method();", className); 291 } else { 292 return String.format("%s.method();", name); 293 } 294 case FIELD: 295 if (isStaticReference == Static.STATIC) { 296 return String.format("%s.field;", className); 297 } else { 298 return String.format("%s.field;", name); 299 } 300 case INNER_CLASS: 301 if (isStaticReference == Static.STATIC) { 302 return String.format("new %s.Inner();", className); 303 } else { 304 return String.format("%s.new Inner();", name); 305 } 306 case INNER_INTERFACE: 307 return String.format("new %s.Inner();", className); 308 default: 309 throw new AssertionError("Unknown code chunk: " + this); 310 } 311 } 312 useCodeChunk()313 public String useCodeChunk() { 314 return useCodeChunk("A"); 315 } 316 } 317 318 public enum AccessModifier { 319 PUBLIC("public"), 320 PROTECTED("protected"), 321 PACKAGE_PRIVATE(""), 322 PRIVATE("private"); 323 324 private final String modifier; 325 AccessModifier(String modifier)326 AccessModifier(String modifier) { 327 this.modifier = modifier; 328 } 329 getModifier()330 public String getModifier() { 331 return modifier; 332 } 333 } 334 335 public enum Static { 336 STATIC("static"), NO(""); 337 338 private final String modifier; 339 Static(String modifier)340 Static(String modifier) { 341 this.modifier = modifier; 342 } 343 getModifier()344 public String getModifier() { 345 return modifier; 346 } 347 } 348 349 public enum CodeChunk { 350 CONSTRUCTOR("#MODIFIER# A() {}"), 351 METHOD("#MODIFIER# int method() { return 10; }"), 352 FIELD("#MODIFIER# int field = 10;"), 353 INNER_CLASS("#MODIFIER# class Inner {}"), 354 INNER_INTERFACE("#MODIFIER# interface Inner {}"); 355 356 private final String code; 357 CodeChunk(String code)358 CodeChunk(String code) { 359 this.code = code; 360 } 361 generateSource(AccessModifier accessModifier, Static isStatic)362 public String generateSource(AccessModifier accessModifier, Static isStatic) { 363 return code.replace("#MODIFIER#", accessModifier.getModifier() + " " + isStatic.getModifier()); 364 } 365 } 366 } 367